我的 Haskell 表达式何时被评估?

When is my Haskell expression evaluated?

如果我定义

λ> data Bar = Bar Int deriving Show
λ> data Foo = Foo Bar deriving Show

λ> let foo = trace "foo" Foo (trace "bar" Bar 100)
λ> let two = trace "two" 2
λ> let g (Foo x)  y = y

然后我想我明白为什么我得到

λ> g foo two
foo
two
2

但是如果我再重复这个,我会得到

λ> g foo two
two
2

而且我不明白为什么 foo 似乎没有针对 g 的第二次调用进行评估,特别是因为它显然(还)不知何故已经可用,而我可以用

验证
λ> foo
Foo bar
(Bar 100)

虽然——再次让我感到困惑——重复前面的给出

λ> foo
Foo (Bar 100)

为什么我的 foo 表达式在某些情况下似乎已经求值,而在其他情况下却没有求值?就此而言,为什么我的 two 表达式 always 需要被评估?

> :t foo
foo :: Foo
> :t two
two :: Num a => a

第二个是多态,所以它是变相的函数!每次使用它时,您都会评估一个新的 trace 调用。

那是因为 two 的类型。让我们检查到目前为止的所有类型:

ghci> :t foo
foo :: Foo
ghci> :t two
two :: Num a => a

啊哈! two 是多态的。因此,它的行为取决于实际Num实例。因此,它需要在 g 中重新评估。我们可以使用 :sprint:

进行检查
ghci> :sprint foo
foo = Foo _             -- simplified

这表明我们从未看过Foo的内容。 fooweak head normal form 中。这回答了你的第二个问题。但回到你的第一个。 :sprint two 会发生什么?

ghci> :sprint two
two = _

如您所见,由于其多态性,two 无法进入 WHNF。毕竟,which WHNF 应该拿吗?您可能希望将其用作 IntegerCurrencyComplex Triple.

顺便说一下,这是存在单态限制的原因,参见"A History of Haskell", section 6.2:

6.2 The monomorphism restriction

A major source of controversy in the early stages was the so-called “monomorphism restriction.” Suppose that genericLength has this overloaded type:

genericLength` :: Num a => [b] -> a

Now consider this definition:

f xs = (len, len)
  where
    len = genericLength xs

It looks as if len should be computed only once, but it can actually be computed twice. Why? Because we can infer the type len :: (Num a) => a; when desugared with the dictionary- passing translation, len becomes a function that is called once for each occurrence of len, each of which might used at a different type.

另见

也就是说,如果我们修复 two 的类型,我们可以很容易地改变它:

ghci> let foo = trace "foo" Foo (trace "bar" Bar 100)
ghci> let two = trace "two" (2 :: Integer)
ghci> let g (Foo x)  y = y

现在输出将完全符合您的预期。或者,您可以使用 :set -XMonomorphismRestriction 启用单态限制,因为它在当前 GHCi 版本中默认禁用:

ghci> :set -XMonomorphismRestriction
ghci> let two = trace "two" 2
ghci> :t two
two :: Integer -- due to defaulting rules