宣言型か?命令型か?ではない。宣言型プログラミングも命令型プログラミングも違いを理解していい感じに使おう。

こんにちは。てぃろです。

最近良く宣言型プログラミングという言葉をよく耳にします。

ここのところReactを書いていることで自分でもよく触れるのですが、まだまだ命令型の書き方をよくしてしまいます。

そんなことをしていると、「宣言型プログラミングってそもそも何だ?」という風に考えてしまったので、ちょっと整理してみようと思って、この記事を書き始めました。

なお、本記事は私個人の見解を示すものであって、学術的な正しさは考慮していません。

宣言型プログラミングって?

宣言型プログラミングは以下のように定義されるようです。

宣言型プログラミングは、問題の解法、すなわちデータ構造とアルゴリズムを記述する命令型プログラミングとは対照的なプログラミングパラダイムです。宣言型プログラミングが記述するものは、問題の定義、すなわち解くべき問題の性質や、その際に満たすべき制約の記述です。

宣言型プログラミングの可能性と限界

上記でも少しわかりにくいところがあると思います。

これを私の解釈で意訳すると「中身の細かい手順は抜きにして、本質的に表現したいことを書く書き方」ということだと思っています。

実は上記で紹介している記事は、2008年の記事で代表で取り上げている言語はPrologという今ではほとんど聞いたことがない言語です。

Prologは述語という形式で論理式を書くもので、いわゆるルールベースのAIであるエキスパートシステムを書くには有用とされていた言語であり、機械学習系の研究室に属していたので少し習う機会がありました。

このように宣言型プログラミングとは最近注目されているだけで、昔から既に存在する考え方でした。

しかしここまで注目されているのは、ここ最近のソフトウェアがあまりにもリッチ(複雑)になりすぎていて、あらゆるステート(状態)を命令型に記述するのが難しくなったからなのだろうと思います。

では、すべてが宣言型でよいのか?と言われると私はそうではないと思います。

プログラミングはあくまでソフトウェアでサービスを実現するための手段でしかないので、宣言型か?命令型か?という二元論ではなく、どちらのいいところも悪いところも知った上で良い感じに使い分けられればいいんじゃないかと思っています。

それでは、それぞれの違いを挙げてみて、どんなときにどちらを使えばいいのか少し考えてみたいと思います。

宣言型プログラミングのほうが、複雑度の増加に強い

上記の定義にある通り、宣言型プログラミングでは問題の定義や制約の記述をするというのが本質のようです。

この言い方は結構学術的で、プログラミングとは少し言い方が違います。言い換えてあげると、

  • 問題の定義 = 関数定義、変数定義、リテラル定義
  • 制約 = 条件式(if、switch、filter…)

という感じでしょうか。

逆に命令型プログラミングがなんだったかを考えてみます。こちらはwikipediaを参照するとこうあります。

命令型プログラミング(英: Imperative Programming)は、プログラムの状態(英語版)を変化させるステートメントを基本文に用いる総称的なプログラミングパラダイムである。ステートメントではコマンド(命令文)が多用される。宣言型プログラミングと対をなしてのプログラミング言語の分類用語としても扱われている。宣言型の数学的性質に対して、命令型はノイマン型コンピュータ向けの計算機科学特有の性質である。

命令型プログラミング

つまり、命令(コマンド)を使用しプログラムの状態を変化させる書き方のことであると言います。もう少し言い換えると、

  • 状態 = 変数定義、リテラル定義
  • 命令 = 関数定義(条件式含む)

という感じだと思います。

命令型プログラミングの定義には注目すべき点があります。それは「ステートメントではコマンド(命令文)が多用される」という点です。

問題(開発したいソフトウェア)が複雑になったとき(変数定義や条件式が増える)ことを考慮すると、宣言型プログラミングでは複雑度は直線的に増加するのに対して、命令型プログラミングでは指数関数的に増加することになると思われます。

宣言型プログラミングにおいては、 変数定義や条件式が増えるということは、そのまま宣言型プログラミングで書き下すべき事柄が増えているだけですので、何かが副次的に増えていくということは考えられません。

命令型プログラミングにおいては、変数定義が増えればそれに関連する条件式の条件は増えます。条件式が増えるならそれに関わる変数定義にも影響してきます。こうして相互に関係性を持つことから、複雑度は爆発的に増えていくことが予想されます。

このように、複雑度が増えれば増えるほど、宣言型プログラミングのほうが優位になると考えられます。

つまり、宣言型プログラミングは状態管理が複雑な場面において非常に有用であると考えられます。

命令型プログラミングは、 順序のある業務ロジックで使える

ただし、命令型でプログラミングしたほうがいい場面だってあると思います。

それは利息計算などの業務ロジックが入るプログラムにおいてです。要するに順序のある手続きが必要になるプログラムが向いていると思われます。

宣言型プログラミングにおいては、宣言のみをするのでそこに順序は存在しませんが、命令型プログラミングでは順序が存在します。

以下のQAで示されている例が面白いのですが、命令型プログラミングでは順序が逆になるとコンパイルできない。宣言型プログラミングでは順序が逆になってもコンパイルできる、というものです。

このように順序を間違えると正しい解が得られなくなってしまうのが命令型プログラミングです。

ただ、上記の例を見ると「宣言型でも命令型でも同じように書けるじゃん」となるのですが、やはり言語特性も合わせてみると、特性に合わない書き方をすると可読性が圧倒的下がります。

本来あるべき手順をそのままプログラムに落とせるほうが、バグは少なく可読性が高いメンテナンスがしやすいプログラムになっていくと思います。

もちろん、宣言型で書ける業務ロジックもあると思いますが、命令型プログラミングは特に複雑な計算手順をふむ必要がある場合においては特に有用なのではないかと思っています。

最後に

今回は宣言型プログラミングと命令型プログラミングについて少し考察してみたコラムでした。

私もまだまだ理解しきれていない部分もありますし、そういう話じゃない!という意見の人もいるでしょう。

学術的には定義がしっかり分かれていることも理解しますが、実際に記述されたコードにおいても、これが宣言型か命令型かという議論が巻き起こるようなどちらともつかない書き方だってあると思います。

しかし、ソフトウェア開発においては、そういう議論をすることそのものは(楽しいですが)本質ではなく、あくまで性能が高く保守性が高い品質の良いプログラムを書くことが最も重要な本質だと私は信じています。

宣言型か命令型かにこだわりすぎることなく、適切な複雑度でプログラムを書けるようになりたいものですね。