Как совместить два составных аппликативных функтора? - PullRequest
1 голос
/ 28 мая 2020

У меня есть два составных аппликативных функтора Maybe [Integer], и я хочу объединить их с <$> / <*>, но я застрял в применении аппликативной операции. Следующее не проверяется типом:

(<*>) (<*>) ((<$>) ((+) <$>) $ Just [1,2,3]) $ Just [4,5,6]

Ожидаемый результат:

Just [5,6,7,6,7,8,7,8,9]

Функторная часть работает, т.е. промежуточное значение, переданное в <*> в качестве первого аргумента, равно Just [Integer -> Integer]. Я привык к S-выражениям, поэтому у меня проблемы с синтаксисом Haskell. Я знаю Compose, но меня интересует простая композиция без абстракции.

Ответы [ 4 ]

4 голосов
/ 28 мая 2020

Как сказал Ли-яо Ся , использование liftA2 значительно упрощает задачу.

Но если вы все еще хотите увидеть, что это становится с точки зрения лежащих в основе операций, мы можем расширить определение liftA2:

liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 f x y = f <$> x <*> y

, чтобы решение стало

(liftA2 . liftA2) (+) (Just [1,2,3]) (Just [4,5,6])
= liftA2 (liftA2 (+)) (Just [1,2,3]) (Just [4,5,6])
= (\f x y -> f <$> x <*> y) ((\f x y -> f <$> x <*> y) (+)) (Just [1,2,3]) (Just [4,5,6])
= ((\f x y -> f <$> x <*> y) (+)) <$> Just [1,2,3] <*> Just [4,5,6]
= (\x y ->  (+) <$> x <*> y) <$> Just [1,2,3] <*> Just [4,5,6]

Теперь это не в безточечном стиле, как в приведенном выше примере, и я действительно не думаю, что полезно преобразовать его в безточечный стиль, но вот результат из http://pointfree.io:

((<*>) . ((+) <$>)) <$> Just [1, 2, 3] <*> Just [4, 5, 6]

мы видим, что это то же самое при расширении eta:

(<*>) . ((+) <$>)
= \x y -> ((<*>) . ((+) <$>)) x y
= \x y -> ((<*>) $ ((+) <$>) x) y
= \x y -> ((<*>) ((+) <$> x)) y
= \x y -> (<*>) ((+) <$> x) y
= \x y -> ((+) <$> x) <*> y
= \x y -> (+) <$> x <*> y
3 голосов
/ 28 мая 2020

liftA2 может быть менее запутанным, чем (<*>).

(+) :: Int -> Int -> Int
liftA2 (+) :: [Int] -> [Int] -> [Int]
liftA2 (liftA2 (+)) :: Maybe [Int] -> Maybe [Int] -> Maybe [Int]

liftA2 (liftA2 (+)) (Just [1,2,3]) (Just [4,5,6])
2 голосов
/ 29 мая 2020

Композиция двух Applicative s всегда равна Applicative (в отличие от случая Monad).

Мы можем использовать это в наших интересах здесь с помощью Compose newtype из Data.Functor.Compose:

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

Требуется небольшая упаковка, но такое решение может быть полезно при определенных обстоятельствах:

example :: Maybe [Int]
example =
  getCompose ((+) <$> Compose (Just [1,2,3]) <*> Compose (Just [4,5,6]))
0 голосов
/ 28 мая 2020

Другой способ - использовать трансформатор ListT . Хотя в этом случае он отлично работает, по какой-то причине это устаревший трансформатор, отмеченный красным «Устарело: этот преобразователь недопустим на большинстве монад» .

import Control.Monad.Trans.List

doit :: (Int-> Int -> Int) -> Maybe [Int] -> Maybe [Int] -> Maybe [Int]
doit f mt1 mt2 = runListT $ f <$> (ListT mt1) <*> (ListT mt2)

λ> doit (+) (Just [1,2,3]) (Just [4,5,6])
Just [5,6,7,6,7,8,7,8,9]
...