gcanti / monocle-ts

Functional optics: a (partial) porting of Scala monocle
https://gcanti.github.io/monocle-ts/
MIT License
1.04k stars 52 forks source link

indicies tracking for Lens and Optional Polymorphic optics #136

Closed waynevanson closed 2 years ago

waynevanson commented 4 years ago

šŸš€ Feature request

I feel this is a good time to discuss this implementation because of the introduction of variadic tuples and the experimental lenses API.

If these types can exist at the runtime level and type together, we could decompose lenses.

Current Behavior

We cannot get the name of the property or index using a lens.

Desired Behavior

import { pipe } from "fp-ts/lib/function";

/** readonly tuple alias to save some keystrokes. */
type ROA = readonly any[];

interface Get<S, A> {
  (s: S): A;
}

interface Set<S, A> {
  (a: A): (s: S) => S;
}

interface Lens<S, A, P extends ROA> {
  _tag: "Lens";
  indicies: P;
  get: Get<S, A>;
  set: Set<S, A>;
}

// FROM MONOCLE-TS

declare function id<S>(): Lens<S, S, []>;

declare function fromProp<S, A, K extends keyof A>(
  k: K
): <P extends ROA>(lens: Lens<S, A, P>) => Lens<S, A[K], [...P, K]>;

// EXAMPLE

interface Address {
  number: number;
  street: string;
  suburb: string;
  state: string;
}

interface User {
  name: string;
  address: Address;
}

const result = pipe(id<User>(), fromProp("address"), fromProp("street"));

type Result = Lens<User, string, ["address", "street"]>;

Suggested Solution

Here is how it would type out.

Who does this impact? Who is this for?

All users. Requires typescript ^4.x.x

Describe alternatives you've considered

Use composable types like Tail to create this feature for users not using typescript ^4.x.x

If you deem this feature unsuitable, I'll have to build a separate library for this feature.

Additional context

I'm unsure if this affects Prism or Iso.

Have you thought about this at all? Are there any great use cases I've missed?

gcanti commented 4 years ago

We cannot get the name of the property or index using a lens

@waynevanson not sure what's the use case for indicies, anyway looks like such a tracking property is meaningful only for a particular kind of lenses.

For example consider the following

import * as L from 'monocle-ts/lib/Lens'

interface S {
  readonly a: string
  readonly b: number
  readonly c: boolean
}

type A = [boolean, string]

export const lens: L.Lens<S, A> = {
  get: (s) => [s.c, s.a],
  set: (a) => (s) => ({ a: a[1], b: s.b, c: a[0] })
}
waynevanson commented 4 years ago

I'll try explore some use cases. I need more time to consider some better use cases, as my initial idea feels too complex.

The main idea is to infer the property type from a lens.

A note. Getting the On further thought, an indice would be optional if it's associated value returns an optional.

waynevanson commented 2 years ago

The use case is polymorphic optics.