我查看了GHC.Prim的模块,发现似乎GHC.Prim中的所有数据都定义为data Float#
而没有=A|B
之类的内容,并且GHC.Prim中的所有函数都被定义为gtFloat# = let x = x in x
。
我的问题是这些定义是否有意义以及它们的含义。
我检查了GHC.Prim的标题如下
{-
This is a generated file (generated by genprimopcode).
It is not code to actually be used. Its only purpose is to be
consumed by haddock.
-}
我想这可能与问题有一些关系,谁可以向我解释一下。
答案 0 :(得分:25)
这很神奇:)
这些是“原始运营商和运营”。它们被硬连线到编译器中,因此没有用于基元的数据构造器,并且所有函数都是底层的,因为它们在纯haskell中不一定是可表达的。
(Bottom代表haskell程序中的“洞”,无限循环或undefined
是底部的例子)
换句话说
这些数据声明/函数用于提供对原始编译器内部的访问。 GHC.Prim存在用于导出这些原语,它实际上并不实现它们或任何东西(例如它的代码实际上并不实用)。所有这些都是在编译器中完成的。
它适用于需要进行极优化的代码。如果您认为可能需要它,那么有关GHC原语的一些有用的reading
答案 1 :(得分:10)
Primops正是那些由运行时提供的操作,因为它们不能在语言中定义(或者出于效率的原因,不应该被定义)。 GHC.Prim
的真正目的不是定义任何东西,而只是导出一些操作,以便Haddock可以记录它们的存在。
在GHC的代码库中此时使用了构造let x = x in x
,因为值undefined
尚未被“定义”。 (等到Prelude。)但请注意,let
循环undefined
结构,就像undefined
一样,在语法上都是正确的,可以有任何类型。也就是说,它是一个无限循环,其语义为⊥,就像let x = z in y
一样。
另请注意,一般情况下,Haskell表达式x
表示“将变量z
更改为表达式x
,只要y
出现在表达式\x -> y
中” 。如果您熟悉lambda演算,则应将此视为将lambda抽象z
应用于术语let x = x in x
的缩减规则。那么Haskell表达式fix
只不过是纯lambda演算之上的一些语法吗?我们来看看。
首先,我们需要考虑Haskell的let表达式的递归性。 lambda演算不允许递归定义,但给定一个原始的定点运算符let x = x in x
, 1 ,我们可以显式地编码递归。例如,Haskell表达式(fix \r x -> r x) z
与x
具有相同的含义。 2 (我已将应用程序右侧的z
重命名为{ {1}}强调它与lambda中的x
没有隐含的关系。)
应用定点运算符fix f = f (fix f)
的通常定义,我们let x = x in x
的翻译减少(或者不是)这样:
(fix \r x -> r x) z ==>
(\s y -> s y) (fix \r x -> r x) z ==>
(\y -> (fix \r x -> r x) y) z ==>
(fix \r x -> r x) z ==> ...
因此,在语言开发的这一点上,我们使用内置的定点运算符从(类型化的)lambda演算的基础引入了se的语义。可爱!
我们需要一个原始的定点操作(也就是语言中内置的操作),因为在简单类型的lambda演算及其近似的同类中定义一个定点组合器是不可能的。 (Haskell的Prelude中fix
的定义与此不矛盾 - 它是递归定义的,但我们需要一个定点运算符来实现递归。)
如果你之前没有看过,你应该阅读lambda演算中的定点递归。关于lambda演算的文本是最好的(有一些免费的在线),但一些谷歌搜索应该让你去。基本思想是我们可以通过抽象递归调用将递归定义转换为非递归定义,然后使用定点组合器将我们的函数(lambda抽象)传递给自身。定义良好的递归定义的基本情况对应于函数的固定点,因此函数执行,一遍又一遍地调用自身直到它到达一个固定点,此时函数返回其结果。该死的很干,呵呵?