简体   繁体   中英

Understanding how haskell applicative functors are applied

I just have a quick question about applicative functors to help me get a grasp on them. This is just stuff I am applying in ghci.

[(+3),((-) 3),(*3)] <*> [4]
[7,-1,12]

This makes sense to me. Basic application. But when trying:

[(Just (+3)),(Just ((-) 3)),(Just (*3))] <*> [Just 4]

I am given a large amount of errors. I have somewhat of an understanding why; there are two data constructors ( [] and Maybe ) and the <*> function only "peels back" one of them. What I'd like to assist my understanding is what exactly haskell is trying to do step by step until it fails, and how you could get around it and have this successfully compute to:

[(Just 7),(Just -1),(Just 12)]

You have two different Applicative instances. It is true that

Just (* 3) <*> Just 4 == Just 12

but the [] instance just tries to apply each "function" in the first list to each value in the second, so you end up trying to apply

(Just (* 3)) (Just 4)

which is an error.

(More precisely, your list of Just values just has the wrong type to act as the first argument for <*> .)

Instead, you need to need to map <*> over the first list.

> (<*>) <$> [(Just (+3)),(Just ((-) 3)),(Just (*3))] <*> [Just 4]
[Just 7,Just (-1),Just 12]

(Mapping a higher-order function over a list is how you usually get a list of wrapped functions in the first place. For example,

> :t [(+3), ((-) 3), (* 3)]
[(+3), ((-) 3), (* 3)] :: Num a => [a -> a]
> :t Just <$> [(+3), ((-) 3), (* 3)]
Just <$> [(+3), ((-) 3), (* 3)] :: Num a => [Maybe (a -> a)]

)


Data.Functor.Compose mentioned in the comments is another option.

> import Data.Functor.Compose
> :t Compose [(Just (+3)),(Just ((-) 3)),(Just (*3))]
Compose [(Just (+3)),(Just ((-) 3)),(Just (*3))]
  :: Num a => Compose [] Maybe (a -> a)
> Compose [(Just (+3)),(Just ((-) 3)),(Just (*3))] <*> Compose [Just 4]
Compose [Just 12,Just (-1),Just 12]
> getCompose <$> Compose [(Just (+3)),(Just ((-) 3)),(Just (*3))] <*> Compose [Just 4]
[Just 12,Just (-1),Just 12]

The definition of Compose is very simple:

newtype Compose f g a = Compose { getCompose: f (g a) }

The magic is that as long as f and g are both (applicative) functors, then Compose fg is also a (applicative) functor.

instance (Functor f, Functor g) => Functor (Compose f g) where
    fmap f (Compose x) = Compose (fmap (fmap f) x)

instance (Applicative f, Applicative g) => Applicative (Compose f g) where
    pure x = Compose (pure (pure x))
    Compose f <*> Compose x = Compose ((<*>) <$> f <*> x)

In the Applicative instance, you can see the same use of (<*>) <$> ... that I used above.

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