「`foldl` と `foldr` はどちらもリストの最初の要素から最後の要素に向かって処理を行いますが、結合演算の結合の仕方が異なります。`foldl` では演算は左結合となり、(((v ⊗ e0) ⊗ e1) ⊗ e2) … のように式を構築します。一方 `foldr` は右結合となり、 e0 ⊗ (e1 ⊗ (e2 ⊗ … (en ⊗ v)…)) のようになります。リストの要素は両方の式で同じ順序で現れます;変わるのは演算の適用のグループ化のみです。(出典: primary)
「厳格(strict)な言語では、評価は内側から外側へ進行します。`foldl` の場合、最初のリスト要素がすぐに必要となるため、定数スタックスペースを使用する末尾再帰実装が可能になります。それに対し `foldr` は、リストの末尾に到達するまでどの約約も開始できないため、リスト長に比例したコールスタックを必ず構築します。結合演算が結合法則と交換法則を満たす(例えば (+) のような)場合、両関数は同じ最終結果を返しますが、約約の順序は異なります。(出典: primary)
「Haskell のような遅延(lazy)言語では、評価は外側から内側へ進行し、必要になったときのみ結果が要求されます。ここでは `foldl` が問題となります:各ステップで部分的に適用された演算の thunk が作成され、最外側の thunk が最後に強制されるまで遅延されるため、リストサイズに線形比例する thunk の鎖が蓄積され、線形スペース消費を引き起こします。厳密版の `foldl'` は各ステップでアキュムレータを強制し、thunk の蓄積を防止して定数スペースの振る舞いを復元します。それに対し `foldr` は第2引数において遅延となる可能性があります;結合関数がそこで遅延である場合、`foldr` は全リストを traversal することなく weak‑head normal 形の結果を返すことができ、これにより演算量の節約と無限リストでの操作が可能になります。(出典: primary)
「経験則として、累積関数が厳密である場合は `foldl'` を使用し、関数が第2引数において遅延である場合は `foldr` を使用してください。リストに対しては素の `foldl` や `foldr'` は避けてください。他のデータ構造では最適な選択はその結合性に依存し、結合性が無関係である場合は `foldMap`/`foldMap'` がしばしばより良い選択となります。(出典: primary)
