Чистые карты в монаде - PullRequest
4 голосов
/ 22 января 2020

Предположим, у меня есть две функции

f :: Monad m => a -> m a
g :: a -> a

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

(return x) >>= f >>= g

Это не работает, потому что g чисто, поэтому я Сначала нужно «искусственно» включить монаду c. Одна возможность -

(return x) >>= f >>= (return . g)

, которая не очень интуитивна для меня. Другая возможность состоит в том, чтобы использовать монаду Аппликативно:

(return g) <*> ((return x) >>= f)

Но это не очень интуитивно понятно из-за разного порядка функций и аргументов:

(>>=) :: Monad m => m a -> (a -> m b) -> m b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

Что такое канонический способ иметь дело с этим? Если (>>==) = (flip ((<*>) . pure)), можно написать ((pure x) >>= f) >>== g, что было бы хорошо, за исключением приоритета оператора. Конечно, чистые функции в коде monadi c являются обычным делом, поэтому наверняка есть стандартный способ с ними справиться?

Редактировать: Первоначально я не говорил этого, но я думал о ситуации, когда я имел несколько функций, некоторые чистые, некоторые монади c, и я хотел применить их в некотором случайном порядке.

Ответы [ 4 ]

11 голосов
/ 22 января 2020

То, что вы здесь описываете, это fmap :: Functor f => (a -> b) -> f a -> f b. Кроме того, поскольку pure x >>= f должно совпадать с f x, мы можем упростить данное выражение до:

<b>fmap</b> g (f x)

или использовать псевдоним инфикса (<$>) :: Functor f => (a -> b) -> f a -> f b :

g <b><$></b> f x
5 голосов
/ 22 января 2020

Я думаю, что самым простым решением является композиция Клейсли >=> из Control.Monad модуля.

Поскольку >=> и (.) являются ассоциативными справа и (.) имеет более высокий приоритет, вы можете написать следующее:

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

f :: Monad m => a -> m a
g :: a -> a
h :: Monad m => a -> m b
j :: b -> b

q :: Monad m => a -> m b
q  =  f >=> return . g >=> h >=> return . j
--       |         |- Use (return .) to transform (a -> b) into (a -> m b)
--       |- Use Kleisli composition >=> to compose Kleisli arrows, i.e.
--       |-   the functions going from values to monadic values, (a -> m b)
4 голосов
/ 22 января 2020

Вы можете дать своему оператору (>>==) соответствующий приоритет, чтобы разрешить удаление скобок

(>>==) = flip ((<*>) . pure)
infixl 1 >>==

Тогда

> (>>==) = flip ((<*>) . pure); infixl 1 >>==
> pure 3 >>= (\x -> [x, x]) >>== (+1)
[4,4]
1 голос
/ 22 января 2020

Решение, которое я нашел благодаря указателю @WillemVanOnsems на Control.Monad и которое соответствует духу того, что я искал, заключается в использовании =<<, который имеет тот же порядок аргументов, что и <*> и <$>:

g <$> (f =<< f =<< g <$> g <$> (f =<< y))

, где сейчас y уже является монади c (скажем, return x). Единственное, что меня не устраивает - это круглые скобки из-за приоритета оператора.

...