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

van Laarhoven lens type (as in Haskell) #7

Open
infinity0 opened this issue Feb 18, 2017 · 9 comments
Open

van Laarhoven lens type (as in Haskell) #7

infinity0 opened this issue Feb 18, 2017 · 9 comments

Comments

@infinity0
Copy link

infinity0 commented Feb 18, 2017

Haskell's van Laarhoven lens type has the advantage of performing the getting and setting in one step, avoiding the need to traverse deep complex data structures twice. It looks like this:

type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t

This can in fact be expressed in ocaml like this:

type ('b, 'a, 't, 's) lens = {
  lens : 'fb 'ft. (('b -> 't) -> 'fb -> 'ft) -> ('a -> 'fb) -> 's -> 'ft
}

The record type is necessary because that is the only place ocaml allows for "internal" polymorphism, and we need an extra initial argument to explicitly supply the fmap function since modular implicits sadly are not yet in OCaml.

But in fact the latter addition can be considered an advantage over Haskell, because we could choose different fmaps for any single data type - e.g. to map either over the first or second element of a pair.

@infinity0
Copy link
Author

infinity0 commented Feb 18, 2017

Upon further exploration, it turns out the above OCaml type is not very easily composable. Haskell picks their definition since those can be composed with normal function composition. However due to the requirement for implicit fmaps it's unlikely we'll get this to work in OCaml, so we might as well do a simpler thing, namely this:

type ('b, 'a, 't, 's) lens = 's -> ('a * ('b -> 't))

This takes a parent value and returns a (child value, setter) pair, where the setter is a closure that is closed over the input parent - thereby giving us the "traverse-only-once" semantics. Then you can define:

let compose
  : type b a t s y x.
    (t, s, y, x) lens ->
    (b, a, t, s) lens ->
    (b, a, y, x) lens = fun lens0 lens1 old_x ->
  let old_s, set0 = lens0 old_x in
  let old_a, set1 = lens1 old_s in
  let set2 b = set0 (set1 b) in
  old_a, set2

let apply lens fmap mknewchild oldparent =
  let oldchild, set = lens oldparent in
  fmap set (mknewchild oldchild)

where apply gives us the Haskell-like usage again.

@pdonadeo
Copy link
Owner

What do you think @astrada?

Note: Alessandro is the author of Lens.

@astrada
Copy link

astrada commented Feb 19, 2017

Nice! Feel free to add them in a new module (to keep both style of lenses).

@pdonadeo
Copy link
Owner

@infinity0 do you think you can send a pull request?

@infinity0
Copy link
Author

Glad you guys like the idea! Sure, I'll start writing up something that can be used.

@infinity0
Copy link
Author

I made a start over at https://gist.github.com/infinity0/3c8461ed60d806bb4d6616a03939788a in case anyone has opinions on the final structure. In the meantime I'll keep working on it when I have time.

@infinity0
Copy link
Author

infinity0 commented Feb 22, 2017

Just read through the "Lenses are the coalgebras for the costate comonad" article linked in the README.md. Very interesting. Looks like the s -> (a * (b -> t)) representation I picked above is a "costate comonad", also called a "store comonad".

@llelf
Copy link

llelf commented Sep 10, 2019

I made a start over at https://gist.github.com/infinity0/3c8461ed60d806bb4d6616a03939788a

👍 @infinity0 What’s the status of it?

@infinity0
Copy link
Author

@llelf Throughout 2017 I developed that code a bit further in a side project I was doing, but never got it to a point where I was satisfied enough with it to release it as a standalone package. There are relationships between the different types of optics that you can't really express in OCaml easily, and it looks like newer theoretical work on lens favours type families based on profunctors rather than the van Laarhoven type families.

I'm not writing OCaml actively these days, but if someone is interested in taking this issue further I can update the gist linked above with the latest version. The main change is the addition of an optic type that lets you compose 2 arbitrary optics - e.g. composing a lens and a prism, or a prism and a traversal, etc. My solution is a bit fudgy however, and I've seen much cleaner solutions in Haskell that are harder to do in OCaml.

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