Last active
September 17, 2020 15:55
-
-
Save ken-okabe/a19b4b7e0eca10cb67c6 to your computer and use it in GitHub Desktop.
関数型プログラミングとオブジェクト指向の抜き差しならない関係について整理して考える
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
##関数型プログラミングとオブジェクト指向の抜き差しならない関係について考え、整理したい | |
Googleで適当に検索すると | |
####オブジェクト指向 関数型プログラミングに関連する検索キーワード | |
オブジェクト指向プログラミング関数プログラミング比較 | |
オブジェクト指向関数プログラミング | |
オブジェクト指向関数型違い | |
関数型言語オブジェクト指向 | |
手続き型オブジェクト指向関数型 | |
オブジェクト指向vs関数型 | |
オブジェクト指向言語関数型言語 | |
関数型言語オブジェクト指向違い | |
とズラリとでてくる、それなりに知的需要があるらしい、この辺のことについて書きます。 | |
----- | |
というか、関数型言語って何?っていう人、知ってるよという人に向けて、 | |
[IQ145で美少女JKな先輩に「関数型プログラミング」を特訓してもらえた僕の5日間の記録](http://qiita.com/kenokabe/items/618692858044a89adbc0) | |
を書きました。 | |
この記事と関連する | |
[【前編】2014年、オブジェクト指向vs関数型プログラミングの壮絶な宗教戦争は終結 OO陣営最後の牙城DOMというWeb標準のUIを破壊した巨砲Facebook-React](http://qiita.com/kenokabe/items/9c650ec8bcb1418c596d) | |
[【後編】2015年、脱オブジェクト指向時代にFacebook-Reactのコードをイミュータブルな宣言型でイベント駆動するFRPで書く](http://qiita.com/kenokabe/items/385a80295046fb9ada6d) | |
を書きました。 | |
----- | |
[オブジェクト指向 v.s. 関数型プログラミング](http://anond.hatelabo.jp/20140409010816) | |
[関数型とオブジェクト指向という一見相反するプログラミングパラダイムの併用について理解した](http://zerobase.hateblo.jp/entry/2013/02/21/171750) | |
[プログラマが知るべき97のこと/関数型プログラミングを学ぶことの重要性](http://ja.wikisource.org/wiki/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%81%8C%E7%9F%A5%E3%82%8B%E3%81%B9%E3%81%8D97%E3%81%AE%E3%81%93%E3%81%A8/%E9%96%A2%E6%95%B0%E5%9E%8B%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%82%92%E5%AD%A6%E3%81%B6%E3%81%93%E3%81%A8%E3%81%AE%E9%87%8D%E8%A6%81%E6%80%A7) | |
[新人プログラマに知っておいてもらいたい人類がオブジェクト指向を手に入れるまでの軌跡](http://qiita.com/hirokidaichi/items/591ad96ab12938878fe1) | |
と本トピックについてそれなりに参考になる情報はあるものの、イマイチはっきりせず、これまで自分自身ぼやっとしたまま、適当にオブジェクト指向で適当に関数型プログラミングと曖昧に臨機応変に、と適当にやってきたのですが、そろそろきちんと自分自身「関数型プログラミングとオブジェクト指向の抜き差しならない関係」についてはっきりさせておきたい、と考え、概念整理した結論を書きます。 | |
##まず端的な結論 | |
結論を端的に言うと、 | |
関数型プログラミングとオブジェクト指向は、「VS」や相反するパラダイムではなく、 | |
###構造体 + 関数型パラダイム = オブジェクト指向 | |
の関係になっている。 | |
つまり、 | |
「関数型プログラミングとオブジェクト指向を臨機応変に併用する」 | |
だとか、 | |
「関数型プログラミングとオブジェクト指向のマルチパラダイムである」 | |
などとすると、概念の重複が起こり、冗長なことになり、なにがなんだかわからない、おかしな話になる、ということです。 | |
「マルチパラダイム」でもそれぞれのパラダイムが[直交する概念](https://www.google.co.jp/search?q=%E7%9B%B4%E4%BA%A4%E3%81%99%E3%82%8B%E6%A6%82%E5%BF%B5&oq=%E7%9B%B4%E4%BA%A4%E3%81%99%E3%82%8B%E6%A6%82%E5%BF%B5&aqs=chrome..69i57.3417j0j9&sourceid=chrome&es_sm=91&ie=UTF-8)、つまり、それぞれの要素が排他的な場合は論理的整合性がはっきりしているけれども、 | |
構造体 + 関数型パラダイム = オブジェクト指向 | |
というように一方のパラダイムが他方のパラダイムと重複する関係性になっているばあい、いろいろややこしいことになる、理解においても実践においても混乱を招く、実際混乱を招いている、ということになります。 | |
##そもそもオブジェクト指向とは何か?何がいいのか? | |
オブジェクト指向の一番簡単な例は、デスクトップ上のアイコンです。 | |
OSのGUIのアイコンとはオブジェクト指向のオブジェクトです。 | |
たとえば、それがMicrosoftWordのファイルであれば、そのアイコン(オブジェクト)をクリックすれば、Wordアプリが開く。 | |
これがオブジェクト指向の作法です。 | |
オブジェクト指向以前の作法では、まず、Wordアプリを開く、次にメニューから開きたいファイルを選択して、開く。しかしここで、Excelのファイルを開こうとしてもWordアプリでは開けません。 | |
オブジェクト指向とは、データがまず中心にあり、それに命令が紐付いているということです。 | |
作業机がある。真ん中に作業対象となるドキュメント・データがある、その周りにずらっと専用の各種ツールが用意されている。そんなイメージです。 | |
デスクトップ上で右クリックしてみると、「新規ファイル作成」みたいなコンテキストメニューがあり、そこには、各種ファイル(主にアスキーファイル)のテンプレートが用意されている。 | |
このテンプレートがオブジェクト指向における、その名の通りテンプレート、雛形であり、クラスと呼ばれるもの。 | |
クリックして新規作成すると、そのテンプレートを元に新規ファイルが作成される。これがオブジェクトであり、インスタンス化と呼ばれる。 | |
このように、プログラマーでない一般的なユーザにもオブジェクト指向は便利に利用されています。 | |
便利な理由とは、データが中心にあり、操作がそこに紐付いている、ということで、 | |
Before 操作 ⇒ データ | |
After データ ⇒ 操作 | |
のパラダイム転換が、オブジェクト指向によってもたらされていることがわかります。 | |
さて、データオリエンテッドで、操作をしていく、というのは、関数型プログラミングの発想であることに気がついたでしょうか? | |
関数型プログラミング自体の説明、導入については「脱アルゴリズム宣言」というコントラバーシャルな文脈で別途書いているので、読んで下さい。 | |
[Swiftで脱アルゴリズム!iOS開発を関数型(宣言型)プログラミングへパラダイムシフトしてみる【脱アルゴリズム宣言①】](http://qiita.com/kenokabe/items/41189c45001321c9e283) | |
##オブジェクト指向のパラダイム転換とは関数型プログラミングパラダイムと本質的に等価 | |
オブジェクト指向のパラダイム転換とは関数型プログラミングパラダイムと本質的に等価です。 | |
[新人プログラマに知っておいてもらいたい人類がオブジェクト指向を手に入れるまでの軌跡](http://qiita.com/hirokidaichi/items/591ad96ab12938878fe1)にも解説されているとおり、 | |
>Simula & C++のオブジェクト指向 | |
>C++の作者であるビャーネ・ストロヴストルップは、オブジェクト指向を「『継承』機構と『多態性』を付加した『抽象データ型』のスーパーセット」として整理した。 | |
>C++ではメソッドのことをメンバー関数と呼ぶ。これはSimulaがメンバープロシージャと読んでいるところに由来する。メソッドは、Smalltalkが発明した用語だ。 | |
なのですが、この「メンバー関数」なるものを備えるオブジェクト指向言語C++がCから如何にパラダイム転換したか経緯を見てみましょう。 | |
この『抽象データ型』というのは、古き良きC言語の頃からある、 | |
[構造体 = Struct](http://ja.wikipedia.org/wiki/%E6%A7%8B%E9%80%A0%E4%BD%93)のことです。 | |
``` | |
struct Person | |
{ | |
string name; // person's name | |
int age; // person's age | |
} | |
``` | |
``` | |
Person tom; | |
tom.name = "Thomas Cruise"; | |
tom.age = 52; | |
``` | |
特に説明は不要だと思います、新たに`Person`という構造体のテンプレートを用意して、そのテンプレートを基準に任意のデータを定義していく。 | |
プログラムの重要な課題である「まとまり」あるいは「モジュール化」を実現するための機能です。 | |
このPerson型に紐付いている`name`や`age`の要素のことを **メンバ** と呼びます。 | |
ここで賢い人はこう考えた。「モジュール化」が大事でこういう構造体の仕組みはあるのはいいが、Cの関数もいっそのこと構造体のメンバに加えられるようにすればどうか?現状Cの言語仕様では無理だから拡張しよう。 | |
それがC++で、**メンバ関数**であり、オブジェクト指向ではよく**メソッド**とも呼ばれます。 | |
構造体のメンバとして、データだけでなく関数も同列に扱えるようにした、のです。 | |
これは、ひとつの関数型プログラミングへのパラダイム転換です。 | |
ただし、CからC++では、このように関数型パラダイム転換が起こったものの、C++は未だ命令型・手続き型パラダイムに留まっており、関数型言語ではありません。 | |
関数型プログラミング言語が備えている機能として、 | |
- 関数はファーストクラスオブジェクトであり、データとして取り回せる | |
という超重要な特徴があります。 | |
もし、C++が関数型言語で、関数がファーストクラスオブジェクトであったならば、そもそも「メンバ関数」とかやる必要はないでしょう。そのまま構造体のメンバーにデータとして追加できるのだから。 | |
以上が、 | |
###構造体 + 関数型パラダイム = オブジェクト指向 | |
という意味です。 | |
だから、関数型パラダイムで関数型プログラミングする際に、オブジェクト指向を意識したり、マルチパラダイムだ、とやることは、すなわち、自分自身のパラダイムを意識しているということで、あまり意味がない、ということになります。 | |
関数型プログラミングで、オブジェクトだ、クラスだ、ということは、単に古き良きCの構造体のことをオブジェクト指向の言葉で言っているということにすぎません。 | |
ここまでは、良かった。 | |
しかし問題はそう単純ではありませんでした。 | |
##「オブジェクト」と「メッセージ」なる便利だと勘違いされて普及した不便なアプローチ | |
ふたたび[新人プログラマに知っておいてもらいたい人類がオブジェクト指向を手に入れるまでの軌跡](http://qiita.com/hirokidaichi/items/591ad96ab12938878fe1)にも解説されているとおり、 | |
>Smalltalk & Objective-Cのオブジェクト指向 | |
>Smalltalkの作者の一人であるアランケイがオブジェクト指向という言葉について次のように定義づけている。 | |
「パーソナルコンピューティングに関わる全てを『オブジェクト』とそれらの間で交わされる『メッセージ送信』によって表現すること」 | |
>C++の世界観とはまた異なっているのがわかると思う。 | |
>仮想機械としてのオブジェクト | |
>アランケイの世界観の中では、メモリとCPUとそれに対する命令を持つ機械をさらに抽象化するとしたら、それは同じくデータと処理と命令セットをもつ仮想機械で抽象化されるべきだと考えていた。 | |
>これは、構造化プログラミングの中でダイクストラが仮想機械として階層的に抽象化すべきだと言っていたこととかぶる。 | |
個人的には、背景の違いこそあれ同じことを言っているように思う。 | |
>オブジェクトは独立した機械と見なせるため、それに対してメッセージを送り、自ら持つデータの責任は自らが負う。 | |
>Smalltalkの実行環境もまた仮想機械として作られている。 | |
C++で、Cの構造体のメンバーを関数に拡張して関数型パラダイムの考えを取り入れたところまではよかった。しかし、この | |
>「パーソナルコンピューティングに関わる全てを『オブジェクト』とそれらの間で交わされる『メッセージ送信』によって表現すること」 | |
>仮想機械としてのオブジェクト | |
というアイデアは間違ったアプローチであると考えます。 | |
>C++の世界観とはまた異なっているのがわかると思う。 | |
と引用元でも言及されていますが、C++の関数型パラダイム転換と言って良い世界観とは違います。 | |
開発者の個人的思想であり、言語の実装とはまた別ではないかと言うこともできますが、オブジェクト指向が、前者のC++の世界観、つまり関数型パラダイム転換ではなく、後者の仮想機械が複数乱立してお互いにメッセージをやりとりし合うというような世界観で圧倒的に普及したのは紛れもない事実でしょう。 | |
オブジェクト指向プログラミング言語で、各オブジェクトをこういう世界観で取り回すと、関数型パラダイムとは真っ向から対立します。 | |
何がまずいか? | |
関数型プログラミング(宣言型プログラミング)の大いなるメリットとは、状態変数を排除し、すべてを数学世界で透過的に取り扱えるようにする、ということです。 | |
[Swiftで脱アルゴリズム!iOS開発を関数型(宣言型)プログラミングへパラダイムシフトしてみる【脱アルゴリズム宣言①】](http://qiita.com/kenokabe/items/41189c45001321c9e283) | |
[数学と別離したプログラミング、時間を抽象化し数学を取り戻すプログラミング【時空プログラミング④】](http://qiita.com/kenokabe/items/b81c7aa8af86314551a0) | |
ところが、アランケイのオブジェクト指向の世界観においては、それぞれのオブジェクトが、仮想機械として状態を保持しながら、互いにやりとりし合う。これは参照透過な数学世界の話ではありません。関数型パラダイムとは真逆の発想です。 | |
プログラミングの問題のほとんどすべては状態変数の乱立、管理の難しさに起因しており、関数型パラダイムでは状態管理を排除するために進化してきたといっても過言ではないでしょう。 | |
##Why OO Sucks | |
これは特に、 | |
[オブジェクト指向はクソか?](http://qiita.com/tokomakoma123/items/fb7530232912dc4176c4) | |
>私はJavaプログラマでしたが、関数型言語であるClojureにより魅力を感じています。 | |
そしてなぜ関数型言語であるClojureについてなぜ私が気に入っているのか考えているときにこの記事を見つけました。 | |
>「Why OO Sucks」という刺激的なタイトルですが、私が理解するためにもこの記事を和訳しました。 | |
> http://www.sics.se/~joe/bluetail/vol1/v1_oo.html | |
>この記事の元はErlangの作者であるJoe Armstrongが書いたそうです。そしてあとから知ったのですが、書かれてから少し時間が経っているため、何人かがすでに訳されているようです。そして数多いディスカッションがなされているようです。 | |
[Why OO Sucks](http://www.sics.se/~joe/bluetail/vol1/v1_oo.html) | |
拒否理由その4 - オブジェクトはプライベートな状態を持つ | |
>(Objection 4 - Objects have private state.) | |
>状態(state)は諸悪の根源です。特に特定の関数の副作用は避けるべきです。しかしながらプログラミング言語において状態は好ましいものではないのに関わらず、実世界では状態は至るところに存在します。 | |
>例えば私は銀行口座の状態に大いなる関心があります。そしていつ私が入金や出金をする場合には銀行の口座が正しく更新されなければとても困ったことになります。 | |
>実世界でこのような状態が存在したとして、この状態を取り扱うためにはプログラミング言語はどのような仕組みを提供すればよいのでしょうか。 | |
>OOPLはプログラマから状態を隠しなさいといいます。状態は隠されてアクセス関数を通してしか見えません。 | |
>伝統的なプログラミング言語であるCやPasalでは状態変数の可視性は言語のスコープのルールによってコントロールされます。 | |
>そして純粋に宣言的な言語では状態は存在しないといいます。このような宣言的言語ではシステムのグローバルな状態はすべての関数の入力と出力になりうるのです。関数型言語におけるモナドや論理型言語におけるDCGでは「状態はあたかも関係のないように」プログラミングすることができます。それでいて必要な場合にはシステムの状態に完全にアクセスをすることができるのです。 | |
>「プログラマから状態を隠す」というOOPLで選択されたオプションはより悪い選択です。状態を公開して状態の厄介さを最小限にしようとする努力をする代わりにOOPLではそれを隠し去ってしまったのです。 | |
と指摘されるとおりです。 | |
どこかで、 **オブジェクト指向の「カプセル化」とは「バグのカプセル化」としか思えない** という指摘を見ましたが、その指摘は要を得ているでしょう。 | |
私もまったく同じ見解であり、最悪の選択肢だと考えます。 | |
整合性があり保守性の高い見通しの良いコードを書くには、オブジェクト指向で「メッセージング」するような「仮想機械」を乱立させ状態変数管理の困難さを招くバグのカプセル化ではなく、あくまで関数型パラダイムで状態変数の管理と副作用を排除し、参照透過にコーディングすべきでしょう。 | |
繰り返しますが、 | |
構造体 + 関数型パラダイム = オブジェクト指向 | |
であるので、関数型パラダイムでコーディングする、ということは、オブジェクト指向に至った関数型パラダイムの拡張は自らの足元を見つめるのと同じなので、いわゆるオブジェクトというのは、Cの構造体のことなんだな、とだけ考えておればよいでしょう。 | |
**我々が歩み寄るべくは、「すべてのものはオブジェクトである!」というような現実世界を模倣した仮想機械の世界ではなく、純粋関数型プログラミングで実装されているような論理が支配する数学世界です。** | |
**プログラムのコードとは本来、数学的実体であり、物理的実体ではない**ので、**物理的な現実世界を模倣したモデルを指向するオブジェクト指向のアプローチは根源的な誤り**です。 | |
##多様性(ポリモーフィズム) | |
>C++の作者であるビャーネ・ストロヴストルップは、オブジェクト指向を「『継承』機構と『多態性』を付加した『抽象データ型』のスーパーセット」として整理した。 | |
この『多様性』というのは、[ポリモーフィズム](http://ja.wikipedia.org/wiki/%E3%83%9D%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%95%E3%82%A3%E3%82%BA%E3%83%A0)とも呼ばれ、C++の開発者の関数型パラダイムの先見性を垣間見ることができます。 | |
どういうことか?というと、C++がオブジェクト指向としてCからパラダイム転換するにあたって、データオリエンテッドになった、という話をしました。 | |
作業机がある。真ん中に作業対象となるドキュメント・データがある、その周りにずらっと専用の各種ツールが用意されている。 | |
作業したいデータに応じて、自動的に紐付けられたツールが用意されるわけですが、毎回そのツールがコロコロと入れ替わっていくと、作業する際に一貫性が損なわれてしまい混乱するわけです。 | |
だから各種ツールはなるだけ目的に応じて抽象度の高い汎用的な方が良い。 | |
種類が限定された使い慣れた道具を同じ使用感で使いまわせるのが望ましい。 | |
ポリモーフィズムの **ポリ** とは多数という意味で、多用途に対応しているということです。 | |
他方、モノモーフィズムとは **モノ** は単数という意味で、単一にしか対応していないので、対象データが入れ替わる毎にコロコロ入れ替える、それ専用に対応しているツールを用意しないといけないということになります。 | |
[ポリモーフィズム](http://ja.wikipedia.org/wiki/%E3%83%9D%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%95%E3%82%A3%E3%82%BA%E3%83%A0)によると、 | |
>例えば、何かの値を文字列形式に変換する最も単純な場合を考える。モノモーフィズムな型システムを持つ言語では、次のように別々の関数になっていなければならない。 | |
古典的な変換関数: | |
数値を文字列にする場合 | |
string = StringFromNumber(number) | |
日付値を文字列にする場合 | |
string = StringFromDate(date) | |
一方ポリモーフィズムな型システムを持つ言語では、StringValue のような汎用の述語を定義し、型別にそれぞれ適切な変換方式を定義させることでオブジェクトの種別によらない抽象度の高い変換形式を実現できる。 | |
多態を行なう変換方式: | |
見た目上、型によらない変換が可能 | |
string = number.StringValue() | |
string = date.StringValue() | |
`number`なり`date`なり異なるタイプのデータであっても、とりあえずそれを用意して、ツールである`StringValue()`を同じように適用したら、きちんと同じように作業が完了する。 | |
さいしょに、対象となるデータのタイプをみこして | |
「えーっとまず、StringFromNumberを用意して」 | |
「この場合は、StringFromDateを用意して」 | |
というようなことは不要です。 | |
このように、関数型プログラミングでは、データが最初にあり、次にそれらを操作するための、抽象度の高い統一的な関数セットが揃っていることが超重要です。 | |
実際、オブジェクト指向プログラミングの特徴として、この多様性(ポリモーフィズム)は多いに宣伝され喧伝されるのですが、これ状態変数をカプセル化するメッセージングの仮想機械の世界とか言ってる限り机上の空論なんですね。 | |
何故ならば、仮想機械の世界観では、関数とは、 | |
カプセル化した状態変数を操作するためのメソッドであり、単に別のオブジェクトにメッセージを送信するメソッド | |
に過ぎず、 | |
データを抽象度の高い適用を可能にする統一的な関数ではあり得ないからです。 | |
関数型プログラミング言語のほとんどすべてに実装されている | |
`map`関数がありますが、これは当然、多様性(ポリモーフィズム)を徹底的に装備している抽象度の高い汎用的な関数です。 | |
ありとあらゆるタイプのデータにたいして臨機応変に引数を与え`map`すると適切な操作ができるように設計されています。いわば **スイス・アーミーナイフみたいな万能関数** です。 | |
さらに重要なのが、`map`関数で操作した結果のデータ、つまり戻り値は、さらに`map`や別のスイス・アーミーナイフ的な抽象度の高い汎用的関数で連続操作することができる。 | |
つまり | |
データ ⇒ 操作 ⇒ 操作 ⇒ 操作 | |
とチェーンで繋ぐように操作していきます。 | |
関数型プログラミングでは、多様性(ポリモーフィズム)は、パラダイムの核となる作法であり、ここを軸に一貫して宣言的にコーディングしていきます。 | |
そして関数型プログラミングでは最初からそういう多様性(ポリモーフィズム)で実装された関数群が言語仕様として、あるいは関数型外部ライブラリずらりとそろっている。 | |
一方で、オブジェクト指向では、この辺の事情を熟知しているごく一部のプログラマーが、ポリモーフィズムでこうやりましょう的なお手本を示すことはありますが、そもそも、 | |
>「パーソナルコンピューティングに関わる全てを『オブジェクト』とそれらの間で交わされる『メッセージ送信』によって表現すること」 | |
>仮想機械としてのオブジェクト | |
という宣伝文句を刷り込まれて実践しているほとんどのオブジェクト指向のプログラマーは、「メッセージ送信」の関数の戻り値に対して別のメソッドで多様的に抽象的に統一的に処理していくとはとても思えません。 | |
彼らにとっては、クラスの関数とは即ち、カプセル化した状態変数を操作するためのメソッドであり、別のオブジェクトの状態変数を遠隔操作するためのメソッドにすぎないからです。 | |
オブジェクト自身の内部にカプセル化された状態変数や別のオブジェクトの状態変数を変化させる、つまり副作用を起こすことがこの「メッセージング」の「メソッド」の意味なのですから、関数(メソッド)の戻り値がどうで、そこからさらに操作できるように多様性を完備するクラス群の設計などという発想とは程遠いはずです。 | |
##オブジェクト指向は命令型・手続き型プログラミングなのか? | |
何が起こっているのか?というと、これは「命令型パラダイム」の作法でプログラミングしているということです。 | |
オブジェクトの内部状態を操作するメッセージングとは、徹頭徹尾、命令型・手続き型の作法であり、もうこうなるとオブジェクト指向の良い面であった関数型パラダイムの転換要素は消滅してしまいます。 | |
そしてオブジェクト指向=命令型・手続き型 となると、それは | |
####オブジェクト指向(命令型・手続き型) VS 関数型プログラミング(宣言型) | |
という図式が成立し、じゃあ、いくら状態変数をカプセル化して隠蔽しても、命令型なんだからフロー制御のためにまた状態変数が必要になるんじゃないの?となります。 | |
というか実際に巷のオブジェクト指向のコードはそうなっている。 | |
## オブジェクト指向言語のpublicやprivateという冗長な構文 | |
[Why OO Sucks](http://www.sics.se/~joe/bluetail/vol1/v1_oo.html) | |
>OOPLはプログラマから状態を隠しなさいといいます。状態は隠されてアクセス関数を通してしか見えません。 | |
>伝統的なプログラミング言語であるCやPasalでは状態変数の可視性は言語のスコープのルールによってコントロールされます。 | |
C++それから、Java,C#,Objective-Cといったオブジェクト指向言語には、 | |
Private | |
Public | |
という変数、クラスの修飾語があります。 | |
関数型言語であるJavaScriptでNodeのプログラミングをしているのに慣れていると、この修飾語を適切に打つのが非常にしんどいです。 | |
>伝統的なプログラミング言語であるCやPasalでは状態変数の可視性は言語のスコープのルールによってコントロールされます。 | |
とありますが、こういうC系のプログラミング言語では、伝統的にすべてブロック{ } のスコープで可視性がコントロールされます。 | |
それに加えて、またPrivate と Publicだとやっているわけですから、同じ機能を実現するために、2つのやり方に増えたということになります。 | |
## コードの構造とは本質的に多重構造であるのに、オブジェクト指向のクラスは? | |
オブジェクト指向とは、なにかおきまりの構造があるかのごとく、自分が見渡すかぎり、インナークラス(クラス内クラス)の使用は一般的ではないようです。 | |
インナークラスが何か特別なテクニックのように紹介されていたり、かなり妙な話でもあります。 | |
そもそもコードの構造とは本質的に多重構造であるのに、だーっと並列的にクラスが同じ階層で並ぶというコードが多い。思い返すと、そもそもクラスとは単にデータのまとまりを表現するための構造体であったのにも関わらずです。 | |
Private,Publicの修飾語の存在と、このインナークラスの実装がややこしい、というのは関連があって、変数やクラスの可視性をPrivate、Publicの修飾語で規程してしまうわけなので、インナークラスというブロックのスコープにより、また可視性が変わるというのは、相容れないのでしょう。 | |
publicなインナークラス、privateなインナークラス、もうブロックで規程されるスコープと、public/privateの修飾子が相まって頭が痛くなってきます。 | |
[あなたの知らない、4つのマニアックなJava文法](http://www.atmarkit.co.jp/ait/articles/1007/27/news103.html)のひとつに紹介されるくらいなのだからJavaでクラスなどという超基本的な構造をネストすることはよほど特殊で予備知識の必要な勇気ある決断なのでしょうか? | |
だから問題を単純化するために、クラスの入れ子構造は避ける、というなんだか本末転倒なやり方を実質的に強いられます。 | |
また、関数がファーストクラスオブジェクトではないことと関連しますが、関数内関数というのができない。これはもともとのC言語の不可解な仕様なのですが、その後のオブジェクト指向言語でおおいに引きずっている「不具合」であるのは間違いありません。 | |
もちろんC#などでも、匿名関数、Delegateの導入でやれないことはないのですが、匿名関数をまたクラスで型定義して、とただただ苦痛です。何故、関数を関数の中に放り込むという多重構造を実現するためだけの目的で、あらかじめ別の場所で型定義をしなければならないのか? | |
このように、関数型プログラミングに慣れ親しむほど、関数型プログラミングでは当たり前のようにできたことが、非関数型でオブジェクト指向プログラミングでは規制されており、自由にできないということを痛感します。 | |
これは、オブジェクト指向が関数型パラダイムで進化した部分はありながら、結局のところ、「すべてのものはオブジェクト」であり、メッセージでやりとりする仮想機械の世界観で平面的な発想で実装されたからのではないかな?と想像しています。 | |
本来コードは数学的な構造であり、物理的構造ではありません。数学世界ではむしろ再帰的な多重構造のほうが本質であり、オブジェクト指向のクラスで一般的な浅い階層しかないコードというのは、いろいろ無理があります。 | |
数学的構造をコード上に表現するための自由な表現手法が規制されている、表現力が欠如しているということです。 | |
##つまり? | |
オブジェクト指向の良い面とは要するに関数型パラダイム・シフトであり、 | |
オブジェクト指向の悪い面とは状態変数の管理を前提とした副作用上等(メッセージング)の、世界に普及した物理オブジェクトの仮想機械モデルである。 | |
故に、関数型プログラミングをしている限りはオブジェクト指向は捨て去って良い。 | |
####関数型プログラミングのパラダイム内での「オブジェクト」というのは、「構造体」と頭のなかで読みかえればそれで十分に事足りる。 | |
##状態変数はどうするのか? | |
状態とひとことに言っても2つある。 | |
1. 手続き型・命令型パラダイムにおける、フロー制御のための(低層の)状態変数(forループの `i` とか) | |
2. 実務・ゲームなどのユーザの入出力(IO)や時間変化に伴う(処理対象に本質的な)状態変数 | |
1の状態変数は、関数型ライブラリや関数型言語仕様に内蔵された関数(スイス・アーミーナイフ的なやつ)で処理すれば相手にする必要はありません。内部ではそういうループだとかは低層処理され、表層からは隠蔽されます。 | |
2の状態=時間変化は時間軸の無限リスト(FRPではストリームと呼ばれることが多い)として数学的構造に抽象化でき、(DVDの動画データが静的なデータであるように)静的なデータとして参照透過に処理する、オブジェクト指向のようにカプセル化して隠蔽しない(隠蔽したところでどうせ破壊的代入をもって副作用を発生させながら扱うことになる)。 | |
ここは関数型プログラミングを実務に応用するにあたり超重要なので、以下に別途記事をまとめています。 | |
[関数リアクティブプログラミング(FRP)で分断された2つの世界を繋ぐ【脱アルゴリズム宣言②】](http://qiita.com/kenokabe/items/a8477694a499ca869cde) | |
[数学と別離したプログラミング、時間を抽象化し数学を取り戻すプログラミング【時空プログラミング④】](http://qiita.com/kenokabe/items/b81c7aa8af86314551a0) | |
##どうやってオブジェクト指向を捨てられるか? | |
まずは、関数型プログラミング言語を使い、関数型、FRPライブラリを使うようにする。 | |
Mac,iOSならObjectiveCではなくSwift | |
AndroidならJavaでうまいことやるしかない(JavaもC#も最近は関数型化しているがかなりしんどい) | |
Web・サーバ系ならNode.js 今やGitHubのAtomエディタというアプリもNode.js | |
ネイティブなら?OSやプログラミング言語、ブラウザ、node.jsのようなゴリゴリのスクラッチからやる開発以外に需要はないのでは? | |
##痛みを伴うが時期は来ている | |
これまでの間、数十年に渡りオブジェクト指向は持て囃されてきました。 | |
しかしオブジェクト指向の良さとは即ち関数型へのパラダイム転換であり、オブジェクト指向の悪さとは仮想機械のメッセージングモデルです。 | |
オブジェクト指向の良さを世界が喧伝するにあたり、不幸なことに本来のプログラミングの数学世界を指向した関数型プログラミングへのパラダイム転換ではなく、現実世界を模倣した後者のほうが「わかりやすい」あるいは「なるほど理にかなっているかも」と持て囃されてきました。 | |
しかし、現実はこうです。 | |
オブジェクトを物理世界のエージェントの如くモデル化するオブジェクト指向!と宣伝されて入門し、いざ学習を深めていくと、「より良いオブジェクト指向の設計のためには、副作用は排除するべき」「参照透過が維持されるようなクラスを作る」などと、**オブジェクト指向の物理モデルの宣伝とは正反対の真理**を諭されます。 | |
カプセル化した状態変数にメッセージを送って操作するのがクラスメソッド! | |
別のオブジェクトにメッセージを遅れるのがクラスメソッド! | |
という作法をさんざん叩きこまれた入門者は、メソッドの多様性(ポリモーフィズム)がメリット、とか言われてどのくらいの人が納得できるでしょうか?そして実際どれだけの人が多様性を担保するように工夫してクラスを設計しているのでしょうか? | |
結局は、全力でアクセル踏んで突き進んでいくと、あなたの進んでいる方向って逆だよ?と宣伝文句と中身が違う矛盾した宣言を突如突きつけられるようなものです。 | |
世界はその重要性に気付き始めており、Java、C#はここ数年の言語仕様の改定で激しい関数型言語仕様の輸入が巻き起こっています。 | |
Appleも旧来のObjectiveCから関数型言語であるSwiftへの大々的な転換を決断しました。 | |
これはCとC++のハイブリッド構造のObjectiveCをさらにオブジェクト指向と関数型のハイブリッド拡張するというカオスな建て増しをするよりも簡潔なアプローチであるので、歓迎します。 | |
Android開発ではJavaがデフォルトですが、Javaの関数型拡張で関数型プログラミングをやるのは、これは建て増し言語仕様でやることになるので、結構しんどいです。 | |
これまでオブジェクト指向が持て囃され、特にメッセージング仮想機械モデルが当然のベストの解法のごとく展開されてきたプログラミングパラダイムの世界から、それをすべて捨て去り、別の世界へパラダイム転換する、というのは痛みを伴うはずです。 | |
しかし、そろそろ真剣にこの辺りむきあう時期に来ているのではないでしょうか? | |
##あわせて読みたい | |
[【前編】2014年、オブジェクト指向vs関数型プログラミングの壮絶な宗教戦争は終結 OO陣営最後の牙城DOMというWeb標準のUIを破壊した巨砲Facebook-React](http://qiita.com/kenokabe/items/9c650ec8bcb1418c596d) | |
[【後編】2015年、脱オブジェクト指向時代にFacebook-Reactのコードをイミュータブルな宣言型でイベント駆動するFRPで書く](http://qiita.com/kenokabe/items/7bc03b751776ec080da8) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment