要するに Haskell で取り回しが難しいのって Monad みたいな抽象じゃなくて,Non-Strict Evaluation なんだよねって話.

foldRight の概念を OCaml で説明したところ,"Right があるのならば Left もありそうだけどどうなの?" という極めて妥当な質問を受けた.まあ,あるよね.以下は OCaml 側からの Haskell へのアプローチなので,関数名は OCaml に従う.

譬え話として,引数に任意の人を1人取り,その人を殴って,殴った人を背中のカゴに放り込む,という関数を考えた.殴られた人の入ったカゴが評価値となる.活字にすると酷いなこれ.まあ,ここで言いたいのは fold(reduce) の向きだけだしそこは勘弁.

ともかく,この関数を fold の評価値として二郎の待ち行列(list 人')に適用してみる.

二郎に並ぶ人々を先頭から殴ってはカゴに放り込み,殴ってはカゴに放り込み.これを最後尾の人まで続けるのが foldRight.リストのネスト構造に対して "→ 向き" に再帰適用が施される.

二郎に並ぶ人々を,一方,まず先頭から最後尾まで全員殴り倒しておいて,最後尾から先頭に向かって一気にカゴに放り込んでいくことも出来るわけだ.これが foldLeft.リストのネスト構造に対して "← 向き" に再帰適用が施される.

話は変わって Haskell の Non-Strict Evaluation.Strict な場合と違って,最後の関数適用から評価しようとする戦略.この戦略の上で,無限リストに foldLeft で任意の関数を適用する場合を考える.すると,見事に無限に殺される.基底部が無限の中に埋れてしまっているのだから当然といえば当然.

上の例で言えば,無限人の待ち行列に最後尾なんて定義できないから,つまり行列の構成要員を全員殴り終えることはできない.こんなふうに,"人を殴る"と"殴った人をカゴに放り込む"っていう2つの動作を,再帰の往路と復路に対応させて分割しちゃうと,往路が無限の彼方に発散する片道切符になる.その点,この2つの動作を各時点(各人)について1セットとして施せば,つまり,人を殴るたびにカゴに放り込んでいけば,いつでも殴るのを中断できる.

といったような,譬え話をした.理解してもらえてたら嬉しいなあ.どうかなあ.