あまり知られていませんが、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' が厳密である」ということは、「アキュムレータの引数が厳密である」ことを意味すると素朴に考えたでしょう。上記はそれに反する。
ただし、厳密さはループのコンスケースでの関数適用の結果にのみ適用されるため、さらに陰湿です。したがって、基本ケースに入ると底が得られますが、帰納的ケースは得られません。
Prelude Data.List> foldl' (const (+1)) undefined []
*** Exception: Prelude.undefined
したがって、引数 2の厳密性 も引数 3 の値に依存します!
これは私がそれをどのように書くかです: 2 番目の引数で「完全に」厳密です。
foldl' f z0 xs0 = go z0 xs0
where
go !z [] = z
go !z (x:xs) = go (f z x) xs
ご覧のとおり、これは 2 番目の引数で本当に厳密です。
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 の博士論文に記載されています。