Skip to content
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

Changing the type of the structure #59

Open
atennapel opened this issue Oct 1, 2018 · 7 comments
Open

Changing the type of the structure #59

atennapel opened this issue Oct 1, 2018 · 7 comments

Comments

@atennapel
Copy link

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?

@gcanti
Copy link
Owner

gcanti commented Oct 2, 2018

without losing type inference

What do you mean? Could you please show your implementation and an example reproducing the issue?

@atennapel
Copy link
Author

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.

@kylegoetz
Copy link

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.

@atennapel
Copy link
Author

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?

@kylegoetz
Copy link

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)

@atennapel
Copy link
Author

That's cool! But does it work with my Pair type above, a type with type parameters.

@anthonyjoeseph
Copy link

Just a note - I think PLens would be required to implement insertAt and updateAt, which are features under discussion

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants