Понимание того, как применяются аппликативные функторы haskell

У меня просто небольшой вопрос о аппликативных функторах, чтобы помочь мне понять их. Это просто материал, который я применяю в 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 ответ

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

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

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

(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 также (аппликативный) функтор.

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 Например, вы можете увидеть такое же использование (<*>) <$> ... что я использовал выше.

Другие вопросы по тегам