Haskellの「何もしない」関数idが大量のメモリを消費するのはなぜですか?


112

Haskellには、入力を変更せずに返す恒等関数があります。定義は簡単です:

id :: a -> a
id x = x

だから楽しみのために、これは出力する必要があります8

f = id id id id id id id id id id id id id id id id id id id id id id id id id id id
main = print $ f 8

数秒後(およびタスクマネージャによると約2 GBのメモリ)、コンパイルはで失敗しghc: out of memoryます。同様に、通訳は言うghci: out of memory

idは非常に単純な関数なので、実行時またはコンパイル時にメモリの負荷になるとは思わないでしょう。使用されているすべてのメモリは何ですか?


11
それらを作成したいとしidます。VIMで、カーソルをの定義の上に置いてf、次のようにします:s/id id/id . id ./g
Tobias Brandt

回答:


135

のタイプを知っていますid

id :: a -> a

これをに特化するとid id、の左側のコピーidは次のタイプになります。

id :: (a -> a) -> (a -> a)

そして、これをの左端idに再び特化するとid id id、次のようになります。

id :: ((a -> a) -> (a -> a)) -> ((a -> a) -> (a -> a))

つまりid、追加したそれぞれが表示され、左端の型シグネチャidは2倍になります。

型はコンパイル中に削除されるため、GHCでのみメモリを消費することに注意してください。プログラムのメモリを消費しません。


岡崎がHaskellに組み込まれたRPN計算機を書いたとき、同様の問題に遭遇したことを覚えています。
dfeuer 2014年

3
問題は、おそらく、GHCがこの種のことをより優雅に処理する方法を見つけるべきかどうかです。特に、完全に書き出されると型は非常に大きくなりますが、膨大な量の重複があります。共有を使用してそのようなものを圧縮できるでしょうか?それらを処理する効率的な方法はありますか?
dfeuer 2014年

5
@dfeuer ghciのタイプを尋ねてみてください。適切な共有を行う必要があることは、応答の速さでわかります。他の何らかの中間表現(例:コア)に変換すると、この共有は失われます(明らかな理由により)。
Daniel Wagner

4
つまり、idが繰り返し繰り返される場合n、そのタイプのスペースはに比例し2^nます。Ryanのコードで推論される型2^27には、型を表すために必要な他の構造に加えて、その型変数への参照が必要になります。
デビッド

58
型推論を単純に行うことは二重指数関数であり、型式で共有を巧みに使用することにより、それを指数関数に下げることができます。しかし、あなたが何をしても、型チェッカーを爆発させるかなり単純な式がいくつかあります。幸いなことに、これらは実際のプログラミングでは発生しません。
2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.