在Data.Primitive.SmallArray,有函数:
indexSmallArray## :: SmallArray a -> Int -> (#a#)
Run Code Online (Sandbox Code Playgroud)
它以单元素未装箱的 tuple返回其结果。
我的问题是:我们从返回未装箱的元组中获得什么?为什么不简单地返回a?
我确实得到了多个元素的未装箱元组的用处。正如Levity Polymorphism论文所说:
最初设想支持从函数返回多个值,未装箱的元组只是用于将多个值绑定在一起的 Haskell 语法。未装箱的元组在运行时根本不存在 [...] 在编译期间,未装箱的元组被完全擦除
似乎目的是将索引到数组的工作与“强制”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,但这会导致不必要的堆分配,而这不会发生在未装箱的元组中。