Applicative Functor - Haskell

93 Views Asked by At

pure (+) <*> (Just 1) <*> (Just 2)

Is the expansion of above expression is correct?

pure (+)  <*> (Just 1) <*> (Just 2)
= (Just (+)) <*> (Just 1) <*> (Just 2)
= (Just (1+)) <*> (Just 2)
= Just 3

if so then why we can't do just this (Just (+)) <*> (Just 2) ?

2

There are 2 best solutions below

0
Geoffrey Warne On BEST ANSWER

I believe you have already answered your question in the comments. So just for the sake of completion...

(Just (+)) <*> (Just 2) is a partially applied function that the REPL cannot print, giving the error:

• No instance for (Show (Integer -> Integer)) arising from a use of ‘print’,

but otherwise it is fine.


No problem here:

> :t (Just (+)) <*> (Just 2)

(Just (+)) <*> (Just 2) :: Num a => Maybe (a -> a).


Completing the function is fine too:

> f = (Just (+)) <*> (Just 2)

> f <*> Just 1

> Just 3

Your understanding seems perfectly sound.


Incidentally, another way of writing this is:

(+) <$> (Just 1) <*> (Just 2)

3
willeM_ Van Onsem On

The expansion is indeed correct. The expression

pure (+) <*> Just 1 <*> Just 2

is equivalent to:

(+) <$> Just 1 <*> Just 2

and:

liftA2 (+) (Just 1) (Just 2)

and not just for Maybe, but for any Applicative type.

A "mechanical" expansion would be to first look at the expression, here the expression

pure (+) <*>1 Just 1 <*>2 Just 2

is equivalent to:

(<*>2) ((<*>1) (pure (+)) (Just 1)) (Just 2)

where I added subscripts to keep track of the two uses of <*>. At this time we don't know what Applicative instance pure will "pick", but this will be determined based on the "type propagation".

The outer (<*>), so (<*>2) has type Applicative f => f (a -> b) -> f a -> f b, and for the inner one we pick (<*>1) :: Applicative g => g (c -> d) -> g c -> g d. The second operand of the two are Num m => Maybe m and Num n => Maybe n. This thus means that both f and g will match with f ~ g ~ Maybe.

This thus also means that pure will take the Maybe instance of Applicative, which means that pure is just equivalent to Just, and therefore pure (+) will - in this case - be Num o => Just (o -> o -> o). If we match this with the type of the inner (<*>), then it means that o ~ m, and with the outer (<*>), it means that o ~ m ~ n. So the type of the expression will be:

pure (+) <*> Just 1 <*> Just 2 :: Num m => Maybe m

and as for the expansion, we can replace pure with Just, so:

Just (+) <*> Just 1 <*> Just 2

then Just (+) <*> Just 1 will convert to Just (1 +) because both operands are a Just, and operand is implemented as:

instance Applicative Maybe where
    pure = Just

    Just f  <*> m       = fmap f m
    Nothing <*> _m      = Nothing

finally the Just (1 +) <*> Just 2, works the same, so:

Just (1 + 2)

which is equivalent to:

Just 3

if so then why we can't do just this (Just (+)) <*> (Just 2).

We can, we only can't show the result. The result of Just (+) <*> Just 2 will be Just (2 +), so that is a function, and functions can not be shown (by default).

But we can for example do:

ghci> Just (1 +) <*> Just 2
Just 3

or we can use the result of Just (+) <*> Just 2 to then work on a second operand:

ghci> (Just (+) <*> Just 2) <*> Just 3
Just 5

The parenthesis are not necessary here, but are used to makes it more explicit how the expression should be read.