Ясно, что >>=
не является способом представления композиции функций. Композиция функций просто выполняется с помощью .
. Однако я не думаю, что ни одна из прочитанных вами статей имела в виду это.
Они имели в виду модернизировать композицию функций для работы непосредственно с монадическими функциями, то есть функциями формы 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
(=<<) :: Monad m => (a -> m b) -> m a -> m b
и(<=<) :: (b -> m c) -> (a -> m b) -> (a -> m c)
. - person rampion   schedule 31.12.2015Applicative
версия:(<*>) :: 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.2015m a
. Это значение должно быть создано какой-то приукрашенной функцией (возможно,return
). Таким образом, bind действительно составляет функцию, которая создала значение, с другой функцией (продолжение). Приложение также может использоваться для композиции, но оно также может воздействовать на литералы. Конечно, литерал можно заменить лямбдой, принимающей единицу, и тогда приложение будет просто упрощенной композицией. - person Bartosz Milewski   schedule 02.01.2016