<< ドメイン駆動設計:4つのアンチパターン | main | 関数型プログラミングってエクセル方眼紙だよね >>

プログラミングのパラダイムをちょっと考えてみた

私が仕事で使っているプログラミング言語は、最近は Java ばかり 。

SQL は、昔は得意技の一つだった。最近はほとんど書いていない(人まかせ)。

HTML, CSS, JavaScript は、画面まわりで必要なので、いちおう書く程度。
XML は、各種の定義ファイルやデータ交換形式として使うが、サンプルの写経レベルだな。

スクリプト言語だと、groovy を年に数回は使う。

プログラミング言語の遍歴


過去に仕事で使ったことがあるプログラミング言語を並べると、

Z80アセンブラ
C言語
Lisp
Prolog
Visual C++
C#
PL/SQL
...

ベターなアセンブラとして、C言語にのめり込み、C++ のわけのわからなさに挫折し、人工知能の研究プロジェクトで、Lisp と Prolog を使っていろいろ試作して、オラクルに転職してから数年間 SQLと PL/SQL の世界にどっぷりつかり、その後 ベターな C 言語として、Java にはまり込んでいった、というのが私のプログラミング言語の遍歴。

どの言語も、どっぷりつかっている時は、その言語が一番おもしろいと思ってやってきた。

Z80アセンブラはコンピュータを直接いじる感覚で、いまでも一番のお気に入りかもしれない。

C 言語を使っていた時も、メモリを直接操作するイメージで、プログラミングしていた気がする。
関数呼び出しがスタック操作で、malloc と free でヒープ領域を自己責任で管理。構造体は、バイトのアラインメントをいつも意識して書く感じがきらいじゃなかった。

Java をはじめた時も、バイトコードとか JVM の実装とかに異様に興味をもっていた気がする。

メモリ操作感覚とは対極にある言語だけど、もう一つのお気に入りが、Prolog。

・条件分岐やループがない。
・記述の順番は関係ない
・ファクト(事実)のセットと、ルール(規則)のセットだけを記述する。

そうすると、処理系が、事実とルールの中から、論理的な答えを推論してくれる。

Prolog を使った、こういう宣言型の論理プログラミングが、プログラミングのあるべき姿なんだと漠然と思っている。

SQL も事実(データ)と導出ルール(SQL)の宣言のみ、格納や取り出しの順番は関係なし、というところは、Prolog と通じるところがある。(だからSQLも嫌いじゃない)

Erlang は Prolog の影響を受けているらしい。パターンマッチングの感じとか似ているかな?

プログラミングのパラダイムって何?


パラダイムをどう定義したり、どう説明すればいいのかはわからないけど、感覚的には「発想」「常識」「基本の枠組み」あたりをイメージしている。
それも「無意識」に使う、体にしみこんだ「発想」「常識」「枠組み」という感じ。

最近、関数型プログラミング向きの言語が復権(?)してきているっぽい。久しぶりに「プログラミングのパラダイム」という言葉を思い出し、この記事を書き始めてみた。

最近の実用的な言語は「マルチパラダイム」、つまり複数のパラダイムをミックスした言語があたりまえらしい。

プログラミングのパラダイムを原理主義的に分類したり、比較することは意味はないかもしれない。

ただ、複数のパラダイムを経験するのはプログラム能力の向上には、とっても良いことだと思う。
複数のパラダイム、つまり異なる発想や枠組を知ることで、より質の高い設計や実装ができるようになるはず。

特定のパラダイムが絶対的に優れているとか、劣っているとか、そういうことはないんだと思う。

まったくの新しいパラダイムが生まれて、そちらに完全にシフトする(他のパラダイムを駆逐する)ということはプログラミングの世界ではないんじゃないだろうか?

必要に応じて複数のパラダイムをミックスして、仕事できる能力がたいせつなんだと思う。
モダン(?)なプログラミング言語は、マルチパラダイムが当たり前になってきている。だから、複数のパラダイムをベストミックスすることが、重要なプログラミングスキルになってきていると思う。

パラダイムって無意識というか、体に染みついた癖みたいなところがあるので、なかなか、マルチパラダイムを文脈に応じて自在にベストミックスというわけにはいきませんが。

今の自分はマルチパラダイムとベストミックスにどんな立ち位置で、どんな方向に進んでいるのか、ちょっと考えてみた。

動的型付+スクリプト vs. 静的型付+コンパイラ


