我知道这可能是显而易见的,但是我的hoogle-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
是一个很长的突变列表,我实际上需要通过10亿倍。我希望能够使用replicateM_
来执行此操作,但无法查看如何提供起始值。我同样认为foldM_
会起作用,但我似乎无法得到它来进行类型检查。也许我做了一些根本错误的事情?我不能说我理解ST monad已经向后和向前。
答案 0 :(得分:3)
您正在寻找的操作是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
。要使其工作,您需要启用RankNTypes扩展,方法是确保{-# LANGUAGE RankNTypes #-}
位于文件的顶部,然后将类型更改为Vector a -> (forall s. [STVector s a -> ST s ()]) -> Vector a
。 s
类型变量范围的缩小要求s
在传递给doIt
的值中具有多态性。有了这个限制,runST
将使用上面的操作成功检查。
有关runST
具有如此有趣类型的原因的详细信息,请参阅Lazy Functional State Threads,介绍ST类型的文章,并说明如何使用它来限制外部纯接口内的可变性。