2016-07-21 5 views
1

Я уверен, что это простое решение, но оно ускользает от меня, и я не могу найти прямой ответ.Обратный подъем на аппликативные функторы

Как правило, при применении liftA2 предполагая бинарную функцию уже снят один раз, подпись выглядит следующим образом:

liftA2' :: (Applicative f1, Applicative f) 
      => (f a -> f b -> f c) -> f1 (f a) -> f1 (f b) -> f1 (f c) 

Можно ли применить «обратный», например liftA2, таких как:

inverseA2 :: (Applicative f, Applicative f1) 
      => (f a -> f b -> f c) -> f (f1 a) -> f (f1 b) -> f (f1 c) 

в качестве конкретного примера, я хотел бы получить функцию:

f :: ([a] -> [b] -> [c]) -> [Maybe a] -> [Maybe b] -> [Maybe c] 

Одним из способов было бы прибегнуть к «упаковке» каждый аргумент [Maybe a] -> Maybe [a] и «распаковывать» Maybe [a] -> [Maybe a] результат применения нормального liftA2. Я хотел бы избежать этого, поскольку, как вы можете себе представить, упаковка разрушительна (например, pack [Just 1, Nothing, Just 2] == Nothing).


Update:, как @ user2407038 отметил, для того, чтобы f применить данную функцию, обязательно нужна функция вдоль линий [Maybe a] -> [a] которые делает потерять информацию. Таким образом, для этих двух конкретных функторов нет очевидного способа удовлетворить дополнительное требование. Но для любых других двух функторов f, f1, которые имеют обратимую функцию forall a . f a -> f1 a, принятый ответ идеально подходит для решения этого вопроса.

+0

Возьмите 'f :: [Int] -> [Char] -> [Int]; f x y = [длина x + длина y] '. Что бы вы ожидали от 'inverseA2 f :: [Maybe Int] -> [Maybe Char] -> [Maybe Int]'? Что относительно 'f x y = [длина x + длина y, head x]'? Я не уверен, что общее определение будет ... – chi

+2

Чтобы 'f' применял данную функцию, она должна вызывать' [a] 'из' [Maybe a] '(естественно, нет способа чтобы получить '[a]' из '[Maybe b]', поскольку нет способа получить 'a' из' b'), и поэтому вы обязательно * нуждаетесь в * функции '[Maybe a] -> [a]' - и, как вы заметили, эта функция обязательно теряет информацию. То же самое относится к общему случаю - существует несколько функторов 'f1, f2', которые имеют обратимую функцию' forall a. f1 a -> f2 a'. – user2407038

+0

@ user2407038, поэтому была причина, по которой я не нашел прямого ответа, так как я искал неправильную проблему. Спасибо, что указали обратимые функции! Я буду больше смотреть на это. –

ответ

1

Я уверен, что вы, вероятно, поняли это, но я не думаю, что вы можете сделать это с ограничениями, которые у вас есть. Если вы немного более либеральным с ограничениями, вы получите что-то, хотя ....

inverseA2 :: (Applicative f, Traversable f, Applicative f1, Traversable f1) 
       => (f a -> f b -> f c) -> f (f1 a) -> f (f1 b) -> f (f1 c) 
inverseA2 f x y = sequenceA (liftA2 f (sequenceA x) (sequenceA y)) 

Единственная причина, я ставлю это на то, что для вашего конкретного примера с Maybe и [], эти ограничения : все довольны, поэтому это возможно для этого случая. Тем не менее, все еще не успокаиваюсь.

Вы также можете попробовать поэкспериментировать с написанием собственных экземпляров для Data.Distributive давая вам distribute, который похож на sequenceA ...

Edited включить @ предложения dfeuer в.

+0

Очень элегантный! 'sequence' отлично описывает, что делают' pack' и 'unpack', спасибо, что указали это (теперь я понял, что я говорю бред, когда говорю о транспозиции, я исправлю это)! К сожалению, как вы сказали, он реализует случай, который я пытаюсь избежать. Как и в приведенном выше примере, оценка 'inverseM2 (\ xy = [length x + length y]) [Nothing, Just 1, Just 3] [Just 'a', Just 'b']' будет возвращать '[Nothing]' вместо из '[Just 5]' –

+2

'Monad' явно переборщил (просто замените' sequence' '' sequenceA'). Кроме того, я бы предположил, что вы можете использовать несколько комбинаций «Traversable» и «Distributive» для контекста. – dfeuer

+0

@dfeuer Спасибо! Я продолжаю забывать о 'sequenceA' ... – Alec

 Смежные вопросы

  • Нет связанных вопросов^_^