Island Life

< PS2で一般的なn×m行列を扱う場合、quad... | ありゃ。Erann Gatの裏だ。生Erann... >

2003/09/15

「コードの再利用」ってのはオブジェクト指向アプローチの メリットとしてよく語られてたけど(最近はそうでもないか)、 実は関数指向アプローチの方がずっと コードの再利用がやりやすいように思える。

OOのコード再利用ってのは、 誰もがagreeできるような性質を持つオブジェクトに対しては可能だけど (stringとか、GUIコンポーネントとか)、そういうものはむしろ少なく、 同じものであっても見方によってモデリングの方法が変わって来ることの 方が多い。全ての見方に対応しようとするとインタフェースが爆発する。 色んな見方で組み立てられるように、要素を分割してモデリングして、 使うときに必要なものだけ集めてやろうとしても、継承使うなら 多重継承やmixinの嵐、コンポジションでやるのも小さなオブジェクトの 管理がめんどい。

関数指向は、ものの操作のみに注目する。 Scheme:オブジェクト指向表現?でちょっと話が出たけど、 例えば木のトラバースをする関数は、(1)対象物がleafであるかどうか、 (2)対象物の子ノードへ操作をマップ、の2種類の操作さえ与えられれば、 対象物が何であるかは知らなくてよいし、そもそも対象物が 木としての動作を本質的に備えている(e.g. treeインタフェースを持つ)ことさえも 要求しない。本来は木じゃないものを、たまたま一時的に 木として見たくなった、といった場合に、呼び出し側はその場で 上記(1)(2)の操作をでっちあげれば、トラバース関数が使える。

言い替えれば、関数指向では、全てのコンポーネントは、対象物に対して何も仮定を 置かなくて済むということだ。なぜなら必要な操作は呼び出し側が提供して くれるから。従って、汎用的な操作を部品化する時に「それが誰に、どのように 使われるか」を気にせずに、アルゴリズムのみに集中できる。

オブジェクト(クラス)指向はむしろ、具体的な問題が与えられた時に、 関数指向で用意された部品を集めて具体的なものを実現する際の骨組みとして 大いに役に立つ。 そういう骨組みは問題毎に異なるから、 多くのクラスはその問題専用に作られ、再利用されることはない。

つうわけで、「部品はFP」「アプリはOO」ってのが結構いいんじゃないかと 思う次第。

Tag: Programming

Past comment(s)

ねる :

なんか非常に同感です。なんか最近schemeを勉強して思っていたことがすっきりしたきがします。 ところで、ここでFPといっているのは closure のことのように思えるのですが、どうなのでしょう。 他のFPの要素 (って何だろう.... 副作用なしなスタイル? lazyness? よくわかってないです) が部品の再利用性の向上に著しく貢献したりするのでしょうか。 いや、Smalltalkでも十分同様なことができるのではないかなと、ふと思ったものですから。むしろ、Smalltalkのほうが closure つくるのが(タイピング数からみて)楽ですから良いのではないかと。

戯 :

  • 前にも言ったけど、インタフェースの有無を問うのはOOPのうちの「一派」に過ぎないのでは?それこそOOPの定義は曖昧なんだから、その一派だけについての欠点を以って話を展開しちゃうと、ちょっと… --ドコに居てもWiLiKi:戯は戯
    • ちなみにそれって、それこそクラス指向ですね。
  • ただ、手続きの無名化とか、手続きが「幾つ」有るか事前に判らない(ことを受け入れる)ことは、重要だと俺も思います。Ruby的Iteratorが無いのが嫌でJava使わなかったりするもんな。
    • で、もしかしてそういうの(卑近にいえばRubyのIterator)も「関数指向」に含めて捉えるべきだということなんでしょうか?まあOOPに特有のものではないのは確かなのですが。#でも関数もオブジェクトに見えちゃうんだな、俺の目には。
  • ところで、本当に何の仮定も置かなければ、データを扱うことが出来ないのでわ。
    • いつどこでどの操作を提供すべきか?というのを自動化しようとしたら、OOPになったのかも。
  • あと、「部品」は「操作」とは限らない。まずそれが「有る」こと、なにかを「保持」してること、がその部品の役割であることも…(と恒例の話題)

shiro :

ここでのOOは言語の話ではなくて、モデリングやデザインの話です。 (だって関数指向とオブジェクト指向を混ぜる話になってるでしょ。 FPと言ったのがまずかったか)。 Java, C++はクロージャ相当のものがすごく書きにくいため混ぜにくいんですが、 ブロックを持つ動的なOOPなら両方の書き方が可能ですし、 そういう前提で話をしています。 で、OOPでも汎用的なライブラリにしようとすればするほど、 ブロックを多用する傾向があるんじゃないかなあと。 respond_to? でディスパッチする(=データにいちいち問い合わせる)より、 ブロックで渡してもらう(=操作を外部から提供してもらう)方が 綺麗だし柔軟性があるよなあと。 C++でもtemplateを多用してゆくとどんどんそっちの(関数的な)方向に向かうんじゃ ない?

ここで言っている関数指向では、あるデータを扱うある操作Aを実装しようとしたとき、 データに対して仮定を置くのではなく、操作Aを実装するためにデータに対して 必要となる操作B, C, ... を呼び出し側から提供してもらう、ってことです。 データに対する仮定は間接的に操作B, C,...により与えられますが、 操作Aにしてみれば、そもそも最初からデータが分離可能な形で 存在しなくたって構わないわけで。

(例えば、「木構造」が単なる2つの整数の配列でC[i]とS[i]で表されているような場合を 考えてみよう。ノードは単に整数のインデックス k で表現される。 C[k]はノードが子を持てば最初の子のインデックスを保持し、そうでなければ -1を保持する。S[k]はノードの次の兄弟へのインデックスか、兄弟が なければ-1を保持する。このデータ構造に、ルートノードのインデックスが 与えられれば、これは立派な木構造だ。 さて、WiLiKi:Scheme:オブジェクト指向表現の関数版tree-walkをこの構造に 適用するのは簡単だ。リンクトノードで表現された木の場合と何ら変わるところは ない。一方、tree-walkが、渡されたノードに対するメソッド呼び出しを使う、 よくあるOOベースで作られていたらどうなるだろう。何らかの中間オブジェクト、 一時的なオブジェクトが必要になるのではないか。それは、クロージャが自動的に やってくれることを手動で模倣することになるだろう)

「何かが有り、何かを保持している」ことをモデル化するには、 その「何か」の見方を決めなければならず、そういう見方は既に アプリケーション依存なんじゃないか、ということです。 だから、アプリケーションから出発する場合はOOモデリングはうまく機能するけど、 具体的なアプリケーションとは独立して部品を作るなら、操作中心に ならざるを得ないのでは。

なお、私は Object == Closure (mod Syntax) な立場ですので、 あるものがオブジェクトか関数かを分けることはできないと考えています。 設計の段階で、どっち側から見るのが便利か、という話をしてます。

ささだ :

読んでいて generics なんてのを思い浮かべました。