6
votes
Just (+) <*> Just 3 <*> Just 5

Just 8

pure (+) <*> Just 3 <*> Just 5

Just 8

pure (*3) <*> [0..10]

[0,3,6,9,12,15,18,21,24,27,30]

Just (*3) <*> [0..10]

Couldn't match type ‘[]’ with ‘Maybe’

Expected type: Maybe b

Actual type: [b]

In the second argument of ‘(<*>)’, namely ‘[0 .. 10]’

In the expression: Just (* 3) <*> [0 .. 10]

In an equation for ‘it’: it = Just (* 3) <*> [0 .. 10]

When are pure and Just interchangeable and when are they different?

2

2 Answers

10
votes

pure is an overloaded operation. It is defined for all types that implement the Applicative class. One of the types that do that is Maybe. So pure in that context is the same as Just. But there are other types that also implement Applicative such as [] (lists). In that case, pure means singleton (i.e. the function that takes a single value and returns the one-element list that contains that value). So

pure (*3) <*> [0..10]

really means:

[(*3)] <*> [0..10]

and not

Just (*3) <*> [0..10]

In this last example you are trying to mix lists with maybes which is why GHC rejects it. In general, haskell figures out what is the exact meaning of pure based on the context, e.g. if you try to use it with maybes, it will interpret it as Just, if you use it with lists interpret it as singleton.

6
votes

The pure function returns a polymorphic value:

Prelude> :t pure "foo"
pure "foo" :: Applicative f => f [Char]

For Maybe, pure is just defined to be Just:

instance Applicative Maybe where
    pure = Just
    -- ...

Other types provide different definitions; as an example, it is defined for lists as

instance Applicative [] where
    pure x    = [x]
    -- ...

Specifying a type for the return value tells Haskell which Applicative instance to use for the definition of pure.

Prelude> pure "foo" :: [[Char]]
["foo"]
Prelude> pure "foo" :: Maybe [Char]
Just "foo"

In addition to providing an explicit type, Haskell can infer which type to use base on how the value is used. For instance, (<*> [1..5]) :: (Num a, Enum a) => [a -> b] -> [b], so in pure (+3) <*> [1..5], we know pure (+3) has to have type [a -> b]. Similarly, in pure (+3) <*> Just 5, we know that pure (+3) must have type Maybe (a->b).

In general, in any expression pure f <*> g, the type of g will determine what type of value pure f needs to return.