延迟评估中的原始函数类型

Type of primitive functions in lazy evaluation

我想知道像 Haskell 这样的惰性函数式编程语言中原始函数的确切类型是什么。

假设 thunk 被评估为弱头部范式的对象。那么,在像C这样的严格语言中,原始函数的类型应该是什么?

我的猜测是:

primitive1 : (thunk, thunk, ...) -> thunk
primitive2 : (thunk, thunk, ...) -> object

我认为原始函数应该作为参数传递 thunk,因为它们可能不需要其中的一些。但是,我不知道他们是否应该 return thunk 或 evaluated 对象,而后者必须用下面的一些函数包装以使其惰性。

lazy : ((thunk, thunk, ...) -> object) -> ((thunk, thunk, ...) -> thunk)

在 GHC 中,Haskell 原始函数(有时称为 PrimOp)是 使用混合指针(指向 "heap objects")调用并取消装箱 类型(包括 C 风格的整数、双精度数等)。 Haskell 类型系统 确保 PrimOps 始终获得对象的数量、顺序和类型 他们期望的指针和未装箱的值。这一点很重要 但是要注意,在原语期望指向特定类型堆对象的指针的地方,比如 String, 它期待一个指向堆对象的指针,该对象可能 是一个列表 构造函数(因为 Haskell String 是一个字符列表) thunk 可以评估为列表构造函数。

因此,Haskell PrimOp 的 "strict" 类型无法区分 在 thunk 和非 thunk 之间。如果有一个原始函数 获取列表的长度,例如(没有),它可能具有类型,使用您的符号, of:

primitiveLength : (list_object) -> unboxed_int

其中 list_object 将是指向 或者 列表的指针 constructor 可以生成列表构造函数的thunk。

这确实是唯一明智的做法。 PrimOp 无法控制它的参数是仍然是一个 thunk 还是已经部分(或完全!)评估了一些先前的计算,因此它必须准备好接受任何一个。

类似地,如果 Haskell PrimOp returns 是堆对象,该对象在技术上可以是 thunk 或非 thunk,并且选择对原语的 "strict" 类型签名没有影响。

实际上,PrimOp return thunk 不是很有用。在惰性语言中,调用原语的事实意味着需要它的 return 值。如果它 return 是 thunk,则需要立即评估该 thunk,那么为什么 return 是 thunk?

(编辑添加:)顺便说一句,上面的 PrimOps 没有什么特别的:用​​户定义的 Haskell 函数也被混合使用指针和未装箱类型调用(而且它们从不 return 砰的一声)。