自分は「静的型付+コンパイラ」にどっぷり。
動的型付+スクリプトで、複数モジュールに分割する規模のプログラムを書いたことがない。

※私は経験がないと言っているだけです。

動的型付+スクリプトは、食わず嫌いかな。
自分がプロとして責任もてるのは、静的型付+コンパイラだけというのが素直な気持ち。
JavaScript はちょっとなあ、と思っているけど、Typescript は面白そうと思う自分がいる。

動的型付+スクリプト言語で、大規模なアプリケーションを開発している現場では、リファクタリング、名前の変更、クラスの抽出、メソッドやフィールドの移動とか、どうやっているだろう。興味はある。

自動テストをたくさん書く?

設計駆動 vs. テスト駆動 ?


プログラム言語のパラダイムからずれるどけど、これもプログラミングパラダイムの一種ですね。

私は、設計駆動派。

単体レベルの自動テストは書かずに済むならそれが一番と個人的には信じている。
自分で仕事していて、自動テストを書かなくても、それほど、バグや変更の副作用に悩んだことがないので、設計スキルを磨けば、単体レベルの自動テストは、むだなんじゃないか、と心のどこかで思っている。

業務系のアプリケーションでは、個々の部品は、それほど複雑なロジックにはならないので。
というか、ロジックが単純明快になるように設計・実装するのが腕の見せ所と思っている。

命令的 vs. オブジェクト指向的 vs. 関数的


「型」という言い方があまり好きではないので、あえて「的」と書いています。
「的」というのは、程度の問題という、意味も込めて。

「わり」と命令的か、「まるで」命令的ではない、という言い方ですね。

手続き型とか構造化プログラミングは「命令的」な世界の一部だと思っています。
(加筆:「ると」さんからのコメントにある通り、この解釈は私の勉強不足かな)

三つのパラダイムは、もちろん特徴(癖?)がある。ひとつのパラダイムが体にしみつくと、他のパラダイムのプログラミングスタイルは、なかなかなじめない、ぴんとこない。

パラダイムの違いを精緻に考えとか、得意じゃないので「ようするにこういうことでしょ」と自分勝手の割り切り解釈ですませている。

私の割り切り解釈のキーワードは「データ」と「ロジック」。

命令的パラダイム

データはデータ、ロジックはロジックと分離して考える。
で、命令型のロジックの基本は「データ」の書き換え。

A = 10;

と書くと、Aの内容(参照先)を、10に上書きしろ、という代入命令になる。

if 文、for 文を駆使(?)して、データを書き換えまくるのが、命令的パラダイム。

関数的パラダイム

こちらも、データとロジックは分離する派。
データを immutable (不変)にするのが大原則であり重要な特徴。
すべてのデータは関数の評価結果として表現できる、という意味でロジック宣言だけのパラダイム、ということができるのかな?

関数的なパラダイムだと、

(A = 10)

は代入ではな「A は 10である」という宣言になり、それを評価すると、真か偽が返ってくる。

Prolog もこっち系。(評価ではなく、パターンマッチングと単一化というのは別の世界?)

関数的なパラダイムだと、関数そのものを返す、関数をパラメータとして渡す、関数の結合や合成ができる。
命令的、オブジェクト指向的なパラダイムがしみ込んでいると、ここが、感覚的にわからないところ。
私も、理屈はある程度わかっているつもりですが、感覚的にはついていけていません。

関数を投げると、言語処理系が文脈ごとに、うまい具合に、うまいタイミングで評価してくれる。
これが、感覚的にどうもわからないというか、なじめないというか...

ループ構造も関数的なパラダイムでは、for 文ではなく、再帰ですよね。

関数的なパラダイムだと、List, Set, Map, Tree などのデータ構造(コレクション)の扱いが、めちゃくちゃエレガントにできる(らしい)。

このコレクションの扱いに向いていることと、immutable 原則とが合わさって、マルチスレッド/マルチコア/マルチサーバーを使った、並行処理とかビックデータ処理とかのプログラミング向きということで、最近は関数的なパラダイムが脚光をあびるているんだと思う。

あと、関数型といわれる言語って記法が記号だらけで予約語も略語が多い。
記述は、冗長になるけどわかりやすさを追求するなら、記号や略語ではなく、日常的な英単語で記述する関数型言語があってもいいんじゃないかなあ。

オブジェクト指向的パラダイム

このパラダイムは、「データ」と「ロジック」は一体化すべし、が基本の発想。

