jozefgの答えの簡単な拡張...
Primops は、まさにランタイムによって提供される操作です。言語内で定義できないためです (または、効率上の理由からそうすべきではありません)。の真の目的はGHC.Prim
何かを定義することではなく、Haddock がその存在を文書化できるようにいくつかの操作をエクスポートすることです。
この構造let x = x in x
は、GHC のコードベースのこの時点で使用されます。これは、値undefined
がまだ「定義」されていないためです。let
(それは Prelude まで待ちます。) しかし、 のような循環構造undefined
は、構文的に正しく、任意の型を持つことができることに注意してください。つまり、そのまま⊥の意味を持つ無限ループundefined
です。
...そして余談
let x = z in y
また、一般的に Haskell 式は「変数x
を式のz
どこにでもあるx
式に変更する」ことを意味することに注意してくださいy
。ラムダ計算に精通している場合は、これがラムダ抽象化\x -> y
を用語 に適用するための簡約規則であることに気付くはずz
です。では、Haskell 式let x = x in x
は、純粋なラムダ計算の上にある構文にすぎないのでしょうか? 見てみましょう。
まず、Haskell の let 式の再帰性を説明する必要があります。ラムダ計算では再帰的な定義は認められませんが、基本的な固定小数点演算子1fix
が与えられると、再帰性を明示的にエンコードできます。たとえば、Haskell 式は と同じ意味を持ちます。2 (アプリケーションの右側にある の名前を に変更して、ラムダの内部との暗黙の関係がないことを強調しました)。let x = x in x
(fix \r x -> r x) z
x
z
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 ==> ...
したがって、言語の開発のこの時点で、組み込みの固定小数点演算子を使用した (型付き) ラムダ計算の基礎から ⊥ のセマンティクスを導入しました。素晴らしい!
単純型付けされたラムダ計算とそれに近いもので固定小数点コンビネータを定義することは不可能であるため、基本的な固定小数点演算 (つまり、言語に組み込まれている演算) が必要です。( fix
Haskell の Prelude での in の定義はこれと矛盾しません。再帰的に定義されていますが、再帰を実装するには固定小数点演算子が必要です。)
これまでに見たことがない場合は、ラムダ計算の固定小数点再帰について読んでください。ラムダ計算に関するテキストが最適ですが (オンラインで無料のテキストがいくつかあります)、Google で調べてみるとよいでしょう。基本的な考え方は、再帰呼び出しを抽象化することで再帰定義を非再帰定義に変換し、固定小数点コンビネータを使用して関数 (ラムダ抽象化) をそれ自体に渡すことができるというものです。適切に定義された再帰的定義の基本ケースは、関数の不動点に対応するため、関数は実行され、不動点に達するまで何度も何度も自分自身を呼び出し、その時点で関数はその結果を返します。かなりきちんとしていますよね?