当试图学习Haskell时,出现的困难之一就是当某些东西需要来自编译器的特殊魔法时。我们想到的一个例子是seq
函数,它无法定义,即您不能使seq2
函数的行为与内置seq
完全相同。因此,在向某人讲授seq
时,您需要提及seq
是特殊的,因为它是编译器的特殊符号。
另一个例子是do
- 符号,它仅适用于Monad
类的实例。
有时,它并不总是显而易见的。例如,延续。编译器是否知道Control.Monad.Cont
或者您是否可以自己发明的旧Haskell?在这种情况下,我认为即使延续是一种非常奇怪的野兽,编译器也不需要特殊的东西。
语言扩展被搁置,其他编译器魔术Haskell学习者应该注意什么?
答案 0 :(得分:5)
几乎所有无法在userland中实现的ghc基元都在ghc-prim包中。 (它甚至有一个名为GHC.Magic
的模块!)
所以浏览它会很有意义。
请注意,除非您确切知道自己在做什么,否则不在用户区代码中使用此模块。它中的大多数可用内容都是base
中的下游模块导出的,有时是修改后的形式。那些下游位置和API被认为更稳定,而ghc-prim
不保证它将如何在版本之间起作用。
GHC特定的东西会在GHC.Exts
中重新导出,但很多其他东西会进入Prelude(例如基本数据类型,以及seq
)或并发库等。< / p>
答案 1 :(得分:1)
多态seq
绝对是神奇的。您可以为任何特定的类型实现seq
,但只有编译器可以为所有可能类型实现一个函数[并避免优化即使它看起来没有了,也离开了。
显然整个IO
monad是非常神奇的,就像并发和并行(par
,forkIO
,MVar
),可变存储,异常抛出和捕获,查询垃圾收集器和运行时统计信息等。
IO
monad可以被认为是ST
monad的特例,这也是魔法。 (它允许真正可变的存储,这需要低级别的东西。)
另一方面,State
monad是完全普通的用户级代码,任何人都可以编写。 Cont
monad也是如此。各种异常/错误monad也是如此。
与语法(do-blocks,list comprehensions)有关的任何内容都是硬连接到语言定义中的。 (但请注意,其中一些响应LANGUAGE RebindableSyntax
,它允许您更改它绑定的功能。)还有deriving
个东西;编译器“了解”一些特殊类以及如何为它们自动生成实例。派生newtype
适用于任何类。 (它只是将一个实例从一种类型复制到另一种类型的相同副本。)
阵列是硬接线的。就像其他编程语言一样。
所有外部功能界面显然都是硬接线。
STM 可以在用户代码中实现(我已经完成了),但它目前是硬连线的。 (我想这会带来显着的性能优势。我还没有尝试过实际测量它。)但是,从概念上讲,这只是一种优化; 可以使用现有的低级并发原语来实现它。