在什么意义上是 (# a #),一个元素未装箱的元组,与 'a' 不同?

dan*_*iaz 11 haskell

Data.Primitive.SmallArray,有函数:

indexSmallArray## :: SmallArray a -> Int -> (#a#)
Run Code Online (Sandbox Code Playgroud)

它以单元素未装箱的 tuple返回其结果。

我的问题是:我们从返回未装箱的元组中获得什么?为什么不简单地返回a

我确实得到了多个元素的未装箱元组的用处。正如Levity Polymorphism论文所说:

最初设想支持从函数返回多个值,未装箱的元组只是用于将多个值绑定在一起的 Haskell 语法。未装箱的元组在运行时根本不存在 [...] 在编译期间,未装箱的元组被完全擦除

dan*_*iaz 8

似乎目的是将索引到数组的工作与“强制”a自身与 WHNF 分离。

如果我们只是返回a,那么评估indexSmallArray##对 WHNF的调用将执行索引并将结果评估为 WHNF。

模式匹配(# a #)将执行索引(从而释放对隐藏在 thunk 后面的数组的引用,可能允许数组被垃圾收集)但不会强制单个组件a到 WHNF(这可能是undefined我们所知道的)。

在使用类似技术的 primop的文档中,我们发现:

primop IndexArrayOp "indexArray#" GenPrimOp
Array# a -> Int# -> (#> a #)
{从不可变数组的指定索引读取。结果被打包成一个未装箱的一元元组;结果本身尚未评估。元组上的模式匹配会强制对数组进行索引,但不会评估元素本身。立即执行此模式匹配对于 (1) 防止在堆上构建额外的 thunk 和 (2) 消除对参数数组的引用,允许它更迅速地被垃圾回收很有用。}

我想我们可以使用像 一样的包装器类型来完成相同的工作data Wrap a = Wrap a,但这会导致不必要的堆分配,而这不会发生在未装箱的元组中。

  • 还有“indexSmallArrayM”,它允许您在任何“Monad”(实际上,它应该是任何“Applicative”)的上下文中进行索引,包括假设的“Wrap”。有一个被接受的 GHC 提案公开了像你的“Wrap”这样的东西,但我不太确定它最终去了哪里或者它叫什么,而且它还没有被广泛使用。 (2认同)