懒惰列表根据其类型的具体性重建?

Jam*_*ham 2 haskell lazy-evaluation typeclass monomorphism-restriction

我在Haskell中编写了一个简单(且非严重)的素数生成器,它具有相互递归的定义,用于生成素数和确定数字的素数:

primes :: (Integral a) => [a]
primes = 2 : filter isPrime [3, 5..]

isPrime :: (Integral a) => a -> Bool
isPrime m = all (indiv m) (takeWhile (<= (intSqrt m)) primes)

intSqrt :: (Integral a) => a -> a
intSqrt 1 = 1
intSqrt n = div (m + (div (n - 1) m)) 2
  where m = intSqrt (n - 1)

indiv :: (Integral a) => a -> a -> Bool
indiv m n = rem m n /= 0
Run Code Online (Sandbox Code Playgroud)

我注意到它似乎重建了已经生成的素数的子列表,每次引用都是primes:

*Main> take 200 primes
[2,3,5,7, ..., 1223]
(2.70 secs, 446142856 bytes)
*Main> take 200 primes
[2,3,5,7, ..., 1223]
(2.49 secs, 445803544 bytes)
Run Code Online (Sandbox Code Playgroud)

但是当我改变类型注释以使用具体的整数类型时,例如

primes :: [Integer]
isPrime :: Integer -> Bool
Run Code Online (Sandbox Code Playgroud)

每个素数只生成一次:

*Main> :r
[1 of 1] Compiling Main             ( Primes.hs, interpreted )
Ok, modules loaded: Main.
*Main> take 200 primes
[2,3,5,7, ..., 1223]
(2.15 secs, 378377848 bytes)
*Main> take 200 primes
[2,3,5,7, ..., 1223]
(0.01 secs, 1626984 bytes)
Run Code Online (Sandbox Code Playgroud)

好像对我很好奇.这种情况有什么特别的原因吗?

dav*_*420 7

当你说

primes :: [Integer]
Run Code Online (Sandbox Code Playgroud)

然后primes是一个常数,但是当你说

primes :: (Integral a) => [a]
Run Code Online (Sandbox Code Playgroud)

那么它是一个带有隐藏参数的函数:Integral无论哪种类型的实例a.和其他函数一样,当您使用相同的参数调用它时,它将重新计算其结果(除非您已明确记住它).

  • 这种令人惊讶的缺乏与"多态常数"一起发生的共享计算是[单态限制](http://www.haskell.org/haskellwiki/Monomorphism_restriction)被引入的原因. (8认同)
  • @JamesCunningham GHC认识到[SPECIALIZED](http://www.haskell.org/ghc/docs/latest/html/users_guide/pragmas.html#specialize-pragma)编译指示(虽然我自己没有使用它). (2认同)