简体   繁体   中英

Haskell: Understanding the pure function for Applicative functors

I am learning about Applicative Functors and the pure function has the following type declaration:

pure :: a -> f a 

I understand that the pure function takes a value of any type and returns an applicative value with that value inside it. So if the applicative instance was Maybe , pure 3 would give Just 3 .

However, what happens when you apply pure to a value that's already inside an applicative value? Eg what happens if you do something like pure Just 3 ?

What happens when you apply pure to a value that's already inside an applicative value?

It just gets wrapped in an additional layer. The end result is an applicative value nested inside another applicative value.

Eg what happens if you do something like pure Just 3 ?

This is an interesting question, although probably not for the reasons you meant. The important point here is to distinguish pure (Just 3) — which was probably what you meant — from pure Just 3 = (pure Just) 3 , which is what you wrote. This gives two scenarios:

  • pure (Just 3) simply applies pure to the value Just 3 , which — as I discussed above — gives Just (Just 3) , which is a nested applicative value.
  • (pure Just) 3 is an interesting case. Recall the types of pure and Just :

     pure :: Applicative f => a -> fa Just :: a -> Maybe a -- so: pure Just :: Applicative f => f (a -> Maybe a) 

    In other words, pure Just takes the function Just and wraps it inside an applicative value.

    Next, we want to take pure Just and apply it to a value 3 . But we can't do this, since f (a -> Maybe a) is a value, not a function! So (pure Just) 3 should result in a type error.

    …except it turns out to typecheck just fine! So we're missing something. In this case, it turns out there is an applicative instance for functions:

     instance Applicative ((->) r) where pure x = \\r -> x (<*>) = _irrelevant_here 

    The syntax is a bit funny, but it basically means that r -> ... is an applicative. This particular instance is known as the Reader monad , and it's very widely used. (For more about this particular data type, see eg here or here .) The idea is that r -> a can compute an a given an r input; in this case, pure x creates a function which ignores its input and returns x always, and f <*> x feeds the r input into both f and x , then combines the two. In this case, we're only using pure , so it's easy to evaluate (pure Just) 3 by hand:

     (pure Just) 3 = (\\r -> Just) 3 = Just 

    So the idea here is that pure wraps Just in an applicative value, which in this case happens to be a function; then, we apply this function to 3 , which gets rid of the wrapper to reveal the original Just .

First of all, pure has the type:

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

To make things simpler, think of the kind of f

:k f 
f :: * -> *

and the kind of a is *

then the type of a , is just a , any a , the most polymorphic of all (but with kind * remember). So you don't really care the value of a, you just have a restriction, and that's the typeclass Applicative, and the kind of f (remember * -> * )

so in this case:

gchi> pure 3 :: Maybe Int
ghci> Just 3

here f is Maybe and a is 3

In the same way

gchi> pure $ Just 3 :: Maybe (Maybe Int)
gchi> Just (Just 3)

here f is again Maybe and a is Just 3

and you can play a little changing the type to pure:

gchi> pure 3 :: [Double]
ghci> [3.0]

here, f is [], and a is 3

same way

ghci> pure [3] :: [[Double]]
ghci> [[3.0]]

finally here, f again, is [] and a is [3]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM