0

I'm learning about applicative functors. In the source for an applicative Maybe, the pure function looks like this:

instance Applicative Maybe where
    pure = Just
    ...etc

With the arguments expanded, I think it looks like:

pure x = Just x

When I call pure (Just 5), it returns Just 5.

Shouldn't it return Just (Just 5)?

Similarly, for List:

instance Applicative [] where  
    pure x = [x]

When I call pure [4,5,6] it returns [4,5,6].

From the signature, it looks like pure [4,5,6] should return [[4,5,6]].

Can someone explain in simple terms what's happening here?


Wait, I think I got it - since there's no context provided, pure [4,5,6] is not using the Applicative for List, it's just using the general case and returning the same value. Is this correct?

Ben
  • 54,723
  • 49
  • 178
  • 224
  • 1
    Try `pure 3` and `(pure :: Maybe Int -> Maybe (Maybe Int)) $Just 3`. I think it's using the general case, and as [ghci tries to default to IO](https://downloads.haskell.org/~ghc/7.10.2/docs/html/users_guide/interactive-evaluation.html), `pure (Just 5)` is evaluated as `IO (Maybe Int)`. – Yosh Jul 23 '16 at 16:10
  • 3
    The type of pure is *not* `m a -> m (m a)` - so there is nothing to say that if the value `x` in `pure x` is already inside an `m`, that the `m` which pure creates would be the same `m`. As @Yosh stated, typing `pure ..` into the GHCi prompt will default the type of pure to `a -> IO a` - if it weren't for defaulting this would produce an ambiguous type error when you tried to print it (as e.g. `pure ()` has type `Applicative f => f ()`) – user2407038 Jul 23 '16 at 17:16

1 Answers1

6

pure is an overloaded function. It takes a value of any type a, and wraps it up into an applicative functor f.

ghci> :t pure
pure :: Applicative f => a -> f a

f is determined by the context. If you're passing pure x into a function which expects a Maybe, the type-checker will infer f ~ Maybe and select Maybe's instance of Applicative and pure x will evaluate to Just x. Likewise, if you pass pure x into a function of lists, the type-checker will use []'s instance of Applicative and pure x will return a singleton list.

ghci> :t map
map :: (a -> b) -> [a] -> [b]  -- map is a list function
ghci> map (+1) (pure 4)        -- so when we pass in (pure 4)
[5]                            -- it means a singleton list

In your case, I'm guessing that you're typing pure x at a GHCI prompt. If you don't provide any context (such as applying a list function to pure x), GHCI will default to assuming you meant to use IO's instance of Applicative, for which pure x is a do-nothing IO action returning x. GHCI then dutifully executes that IO action.

ghci> pure 'a'
'a'
ghci> pure (Just 3)
Just 3

If the type-checker can't see the context based on your code, you can declare the context manually using a type annotation.

ghci> pure "hello" :: Maybe String
Just "hello"
ghci> pure (Just 3) :: Maybe (Maybe Int)
Just (Just 3)
ghci> pure [1,2,3] :: [[Int]]
[[1,2,3]]
Benjamin Hodgson
  • 42,952
  • 15
  • 108
  • 157
  • Correct presumption re: GHCI prompt, I should have mentioned that. Interesting that it takes the IO context, that was the part I was missing. Thank you! – Ben Jul 23 '16 at 18:04