为什么Haskell的"无所事事"功能,id,消耗大量内存?

Rya*_*yan 112 haskell ghc

Haskell有一个标识函数,它返回输入不变.定义很简单:

id :: a -> a
id x = x
Run Code Online (Sandbox Code Playgroud)

所以为了好玩,这应该输出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
Run Code Online (Sandbox Code Playgroud)

几秒钟后(根据任务管理器大约2 GB的内存),编译失败了ghc: out of memory.同样,翻译说ghci: out of memory.

由于它id是一个非常简单的函数,我不希望它在运行时或编译时成为内存负担.什么是用于的内存?

Die*_*Epp 134

我们知道的类型id,

id :: a -> a
Run Code Online (Sandbox Code Playgroud)

当我们将其专门化时,has类型id id左侧副本id:

id :: (a -> a) -> (a -> a)
Run Code Online (Sandbox Code Playgroud)

然后当你再次专注于最左边id的时候id id id,你得到:

id :: ((a -> a) -> (a -> a)) -> ((a -> a) -> (a -> a))
Run Code Online (Sandbox Code Playgroud)

所以你看到id你添加的每一个,最左边的类型签名id是两倍大.

请注意,在编译期间会删除类型,因此这只会占用GHC中的内存.它不会占用程序中的内存.

  • 天真地进行类型推断是双指数的,通过巧妙地在类型表达式中使用共享,可以将其降低到指数.但无论你做什么,都会有一些相当简单的表达式会使类型检查器爆炸.幸运的是,这些在实际编程中不会发生. (57认同)
  • 有趣但相关:http://britton.disted.camosun.bc.ca/jbchessgrain.htm (9认同)
  • @dfeuer尝试询问ghci中的类型.您将通过响应的速度看到它必须进行适当的共享.我怀疑这种共享失败 - 出于显而易见的原因 - 一旦你翻译成其他一些中间表示(例如核心). (5认同)
  • 这意味着如果`id`重复"n"次,则其类型的空间与"2 ^ n"成比例.除了表示类型所需的其他结构之外,Ryan代码中推断的类型还需要对其类型变量进行"2 ^ 27"引用,这可能比您期望的大多数类型要大得多. (4认同)
  • 也许问题是,GHC是否应该找到一种更优雅地处理这类事情的方法.特别是,当完整写出时类型非常大,但是有大量的重复 - 可以共享用来压缩这些东西吗?有没有一种有效的方法来处理它们? (3认同)