【发布时间】:2020-03-23 02:16:32
【问题描述】:
正如标题所说:对 Haskell 函数返回单元进行评估有什么保证?有人会认为在这种情况下不需要运行任何类型的评估,编译器可以用立即的 () 值替换所有此类调用,除非存在明确的严格要求,在这种情况下,代码可能必须决定是否应该返回() 或底部。
我在 GHCi 中对此进行了实验,但似乎发生了相反的情况,即似乎对这样的函数进行了评估。一个非常原始的例子是
f :: a -> ()
f _ = undefined
由于undefined 的存在,评估f 1 会引发错误,因此肯定会发生一些评估。不过,目前尚不清楚评估的深度。有时它似乎需要评估所有对返回()的函数的调用。示例:
g :: [a] -> ()
g [] = ()
g (_:xs) = g xs
如果出现g (let x = 1:x in x),此代码将无限循环。但后来
f :: a -> ()
f _ = undefined
h :: a -> ()
h _ = ()
可用于表明h (f 1) 返回(),因此在这种情况下,并非所有单位值子表达式都被计算。这里的一般规则是什么?
ETA:我当然知道懒惰。我在问是什么阻止了编译器编写者使这种特殊情况比通常更懒惰。
ETA2:示例摘要:GHC 似乎将() 视为与任何其他类型完全一样,即,好像存在一个关于应该从函数返回哪个类型的常规值的问题。优化算法似乎没有(ab)使用只有一个这样的值这一事实。
ETA3:当我说 Haskell 时,我指的是 Haskell-as-defined-by-the-Report,而不是 Haskell-the-H-in-GHC。似乎是一个没有像我想象的那样广泛共享的假设(这是“100% 的读者”),或者我可能能够提出一个更清晰的问题。即便如此,我还是很遗憾更改了问题的标题,因为它最初询问了调用这样一个函数的保证。
ETA4:看来这个问题已经解决了,我认为它没有答案。 (我一直在寻找一个“关闭问题”功能,但只找到了“回答你自己的问题”,由于无法回答,我没有走那条路。)没有人从报告中提出任何可以决定它的东西,我很想将其解释为一个强有力但不确定的“不能保证这种语言”的答案。我们所知道的是,当前的 GHC 实现不会跳过对此类函数的评估。
我在将 OCaml 应用程序移植到 Haskell 时遇到了实际问题。原始应用程序具有多种类型的相互递归结构,并且代码在 1..6 或 7 中为 N 声明了许多称为 assert_structureN_is_correct 的函数,如果结构确实正确,则每个函数都返回单位,如果它不是。此外,这些函数在分解正确性条件时相互调用。在 Haskell 中,使用 Either String monad 可以更好地处理这个问题,所以我以这种方式转录它,但作为理论问题的问题仍然存在。感谢所有输入和回复。
【问题讨论】:
-
这是工作上的懒惰。除非需要函数的结果(例如,通过对构造函数的模式匹配),否则不会评估函数的主体。要观察差异,请尝试比较
h1::()->() ; h1 () = ()和h2::()->() ; h2 _ = ()。同时运行h1 (f 1)和h2 (f 1),看到只有第一个需要(f 1)。 -
“懒惰似乎决定了它被 () 取代而没有发生任何类型的评估。”那是什么意思?在所有情况下,
f 1都被undefined“替换”。 -
函数
... -> ()可以 1) 终止并返回(),2) 以异常/运行时错误终止并且无法返回任何内容,或者 3) 发散(无限递归)。 GHC 不会优化代码,假设只有 1) 可能发生:如果需要f 1,它不会跳过其评估并返回()。 Haskell 语义是评估它,看看 1,2,3 之间会发生什么。 -
这个问题中的
()(类型或值)真的没有什么特别之处。如果您在任何地方将() :: ()替换为0 :: Int,所有相同的观察结果都会发生。这些都是懒惰评估的无聊旧后果。 -
不,“避免”等不是 Haskell 语义。并且
()类型有两个可能的值,()和undefined。
标签: haskell language-lawyer lazy-evaluation semantics unit-type