Haskellツイートhttp://www.well-typed.com/blog/90/foldlに関するこの記事(英文)が面白かったので、勝手翻訳しました。foldlなんとかなるといいですね。壊れているとみんなが知っている。四半世紀近く壊れたままだ。これを修正する時が来た!今日、私はPrelude.foldlをData.List.foldl'として知られる実装で再定義することを提案する。Haskellでは次のように言っている。`foldl`のサンクの挙動のため、実アプリではこの関数を使わないようにするのが望ましい。問題がない場合でも、`foldl`の使用は不要なオーバーヘッドを払うことになる。代わりに`Data.List`をインポートして、`foldl'`を使うこと。この本のオンライン版の最初のユーザーコメントは次のようなものだった。なんでData.Listのfoldlの実装がPreludeにないんですか?追加:なんで`foldl'`がデフォルトじゃないんですか?いい質問だ。OK、それじゃあfoldlとfoldl'の違いを話さねばならない。xs無味乾燥な技術的な違いは、foldl'は次の再帰呼び出しの前に関数呼び出しを評価するというところだ。広い範囲を眺めてみることにしよう。右畳み込み、左畳み込み最初にHaskellを勉強したとき、リストの畳み込み方には右からと左からの二つがあると教わったはずだ。括弧が左にネストしているか、右にネストしているか、を表している。実行時にはもちろんリストは左(先頭)の要素から見なければならない。それから、左右の畳み込みについて違った考え方を勉強することになる。右畳み込みはデマンドドリブンなイテレータとして使えるということだ。右畳み込みでは第2引数に対して非正格な関数(例えば(:))を渡す。インクリメンタルな、あるいはショートカットの振る舞いが必要か(foldr)によって決めることが多い。アキュームレータのサンクこれまたHaskellの勉強にて、foldlは次のような頭のおかしい挙動をするのだと教わる。3)ループとして利用する際、こういうことに気を付けなければならない。6もちろんこれはfoldl'で解決できる。foldl'では`+の呼び出しは次の再帰呼び出しの前に評価される。いう時に(foldl’ではなく)foldlが役に立つのか?短い答えは「そんなことはまずない」である。実際にはそんなケースはないのだとだんだん分かってくる。評価の遅延には全く何の意味もない。それも考えなくてもよい。普通はまずfoldrが第一候補に挙がるからである。先行評価することによる害もないだろう。foldlはリスト全体を走査する必要があることを思い出して欲しい。foldrのショートカット動作はfoldlでは不可能なのだ。foldl'が過剰に正格だということを示す作為的な例は、作ろうと思えば作れる。lastとlast'を次のように定義する。list")これを試してみるとこうなる。(最後の一つ以外の)要素は実際には必要としないためである。この例はまた、頭の悪い実装でもある。普通の定義の方がよほどわかりやすい。list"foldlでうまく行ってfoldl'ではだめなケースはすべてこうした方がうまく書ける。いう例は、作為的なものであるか、あるいは別の書き方をした方が分かりやすい。要求なのだと指摘する人がいる。もってナンセンスである。foldlではなくてfoldrを使うべきである。foldl'が用意されていなかったということだ。明確にfoldl'ではなくてfoldlを使う必要があると思ったこと場面が3回ぐらいある。正確な回数じゃないのかといえば、正確に思い出せるのがそのうちの1回だけだからである。それを使うことを止められなかった。間違ってfoldlを使っているわけではないことを示すためにコメントが必要だった。して第一候補に鎮座しているのだろう。それゆえ、foldlを「古典的」に定義する以外、選択肢がそもそも存在しなかった。1.3にseq関数が入ることになる。1.3のseq関数はEvalクラスの一部だったので、どこでも使えるというわけではなかった。1.3では、foldl'は次のような型で定義しなければならなかっただろう。foldlの定義は変わらなかった。そこでHugsとGHC、その他の処理系は非標準のfoldl'を追加した。当時の人々は互換性の問題を考えるのが面倒だと考えたのではなかろうか。非標準のfoldl'を追加することは簡単だが、標準を変えることはそれほどには簡単ではない。最初からseqが有ったのならば、foldlはそれを使って実装されたのではなかろうか。1.0の5年も前から既にseqが存在していた。Orwellの正格foldl!Orwellは興味深い例だ。Mirandaおよび初期のHaskellに非常によく似ている。消息筋によれば、Orwellのfoldlは今日foldl'として定義されるもの、すなわち正格評価版だったそうだ。Wadlerに直接尋ねることにした。Philは親切にも、私の為にマニュアルを発掘して問題の定義の部分を見つけてくれた。xsstrictという関数が使われているところに注目したい。Orwellのstrict関数は次のように(あるいはそれと等価に)定義されていたのだろう。x(これは近頃のHaskellでは($!)と呼ばれているものだ。)つまり、私の情報が正しいのならば、Orwellはfoldlを正格なバージョンに変更したということだ!私はこれが正しい決定「だった」と、そして正しい決定「である」と主張する。後方互換性への恐れ及び怠慢が招いた結果に過ぎないのだと主張する。うっかり出来心でfoldlを使ってしまう我々の助けになるだろう。迷える初心者の助けにもなるはずだ。講師の救いにもなるはずだ。Orwellはこの過ちを24年前に修正した。1.0よりも前のことだ。foldl'の定義としてふさわしいものが2つあることをお断りしておく。(これもまた別の歴史的な事故だと疑っている)。本文中に引用したバージョンが「標準的な」バージョンである。bangパターンを用いて、もう少し綺麗に書くことができる。xsもう一つのバージョンは次の通り。評価した状態にしておくという違いがある。これらの二つはほとんど同じ挙動を示す。異なる結果になる入力を見つけるのはちょっと手間だが、ここに一つを示す。最初に渡された値は依然として評価しなくてもよい。別のバージョンでは、アキュームレータは常に評価されることが伺える。二つ目のバージョンは、コード生成という観点からは魅力的である。これにより例えばIntをInt#にunboxして、引数をヒープではなくレジスタで渡すことができる。標準的なバージョンのfoldl'では、ループの初回のみ、特別ケースとして扱わなければならない。アキュームレータがまだ評価されていないかもしれないからである。foldl''ではそういう心配はない。最初のイテレーションから正しくunbox化できる。GHCは大抵の場合は初期値が常に評価されるかどうかを推論することができるが、常にではない。fusionの作業中にこれに気づいた。標準リスト関数との正格性の比較テストの実行中に失敗した。完全正格バージョンにするべきかもしれない。Bootswatch.。