Reputation: 692
I'm confused about Data.Functor.Constant's type constructor and also how it works with applicative.
First the constructor:
When I examine the type of Constant :: a -> Constant a b
I see it takes an a
, but returns a Constant a b
Where does the b
come from and why does it exist?
Secondly, I'm struggling with Applicative:
I understand Constant needs to have a Monoid inside to be an Applicative instance.
A law it must obey is: pure id <*> Constant x = x
I thought that was the same as: Constant id <*> Constant x = x
but I guess I'm wrong about that since the follow code clearly shows pure acts differently.
:t pure id <*> Constant "hello" // Constant [Char] b
:t Constant id <*> Constant "hello" // Couldn't match expected type `a0 -> a0' with actual type `[Char]'
:t pure id <*> Constant reverse // Constant ([a] -> [a]) b
:t Constant id <*> Constant reverse // Constant ([a] -> [a]) b
I see that it only works if x
is the same monoid unless I use pure. So I'm not sure why pure is working differently. I suspect this has to do with that b
which is why they're in the same question.
To sum up the two questions:
What does b
do in the Constant constructor?
Why does pure work even though the monoids are different inside?
Thanks so much!
Upvotes: 15
Views: 1159
Reputation: 47392
Okay, so you have this type
data Const a b = Const { getConst :: a }
Your first question was "Where does the b
come from?"
The answer is that it doesn't come from anywhere. In the same way that you can think of Maybe b
as a container that holds either 0 or 1 values of type b
, a Const a b
is a container that holds exactly 0 values of type b
(but does definitely hold a value of type a
).
Your second question was "Why is it there?"
Well, sometimes it's useful to have a functor that says it might contain values of type b
, but actually holds something else (e.g. think of the Either a b
functor -- the difference is that Either a b
might hold a value of type b
, whereas Const a b
definitely doesn't).
Then you asked about the code snippets pure id <*> Const "hello"
and Const id <*> Const "hello"
. You thought that these were the same, but they're not. The reason is that the Applicative
instance for Const
looks like
instance Monoid m => Applicative (Const m) where
-- pure :: a -> Const m a
pure _ = Const mempty
-- <*> :: Const m (a -> b) -> Const m a -> Const m b
Const m1 <*> Const m2 = Const (m1 <> m2)
Since there aren't actually any values having the type of the second parameter, we only have to deal with those having the type of the first parameter, which we know is a monoid. That's why we can make Const
an instance of Applicative
-- we need to pull a value of type m
from somewhere, and the Monoid
instance gives us a way to make one from nowhere (using mempty
).
So what happens in your examples? You have pure id <*> Const "hello"
which must have type Const String a
since id :: a -> a
. The monoid in this case is String
. We have mempty = ""
for a String
, and (<>) = (++)
. So you end up with
pure id <*> Const "hello" = Const "" <*> Const "hello"
= Const ("" <> "hello")
= Const ("" ++ "hello")
= Const "hello"
On the other hand, when you write Const id <*> Const "hello"
the left-hand argument has type Const (a -> a) b
and the right has type Const String b
and you see that the types don't match, which is why you get a type error.
Now, why is this ever useful? One application is in the lens library, which lets you use getters and setters (familiar from imperative programming) in a pure functional setting. A simple definition of a lens is
type Lens b a = forall f. Functor f => (a -> f a) -> (b -> f b)
i.e. if you give it a function that transforms values of type a
, it will give you back a function that transforms values of type b
. What is that useful for? Well, let's pick a random function of type a -> f a
for a particular functor f
. If we choose the Identity
functor, which looks like
data Identity a = Identity { getIdentity :: a }
then if l
is a lens, the definition
modify :: Lens b a -> (a -> a) -> (b -> b)
modify l f = runIdentity . l (Identity . f)
provides you with a way to take functions that transform a
s and turn them into functions that transform b
s.
Another function of type a -> f a
we could pass in is Const :: a -> Const a a
(notice that we've specialized so that the second type is the same as the first). Then the action of the lens l
is to turn it into a function of type b -> Const a b
, which tells us that it might contain a b
, but actually sneakily it really contains an a
! Once we've applied it to something of type b
in order to get a Const a b
, we can hit it with getConst :: Const a b -> a
to pull a value of type a
out of the hat. So this gives us a way to extract values of type a
from a b
-- i.e it's a getter. The definition looks like
get :: Lens b a -> b -> a
get l = getConst . l Const
As an example of a lens, you could define
first :: Lens (a,b) a
first f (a,b) = fmap (\x -> (x,b)) (f a)
so that you could open up a GHCI session and write
>> get first (1,2)
1
>> modify first (*2) (3,4)
(6,4)
which, as you might imagine, is useful in all kinds of situations.
Upvotes: 27