序列部分应用的Monadic动作

时间:2017-12-23 01:01:03

标签: haskell state-monad

我知道这可能是显而易见的,但是我的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已经向后和向前。

1 个答案:

答案 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 as类型变量范围的缩小要求s在传递给doIt的值中具有多态性。有了这个限制,runST将使用上面的操作成功检查。

有关runST具有如此有趣类型的原因的详细信息,请参阅Lazy Functional State Threads,介绍ST类型的文章,并说明如何使用它来限制外部纯接口内的可变性。