Haskell 中的 'where' 子句中的某些内容只计算一次吗?

Is something in the 'where' clause in Haskell only calculated once?

当我有以下代码时:

func n = m ++ [1] ++ m ++ [0] ++ m
    where m = func2 n

func2函数调用了多少次?只有一次,在 where 子句中?还是每次用m都重新计算?

也许它根本就没有被求值(偷懒的乐趣)——但如果是,它应该只被求值一次——如果你愿意,你可以自己尝试 trace:

import Debug.Trace(trace)

func n = m ++ [1] ++ m ++ [0] ++ m
  where m = func2 n

func2 n = trace "called..." [n]

这是 GHCi 中的示例:

λ> func 3
called...
[3,1,3,0,3]

这里是您看到它可能不会被调用的地方(直到您最终需要对其求值):

λ> let v = func 4

λ> v
called...
[4,1,4,0,4]

请参阅:起初它不会被调用 - 只有当您最终评估 v(打印它)时,您才会收到调用。

Carsten 的答案(该值最多计算一次)是正确的,只要您没有禁用单态限制。如果你有,那么 m 可能有一个涉及类型 class 的多态推断类型,然后 m 并不是一个真正的正常值,而是一个接受类型 [=] 的函数19=] 字典并产生一个值。考虑这个例子。

{-# LANGUAGE NoMonomorphismRestriction #-}

import Debug.Trace(trace)

func n = m ++ [1] ++ m ++ [0] ++ m
  where m = func2 n                     -- m :: Monad t => t a (where n :: a)

func2 n = trace "called..." (return n)  -- func2 :: Monad t => a -> t a

然后在 ghci 打印中评估 func 3

called...
[3,1called...
,3,0called...
,3]

要添加到@Carstan 和@Reid Barton 的答案中,它还取决于您是否是 运行 编译代码,例如:

{-# LANGUAGE NoMonomorphismRestriction #-}

import Debug.Trace(trace)

func n = m ++ [1] ++ m ++ [0] ++ m
  where m = func2 n                     -- m :: Monad t => t a (where n :: a)

func2 n = trace "called..." (return n)

main = let xs = func 3 :: [Int]
       in print xs

ghci运行main打印出called3次。然而,在编译时,它只在使用 -O2 时打印出 called 一次。 (使用 7.10.2 测试。)