-
Notifications
You must be signed in to change notification settings - Fork 24
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
Use profunctor encoding & other improvements #19
Conversation
I fixed compilation with GHC 8.2.2 (it looks like some weird bug, it typechecked with 8.0.2 and 8.4.3). |
Thanks for this! There's a lot here to page back in and review, so apologies that it is taking us a while to find time to dig into it. I'll try to go through the details and understand the impact of these changes. One question is to what extent this unavoidably moves us away from API-compatibility with |
The only thing I can think of is that there is no Other than that, what I noticed:
What is more (this is not related to the encoding, rather than wrapping things in newtypes), optic transformers (such as e.g. choosing) cannot be written for many flavors as one function (as an example, see my implementation of Actually, if it comes to |
I added implementation of indexed traversals, folds and setters. Maybe stacking all of it in a single PR is not the best idea, but in principle profunctor encoding doesn't bring anything new, so I wanted to make it more complete and appealing to review (heh). Implements #12. This explores idea(s) from http://oleg.fi/gists/posts/2017-04-26-indexed-poptics.html (specifically, CPS variant) to make regular optics "index-preserving" (which is reflected by the additional type parameter), so that both indexed and regular optics have the same form and can seamlessly compose. λ> iover (_Left % imapped % _1) (,) $ Left [('a', "hey"), ('b', "there"), ('c', "bye")]
Left [((0,'a'),"hey"),((1,'b'),"there"),((2,'c'),"bye")]
λ> iover (imapped % _Left) (,) $ [Left 'a', Right "hi", Left 'c']
[Left (0,'a'),Right "hi",Left (2,'c')] The difference from how lens library handles these:
lens: λ> iover (itraversed . itraversed) (,) ["hey", "bye"]
[[(0,'h'),(1,'e'),(2,'y')],[(0,'b'),(1,'y'),(2,'e')]] optics: λ> iover (icompose (,) % itraversed % itraversed) (,) ["hey", "bye"]
[[((0,0),'h'),((0,1),'e'),((0,2),'y')],[((1,0),'b'),((1,1),'y'),((1,2),'e')]]
λ> :t itraversed % itraversed
itraversed % itraversed
:: (TraversableWithIndex i1 t1, TraversableWithIndex i2 t2) =>
Optic
An_IxTraversal i3 (i1 -> i2 -> i3) (t1 (t2 a)) (t1 (t2 b)) a b
λ> :t itraversed % itraversed % itraversed
itraversed % itraversed % itraversed
:: (TraversableWithIndex i1 t1, TraversableWithIndex i2 t2,
TraversableWithIndex i3 t3) =>
Optic
An_IxTraversal
i4
(i1 -> i2 -> i3 -> i4)
(t1 (t2 (t3 a)))
(t1 (t2 (t3 b)))
a
b
λ> :t icompose3 (,,) % itraversed % itraversed % itraversed
icompose3 (,,) % itraversed % itraversed % itraversed
:: (TraversableWithIndex a1 t1, TraversableWithIndex b1 t2,
TraversableWithIndex c t3) =>
Optic
An_IxTraversal
r
((a1, b1, c) -> r)
(t1 (t2 (t3 a2)))
(t1 (t2 (t3 b2)))
a2
b2
λ> :t itraverseOf $ icompose3 (,,) % itraversed % itraversed % itraversed
itraverseOf $ icompose3 (,,) % itraversed % itraversed % itraversed
:: (TraversableWithIndex a1 t1, TraversableWithIndex b1 t2,
TraversableWithIndex c t3, Applicative f) =>
((a1, b1, c) -> a2 -> f b2)
-> t1 (t2 (t3 a2)) -> f (t1 (t2 (t3 b2)))
Illustration of current optic hierarchy:
|
Interesting! Thanks for exploring this. I've had a brief play around with it, and I quite like this approach to indexed optics. Here are some scattered initial thoughts... Have you thought about coindexed prisms at all? The Haddocks should explain the The reorganisation means that the definitions of optic flavours are now a bit less modular, although we couldn't add in optic flavours to the subtyping hierarchy after the fact anyway, so this is no great loss. I imagine that making In #18 you sketched a hierarchy in which The Generalising |
Not really, I was deterred by 9 type variables ;)
I had it done without the
Right, I fixed that.
Hmm, yes, I didn't think of its usage like that. Now I feel less bad for introducing these classes, haha. |
Heh, I don't blame you... I do wonder if it is possible to get away with a single type parameter that gets instantiated to a promoted datatype with multiple fields, i.e. something like data IX = I Type Type Type Type -- i j k l
newtype Optic k (ix :: IX) s t a b but perhaps it's difficult to keep the parameter as a variable even when not using (co)indexed stuff. Thanks, I now understand |
We agreed to merge this and continue polishing on |
So this is quite big, but roughly speaking it does the following:
Implements #18, solves #10 and #15.