在一个STArrays列表上映射runSTArray?

时间:2011-11-28 15:30:17

标签: arrays haskell state higher-rank-types

我有一个函数,它从树中递归地创建一个扁平的矩阵列表,这些矩阵必须是可变的,因为它们的元素在创建过程中经常更新。到目前为止,我已经提出了一个具有签名的递归解决方案:

doAll :: .. -> [ST s (STArray s (Int, Int) Int)]

我不直接返回[UArray (Int,Int) Int]的原因是因为doAll是递归调用的,修改了列表中矩阵的元素并附加了新的矩阵。我不想不必要地冻结和解冻基质。

到目前为止一切顺利。我可以检查n

中的Array (Int, Int) Int - 矩阵(类型ghci
runSTArray (matrices !! 0)
runSTArray (matrices !! 1)

确实我的算法得到了正确的结果。但是,我找不到将runSTUArray映射到doAll返回的列表的方法:

map (runSTArray) matrices

Couldn't match expected type `forall s. ST s (STArray s i0 e0)'
            with actual type `ST s0 (STArray s0 (Int, Int) Int)'

如果我尝试在列表上递归计算或尝试评估函数中包含的单个元素,则会出现同样的问题

有人可以解释一下发生了什么(我真的不明白forall关键字的含义)以及我如何评估列表中的数组?

2 个答案:

答案 0 :(得分:10)

这是使ST安全的类型技巧的不幸结果。首先,您需要了解ST的工作原理。从ST monad到纯代码的唯一方法是使用runST函数或其他基于runSTArray的函数。这些都是forall s.的形式。这意味着,为了从STArray构造一个Array,编译器必须能够确定它可以替换它所喜欢的任何类型的s内的runST类型变量。

现在考虑函数map :: (a -> b) -> [a] -> [b]。这表明列表中的每个元素必须具有完全相同的类型(a),因此也必须具有相同的s。但是这个额外的约束违反了runSTArray的类型,它声明编译器必须能够自由地替换s的其他值。

你可以通过定义一个新函数来解决这个问题,首先冻结ST monad中的数组,然后运行生成的ST动作:

runSTArrays :: Ix ix => (forall s. [ST s (STArray s ix a)]) -> [Array ix a]
runSTArrays arrayList = runST $ (sequence arrayList >>= mapM freeze)

请注意,forall需要RankNTypes扩展名。

答案 1 :(得分:4)

你刚刚反对类型系统的限制。

runSTArray的排名类型较高。您必须将状态类型变量唯一的ST动作传递给它。然而,在Haskell中,通常不可能在列表中包含这样的值。

整个事情是一个聪明的方案,以确保您在ST动作中产生的值无法从那里逃脱。这意味着,您的设计看起来有点破碎。

一个建议:你不能处理另一个ST动作中的值,比如

sequence [ ... your ST s (STArray s x) ...] >>= processing
  where
     processing :: [STArray s x] -> ST s (your results)