Reputation: 661
I'm following a blog post on lens' traversals. In it, there is a composition of
traverse.posts
and it ends up with type
(Traversable t, Applicative f) =>
([Post] -> f [Post]) -> t User -> f (t User)
I understand that it is function composition., but I'm a bit confused on how it ends up with that type.
Can anyone help me to walk through this?
Upvotes: 2
Views: 106
Reputation: 4646
We have the following types for the functions:
traverse :: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b)
posts :: Functor f => ([Post] -> f [Post]) -> User -> f User
Let's line up the types for traverse
and posts
(in that order) so we can see what's going on (I'll omit the type-class constraints for this), and let's add the implied parentheses around the results of the functions (a -> b -> c
is a shortcut for a -> (b -> c)
)
(a -> f b ) -> (t a -> f (t b))
([Post] -> f [Post]) -> (User -> f User)
From this we can see that a ~ User
, b ~ User
(~
here means 'is identified with', or 'is equal to'), so let's specialize traverse and write down the types of the functions again:
(User -> f User) -> (t User -> f (t User))
([Post] -> f [Post]) -> (User -> f User)
From this we can see that the composition will have the type (omitting the constraints for now, again)
([Post] -> f [Post]) -> (t User -> f (t User))
Or without the optional parentheses
([Post] -> f [Post]) -> t User -> f (t User)
Now what about the constraints? Well the use of posts
gives us the Functor f
constraint, and traverse
gives us Applicative f
and Traversable t
, but because Applicative
is a sub-class of Functor
(it's defined as class Functor f => Applicative f where -- ...
), we don't have to specify the Functor f
constraint, it's already there.
So we are left with:
traverse . posts :: (Applicative f, Traversable t) =>
([Post] -> f [Post]) -> t User -> f (t User)
Upvotes: 4