是否有"链"哈斯克尔的monad函数?

时间:2015-10-20 03:55:29

标签: haskell monads

解释"重复"

有人指出Is this a case for foldM?可能是重复的。现在,我有一个强烈的意见,即可以用相同答案回答的两个问题不一定是重复的! "什么是1 - 2"和"什么是我^ 2"两者都产生" -1"但不,它们不是重复的问题。我的问题(已经回答,类似)是关于"函数iterateM是否存在于Haskell标准库"中,而不是"如何实现链式monad操作"。

问题

当我写一些项目时,我发现自己编写了这个组合器:

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搜索,但没有找到任何附带&#34;标准&#34;哈斯克尔。是否有预定义的正式功能?

3 个答案:

答案 0 :(得分:10)

您可以使用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

答案 1 :(得分:9)

Carsten mentioned replicate,这并不是一个糟糕的想法。

import Control.Monad
repeatM n f = foldr (>=>) pure (replicate n f)

这背后的想法是,对于任何monad ma -> 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类别的一个monoid,因此我们可以考虑折叠这些箭头的列表。 / p>

上面的代码所做的是将合成运算符折叠成n fx >=> y >=> z >=> w副本列表,像往常一样以身份完成。翻转合成操作符实际上使我们进入了双重类别;对于许多常见的monad,w <=< z <=< y <=< x<=<更有效;因为在这种情况下所有的箭头都是相同的,所以我们似乎也可以。请注意,对于延迟状态monad以及读者monad,最好使用未翻转的>=>运算符;对于IOST s和通常的严格状态, <receiver android:name="android.app.admin.DeviceAdminReceiver" android:label="@string/admin_app" android:description="@string/admin_app_description" android:permission="android.permission.BIND_DEVICE_ADMIN"> <meta-data android:name="android.app.device_admin" android:resource="@xml/admin_app_config"/> <intent-filter> <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/> </intent-filter> </receiver> 通常会更好。

注意:我不是类别理论家,所以上面的解释可能有错误。

答案 2 :(得分:2)

我发现自己经常想要这个功能,我希望它有一个标准名称。但是,该名称不会是repeatM - 这将是无限重复,如果它存在,就像forever一样,只是为了与其他库保持一致(并且repeatM在某些库中定义)。

正如已经给出的答案的另一个观点,我指出(s -> m s)看起来有点像状态类型为s的状态monad中的一个动作。

实际上,它与StateT s m ()是同构的 - 一个不返回任何值的动作,因为它所做的所有工作都以它改变状态的方式封装。在这个monad中,你想要的功能是replicateM。你可以用这种方式在haskell中编写它,虽然它可能看起来比直接写它更丑。

首先将s -> m s转换为StateT使用的等效表单,添加无信息(),使用liftM在返回类型上映射函数。

> :t \f -> liftM (\x -> ((),x)) . f
\f -> liftM (\x -> ((),x)) . f :: Monad m => (a -> m t) -> a -> m ((), t)

(本来可以使用fmap,但Monad约束在这里看起来更清晰;如果你愿意,可以使用TupleSections;如果你发现表示法更容易阅读,那么它只是\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不会很有趣:

> :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