问题 在一个STArrays列表上映射runSTArray?


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

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

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

到现在为止还挺好。我可以检查一下 n-th矩阵(类型 Array (Int, Int) Intghci

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 关键字)以及如何评估列表中的数组?


10928
2017-11-28 15:30


起源

mail-archive.com/haskell-cafe@haskell.org/msg47957.html - Josh Lee


答案:


这是类型技巧的一个不幸结果 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 延期。


10
2017-11-28 16:19



谢谢你的解释,这很有道理。我必须删除 runST 在你的 runSTArrays但是,然后单独调用它。 ghc 不能推断出上下文,也不接受显式类型注释。 - bbtrb
对于那个很抱歉;我已在此代码中添加了相应的类型注释。 GHC不会推断出更高级别的注释(forall),因此需要手动提供。 - John L
是 sequence 有一个占位符,程序将有“一些函数来更新数组内容”? - misterbee
@misterbee - 不, sequence 是否有转换列表 ST s (STArray s ix a) 将操作转换为生成STArrays列表的单个ST操作。如果需要更新数组内容的函数(我不相信它们是用于OP),它们会进入传递给mapM的函数,例如 mapM (\array -> updateArray array >> freeze array)。 - John L
@John L,谢谢。我问过因为我有一个与OP类似的情况,我希望以“mdo”的方式有效地更新一对STArrays中的许多值,后来的更改依赖于先前的更改(数组基本上代表状态 - 搜索算法的空间) - misterbee


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

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

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

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

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

4
2017-11-28 16:15



我会感兴趣的是我的设计可以被打破(不是我怀疑它,我对haskell来说几乎是新手)。您是否有一些关于如何管理不断增加的可变矩阵列表的建议? - bbtrb
@bbtrb - 也许这不是设计本身,而是希望使用列表 ST s ... 的东西。基本上,这样的矩阵是可变数据,这意味着您不能(或至少不应该)在ST或IO操作之外使用它们。正如约翰L告诉你的那样,这是由runST *系列函数强制执行的。 freeze 只是一种告诉Haskell系统的方法,从此你想要将矩阵(或其他)视为只读值,然后它允许在ST动作中构造的值的转义(副本)。 - Ingo