Понимание того, как применяются аппликативные функторы haskell - PullRequest
0 голосов
/ 28 февраля 2019

У меня просто небольшой вопрос о аппликативных функторах, чтобы помочь мне понять их.Это просто материал, который я применяю в ghci.

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

Это имеет смысл для меня.Основное применение.Но при попытке:

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

мне выдается большое количество ошибок.Я немного понимаю почему;Есть два конструктора данных ([] и Maybe), а функция <*> только «откатывает» один из них.Что я хотел бы помочь в моем понимании, так это то, что именно haskell пытается сделать шаг за шагом, пока он не потерпит неудачу, и как вы можете обойти это и успешно вычислить это для:

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

1 Ответ

0 голосов
/ 28 февраля 2019

У вас есть два разных Applicative экземпляра.Это правда, что

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

, но экземпляр [] просто пытается применить каждую "функцию" в первом списке к каждому значению во втором, так что вы в конечном итоге пытаетесьapply

(Just (* 3)) (Just 4)

, что является ошибкой.

(Точнее, ваш список значений Just просто имеет неправильный тип, чтобы действовать в качестве первого аргумента для <*>.)

Вместо этого вам нужно сопоставить <*> с первым списком.

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

(Отображение функции более высокого порядка по списку - это то, как вы обычно получаете список упакованных функцийво-первых. Например,

> :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, упомянутый в комментариях, является еще одним вариантом.

> 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]

ОпределениеCompose очень просто:

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

Волшебство в том, что, если f и g являются (аппликативными) функторами, то Compose f g равно также a(аппликативный) функтор.

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)

В экземпляре Applicative вы можете увидеть то же использование (<*>) <$> ..., которое я использовал выше.

...