命令的なパラダイムも、関数的なパラダイムも「ロジック」をデータと分離することで、いろいろ面白いこと(?)ができると考える。
データとロジックを分離するから「疎結合」になり、扱いやすくなる、という発想かな?

オブジェクト指向的なパラダイムでは「データ」と「ロジック」を一体にして「凝集度」を高めたオブジェクト(ソフトウェア部品)を用意することで全体のソフトウェアが組み立てやすくなる、という発想。
かつ、オブジェクト内部のデータ(状態)は、外には見せないのが基本の発想。

C言語の構造体宣言とは正反対の発想。
C言語だと、ロジック追うには、まず構造体の宣言を読む。構造体を隠ぺいされたら、わけがわからない。
オブジェクト指向だと、オブジェクト内部のデータ構造は、プログラムを理解するために知るべき情報ではない。

部品(オブジェクト)間の関係は、できるだけ「疎結合」にする。
データやロジックの実装は隠蔽して、オブジェクト同士は、できるだけ単純な「メッセージ」のやりとり方式にすることで、結合度を下げる。

多態(ポリモーフィズム)は、同じメッセージを送っても、その時の文脈に応じて、適切なオブジェクトが適切に反応するという仕組み。(メールの宛先は info で、実際には、その日の受付担当者が処理する、というモデル?)

Java は、オブジェクト指向的な言語という一面は確かにあるけれど、別の一面は、C 言語そのままの命令的なパラダイムの言語でもある。

Java でプログラミングをしていても、

・public 宣言だらけ
・get/set で、隠蔽したはずのデータ(状態)を、生のまま直接操作
・インタフェースや委譲など、間接参照の仕組みを使わない
・実装クラスへの参照が網の目のように広がっている(ドット連結)
・メソッドは、パラメータ渡しと返値というC言語の関数的に設計

こういう書き方だと、オブジェクト指向的なパラダイムが目指す世界とは、だいぶ違う世界。
Java を使って、命令的パラダイムだけでがんばっている書き方。
それも構造化プログラミング以前のグローバル変数、goto文天国の臭いがぷんぷんする書き方。
public な setter/getter って、グローバル変数そのものなんだけどなあ。

自分は Java でプログラムを書くなら、オブジェクト指向のパラダイムを中心で行きたいと思う。
言語仕様として、C言語的な命令的、手続的な書き方ができちゃうだけに、意識してオブジェクト指向的に設計し実装する必要がある。

Java でオブジェクト指向的に書く場合も、関数的なパラダイムから取り入れたい発想は多い。

まず、immutable (不変)。これがほんとうに大切。

Web アプリケーションしかり、マルチスレッドプログラミングしかり。
immutable なオブジェクトを、積極的に活用することで、プログラムの安定感がおどろくほど向上するし、変更の副作用も、激減する。

ドメイン駆動というか、業務のロジックとしても「 immutable 」が正しいことが多い。
ビジネスは、起きたこと、約束したことを「事実」として記録し不変にする(改ざんしない)ことが大原則。

情報の変更が発生した場合は「上書き」ではなく「変更があった」という事実を不変データとして「追加」で記録するのが業務アプリケーションの基本パターンですね。

オブジェクト指向的なパラダイムの「データ」と「ロジック」の凝集は良いことばかりではない。両刃の剣。
設計を間違えると悲惨なことになる。
特に、肥大化したクラスは、データとロジックのごった煮になっちゃって、手がつけられなくなる。

オブジェクト指向でも関数的パラダイムの「関数」を分離して扱いたいシーンはいろいろある。

Java の場合、匿名 inner クラスでメソッドをラッピングしたり、Comparator インタフェースを実装したオブジェクトをコレクションに渡して、処理を委譲する、などの仕組みを使う。
関数的なパラダイムで「関数」そのものを渡せれば、ここらへんはもっとすっきり書けるはず。

そうなれば、Java の中途半端なコレクションフレームワークも、もっとエレガントになるはずなんだけどなあ。
拡張 for 文でがんばるシーンが激減できそう。

関数的なパラダイムの言語は immutable で関数が第一級の構成要素、というのが、言語仕様として枠組になっている。

Java とかだと、immutable や関数渡しのテクニックは、言語仕様としては明示できないので、設計や実装のガイドラインとして、あるいはイデオムや小技として使うことになる。

クラスベース vs. プロトタイプベース


この二つを「オブジェクト指向的なパラダイム」と、ひとくくりにするのは、まずいのかもしれませんね。

クラスベースとプロトタイプベースでは、別の発想のプログラミングが必要だから、別のパラダイムと考えたほうが良い気がする。

