模式匹配元组不是完全懒惰吗?
Is pattern matching tuples not fully lazy?
我 运行 在 Haskell 中发现了一些非常不直观的行为,我不确定它是否是 Haskell 中的错误。如果这是预期的行为,具体是什么导致了无限循环?
import Data.Function (fix)
main=putStrLn$show$fst$
fix (\rec-> (\(a1,a2)->(3,7)) rec)
无限循环,我假设是因为要将 rec 模式匹配到它计算的 rec (a1,a2) 中。但我觉得不需要,因为一切都会匹配。例如,这些都可以正常工作:
fix (\rec-> (\a->(3,7)) rec)
fix (\rec-> (\a->let (a1,a2)=a in (3,7)) rec)
有趣的是,这退出时出现错误
fix (\rec-> (\(a1,a2)->(3,7)) undefined)
但我可以发誓我遇到这种行为(更复杂)的原始代码在这种情况下确实有效。我可以尝试重新创建它。
正确,元组模式匹配不是惰性的。事实上,除了两个值得注意的例外,没有模式匹配是惰性的。大致来说,模式匹配的“要点”是强制评估匹配的监督者。
当然,模式匹配可能会出现在懒惰意味着永远不会执行的地方;例如,
const 3 (case loop of (_, _) -> "hi!")
不会循环。但这并不是因为匹配是惰性的——而是因为 const
是惰性的并且永远不会导致匹配被执行。您的 let
示例是相似的,因为 let
的语义是在评估 let
.
的主体之前不会强制使用它绑定的变量
两个值得注意的例外之一是特殊的模式修饰符,~
;将 ~
放在模式之前表示使匹配变得惰性,因此向编译器声明修改后的模式 始终匹配 。 (如果它不匹配,一旦强制其中一个绑定变量,您就会崩溃,而不是通常的 fall-through-to-next-pattern 行为!)因此,fix
的一个修复是:
fix (\rec -> (\ ~(a1, a2) -> (3, 7)) rec)
当然,不需要 rec
的显式名称。你也可以这样写:
fix (\ ~(a1, a2) -> (3, 7))
另一个值得注意的例外是新类型匹配,这里不相关。
如果在 let
中,则元组(或任何其他)模式匹配是惰性的,如果在 case
中,则不是惰性的。 , a lambda application is reduced via case
,不是通过 let
.
(更正:对于可反驳的模式 pat
,(\pat -> ...) val
与 (\x -> case x of pat -> ...) val
== let x = val in case x of pat -> ...
相同,所以不是减少是通过 case
完成的,但是身份 (\pat -> ...)
== (\x -> case x of pat -> ...)
must hold).
因此您的代码减少为
fix (\rec-> (\(a1,a2)->(3,7)) rec)
=
let { x = (\rec-> (\(a1,a2)->(3,7)) rec) x } in x
=
let { x = (\(a1,a2)->(3,7)) x } in x
=
let { x = (\y -> case y of (a1,a2)->(3,7)) x } in x
=
let { x = case x of {(a1,a2)->(3,7)} } in x
这就是为什么 x
与 (_,_)
进行模式匹配的原因 在 之前 是 将是 (3,7)
(但不是!)。
因此,Haskell 不是 一种声明性语言。它的语义是由least不动点。
我 运行 在 Haskell 中发现了一些非常不直观的行为,我不确定它是否是 Haskell 中的错误。如果这是预期的行为,具体是什么导致了无限循环?
import Data.Function (fix)
main=putStrLn$show$fst$
fix (\rec-> (\(a1,a2)->(3,7)) rec)
无限循环,我假设是因为要将 rec 模式匹配到它计算的 rec (a1,a2) 中。但我觉得不需要,因为一切都会匹配。例如,这些都可以正常工作:
fix (\rec-> (\a->(3,7)) rec)
fix (\rec-> (\a->let (a1,a2)=a in (3,7)) rec)
有趣的是,这退出时出现错误
fix (\rec-> (\(a1,a2)->(3,7)) undefined)
但我可以发誓我遇到这种行为(更复杂)的原始代码在这种情况下确实有效。我可以尝试重新创建它。
正确,元组模式匹配不是惰性的。事实上,除了两个值得注意的例外,没有模式匹配是惰性的。大致来说,模式匹配的“要点”是强制评估匹配的监督者。
当然,模式匹配可能会出现在懒惰意味着永远不会执行的地方;例如,
const 3 (case loop of (_, _) -> "hi!")
不会循环。但这并不是因为匹配是惰性的——而是因为 const
是惰性的并且永远不会导致匹配被执行。您的 let
示例是相似的,因为 let
的语义是在评估 let
.
两个值得注意的例外之一是特殊的模式修饰符,~
;将 ~
放在模式之前表示使匹配变得惰性,因此向编译器声明修改后的模式 始终匹配 。 (如果它不匹配,一旦强制其中一个绑定变量,您就会崩溃,而不是通常的 fall-through-to-next-pattern 行为!)因此,fix
的一个修复是:
fix (\rec -> (\ ~(a1, a2) -> (3, 7)) rec)
当然,不需要 rec
的显式名称。你也可以这样写:
fix (\ ~(a1, a2) -> (3, 7))
另一个值得注意的例外是新类型匹配,这里不相关。
如果在 let
中,则元组(或任何其他)模式匹配是惰性的,如果在 case
中,则不是惰性的。 case
,不是通过 let
.
(更正:对于可反驳的模式 pat
,(\pat -> ...) val
与 (\x -> case x of pat -> ...) val
== let x = val in case x of pat -> ...
相同,所以不是减少是通过 case
完成的,但是身份 (\pat -> ...)
== (\x -> case x of pat -> ...)
must hold).
因此您的代码减少为
fix (\rec-> (\(a1,a2)->(3,7)) rec)
=
let { x = (\rec-> (\(a1,a2)->(3,7)) rec) x } in x
=
let { x = (\(a1,a2)->(3,7)) x } in x
=
let { x = (\y -> case y of (a1,a2)->(3,7)) x } in x
=
let { x = case x of {(a1,a2)->(3,7)} } in x
这就是为什么 x
与 (_,_)
进行模式匹配的原因 在 之前 是 将是 (3,7)
(但不是!)。
因此,Haskell 不是 一种声明性语言。它的语义是由least不动点。