見出し画像

関数型プログラミング事始め (8) 遅延評価の苦難

関数型プログラミングがはじめての方へ贈る入門の書
前節:高階関数の苦難 次節:Lispは関数型
参考書:
・五味 弘「はじめてのLisp関数型プログラミング」技術評論社(2016)
・大山口 通夫、五味 弘「プログラミング言語論」コロナ社(2008)
・五味 弘「関数型プログラミングと数学(ITと数学)」技術評論社(2021)

遅延評価は関数実行のときに引数の実行順序を通常の左側・内側優先の順序を変更し、その順序を通常よりも遅延することです。これにより、引数の値が必要になったときに実行することで、引数の値が特定の条件で不要であるときに無駄な実行をしない効果があります。しかし遅延評価は通常の実行順序ではないので、プログラムの了解性を著しく悪くします。でも慣れれば慣れます(トートロジー)。

(6) 遅延評価の苦難 - 左側優先と内側優先

関数の実行順序は太古の昔から、左側優先評価 (leftmost evaluation (reduction))・内側優先評価 (innermost evaluation)と決まっています。この実行順序を正格評価 (strict evaluation)と言います。そしてこの正格評価の順序は未来永劫、変わらないでしょう。

例えば、f(g(x),h(y))の実行順序は、(1) x、(2) g(x)、(3) y、(4) h(y)、(5) f(g(x),h(y))となります。これはあまりも当たり前のことなので、誰もが疑わない実行順序の規則です。

しかしこの順序でないものがあります。例えば、if文がそうです。if文の実行順序は条件節の実行結果の値によって、then節かelse節のどちらかが実行されます。これは左側優先の順序ではありません。他にfor文なども同様です。

 (考察) 遅延評価 - 必須呼び出し

関数型プログラミングでは正格評価以外の遅延評価 (lazy evaluation)が可能です。この遅延評価の機能を使うことで、引数の値が必要になったときに引数の実行を行う必須呼び出し (必要呼び出し) (call by need)が行えます。これにより無駄な引数実行がなくなり、高速で省メモリな実行になります。

プログラミング言語によっては手動でプログラミングして必須呼び出しを実装するか、または自動的に必須呼び出しをするようにできます。

しかしプログラムを読むときに、引数の実行がいつされるかが不明になります。これはプログラムの了解性を低下させています。特にデバッグをするときに非常に面倒なものになります。

例えば、if文は必須呼び出しです。then節が実行されるか、else節が実行されるかは実行してみないとわかりません。このため、テストケースを作成するときには、両方の分岐を網羅する必要があります。なおこれを分岐網羅(branch coverage) C1と言います。

このように遅延評価はプログラムの了解性を低下させ、テストコストを増大させ、デバッグを難解にします。でも遅延評価による高速化・省メモリはあまりにも大きい恩恵ですので、諦めてデバッグしてください。

(7) その他の苦難

今まで紹介してきた苦難以外にも、関数型プログラミングにはまだまだ多くの苦難があります。例えば、関数型プログラミングと言えば、モナドがあります。でも安心してください。原理主義者的純粋関数型プログラミングを目指さない限り、使わなくても大丈夫です。宗教論争をせずに平和的に関数型プログラミングを楽しめます。

関数型プログラミングの設計はオブジェクト指向設計でも手続き型の機能中心設計でもありません。新しいパラダイムになります。これも苦難になります。これについては後述するようにします。お楽しみにしてください。

(次回予告) 2章 Lisp超入門 - 関数型プログラミングのための

次回は関数型プログラミング言語の一つとしてLispを取り上げ、その言語仕様を関数型プログラミングするための超入門を紹介する予定です。

参考:プログラミング言語はどれがお得?(前編)|五味弘 (note.com)
参考:プログラミング言語はどれがお得?(後編)|五味弘 (note.com)



よろしければサポートをお願いします!