-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Modifying properties with Lenses #140
Comments
Related to #59 if I am reading this correctly. |
So I was experimenting with a bunch of Typescript 4 features (including 4.1), and came up with the following proof of concept (that incidentally also goes for the same idea as in #136 ): import * as fpTs from 'fp-ts';
const { pipe } = fpTs;
type Foo = {
foo: {
bar: {
baz: number;
};
};
};
type GetType<S, P extends unknown[]> = P extends []
? S
: P extends [infer Head, ...infer Tail]
? Head extends keyof S
? GetType<S[Head], Tail>
: never
: never;
type SetType<S, P extends unknown[], N = unknown> = P extends []
? N
: P extends [infer Head, ...infer Tail]
? {
[K in keyof S]: Tail extends [] ? N : K extends Head ? SetType<S[K], Tail, N> : S[K];
}
: never;
interface Lens<S, P extends unknown[]> {
get: (s: S) => GetType<S, P>;
set: <A>(a: A) => (s: S) => SetType<S, P, A>;
}
const id: <S>() => Lens<S, []> = () => ({
get: (s) => s,
set: a => s => a
})
const prop = <S, P extends unknown[], Prop extends keyof GetType<S, P>>(prop: Prop) => (lens: Lens<S, P>): Lens<S, [...P, Prop]> => ({
get: s => lens.get(s)[prop] as GetType<S, [...P, Prop]>,
set: <A>(ap: A) => s => {
const oa = lens.get(s);
if (ap === oa[prop]) return s as SetType<S, [...P, Prop], A>;
return lens.set({ ...oa, [prop]: ap })(s) as SetType<S, [...P, Prop], A>;
}
});
const set = <A>(a: A) => <S, P extends unknown[]>(lens: Lens<S, P>) => (s: S): SetType<S, P, A> => {
const o = lens.get(s);
return lens.set(o)(s) as SetType<S, P, A>;
};
const a = pipe(
id<Foo>(),
prop('foo'),
prop('bar'),
prop('baz'),
set('abc')
);
const b = a({ foo: { bar: { baz: 123 }}}); // { foo: { bar: { baz: string } } } As you can see there are a few casts going on, which I haven't figured out how to avoid yet. |
So this is a request for "polymorphic" optics. I asked something similar on SO with a functional programming tag (and Scala tag) and got the name, and then realized that the Scala Monocle lib has Lens/Prism/etc. as a special case (using a type alias) of the polymorphic PLens/PPrism/etc. For example, I started porting the polymorphic versions over to TypeScript from Scala (primarily because a project I'm working on really could benefit from a polymorphic traversal (in the UI needing to traverse my I figured if I made headway I'd offer a PR. Otherwise I'd just keep using it on my own projects. It would mean all the existing code for a non-polymorphic could be deleted and replaced with just a typealias, and I'm a bit uncomfortable with parachuting in with my code and wiping out real work people have done here. I wonder if there was a reason the full polymorphic versions weren't ported over to this project, or if it was because it just wasn't important at the time things started. |
🚀 Feature request
Current Behavior
Current
modify
andset
functions in Prism, Optional, or Traverse only allow modifications to properties of the same type.Desired Behavior
Methods to return new structures with changed values. For example:
If I understand correctly, I believe there's a way to use Iso and/or the
imap
function to be able to switch loss-less between two types, but expanding the interface to have different output types might be worth the effort. For example, Haskell provides Setter: http://hackage.haskell.org/package/lens-4.19.2/docs/Control-Lens-Setter.html which as 4 type parameters.I am not proficient enough to come up with a proper solution in
monocle-ts
though.The text was updated successfully, but these errors were encountered: