在评估这些表达式时奇数重用

时间:2015-09-06 12:24:00

标签: haskell

我正在尝试使用Debug.Trace.trace来计算一个函数被评估的次数,并看到一些令人惊讶的结果。

ghci> import Debug.Trace

ghci> let x = (trace " Eval'd!" (* 2)) 3 in (x, x)
( Eval'd!
6, Eval'd!
6)

ghci> let x = (trace " Eval'd!" (* 2)) 3 in x `seq` (x, x)
Eval'd!
( Eval'd!
6, Eval'd!
6)

ghci> let x = (trace " Eval'd!" (* 2)) (3 :: Int) in (x, x)
( Eval'd!
6,6)

我假设Eval'd每次评估(* 2)函数时都会打印一次x。这是正确的假设吗?

其次,为什么这个功能不止一次打印过?我怀疑Num属于某种未指定类型的(x, x) :: Num a => (a, a)类型类与它有关,因为第三种情况有效,但我想不出解释。

(x, x) :: (Int, Int)保证元组的两个元素具有与x相同的值,那么为什么eval (x, x)两次?

更新:

实际上我假设Num a => (a, a)的类型是(x, x) :: (Num t, Num t1) => (t, t1)。但它显然是t ~ t1

为什么GHC没有意识到ggsave在这里?我怀疑这与我的问题的答案有关。

1 个答案:

答案 0 :(得分:3)

他们保证是同一类型:

Prelude Debug.Trace> :t let x = (trace " Eval'd!" (* 2)) 3 in (x, x)
let x = (trace " Eval'd!" (* 2)) 3 in (x, x)
  :: (Num t, Num t1) => (t, t1)

另外,如果你把它放在一个文件中,它只会被评估一次,即使从GHCi调用也是如此。 (这是因为在文件中的声明但不是GHCi中,默认情况下dreaded monomorphism restriction处于启用状态):

import Debug.Trace
main = print $ let x = (trace " Eval'd!" (* 2)) 3 in (x, x)

此外,

let x = (trace " Eval'd!" 6) in (x,x)

行为大致相同,所以它实际上都存在于类型(类)歧义中。

它不共享x的所有用途的原因是因为在GHC核心级x,未经优化,实际上是函数采用Num类型类字典参数。要分享它,它必须做足够的分析才能看出类型是相同的。

它没有意识到的原因基本上是GHCi用于快速代码实验的转变,而不是用于创建好的代码,所以它几乎没有在所有进行优化,所以它几乎是纯粹的运气是否检测到这些事情。

(GHCi字节码设计中还有一个outstanding hole,这意味着你无法为它启用优化级别,即使你想要它。基本上它不支持“unboxed”元组“,GHC优化使用了很多。”