Оператор связывания монады (›› =) ближе к композиции (объединению в цепочку) или применению функции?

Во многих статьях я читал, что оператор монады >>= - это способ представления композиции функций. Но для меня это больше похоже на какое-то приложение с расширенными функциями

($)   :: (a -> b) -> a -> b
(>>=) :: Monad m => m a -> (a -> m b) -> m b

Для композиции у нас есть

(.)   :: (b -> c) -> (a -> b) -> a -> c
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c

Просьба уточнить.


person Sergii Vorobei    schedule 31.12.2015    source источник
comment
Сходство еще более очевидно с их перевернутыми вариантами: (=<<) :: Monad m => (a -> m b) -> m a -> m b и (<=<) :: (b -> m c) -> (a -> m b) -> (a -> m c).   -  person rampion    schedule 31.12.2015
comment
Также есть Applicative версия: (<*>) :: Applicative m => m (a -> b) -> m a -> m b и liftA2 (.) :: Applicative m => m (b -> c) -> m (a -> b) -> m (a -> c)   -  person rampion    schedule 31.12.2015
comment
На ваш вопрос нет настоящего ответа. В конечном счете, монадическое связывание - это то, чем оно является, ни меньше, ни больше. Тем не менее, ваша точка зрения полностью * разумна * - просто вместо того, чтобы пытаться найти здесь истину, возможно, вам следует просто признать, что аналогия с композицией зашла так далеко (а это очень далеко!), И не беспокоиться о ее недостатках. .   -  person Luis Casillas    schedule 01.01.2016
comment
Один из способов подумать об этом состоит в том, что приложение-функция принимает значение простого типа и применяет к нему функцию. Bind, с другой стороны, принимает значение типа украшенный, m a. Это значение должно быть создано какой-то приукрашенной функцией (возможно, return). Таким образом, bind действительно составляет функцию, которая создала значение, с другой функцией (продолжение). Приложение также может использоваться для композиции, но оно также может воздействовать на литералы. Конечно, литерал можно заменить лямбдой, принимающей единицу, и тогда приложение будет просто упрощенной композицией.   -  person Bartosz Milewski    schedule 02.01.2016


Ответы (2)


Ясно, что >>= не является способом представления композиции функций. Композиция функций просто выполняется с помощью .. Однако я не думаю, что ни одна из прочитанных вами статей имела в виду это.

Они имели в виду модернизировать композицию функций для работы непосредственно с монадическими функциями, то есть функциями формы a -> m b. Технический термин для таких функций - стрелки Клейсли, и действительно, они могут состоять из <=< или >=>. (В качестве альтернативы вы можете использовать экземпляр Category, тогда вы также можете составить их с помощью . или >>>.)

Однако разговор о стрелках / категориях обычно сбивает с толку, особенно новичков, точно так же, как часто бывают бессочечные определения обычных функций. сбивает с толку. К счастью, Haskell позволяет нам выражать функции в более знакомом стиле, который фокусируется на результатах функций, а не на самих функциях как абстрактных морфизмах . Это делается с помощью лямбда-абстракции: вместо

q = h . g . f

вы можете написать

q = (\x -> (\y -> (\z -> h z) (g y)) (f x))

... конечно, предпочтительным стилем будет (это всего лишь синтаксический сахар для лямбда-абстракции!)

q x = let y = f x
          z = g y
      in h z

Обратите внимание, как в лямбда-выражении композиция была заменена приложением:

q = \x -> (\y -> (\z -> h z) $ g y) $ f x

Адаптировано к стрелкам Клейсли, это означает вместо

q = h <=< g <=< f

ты пишешь

q = \x -> (\y -> (\z -> h z) =<< g y) =<< f x

что снова выглядит намного лучше с перевернутыми операторами или синтаксическим сахаром:

q x = do y <- f x
         z <- g y
         h z

Итак, действительно, =<< для <=<, как $ для .. Причина, по которой все еще имеет смысл называть его оператором композиции, заключается в том, что, помимо применения к значениям, оператор >>= также выполняет нетривиальную часть композиции стрелок Клейсли, в которой композиция функций не нуждается: соединение монадических слоев.


Причина, по которой это работает, заключается в том, что Hask является декартовой закрытой категорией, в частности, четко обозначенной категории. В такой категории стрелки могут, в широком смысле, определяться набором всех их результатов при применении к простым значениям аргументов.

@adamse отмечает, что let на самом деле не является синтаксическим сахаром для лямбда-абстракции. Это особенно актуально в случае рекурсивных определений, которые нельзя напрямую записать с помощью лямбда. Но в таких простых случаях, как здесь, let действительно ведет себя как синтаксический сахар для лямбда-выражений, точно так же, как do нотация является синтаксическим сахаром для лямбда-выражений и >>=. (Кстати, есть расширение, которое позволяет рекурсию даже в do нотации ... оно обходит лямбда-ограничение с помощью комбинаторов с фиксированной точкой .)

person leftaroundabout    schedule 31.12.2015
comment
Я думал, что цитата о представлении композиции функций относится к _1 _ = _ 2_ для функций как к экземпляру монады. - person Bergi; 31.12.2015
comment
Я не думаю, что будет правильным сказать, что привязки let являются сахаром для лямбда-выражений в Haskell: haskell.org/onlinereport/haskell2010/ - person adamse; 31.12.2015
comment
@adamse: верно, let действительно более общий, чем лямбды. - person leftaroundabout; 31.12.2015

Рассмотрим это в качестве иллюстрации:

($)                ::   (a -> b) ->   a ->   b
let g=g in (g  $)  ::                 a ->   b
            g      ::   (a -> b)
                                     _____
Functor f =>                        /     \
(<$>)              ::   (a -> b) -> f a -> f b
let g=g in (g <$>) ::               f a -> f b
            g      ::   (a -> b) 
                       ___________________
Applicative f =>      /             /     \
(<*>)              :: f (a -> b) -> f a -> f b
let h=h in (h <*>) ::               f a -> f b
            h      :: f (a -> b)
                             _____________
Monad m =>                  /.------.     \
(=<<)              :: (a -> m b) -> m a -> m b
let k=k in (k =<<) ::               m a -> m b
            k      :: (a -> m b)

Итак, да, каждый из них, (g <$>), (h <*>) или (k =<<), представляет собой своего рода приложение-функцию, продвинутое в "контекст" либо в функтор, либо в аппликативный функтор, либо в монаду. А (g $) - это просто обычное приложение обычной функции.

С функторами функции не влияют на f компонент вещи в целом. Они работают строго внутри и не могут повлиять на «упаковку».

В Applicatives функции заключены в f, который объединяется с оболочкой аргумента (как части приложения) для создания оболочки результата.

В монадах сами функции теперь производят обернутые результаты, извлекая свои аргументы каким-то образом из обернутого аргумента (как часть приложения).

Мы можем рассматривать три оператора как своего рода маркировку функции, как математики любят писать, скажем, f', f^ или f* (и в исходной работе Эудженио Моджи (1) f* именно то, что использовалась, обозначая продвигаемую функцию (f =<<)).

И, конечно же, с повышенными функциями :: f a -> f b мы можем объединить их в цепочки, потому что теперь типы выстраиваются в одну линию. Раскрутка - это то, что позволяет композиция.


(1) «Понятия вычислений и монад», Эудженио Моджи, июль 1991 г.


Итак, функтор «волшебным образом работает внутри» «труб»; аппликативный - «сборные трубы, построенные из компонентов заранее»; а монады «строят трубопроводные сети на ходу». Иллюстрация:

введите описание изображения здесь

person Will Ness    schedule 02.01.2016