功能评估和懒惰的顺序

时间:2018-04-17 20:05:58

标签: haskell

关于“懒惰”和评价函数的顺序的一些思考

考虑以下代码:

testF x =  ((length x) >= 1) && ((head x) == "foo")

testG x =  ((head x) == "foo") && ((length x) >= 1)

testH x = False && ((head x) == "foo") 

testI x = ((head x) == "foo") && False

以及使用空列表执行的结果

*Main> testF []
False
*Main> testG []
*** Exception: Prelude.head: empty list
*Main> testH []
False
*Main> testI []
*** Exception: Prelude.head: empty list

关于testF和testG的问题:我读到Haskell评估最左边的最外层,它会保证结果;它是否真的是语言的要求,或者这个testF []评​​估实际上是未定义的行为(例如在C ++中它是直到C ++ 11的情况)。

关于testH和testI的问题:乍一看似乎显而易见的事情看起来并不那么明显:即使它评估最左边的最外层,我们所拥有的保证比执行不执行某种反向排序评估:

考虑一下:

myAnd _ False = False
myAnd False _ = False
myAnd a b = (&&) a b

testJ x =  myAnd ((head x) == "foo") False 
testK x =  myAnd False ((head x) == "foo")  

给出了恢复的行为

*Main> testJ []
False
*Main> testK []
*** Exception: Prelude.head: empty list

最后,上面的所有例子都使(&&)运算符在交换性定律(a& b == b&& a)中失败,所以我最终认为所有这些调用,理想情况下,应评估两个参数并引发异常。

2 个答案:

答案 0 :(得分:11)

Haskell并不评估所有情况下最左边的第一个。如您所见,这是由于您的定义。采用&&

的定义
(&&) :: Bool -> Bool -> Bool
(&&) False _ = False
(&&) _ False = False
(&&) _ _     = True

请注意,它首先需要左参数的模式。这会强制首先评估左参数,并且可以使第二个参数不被评估。在myAnd函数中,由于模式匹配,您可以切换评估顺序。如果您交换了第2行和第3行(您所做的),评估顺序也会交换。

此处绝对没有未定义的行为。不要把头埋在C里!

但是,如果函数模式匹配两个(或更多)参数,那么它会先评估最左边的参数。所以,例如:

func1 True False = True -- Both arguments evaluated
func1 _ _        = False

test1 = func1 (error "leftmost") (error "rightmost")
-- Running test1 results in the error "leftmost"

关于交换性的观点非常尖锐。从技术上讲,这确实不允许交换法。但是,在推理Haskell代码时,我们通常忽略底部(errorundefined到非数学家)有两个原因。一,它使事情更好,两个,你不应该遇到error:这意味着你出错了。所以在实践中,不:&&在功能上是可交换的。不应该在你的Haskell代码中发生error,并且由于这个原因无法捕获它们;正如你所指出的,它打破了很好的代数性质。仅将其用于不应发生的事件,如head的实现。

还有许多类似于Haskell的其他语言,尤其是Agda和Idris,由于这个原因,它们完全取消errorundefined,主要是因为它们具有逻辑一致性的特定目标,为了能够完全证明您的代码是正确的。然而,Haskell(可能是为了保持更主流)并不这样做。

答案 1 :(得分:9)

这种行为是有保障的。

Haskell 2010第9节中的前奏are defined to behave like this中的

public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath ) // This line is wrong, but the correct path would be set here .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); string myString = Configuration["blah:blah:blah"]; ||

&&

模式匹配也是defined to work left-to-right(3.17.2),_匹配⊥:

  

模式       根据以下规则,匹配从左到右,从外到内进行匹配:[...]将通配符模式_与任何值匹配总是成功,并且不进行绑定。

旧的Haskell 98规范也是如此。