Chien
Chien

Reputation: 355

Problems Understanding Haskell pure and <$>

I was just trying out some simple Haskell codes after learning Applicative and Monad, and was confused by the application of pure and <$>. I do understand the definitions, but in some special cases they seem to be mix up a little bit.

Say, I wish to get a function foo, which takes a Maybe Int and a Maybe [Int], and concat the first argument to the second one. A typical definition could be:

foo :: Maybe Int -> Maybe [Int] -> Maybe [Int]
foo ma mb = (:) <$> ma <*> mb

so that foo (Just 1) (Just [2]) would give Just [1,2].

But it seems pure makes no sense in this case, no matter how hard I tried to make the (:) an applicative one. Furthermore, pure (:) (Just 1) works in the exactly same manner as (:) does, but how could it be? I suppose (:) takes two arguments, so the previous one should be an applicative curried function?

Thank you so much!

Upvotes: 2

Views: 149

Answers (1)

chi
chi

Reputation: 116139

Furthermore, pure (:) (Just 1) works in the exactly same manner as (:) does, but how could it be?

Because of polymorphism and instance resolution. We have that

pure (:) :: Applicative f => f (a -> [a] -> [a])

Note how this works for any applicative f, not only for Maybe.

Then we apply it to (Just 1) which (after defaulting) has type, say, Maybe Int. So, to type check everything we need

pure (:) :: Maybe Int -> result

for some result type. We can write the above as

pure (:) :: (->) (Maybe Int) result

We then try to infer what f is, searching for a solution of

f (a -> [a] -> [a]) ~ (->) (Maybe Int) result

ergo,

f ~ (->) (Maybe Int)
(a -> [a] -> [a]) ~ result

Since (->) (Maybe Int) is an applicative, we accept this f. We then get

pure (:) (Just 1) :: a -> [a] -> [a]    -- AKA result

Now, what is pure in the (->) (Maybe Int) applicative?

instance Applicative ((->) b) where
   pure x = \_ -> x
   -- ...

So, pure simply discards the second Just 1 argument and returns the first one (:).

Concluding: the issue is that pure (:) does not have to work on the applicative you had in mind, Maybe, but is more general and will search for any applicative that makes the code to type check. If you apply pure (:) to something, the (->) b instance gets selected, and the second argument discarded.

There is a big difference from pure f y (which is equivalent to f) and pure f <*> y (which is equivalent to f <$> y).

Upvotes: 8

Related Questions