Есть ли в Haskell псевдонимы для (liftM. LiftM), (liftM. LiftM. LiftM) и т. Д.?

Есть ли в Haskell псевдонимы для (liftM. LiftM), (liftM. LiftM. LiftM) и т. Д.?

Чтобы мне не пришлось быть таким многословным, например:

(liftM . liftM) (+ 1) [Just 1,  Just 2]  = [Just 2, Just 3]
(liftM2 . liftM2) (+) [Just 1] [Just 2]  = [Just 3]

person Lay González    schedule 23.02.2015    source источник
comment
Я считаю, что это именно то, чем должны быть преобразователи монад, хотя для такой простой вариант использования, вероятно, лучше просто создать псевдонимы для ваших функций локально самостоятельно, например myMonadicAdd = (liftM . liftM ) (+), поскольку использование вложенных монад будет специфичным для вашего приложения.   -  person ely    schedule 23.02.2015
comment
liftM . liftM имеет тип, несовместимый с преобразователями монад, поэтому, если вам нужна эта функция очень, вам следует подумать о рефакторинге кода для использования преобразователя монад. Если этот конкретный пример является вашим фактическим вариантом использования, это может быть не очень хороший совет, потому что ListT Maybe a и [Maybe a] - это не одно и то же.   -  person user2407038    schedule 23.02.2015
comment
@ user2407038 Это не мой реальный вариант использования, но я подумал, что это очень просто, это будет хорошим примером для развития интуиции. Затем я попытался использовать ListT Maybe a и обнаружил, что типы действительно не совпадают, что очень плохо.   -  person Lay González    schedule 23.02.2015
comment
Интересно, почему у нас есть liftIO, а не общее решение, которое выполняет все необходимые лифты, работающие с любой монадой. @ Mr.F маленькая ошибка: (liftM . liftM) (+) - ошибка типа, liftM2 нужен для (+)   -  person Lay González    schedule 23.02.2015
comment
Похоже, вам нужен способ поднять на произвольную глубину. К сожалению, нет прямого способа реализовать это, не прибегая к чему-то вроде Template Haskell. Хотя необходимость иногда определять fmap2 немного раздражает, на практике это так редко требуется и так легко определяется, что они не включили для него псевдоним. Вот сообщение в блоге, в котором обсуждается это: byorgey.wordpress .com / 2007/08/16 / mapping-over-a-nested-functor.   -  person danem    schedule 23.02.2015
comment
@ LayGonzález Да, опечатка была в том, что я имел в виду (+1), как в вашем liftM . liftM примере. Было слишком поздно редактировать комментарий, когда я понял, что остановился на 1. Я не собирался использовать пример liftM2.   -  person ely    schedule 23.02.2015
comment
@ user2407038 liftM2.liftM2 работает, потому что в этих случаях liftM2 = liftA2 и аппликативные функторы do составляют (в отличие от монад в целом).   -  person AndrewC    schedule 23.02.2015
comment
@AndrewC, если <$> == liftA, есть ли операнд, скажем <$$>, то <$$> == liftA.liftA?   -  person Lay González    schedule 23.02.2015
comment
@ LayGonzález Мне нравится эта идея - и ты вдохновил меня написать ответ.   -  person AndrewC    schedule 24.02.2015


Ответы (1)


В базе нет такой вещи, но молодец, что задал мне самый интересный вопрос на Stack Overflow в течение некоторого времени.

Функторы и функторы Applicative закрываются при композиции (что, конечно, не относится к монадам в целом, поэтому необходимы преобразователи монад), поэтому liftA2.liftA2 работает здесь, а liftM2 обычно просто liftA2, особенно теперь Applicative становится суперклассом. Монады.

Отступление:

Вы можете использовать композицию newtype в пакете Data.Functor.Compose составить аппликатив, но вы можете создавать новые аппликативы из старых и другими способами - я настоятельно рекомендую Gershom Сообщение Базермана «Абстрагирование с помощью аппликативов» в Comonad Reader для людей, которые хотят понять, насколько прекрасна комбинированная структура аппликативов по сравнению со стеком преобразователей монад - теперь я всегда ищу возможность делать вещи просто аппликативными, а не монадический, где я могу получить нужную мне функциональность. Часто я могу использовать Applicative, чтобы объединить все входные данные в значение, которое я хочу вывести, а затем направить его напрямую туда, где я собираюсь, используя >>=.

Ваши функции и операторы

Конечно, ничто не мешает вам определять свои собственные функции:

liftliftA2 :: (Applicative f, Applicative g) =>
              (a -> b -> c) -> f (g a) -> f (g b) -> f (g c)
liftliftA2 = liftA2.liftA2

но он ненамного короче liftA2.liftA2.

Мне нравится ваша идея сделать вложенные операторы Applicative, но я переключусь на увеличение угловых скобок, а не на повторение внутреннего оператора, потому что <**> конфликтует с (<**>) = flip (<*>) в Control.Applicative, и это более логично.

import Control.Applicative

(<<$>>) :: (Functor f, Functor g) => 
           (a -> b) -> f (g a) -> f (g b)
(<<$>>) = fmap.fmap

(<<*>>) :: (Functor m, Applicative m, Applicative n) =>
            m (n (a -> b)) -> m (n a) -> m (n b)
mnf <<*>> mna = (<*>) <$> mnf <*> mna

давая

ghci> (+) <<$>> [Just 5] <<*>> [Just 7,Just 10]
[Just 12,Just 15]

и, конечно, вы можете продолжать:

(<<<$>>>) :: (Functor f, Functor g, Functor h) => 
             (a -> b) -> f (g (h a)) -> f (g (h b))
(<<<$>>>) = fmap.fmap.fmap

(<<<*>>>) :: (Functor l,Functor m, Applicative l, Applicative m, Applicative n) =>
             l (m (n (a -> b))) -> l (m (n a)) -> l (m (n b))
lmnf <<<*>>> lmna = (<*>) <<$>> lmnf <<*>> lmna

Что позволяет сделать кажущееся невероятным

ghci> subtract <<<$>>> Right [Just 5,Nothing,Just 10] <<<*>>> Right [Just 100,Just 20]
Right [Just 95,Just 15,Nothing,Nothing,Just 90,Just 10]

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

person AndrewC    schedule 24.02.2015
comment
Хотя это не то, что я спросил, он отвечает на то, что я хотел знать. Я изучу твой ответ и пост Базермана. Спасибо. - person Lay González; 24.02.2015