JavaScript だと .prototype のうまい使い方が、いろいろな技のネタになっているようですね。

クラスベースでも、Ruby だとクラスもオブジェクトとして扱うという発想のようです。
ここらへんも、こってこっての Java プログラマにはなじみにくいんだよなあ。

プリミティブ vs. コレクション vs. 型のユーザ定義


データをどういう扱い方をするかの発想の違い。

プリミティブ重視

命令パラダイムでは、データは、整数や float など、プリミティブ(固定のバイトサイズのビットパターン)が、根底にある発想。

アセンブラなら、メモリ番地へのラベル、C 言語ならポインタを使って、固定のバイトサイズのメモリをいじりまくる。
(だから、効率的なコードが書ける、という側面もある)

コレクション重視

関数的なパラダイムは、データの扱いかたは、なんといってもコレクション、特に「リスト」ですね。
Lisp に代表されるように、あらゆるものをリストとして数学的なモデルで処理することが、関数的パラダイムの基本思想。

数式を美しいと思うか、わけわからんかと思うか?
美しいいと思う人は、まちがいなく、関数的パラダイムに向いている。
わけわからんと思う人は、オブジェクト指向的パラダイム向き(だと思う)。
オブジェクト指向的なパラダイムでは、日常使っている言葉を、そのままクラス名やメソッド名にすることが、基本の発想なんだと思う。

関数的なパラダイムは数学、オブジェクト指向のパラダイムは文学、と言った人がいるが、そういう感じがする。

命令的パラダイムに向く人?
メモリとCPUの構造が面白いと思う人は命令的パラダイム向きかな。
メモリとCPUはよくわからないが、プログラミングと言えば命令的・手続的パラダイムしか経験がない、という人が多いような気がする。命令的なパラダイムに向いていたわけではなく、最初に命令的なプログラミングを経験してそのまま、というパターンが多いのかもしれない。

Java の入門書なんて、これでもかというくらい命令的パラダイムに振り切った説明やサンプルばかりだからなあ。

型のユーザ定義

(クラスベースの)オブジェクト指向的なパラダイムでは、いろいろな型を必要に応じて宣言して、それを部品としてプログラムを組み立てていくスタイル。

ドメインモデル中心でソフトウェアを設計開発する、ドメイン駆動設計やるなら現時点ではオブジェクト指向的なパラダイムの一択だと思ってやっています。
(Prolog の論理パラダイムを遠くに見ながら)

業務の言葉を使ってロジックを組み立てるには、やはりオブジェクト指向のパラダイムがやりやすいと思う。

宣言的 vs. 手続き的


最後に「ロジック」記述のパラダイムについて。

ロジックを説明するときに、

・手続き
・処理
・計算

とか、命令的パラダイムの説明が多いように思う。

これは、いろいろなプログラミングパラダイムを考えるときは、よろしくない。

ロジック(論理)の記述方法は、命令的な手続の記述とは限らない。

ビジネスのルール・約束事を、宣言的ルールとして書くことも「ロジック」の記述。

論理(ロジック)を「手続き的」に書くか「宣言的」に書くかは、プログラミングのパラダイムの大きな分かれ目の一つ。

Prolog のように、言語仕様として宣言的にしか書きようがない言語だと、話は簡単ですね。
SQL, HTML, CSS, XML もみんな宣言的に書くための言語。

問題は、手続き的にも宣言的に書けてしまう言語。

Java は、母体が C 言語だから、命令的パラダイムで手続き的に書く方が一般的かもしれない。
Java の初心者向けの本やサンプルが、命令的パラダイムの説明だけだし。

Java は広く使われるようになったけど、オブジェクト指向的なパラダイムでのプログラミングは、それほど普及していない。

Java で書けば、自然にオブジェクト指向のプログラムになるわけではない。

コード量からいったら、命令的パラダイムで書かれた Java プログラムが圧倒的に多いんだろうなあ。
Java 言語の入門書や解説書、サンプルコード、Java 自体のソースコード、公開されたさまざまなオープンソースコード...

どれを見ても、命令的なパラダイムの発想や書き方が多い。
それらを参考に学習すれば、そりゃ命令的なパラダイムが体にしみついちゃいますよねえ。

JavaScript だと、命令的にも、関数的にも、オブジェクト指向的にも、どうにでも書けちゃう。
Scala とかもそうかな。 それがいいことなのかどうか...

What vs. How


