map (1+) list
Haskell のようなものを書くとき、 の内部表現は(1+)
何ですか? の部分適用なので(+)
、引数1
をどこかに保存しなければならないのですが、これが頭に浮かびません。カリー化と部分適用の実装方法を簡単に説明してもらえますか?
3 に答える
部分的に適用された関数 (実際、Haskell ヒープ内の他のほとんどすべての関数) は、コード ポインターと引数スロットを組み合わせた構造体であるクロージャーとして表されます。具体的には、完全に評価されていない値をthunksと呼びます。
ボックス化されたデータに関するこの以前の質問と、サンクの表現方法に関する GHC マニュアルを参照してください。
このように考えてみてください。 すべては、結果を得るために直接呼び出さなければならないコードの塊 (「サンク」) によって表されます。リテラル を記述すると、呼び出されたとき1
に 1 (実際にはfromIntegral 1
) を返すコードのチャンクにコンパイルされ、そのコードのチャンクがリテラル 1 の代わりに使用されます。これも怠惰の鍵です。何かを計算する代わりにすぐに、呼び出されたときに計算を行うサンクが作成されます。その式を別の関数に渡す場合、渡されるのはラッパー サンクであるため、何かが結果を明示的に調べる必要があるまで/ない限り、計算は行われません。
Haskell では、関数アプリケーションは同じように表現されます。つまり、1 つのパラメーターを処理し、次のパラメーターを処理するか結果を生成するサンクを返すサンクです。(1+)
関数 applicationも同様です(+) 1
: (+)
は、単一の数値が渡されることを期待するサンクであり、別の単一の数値が渡されることを期待するサンクを返します。は厳密であるため(+)
、実際の加算を行うために呼び出す必要があるサンクを返す代わりに、その 2 番目のサンクが実際に加算を行います。そのため、その 2 番目のサンクに評価されます。このサンクは、 によって反復されるとき(1+)
に提供される別の数値で呼び出す必要があります。map
list
また、Simon Peyton Jones と David Lester による書籍、Implementing Functional Languages: A Tutorialも参照してください。