如果我对一个类型只有一个构造函数的表达式进行模式匹配,那还是会强制运行时将表达式计算为WHNF吗?
我做了一个似乎表明它没有评估的实验:
Prelude> data Test = Test Int Int
Prelude> let errorpr () = error "Fail"
Prelude> let makeTest f = let (x,y) = f () in Test x y
Prelude> let x = makeTest errorpr
Prelude> let Test z1 z2 = x
Prelude> :sprint z1
z1 = _
Prelude> :sprint z2
z2 = _
Prelude> :sprint x
x = _
我原本应该得到一个错误或者:sprint x得到
x = Test _ _
但事实并非如此。
显然我不明白“让”是如何运作的。请参阅下面的答案
答案 0 :(得分:6)
Prelude> let x = makeTest errorpr
Prelude> let Test z1 z2 = x
最后一行不会强制评估任何内容:let
中的模式(隐式)懒惰模式(也称为无可辩驳的模式)。尝试改为
Prelude> let x = makeTest errorpr
Prelude> case x of Test z1 z2 -> "hello!"
Prelude> :sprint x
你应该注意Test _ _
之类的东西,因为case
中的模式不是懒惰的。{1}}。相比之下,
Prelude> let x = makeTest errorpr
Prelude> case x of ~(Test z1 z2) -> "hello!" -- lazy pattern!
Prelude> :sprint x
应仅打印_
,就像使用let
时一样。
以上适用于data
类型。相反,newtype
不提升内部类型,而是直接使用相同的表示。也就是说,newtype
值构造和模式匹配在运行时是 no-ops :它们在类型检查后大致被编译器擦除。
答案 1 :(得分:3)
(由于提到了许多构造函数的可能性,我推测该问题涉及data
类型。如果使用newtype
,答案会有所不同。请参阅注释。)
是的,它会的。试试例如:
data T = C Int
unT (C n) = 42
main = print $ unT undefined
你会得到一个未定义的异常,而不是42。
但当然这取决于模式。如果您将unT
的定义替换为:
unT ~(C n) = 42
使用延迟模式,结果将为42.另外,请注意let
表达式中使用的构造函数模式(例如,let (C n) = something
也等同于惰性模式。
关于问题的其余部分,makeTest
会生成一个Test
值,其参数未被使用。如果它们已被使用,您当然会收到错误(例如,您使用print
代替:sprint
)。 x
未给出Test _ _
的原因是因为第5行中的let
表达式对应于惰性模式(Haskell 98报告中的第3.12节)。就像你写了let ~(Test z1 z2) = x
一样。因此,永远不会评估x
。