[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