monad 中的纯映射
Pure maps in a monad
假设我有两个函数
f :: Monad m => a -> m a
g :: a -> a
我想像这样连续应用于某些元素:
(return x) >>= f >>= g
这是行不通的,因为 g 是纯的,所以我首先需要 "artificially" 将它变成单子的。一种可能是
(return x) >>= f >>= (return . g)
这对我来说不是很直观。另一种可能性是使用 Monad 是 Applicative:
(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
,除了运算符优先级之外,这很好。当然,monadic 代码中的纯函数很常见,所以肯定有处理它们的标准方法吗?
编辑:我最初并没有这么说,但我在考虑这样一种情况,我有几个函数,一些是纯函数,一些是单子函数,我想以某种随机顺序应用它们。
你这里描述的是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
感谢@WillemVanOnsems 指向 Control.Monad 的指针,我找到了一个解决方案,这符合我一直在寻找的精神,即使用 =<<
,它具有与 [=] 相同的参数顺序12=] 和 <$>
:
g <$> (f =<< f =<< g <$> g <$> (f =<< y))
现在 y
已经是 monadic(比如 return x
)。我唯一不满意的是由于运算符优先级而导致的括号。
您可以为 (>>==)
运算符提供适当的优先级以允许删除括号
(>>==) = flip ((<*>) . pure)
infixl 1 >>==
然后
> (>>==) = flip ((<*>) . pure); infixl 1 >>==
> pure 3 >>= (\x -> [x, x]) >>== (+1)
[4,4]
我认为最直接的解决方案是 Control.Monad
模块中的 Kleisli 组合 >=>
。
由于>=>
和(.)
都是右结合的,(.)
具有更高的优先级,可以这样写:
-- (>=>) :: 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)
假设我有两个函数
f :: Monad m => a -> m a
g :: a -> a
我想像这样连续应用于某些元素:
(return x) >>= f >>= g
这是行不通的,因为 g 是纯的,所以我首先需要 "artificially" 将它变成单子的。一种可能是
(return x) >>= f >>= (return . g)
这对我来说不是很直观。另一种可能性是使用 Monad 是 Applicative:
(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
,除了运算符优先级之外,这很好。当然,monadic 代码中的纯函数很常见,所以肯定有处理它们的标准方法吗?
编辑:我最初并没有这么说,但我在考虑这样一种情况,我有几个函数,一些是纯函数,一些是单子函数,我想以某种随机顺序应用它们。
你这里描述的是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
感谢@WillemVanOnsems 指向 Control.Monad 的指针,我找到了一个解决方案,这符合我一直在寻找的精神,即使用 =<<
,它具有与 [=] 相同的参数顺序12=] 和 <$>
:
g <$> (f =<< f =<< g <$> g <$> (f =<< y))
现在 y
已经是 monadic(比如 return x
)。我唯一不满意的是由于运算符优先级而导致的括号。
您可以为 (>>==)
运算符提供适当的优先级以允许删除括号
(>>==) = flip ((<*>) . pure)
infixl 1 >>==
然后
> (>>==) = flip ((<*>) . pure); infixl 1 >>==
> pure 3 >>= (\x -> [x, x]) >>== (+1)
[4,4]
我认为最直接的解决方案是 Control.Monad
模块中的 Kleisli 组合 >=>
。
由于>=>
和(.)
都是右结合的,(.)
具有更高的优先级,可以这样写:
-- (>=>) :: 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)