Changing the type of the structure
In Haskell you can change the type of the structure:
type Lens s t a b = s -> (a, b -> t) (or whatever representation you choose)
In none of the typescript lens implementations I've seen this.
I tried myself but I couldn't implement it this way without losing type inference.
Have you ever thought about it?
without losing type inference
What do you mean? Could you please show your implementation and an example reproducing the issue?
My attempt at this:
interface Pair<A, B> { fst: A; snd: B }
const pair = <A, B>(fst: A, snd: B): Pair<A, B> => ({ fst, snd });
type Lens<S, T, A, B> = (val: S) => Pair<A, (val: B) => T>;
const _fst = <A, C>(s: Pair<A, C>) => pair(s.fst, <B>(b: B): Pair<B, C> => pair(b, s.snd));
const _snd = <A, C>(s: Pair<C, A>) => pair(s.snd, <B>(b: B): Pair<C, B> => pair(s.fst, b));
function view<S, T, A, B>(l: Lens<S, T, A, B>, val: S): A {
return l(val).fst;
}
function set<S, T, A, B>(l: Lens<S, T, A, B>, val: S, x: B): T {
return l(val).snd(x);
}
function over<S, T, A, B>(l: Lens<S, T, A, B>, val: S, f: (x: A) => B): T {
const r = l(val);
return r.snd(f(r.fst));
}
const p = pair(1, 'a');
const stringify = (x: number) => '' + x;
const first = view<Pair<number, string>, Pair<any, string>, number, any>(_fst, p);
const stringifyFstP = over<typeof p, Pair<string, string>, number, string>(_fst, p, stringify);
The last two lines won't work unless you explicitly give the generic type parameters.
These exist in the original Scala monocle library and I've partially ported them for my own projects. If there's interest, I could finish and file a PR. But it would obviate lots of code, since the current optics would be type aliases of the "full" ones.
So that means you know a way to implement this that would not force the user to write down the type arguments explicitly every time?
interface PLens<S,T,A,B> {
readonly get: (s:S) => A
readonly set: (b:B) => (s:S) => T
}
interface Foo{
x:number
}
interface Bar{
x:string
}
const l: PLens<Foo,Bar,number,string> = {
get: s => s.x,
set: b => s => ({ x: b }),
}
const _ = {
x: 5
}
const got = l.get(_) // TS infers this is type number
const sat = l.set('howdy')(_) // tS infers this is type Bar
console.log('should be 5:', got)
console.log('should be {x:"howdy"}:', sat)
That's cool! But does it work with my Pair type above, a type with type parameters.
Just a note - I think PLens would be required to implement insertAt and updateAt, which are features under discussion