Composing Getter and Setter

Posted on 2017-11-17 by Oleg Grenrus lens

Adam Gundry gave a talk about optics at Haskell Exchange 2017, Through a Glass, Abstractly: Lenses and the Power of Abstraction. One take-away point from the talk is, that you want composition of Getter and Setter to be an error. You can compose getters with setters, in teir van Laarhoven or profunctor encoding of optics, but you can't do anything useful. So you'd rather fail early.

Except, it's Friday.

As this is a literal Haskell file, First a moderate prelude:

Let's work with profunctor encoding of optics, then

If we try to compose Getter and Setter, it will succeed, we'll get

What would satisfy that? Well, a Boring type:

How that could be useful? By fast and loose reasoning it won't. But in Haskell, we also have seq.

If we take care, and write instances which force the arguments:

by using strict Proxy':

we can define something interesting (maybe):

partialNF will use an optic to drill inside the structure, and evaluate the values inside.

#Examples

First example is using Stream:

Let's us define some optics, 'Getter' for the head, and 'Setter' for the tail of the 'Stream'. We don't define tl using setting, we'll see later why so.

Now we can play with these:

λ> partialNF hd $ let s = () ::: s in s
()

that's quite boring.

How about:

λ> partialNF hd $ let s = () ::: error "friday" ::: s in s
()

or

λ> partialNF (tl . tl . hd) $ let s = () ::: error "friday" ::: s in s
()

Still, quite boring.

λ> partialNF (tl . hd) $ let s = () ::: error "friday" ::: s in s
*** Exception: friday

BOOM!

However, if we'd define:

Then the magic of laziness will prevent us from doing bad stuff :)

λ> partialNF (tl' . hd) $ let s = () ::: error "friday" ::: s in s
()

For the same reason

λ> partialNF (map' . first') [(1,2), (error "friday!",4)]
()

doesn't throw, but

λ> partialNF (traverse' . first') [(1,2), (error "friday!",4)]
*** Exception: friday!

does.

To conclude, a composition of Getter and Setter has at least one, though quite questionable use case. However, partial normalisation doesn't if a Setter is defined using map' or roam, as they don't give opportunity to force the values "inside" the Functor.

#Appendix

Various definitions

Site proudly generated by Hakyll