Haskell 编译器魔法:什么需要编译器进行特殊处理?

Haskell compiler magic: what requires a special treatment from the compiler?

当尝试学习 Haskell 时,出现的困难之一是当某些东西需要编译器的特殊魔法时的能力。想到的一个例子是无法定义的 seq 函数,即您不能使 seq2 函数的行为与内置 seq 完全相同。因此,当教别人关于 seq 时,你需要提到 seq 是特殊的,因为它是编译器的特殊符号。

另一个示例是 do-notation,它仅适用于 Monad class.

的实例

有时,它并不总是很明显。例如,延续。编译器知道 Control.Monad.Cont 还是您可以自己发明的普通旧 Haskell?在这种情况下,我认为编译器不需要任何特殊的东西,即使延续是一种非常奇怪的野兽。

撇开语言扩展不谈,学习者还应该了解哪些其他编译器魔法 Haskell?

几乎所有不能在用户空间实现的ghc原语都在ghc-prim包中。 (它甚至有一个名为 GHC.Magic 的模块!)

所以浏览一下会有很好的感觉。

请注意,您应该在用户区代码中使用此模块,除非您确切地知道自己在做什么。它的大部分可用内容在 base 的下游模块中导出,有时以修改的形式导出。这些下游位置和 API 被认为更稳定,而 ghc-prim 不保证它在不同版本之间的行为方式。

GHC 特定的东西在 GHC.Exts 中重新导出,但许多其他东西进入 Prelude(例如基本数据类型,以及 seq)或并发库等.

多态seq绝对是魔法。您可以为任何特定类型实现seq,但只有编译器可以为所有[=实现一个函数45=] 可能的类型 [并避免优化它,即使它看起来没有操作]。

显然整个 IO monad 非常神奇,与并发和并行性(parforkIOMVar)、可变存储、异常的一切一样抛出和捕获,查询垃圾收集器和 运行-time stats 等

IO monad可以认为是ST monad的一个特例,也是很神奇的。 (它允许真正可变的存储,这需要低级的东西。)

另一方面,State monad 是完全普通的用户级代码,任何人都可以编写。 Cont monad 也是如此。各种异常/错误单子也是如此。

任何与语法(do-blocks,列表理解)有关的东西都硬连接到语言定义中。 (不过请注意,其中一些响应 LANGUAGE RebindableSyntax,这使您可以更改它绑定的功能。)还有 deriving 东西;编译器 "knows about" 一些特殊的 classes 以及如何为它们自动生成实例。 newtype 的推导适用于 any class。 (它只是将一个实例从一种类型复制到该类型的另一个相同副本。)

数组是硬连线的。很像其他所有编程语言。

所有的外部函数接口显然是硬连接的。

STM 可以在用户代码中实现(我已经做到了),但它目前是硬连线的。 (我想这会带来显着的性能优势。我还没有尝试实际测量它。)但是,从概念上讲,这只是一种优化;您可以使用现有的低级并发原语实现它。