Haskell Lens Tutorial with Traverse
I am trying to follow this tutorial: http://blog.jakubarnold.cz/2014/08/06/lens-tutorial-stab-traversal-part-2.html
I am using the following code which I load into ghci:
{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
import Control.Applicative
import Data.Functor.Identity
import Data.Traversable
-- Define Lens type.
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
type Lens' s a = Lens s s a a
-- Lens view function. Omitting other functions for brevity.
view :: Lens s t a b -> s -> a
view ln x = getConst $ ln Const x
-- Tutorial sample data types
data User = User String [Post] deriving Show
data Post = Post String deriving Show
-- Tutorial sample data
john = User "John" $ map (Post) ["abc","def","xyz"]
albert = User "Albert" $ map (Post) ["ghi","jkl","mno"]
users = [john, albert]
-- A lens
posts :: Lens' User [Post]
posts fn (User n ps) = fmap (\newPosts -> User n newPosts) $ fn ps
From there, simple things like this:
view posts john
However, when I try the next step, it doesn't work:
view (traverse.posts) users
I get:
Could not deduce (Applicative f) arising from a use of βtraverseβ
from the context (Functor f)
bound by a type expected by the context:
Functor f => ([Post] -> f [Post]) -> [User] -> f [User]
at <interactive>:58:1-27
Possible fix:
add (Applicative f) to the context of
a type expected by the context:
Functor f => ([Post] -> f [Post]) -> [User] -> f [User]
In the first argument of β(.)β, namely βtraverseβ
In the first argument of βviewβ, namely β(traverse . posts)β
In the expression: view (traverse . posts) users
I see that the lens has a Functor constraint and the traverse has a more constrained f-type constraint as an applicative. Why exactly does this not work, and why does the blog tutorial suggest it works?
source to share
view
has a type that is less restrictive than Lens s t a b -> s -> a
.
If you drop the type signature, ghci will tell you the type for view
:t view
view :: ((a1 -> Const a1 b1) -> t -> Const a b) -> t -> a
This is less restrictive because functors Lens
must be defined forall
, while the first argument to view must only be defined for Const a1
.
If we rename the type variables based on the names from Lens
and restrict a1 ~ a
, this signature will make more sense
type Lens s t a b = forall f. Functor f =>
(a -> f b) -> s -> f t
view :: ((a -> Const a b) -> s -> Const a t) -> s -> a
view ln x = getConst $ ln Const x
source to share