它并不广为人知,但foldl'
实际上它的累加器参数并不严格!回想一下类型:
foldl' :: (a -> b -> a) -> a -> [b] -> a
它在参数 2 中的严格性取决于为参数 1 给出的函数的严格性,正如您通过传递看到的那样const
:
Prelude Data.List> foldl' (const (+1)) undefined [1]
2
Prelude Data.List> foldl' (const (+1)) undefined [1..4]
5
你会天真地认为,“foldl' 是严格的”意味着“在累加器参数中是严格的”。以上与此相矛盾。
然而,它更加隐蔽,因为严格性只针对循环的 cons 情况下函数应用的结果。所以如果你输入基本情况,你仍然会得到底部,但不是归纳情况:
Prelude Data.List> foldl' (const (+1)) undefined []
*** Exception: Prelude.undefined
所以参数 2的严格性 也取决于参数 3 的值!
这就是我写它的方式:在它的第二个论点中“完全”严格。
foldl' f z0 xs0 = go z0 xs0
where
go !z [] = z
go !z (x:xs) = go (f z x) xs
如您所见,第二个论点确实很严格:
Prelude Data.List.Stream> foldl' (\a b -> 1) undefined [undefined]
*** Exception: Prelude.undefined
与Haskell2010版本相比:
Prelude Data.List.Stream> Data.List.foldl' (\a b -> 1 ) undefined [undefined]
1
这实际上具有实际影响——当前定义不会一致地拆箱其累加器参数。
历史记录:这是我们在 2007 年为流融合论文指定列表库的严格语义时发现的,指定严格的方法在 Duncan Coutt 的博士论文中给出。