宣言的に書くなら、What に注目すべし。
What に名前をつけて、それを、パッケージ名、クラス名、メソッド名、変数名に積極的に使う。

What は、結果として「何が欲しいか?」とか、関心の焦点は「何か?」という問いかけから見つけていく。

手続き的に書くなら、How に注目して、それに名前をつける。

業務アプリケーションの場合、業務手順は文字通り「手続き」的に書くのが正しい。
ただしは、それは業務の概念での話。

業務アプリケーションを、コンピュータ上の一連のデータ操作の命令、手続きとして書くのが広く普及しているアプリケーションプログラミングのスタイルのようです。

コンピュータのデータ操作の手続き( How ) を隠蔽して、表に見せる顔は「何が欲しいか?」、「関心の焦点は何か?」という発想で、What 重視でクラスやメソッドを組み立てるのが、良いオブジェクト指向プログラミングなんだと思う。

What 指向の場合は、基本的に記述の順番は関係ない。

論理プログラミング的な発想だと、

A=B;

は、代入ではなく、AはBである、という宣言になる。

こういう宣言的な書き方は、データ(状態)が immutable であれば、どういうタイミングで、どういう順番で評価しても、同じ結果になります。

What 指向というのは、そういう意味で関数的パラダイムの発想ですね。

結論(?)


Java でオブジェクト指向的に書くことを大切にしたい、と言いながら、

・immutable で
・What 重視で
・宣言的に

となると、それって関数的なパラダイムでは、という突っ込みがありそうですね。

私もよくわかっていないんですよ。

Java 言語を使いながら、オブジェクト指向的パラダイムの進化形を目指している?
関数的パラダイムに入り込みながら、実装の道具としては Java に執着してミスマッチを起こしている?

私の今の感覚では、オブジェクト指向的なパラダイムの発展過程なんですけどねえ。

どうなんでしょう?

コメント
ダイクストラは構造的プログラミングにおいて抽象データ型も重視していて、抽象データ型とそれを扱うための抽象ステートメントを他と分離して抽象データ型の実装が変わっても抽象ステートメントの実装を変えるだけでよくするべきと主張しています (http://homepages.cs.ncl.ac.uk/brian.randell/NATO/ の右の本の“Structured programming”という記事)。
そのため命令的パラダイムについて「データはデータ、ロジックはロジックと分離」するとし、「プリミティブ重視」とした上で、構造化プログラミングを「命令的」な世界の一部とするのは少なくとも歴史的には間違っています。

一方、ダイクストラやホーアらによる“Structured Programming”という書籍 http://dl.acm.org/citation.cfm?id=1243380 の構成は、第2部がデータの構造化についてで第3部はSimulaによる原始的なオブジェクト指向の紹介となっています。そのため構造化プログラミングは、マクロの無いアセンブラのような命令的な世界と比べると、どちらかといえばオブジェクト指向に近いです。

るとさん

コメントありがとうございます。

原典といえるような論文で議論されているのはそういうことなんですね。

構造化プログラミングというと goto 文論争のイメージだったんですが、認識あらためないといけないなあ。

勉強になりました。ありがとうございます。
  • masuda220
  • 2012/12/07 11:56 PM
コメントする









この記事のトラックバックURL
トラックバック
calendar
    123
45678910
11121314151617
18192021222324
252627282930 
<< June 2017 >>
システム設計日記を検索
プロフィール
リンク
システム開発日記(実装編)
有限会社 システム設計
twitter @masuda220
selected entries
recent comment
  • 番号より名前。 ニーモニックコードより名前。 【パターン】
    師子乃 (03/10)
  • Smart UI が優れている?
    masuda220 (03/10)
  • Smart UI が優れている?
    kagehiens (03/09)
  • オブジェクト指向プログラミングの教え方?
    masuda220 (12/05)
  • オブジェクト指向プログラミングの教え方?
    ZACKY (12/04)
  • 「オブジェクトの設計力」 スキルアップ講座やります
    masuda220 (08/14)
  • 「オブジェクトの設計力」 スキルアップ講座やります
    kompiro (08/14)
  • 「オブジェクトの設計力」 スキルアップ講座やります
    masuda220 (06/13)
  • 「オブジェクトの設計力」 スキルアップ講座やります
    JHashimoto (06/13)
  • 「オブジェクトの設計力」 スキルアップ講座やります
    masuda220 (02/28)
recent trackback
categories
archives
others
mobile
qrcode
powered
無料ブログ作成サービス JUGEM