序列部分应用的Monadic动作
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了序列部分应用的Monadic动作相关的知识,希望对你有一定的参考价值。
我知道这可能是显而易见的,但我的google-fu在这里让我失望。我有一个类型的动作列表:
import Data.Vector.Mutable (STVector)
[STVector s a -> ST s ()]
也就是说,一组采取启动MVector并以某种方式改变它的动作
我也有一个起始向量
import Data.Vector (Vector, thaw, freeze)
v :: Vector a
解冻v
后,如何将这些动作排序为最终结果?
doIt :: forall s. Vector a -> [STVector s a -> ST s ()] -> Vector a
doIt v ops = runST $ do
v' <- thaw v
-- do all the things to v'
unfreeze v'
如果上下文有帮助,我正在尝试代码的第16天谜题(第2部分),所以ops
是一个很长的突变列表,我实际上需要经历十亿次。我希望能够使用replicateM_
来做到这一点,但无法看到如何提供起始值。我同样认为foldM_
会工作,但我似乎无法得到它来检查。也许我在做一些根本错误的事情?我不能说我了解ST monad的前后任务。
您正在寻找的操作是traverse_
。它访问数据结构中的每个值,应用具有monadic返回类型的函数,并在丢弃其结果的同时对它们的效果进行排序。 “丢弃他们的结果”部分很重要。这意味着在这种情况下traverse_
的返回类型将是ST s ()
而不是ST s [()]
。差异很大。这意味着该操作不会建立一个巨大的()
值列表,最终扔掉。
您要传递给traverse_
的函数是指“将函数应用于v'
”的函数。这可以写成f -> f v'
,但有一个较短的版本,这是惯用的,并使用$
运算符。请注意,您可以将上面的lambda重写为f -> f $ v'
,可以将其重写为($ v')
部分。
所以你有traverse_ ($ v') ops
,这是正确的操作,但它仍然不会打字检查。那是因为runST
的类型,这要求s
中的ST s
类型是多态的。使用您的类型签名,s
由doIt
的调用者选择。要使其工作,您需要通过确保{-# LANGUAGE RankNTypes #-}
位于文件的顶部,然后将类型更改为Vector a -> (forall s. [STVector s a -> ST s ()]) -> Vector a
来启用RankNTypes扩展。这种s
类型变量范围的缩小要求s
在传递给doIt
的值中具有多态性。有了这个限制,runST
将通过上述操作成功进行检查。
有关为什么runST
具有如此有趣类型的更多信息,请参阅Lazy Functional State Threads,该文章介绍了ST类型并展示了它如何用于约束外部纯接口内的可变性。
以上是关于序列部分应用的Monadic动作的主要内容,如果未能解决你的问题,请参考以下文章
Scalaz(33)- Free :算式-Monadic Programming