[Haskell-beginners] Lens

Brent Yorgey byorgey at seas.upenn.edu
Mon Jul 15 15:16:41 CEST 2013


Hi Emmanuel,

On Sun, Jul 14, 2013 at 10:47:16PM +0200, Emmanuel Surleau wrote:
> Hello there,
>
> The first (concrete) problem I ran into is how to update the members of a
> set with the result of an IO action. I have managed to do this with a pure
> function (prefixName) but I'm not sure of how to do this with
> promptName.

Unfortunately you cannot do this using a Set.  The reason is that
modifying the contents of a Set might result in a smaller Set and that
cannot possibly satisfy the lens laws.  However, if we change the Set
of dogs to be a list, we can do this using the (%%~) operator:

>   ... same as before ...
>
>   data Dogs = Dogs { _dogs :: [Dog] }
>          deriving Show
>   makeLenses ''Dogs
>
>   main :: IO ()
>   main = do
>         ... as before ...
>
>         -- change dog names by prompting the user
>         doggies' <- doggies & (dogs.traverse.name) %%~ prefixName
>         print doggies'
>
>         return ()

But astoundingly, if you look at the implementation of (%%~), it
is... (%%~) = id!  So this code also works:

  doggies' <- (dogs.traverse.name) prefixName doggies

That's right, the magic solution is to just treat the
(dogs.traverse.name) lens as a *function* and apply it to prefixName!

To understand why this works we have to look a bit at the
implementation.  I am not surprised that you were baffled by it
because it looks quite magical if you don't understand where it comes
from.

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

Instead of trying to understand why this is the definition, let's just
see what happens if we take the right-hand side and set f = IO:

  (a -> IO b) -> s -> IO t

In the above example, a = b = String, and s = t = Dogs, that is,
(dogs.traverse.name) has type

  Lens Dogs Dogs String String

which expands to

  (String -> IO String) -> Dogs -> IO Dogs

(actually this is a lie, it is really just a Traversal and not a Lens,
but it's the same idea).  So if we apply it to 'prefixName :: String
-> IO String' we get a
function of type  Dogs -> IO Dogs! Nifty!

Now, to answer your specific questions:

> a) I'm not sure why the explicit forall is needed here (isn't this
> equivalent to just Functor f => ...)?

Yes, it is equivalent; the explicit forall is not strictly necessary.
The forall is there to emphasize that the 'f' does not show up on the
left-hand side: something of type  Lens s t a b  is a function which
works for *any* specific Functor f that a user might choose. (E.g., we
specifically chose IO in the example above).  So e.g. a lens cannot do
IO operations, because then it would only work for IO and not for
other Functors.  So a lens may only interact with the f via the fmap
function.  (Similarly a Traversal must work with all Applicatives, and
so on.)

> b) My understanding is that a lens packs both getter and setters, but I
> don't know which is supposed to be which here...

You can think of lenses as generalizations of getters+setters, but
that is NOT how they are implemented!  Nothing in the type (a -> f b)
-> s -> f t  corresponds directly to getters or setters.

For understanding more about what this type means and why it
corresponds to the idea of lenses, I highly recommend the video of
Edward's presentation to the NY Haskell user's group:
http://youtu.be/cefnmjtAolY?hd=1 .

> c) Is there any kind of in-depth guide to Control.Lens somewhere? I have
> found some examples and tutorials but nothing that seemed to do more than
> scratch the surface.

You can find pretty much everything there is on lens here:

  http://lens.github.io/

-Brent




More information about the Beginners mailing list