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