Haskell 中是否有 "chain" monad 函数?
Is there a "chain" monad function in Haskell?
解释一个"duplicate"
有人指出 Is this a case for foldM? 可能是重复的。现在,我有一个强烈的看法,可以用相同答案回答的两个问题不一定是重复的! "What is 1 - 2" 和 "What is i^2" 都产生“-1”,但不,它们不是重复的问题。我的问题(已经回答了)是关于 "whether the function iterateM
exists in Haskell standard library",而不是 "How to implement a chained monad action"。
问题
当我写一些项目时,我发现自己写了这个组合器:
repeatM :: Monad m => Int -> (a -> m a) -> a -> m a
repeatM 0 _ a = return a
repeatM n f a = (repeatM (n-1) f) =<< f a
它只是执行一个 monadic 动作 n
次,将之前的结果输入到下一个动作中。我尝试了一些 hoogle
搜索和一些 Google 搜索,但没有找到 "standard" Haskell 附带的任何内容。有这样一个预定义的形式函数吗?
您可以使用foldM
,例如:
import Control.Monad
f a = do print a; return (a+2)
repeatM n f a0 = foldM (\a _ -> f a) a0 [1..n]
test = repeatM 5 f 3
-- output: 3 5 7 9 11
Carsten replicate
,这个想法不错。
import Control.Monad
repeatM n f = foldr (>=>) pure (replicate n f)
这背后的想法是,对于任何 monad m
,类型 a -> m b
的函数形成 m
的 Kleisli 范畴,带有标识箭头
pure :: a -> m a
(也称为return
)
和组合运算符
(<=<) :: (b -> m c) -> (a -> m b) -> a -> m c
f <=< g = \a -> f =<< g a
由于我们实际上是在处理 a -> m a
类型的函数,我们实际上是在看 Kleisli 类别的一个幺半群,所以我们可以考虑折叠 lists这些箭头。
上面的代码所做的是折叠组合运算符,将其翻转到 f
的 n
副本列表中,像往常一样以标识结束。翻转组合运算符实际上将我们置于对偶类别中;对于许多常见的单子,x >=> y >=> z >=> w
比 w <=< z <=< y <=< x
更有效;因为在这种情况下所有的箭头都是 相同的 ,看来我们也可以。请注意,对于惰性状态 monad 以及 reader monad,最好使用未翻转的 <=<
运算符; >=>
通常对 IO
、ST s
和通常的严格状态更好。
注意:我不是范畴论者,所以上面的解释可能有错误。
我发现自己经常想要这个功能,我希望它有一个标准的名字。然而,该名称不会是 repeatM
- 这将是无限重复的,例如 forever
如果它存在,只是为了与其他库保持一致(并且 repeatM
在某些库中以这种方式定义).
正如已经给出的答案的另一个观点,我指出 (s -> m s)
看起来有点像状态类型为 s
.
的 State monad 中的一个动作
事实上,它与 StateT s m ()
同构 - 一个 return 没有价值的动作,因为它所做的所有工作都封装在它改变状态的方式中。在这个 monad 中,你真正想要的函数是 replicateM
。你可以在 haskell 中这样写,虽然它可能看起来比直接写它更难看。
首先将 s -> m s
转换为 StateT
使用的等效形式,添加无信息 ()
,使用 liftM
将函数映射到 return类型。
> :t \f -> liftM (\x -> ((),x)) . f
\f -> liftM (\x -> ((),x)) . f :: Monad m => (a -> m t) -> a -> m ((), t)
(本可以使用 fmap
但 Monad 约束在这里似乎更清晰;如果您愿意,本可以使用 TupleSections
;如果您发现 do 表示法更易于阅读,它只是 \f s -> do x <- f s; return ((),s)
).
现在这有正确的类型可以用 StateT 结束:
> :t StateT . \f -> liftM (\x -> ((),x)) . f
StateT . \f -> liftM (\x -> ((),x)) . f :: Monad m => (s -> m s) -> StateT s m ()
然后您可以使用 replicateM_
版本复制它 n 次,因为来自 replicateM
的 returned 列表 [()]
不会有趣:
> :t \n -> replicateM_ n . StateT . \f -> liftM (\x -> ((),x)) . f
\n -> replicateM_ n . StateT . \f -> liftM (\x -> ((),x)) . f :: Monad m => Int -> (s -> m s) -> StateT s m ()
最后你可以使用 execStateT
回到你最初工作的 Monad:
runNTimes :: Monad m => Int -> (s -> m s) -> s -> m s
runNTimes n act =
execStateT . replicateM_ n . StateT . (\f -> liftM (\x -> ((),x)) . f) $ act
解释一个"duplicate"
有人指出 Is this a case for foldM? 可能是重复的。现在,我有一个强烈的看法,可以用相同答案回答的两个问题不一定是重复的! "What is 1 - 2" 和 "What is i^2" 都产生“-1”,但不,它们不是重复的问题。我的问题(已经回答了)是关于 "whether the function iterateM
exists in Haskell standard library",而不是 "How to implement a chained monad action"。
问题
当我写一些项目时,我发现自己写了这个组合器:
repeatM :: Monad m => Int -> (a -> m a) -> a -> m a
repeatM 0 _ a = return a
repeatM n f a = (repeatM (n-1) f) =<< f a
它只是执行一个 monadic 动作 n
次,将之前的结果输入到下一个动作中。我尝试了一些 hoogle
搜索和一些 Google 搜索,但没有找到 "standard" Haskell 附带的任何内容。有这样一个预定义的形式函数吗?
您可以使用foldM
,例如:
import Control.Monad
f a = do print a; return (a+2)
repeatM n f a0 = foldM (\a _ -> f a) a0 [1..n]
test = repeatM 5 f 3
-- output: 3 5 7 9 11
Carsten replicate
,这个想法不错。
import Control.Monad
repeatM n f = foldr (>=>) pure (replicate n f)
这背后的想法是,对于任何 monad m
,类型 a -> m b
的函数形成 m
的 Kleisli 范畴,带有标识箭头
pure :: a -> m a
(也称为return
)
和组合运算符
(<=<) :: (b -> m c) -> (a -> m b) -> a -> m c
f <=< g = \a -> f =<< g a
由于我们实际上是在处理 a -> m a
类型的函数,我们实际上是在看 Kleisli 类别的一个幺半群,所以我们可以考虑折叠 lists这些箭头。
上面的代码所做的是折叠组合运算符,将其翻转到 f
的 n
副本列表中,像往常一样以标识结束。翻转组合运算符实际上将我们置于对偶类别中;对于许多常见的单子,x >=> y >=> z >=> w
比 w <=< z <=< y <=< x
更有效;因为在这种情况下所有的箭头都是 相同的 ,看来我们也可以。请注意,对于惰性状态 monad 以及 reader monad,最好使用未翻转的 <=<
运算符; >=>
通常对 IO
、ST s
和通常的严格状态更好。
注意:我不是范畴论者,所以上面的解释可能有错误。
我发现自己经常想要这个功能,我希望它有一个标准的名字。然而,该名称不会是 repeatM
- 这将是无限重复的,例如 forever
如果它存在,只是为了与其他库保持一致(并且 repeatM
在某些库中以这种方式定义).
正如已经给出的答案的另一个观点,我指出 (s -> m s)
看起来有点像状态类型为 s
.
事实上,它与 StateT s m ()
同构 - 一个 return 没有价值的动作,因为它所做的所有工作都封装在它改变状态的方式中。在这个 monad 中,你真正想要的函数是 replicateM
。你可以在 haskell 中这样写,虽然它可能看起来比直接写它更难看。
首先将 s -> m s
转换为 StateT
使用的等效形式,添加无信息 ()
,使用 liftM
将函数映射到 return类型。
> :t \f -> liftM (\x -> ((),x)) . f
\f -> liftM (\x -> ((),x)) . f :: Monad m => (a -> m t) -> a -> m ((), t)
(本可以使用 fmap
但 Monad 约束在这里似乎更清晰;如果您愿意,本可以使用 TupleSections
;如果您发现 do 表示法更易于阅读,它只是 \f s -> do x <- f s; return ((),s)
).
现在这有正确的类型可以用 StateT 结束:
> :t StateT . \f -> liftM (\x -> ((),x)) . f
StateT . \f -> liftM (\x -> ((),x)) . f :: Monad m => (s -> m s) -> StateT s m ()
然后您可以使用 replicateM_
版本复制它 n 次,因为来自 replicateM
的 returned 列表 [()]
不会有趣:
> :t \n -> replicateM_ n . StateT . \f -> liftM (\x -> ((),x)) . f
\n -> replicateM_ n . StateT . \f -> liftM (\x -> ((),x)) . f :: Monad m => Int -> (s -> m s) -> StateT s m ()
最后你可以使用 execStateT
回到你最初工作的 Monad:
runNTimes :: Monad m => Int -> (s -> m s) -> s -> m s
runNTimes n act =
execStateT . replicateM_ n . StateT . (\f -> liftM (\x -> ((),x)) . f) $ act