16

我查看了 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.
-}

我想这可能与问题有关,谁能向我解释一下。

4

2 回答 2

25

这是魔法 :)

这些是“原始运算符和操作”。它们被硬连线到编译器中,因此没有原语的数据构造函数,并且所有函数都是底部的,因为它们不一定在纯 haskell 中可表达。

(底部代表haskell程序中的一个“洞”,一个无限循环或者undefined是底部的例子)

换一种方式

这些数据声明/函数将提供对原始编译器内部的访问。GHC.Prim 的存在是为了导出这些原语,它实际上并没有实现它们或任何东西(例如,它的代码实际上没有用)。所有这些都在编译器中完成。

它适用于需要极度优化的代码。如果您认为您可能需要它,请阅读有关 GHC 中原语的一些有用信息

于 2013-04-09T04:54:48.963 回答
10

jozefg答案的简短扩展......

Primops 正是那些由运行时提供的操作,因为它们不能在语言中定义(或者出于效率的原因不应该定义)。的真正目的GHC.Prim不是定义任何东西,而只是导出一些操作,以便 Haddock 可以记录它们的存在。

在 GHC 的代码库中此时使用该构造let x = x in x,因为该值undefined尚未,嗯,“定义”。(这要等到 Prelude。)但是请注意,循环let结构就像 一样undefined,在语法上都是正确的,并且可以具有任何类型。也就是说,它是一个具有 ⊥ 语义的无限循环,就像它一样undefined

……还有一个旁白

另请注意,通常 Haskell 表达式的let x = z in y意思是“将变量更改x为表达式中出现的z任何位置”。如果您熟悉 lambda 演算,您应该认识到这是将 lambda 抽象应用于术语的简化规则。那么 Haskell 表达式只不过是纯 lambda 演算之上的一些语法吗?让我们来看看。xy\x -> yzlet x = x in x

首先,我们需要考虑 Haskell 的 let 表达式的递归性。lambda 演算不允许递归定义,但给定一个原始的定点运算符fix1,我们可以显式地编码递归性。例如,Haskell 表达式let x = x in x与 具有相同的含义(fix \r x -> r x) z2(我已将x应用程序右侧的重命名z以强调它x与 lambda 内部没有隐式关系)。

应用定点运算符的通常定义fix f = f (fix f),我们对let x = x in xreduce(或者更确切地说不是)的翻译如下:

(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 演算的基础上引入了 ⊥ 的语义,并带有内置的定点运算符。迷人的!


  1. 我们需要一个原始的定点运算(即内置于语言中的运算),因为不可能在简单类型的 lambda 演算及其近亲中定义定点组合子。(fixHaskell 的 Prelude 中的定义并不矛盾——它是递归定义的,但我们需要一个定点运算符来实现递归。)

  2. 如果您以前没有看过这个,您应该阅读 lambda 演算中的定点递归。关于 lambda 演算的文本是最好的(网上有一些免费的),但一些谷歌搜索应该能让你继续前进。基本思想是,我们可以通过对递归调用进行抽象来将递归定义转换为非递归定义,然后使用定点组合器将我们的函数(lambda 抽象)传递给自身。定义良好的递归定义的基本情况对应于我们函数的一个固定点,因此函数会执行,一遍又一遍地调用自己,直到它到达一个固定点,此时函数返回其结果。非常整洁,对吧?

于 2013-04-10T01:42:36.163 回答