アウトライナーの新しい形を考える

アウトライナーというソフトウェアがある。

アウトライナーはその名が表す通り、情報のアウトライン(輪郭)を作るためのソフトウェアである。情報のアウトラインを作るというのは、別の分野の言葉で言い換えれば、知識や考えの「デッサン」をすることに相当するだろうか。考えを文章として精密に表現する前に、その論の構造だけを抜き出して、全体として筋が通るような組み立てを考える。

そもそもどのようなときに考えのデッサンをする必要性に迫られるのかを考えてみると、比較的込み入ったことを考えたり、あるいは、ある程度ボリュームのある論理的な文章を書いたりするときなど、頭の中だけでは容易に解決できないような複雑な問題に取り組むときに限られるだろうし、そう考えると日常的にアウトライナーを利用している人はそれほど多くないのかもしれない。

それでも、日常生活の中でちょっとした考えをまとめたり、あるいは何か記録しておきたい事柄があった場合に、何らかのツール(紙のノートでも、パソコンやスマホでも)を使ってそれらを「箇条書き」にするという経験は誰にでもあるのではないだろうか。

あるいは、単に箇条書きにするだけではなく、字下げなどをして項目に階層を持たせて、もう一段深い掘り下げを行なっている人も少なくないのかもしれない。その階層構造の導入がいわゆるアウトラインを作ることに他ならない。

字下げをして階層を作れるなら、アウトライナーなんて専用のソフトウェアは必要ないのではと思われるかもしれない。アウトライナーの強みは情報の構造を構造として扱えることにある。例えば、同じ階層にある項目を並び替えたり、ある項目を配下の階層ごと別の場所に移動したり、階層の中を上下に移動させたりなど、構造自体の操作を容易にしてくれる。あるいは、その構造を眺めるときに、ある項目にフォーカスしたり、その場では不要な項目を折り畳んで隠したりと、情報の見え方も柔軟にカスマイズできる。

筆者が地道に開発を続けている Cotoami も、ジャンルとしてはアウトライナーに近いソフトウェアである。Cotoami 以外にもそのようなソフトウェアを以前から作っていて、自分でもそれらを日常的に使ってきたので、アウトライナーというソフトウェアの存在は知っていても、実際にそれを利用したことはなかった。

筆者がアウトライナーに抱いていたイメージは、扱う構造がツリー(階層)であるがゆえに、表現できる情報は、ある時点、ある視点からの知識のスナップショットに過ぎないということだった。ツリーというのは一次元の構造である。あるノードは必ず一つのノードに所属し、文脈は一意に固定される。

最終的なアウトプットとしては当然スナップショットにならざるを得ないにしても、そこに至るまでの思考の過程においていくつかの文脈が入り乱れることもあるだろうし、そのようなものを表現できる思考のツールが必要ではないかと考えたのだ。

しかし、ある時『アウトライン・プロセッシング入門』という本にたまたま出会って、それを読んでみたところ、まさに自分が抱いていたような懸念に言及した上で、本来あるべきアウトライナーの利用方法が語られていて、アウトライナーを利用していても結局はそこに辿り着くのだと分かって大きな感銘を受けた。

例えば、アウトライナーでは、最上位の項目(テーマ)からスタートして、下位の項目に向かって徐々に内容を詳細化していく、いわゆるトップダウンのアプローチでアウトラインを作っていくのではないかという先入観があるが(これは同じツリー構造を利用したマインドマップでも同様)、

実践的なアウトライン・プロセッシングは、トップダウンとボトムアップを相互に行き来する形で行われます。トップダウンでの成果とボトムアップでの成果を相互にフィードバックすることで、ランダムに浮かんでくるアイデアや思考の断片を全体の中に位置づけ、統合していきます。 私はこのプロセスを「シェイク」と呼んでいます。行ったり来たりしながら「揺さぶる」からです。 – 『アウトライン・プロセッシング入門

という感じで、実際には「トップダウンとボトムアップを相互に行き来する」ことで、アウトラインを作る過程で新たに発見した知見を構造に反映していくという、より発見的なアプローチが推奨されている。

「トップダウンとボトムアップを相互に行き来する」という考え方については、Cotoami も全くその通りなのだが、アウトライナーとの違いをあえて挙げるとすれば、Cotoami では基本にランダムな入力があって、そこから構造を組み立てていくという、どちらかと言えばボトムアップに焦点を置いたデザインになっているところだろうか。

Cotoami では、全ての情報がフラットに表示されるタイムライン(複数人で利用する場合はチャットして機能する)と、組み立てられた構造を見せるドキュメントビューやグラフビューが分かれていて、片方はフロー、もう片方はストックという感じで、これらを相互に行き来しながら、構造を何重にも組み立てていくという仕組みになっている。

例えば、ある項目(Cotoami では「コト」と呼んでいる)は、複数の文脈に同時に関係しているかもしれないが、ツリー構造ではそれを表現することができない。そこで、関係をもっと自由に作ることのできるネットワーク構造を導入して、Aという文脈におけるX、Bという文脈におけるX、という感じで同じ情報を複数の視点から見れるようになっている。先ほど、アウトライナーはスナップショット的だと書いたが、アウトライナーと異なる構造を採用している Cotoami はデータベース的なソフトウェアだと言えるかもしれない。

興味深いことに『アウトライン・プロセッシング入門』では、ツリー構造とネットワーク構造の対立についても言及されている。

アウトライナーで扱う「生きたアウトライン」は、見た目はツリーでも通常のツリーよりずっと複雑な内容を扱うことができます。なぜならそれは「流動的なツリー」だからです。それは常に変化する可能性をはらんだ「プロセス」であり、完成品ではありません。

確かにアウトライン上では、ある記述は「A」と「B」どちらかにしか分類できません。しかしアウトライナーに入っている限り、それはある記述が「A」と「B」のどちらに含まれるべきかという思考プロセスの、最新のスナップショットにすぎません。今「A」に分類されている内容が、次の瞬間には「B」に再分類される可能性を常にはらんでいます。見た目上「A」に分類されているけれど、同時に「B」でもある可能性が常にある。それはもはや単純なツリーではありません。

そして重要なのは、アウトラインを最終的に完成品(たとえば文章)としてアウトプットするためには、いずれにしても「A」か「B」かを選ばなければならないということです。複雑なものを勇気を持って単純化しなければならない瞬間がくるのです。そのこと自体に思考を強制し、発動する効果があります。

実は発想と呼ばれるものの多くが、この強制的な単純化から生まれてくるのではないでしょうか。「文章にしたり人に説明したりするとその対象がよりよく理解できる」と言いますが、それは複雑で絡み合った情報を、単純なツリーやリニアな語りに強制的に変換しなければならないからです。その過程で情報は咀嚼され、自分の視点が確定します。アウトライナーで編集されるアウトラインは、「流動的なツリー」であることでそのプロセスを促すのです。

一方の網目構造では、複雑なものが複雑なまま存在できてしまいます。それは一見するといいことですが、ともすると素材が素材のままになってしまう可能性があります。

たとえば情報を保管し、必要に応じて引き出すということを第一義に考えれば、複雑な情報を複雑なまま扱える網目構造のほうが優れているかもしれません。しかし「文章を書き、考える」道具、つまり発想ツールとしてみた場合の有効性は、アウトライナー否定派がいうほど単純には決められないのです。

アウトライン・プロセッシング入門

アウトライナーで扱うのは「生きたアウトライン」であって、ある項目がどこかに所属するというのは、その瞬間のスナップショットに過ぎず、常に変わり得るということ、そして発想を具体化するためにはいずれにせよ「A」か「B」かを選ばなければならないということ、その強制的な単純化が発想にとって重要なのではないかということ、いずれも深く納得できる話で、Cotoami が採用するネットワーク構造(網目構造)に対して、説得力のある反論になっている。

実は、この本を通じて主張されている、「考えてから書く」のではなく、書きながら考えて、そこで発見したことを構造にフィードバックさせていくといったような発見的なやり方を踏まえていれば、アウトライナーでも Cotoami でも基本的にはそれほど違いはないと個人的には考えている。これはツールというよりもマインドセットの問題だからだ。

この問題はプログラミング言語における、オブジェクト指向と関数型の対立に似ている。世の中にはオブジェクト指向言語とか、関数型言語というカテゴリーでプログラミング言語が分類されることがあるが、それらのカテゴリーがプログラムデザインのマインドセットを決定するわけではない。オブジェクト指向言語で関数型プログラミングを実践することは可能であるし、同じように関数型言語でオブジェクト指向プログラミングを実践できる。

もちろん個々のツールがどういうアプローチに向いているかという傾向はある。アウトライナーがスナップショット的、Cotoami がデータベース的というのは、その傾向を表している。

ただ、Cotoami としては、現状のアウトライナーではなかなか難しいと思われるアプローチを可能にしていきたいところである。そうすることによって、知的生産の可能性を少しでも広げていきたい。そのためには『アウトライン・プロセッシング入門』で紹介されているアプローチの先にはどのような展開があり得るかということについて考えなければならない。

その一つの可能性が「コトノマ」というコンセプトである。

Cotoami のデータベースでは、アウトライナーの一つの項目に相当する「コト」という単位の情報が相互につながりを持って、単純なツリーではなく、自由なネットワークを構成できるようになっている。ネットワークが大きくなるにつれて、他より多くのつながりを持つコトが現れるだろう。そのようなコトは、参加者にとって重要なコンセプトを表している可能性が高い。そのコトを「コトノマ」に格上げすることによって、そのコンセプトについてさらに深く掘り下げることができるようになる。

そして、そのコンセプトについて掘り下げる過程でさらなるコンセプトの発見があり…という連鎖を Cotoami では期待している。

この機能は、アウトライナーの「フォーカス」という機能の発展形だと言えるかもしれない。

アウトライナーの持つ「文章の集合体」としての性質は、アウトライナーを開発する側でも必ずしも意識していない場合があるように感じます。 この点に自覚的なアウトライナーは、それをサポートする「フォーカス」という機能を持っています(アウトライナーによって「ズーム」「ホイスト」「巻き上げ」などとも呼ばれます)。ひと言でいうと、アウトラインの中の任意の項目を一時的に最上位階層として表示する機能です。他の項目は見えなくなるので、その項目に集中することができます。 – 『アウトライン・プロセッシング入門

コトノマは、そのテーマについての情報を集めるための専用のタイムラインを持つ他は、他のプレインなコトと同様に扱うことができる。ネットワークの中で重要なポイントを表すための特殊なコトだと言えるだろう。

ネットワークを育てる内に、その過程で発見した重要な事柄がコトノマとしてネットワーク状に結節点を作る。そのコトノマのリストは、ネットワークを育てた参加者の足跡であると同時に、外からデータベースを見るときの入り口としても機能する。

Cotoami の基本的な考え方で、アウトライナーと一線を画すのは、そもそもデータベース的なシステムだということもあるが、一人の人間がその全貌を掴めないぐらいの膨大なネットワークを作るというユースケースまで想定しているところだ。一人で利用するケースから、チーム、不特定多数のコラボレーションまで、あらゆる情報を蓄積して巨大なネットワークを作る。

そのときそのときに必要なアウトプットのために、スナップショット的なツリーを作ることはあっても、全体として綺麗に整理された状態を保つということにはこだわらない。かつて、野口悠紀雄氏が「「超」整理法」で書いていたように、探すための分類・整理は結果的に徒労に終わることになるし、整理は情報を既知のカテゴリーに収めることになって、むしろ新しい発見を遠ざける原因になってしまう。

「発想と呼ばれるものの多くが、この強制的な単純化から生まれてくる」という仮説が、確かにそうかもと思わせる一方、拙速な整理が、結局のところ既知のカテゴリーに立ち返ってしまう原因になるのではないかという予感もある。何か新しいことを発見するために、カテゴリーは結果として横断されなければならない。

そのために「コトノマの連鎖」というものが重要になってくるのではないかと考えている。投稿した情報を眺めていく過程で生まれたコンセプトをコトノマに変換し、そこからまた発見される隣接概念をまたコトノマ化する。そのような隣接概念の渡り歩きによって生まれるネットワークを可視化することによって、その足跡が結果として未知のカテゴリーを生むのではないかと期待している。

何気ない会話からどうやってストックに耐え得る知識を引き出すか?

情報のフローとストックを同じシステムの中で循環させるという Cotoami の枠組みが、とりあえず一通り出来上がってきたので、実験しながら改善策を探るというフェーズに入っている。

公式の Cotoami サーバー https://cotoa.me では、公開でその実験を行なっているのだが、参加者がまだ少ないものの、会話の中からストックとなる情報を組み立てる過程が少しずつではあるが現れてきている。

その循環のプロセスは以下の通りである。

例えば、普通のチャットのように、タイムラインにただ投稿したものは構造のないフラットな、時系列の情報になる。これがいわゆるフローの状態だ。情報はどんどん流れていって、常に最新の情報だけを目にすることになる。

この常に流れていく一次元のタイムラインに、自然な形で構造を導入するとしたら、どのような方法があるだろうか? 最も簡単な方法は、既存の情報に対して reply(返信)をすることである。reply によって、時系列の上に、文脈(話題)の構造が生まれる。

この仕組みは全く新しいものではなく、2chの安価やツリー型の掲示板など、Webの黎明期からあちこちで利用されている。ただし、その構造を見せる方法には色んなバリエーションがある。例えば Cotoami では、タイムラインに複数の話題が混在していても個別に遡っていくことができる。

以下のように、PC上のブラウザであれば、reply の連鎖によって生まれたスレッドを同時に複数開いて比較することも出来る。

reply の次の段階では、タイムライン上で重要だと思うコト(Cotoamiでは個々の発言を「コト」と呼ぶ)を pin して、流れていかないように確保する。

普段通りにチャットする流れで、reply によって会話の繋がりを作るのは自然に行えるが、その発言の中で何を pin しておくかという判断は思ったよりも単純ではない。特に、気ままにチャットを楽しんでいるようなときは、そのようなアクションがあることさえ忘れてしまう。自発的な選択という行為は案外ハードルが高いし、会話に参加する人数が多くなるほど、何を選択するか、という問題の重みは大きくなる。

あるいは、UIのデザインでその障壁を下げることは可能かもしれない。例えば、”pin” ではなくて、”いいね” のような、会話の中で自然に行えるアクションにするとしたら、どのようなものがあるだろうか?(例えば「メモメモφ(・ω・`)」ボタン?)

pin で起点となる情報が出来たら、そこに has-a 関係となるようなコトを追加して構造を作っていく。

現状の Cotoami では、has-a 関係と reply の区別はなく、双方ともポストとポストの間にコネクションを作って辿れるようにする、という同じアクションである。将来的にはコネクションのタイプを指定できるようにしても面白いかもしれない。

そして、最後はコトのコトノマ(カテゴリー)化。Cotoami で最も重要なステップである。

「コトノマ」とはチャットの文脈で言えば、いわゆるチャットルームに相当するものである。コトをコトノマに変換することによって、その話題についての専用のチャット・タイムラインを用意でき、議論をさらに掘り下げることが出来る。と同時に、コトノマはコトと同じように扱えるので、相互に繋げたり、タイムラインにポストしたりすることも出来る。

Cotoami の最終的な目的は、このコトノマのネットワークを作ることである。そしてコトノマは、タイムラインでやり取りされている内容から自然に生まれてくるのが望ましい。そうなれば、出来上がったコトノマのリストは、参加者にとって(先入観からではない)自然に重要なコンセプトになるはずだというのが Cotoami の仮説である。

ここまでの流れを追うと、コトノマが出来上がっていく過程というのは、基本的には「連想」によって駆動されることになる。なので、あるテーマについて話しているときは、その隣接概念がコトノマの候補として上がってくるケースが多い。ただ、個人的にはこの連想だけだとつまらないと思っている。それとは少し違う方法でコンセプトの発見に誘導できないか、というのが今後の大きなテーマである。

チャットでウィキペディアをつくる

例えば、何か新しいことを始めようと思うと、どうしても「新しいことを始める」ためのフォーマットに乗っかってしまう。

これは、「やるぞ」っていう意気込みが強ければ強いほど、むしろそうなってしまったりするし、その何かをやろうという構えが、本当はもっと柔軟に考えられたかもしれない可能性を狭めることになったりしないだろうか?

例えば、新しいことを始めるぞということで、いついつどこに集まってブレストでもやろうということになって、立派な会議室なんかを予約して、場合によっては上司や先輩、あるいはよく知らない人などが集まった空間で、本当に価値のあるアイデアが出たりするものなのだろうか?

それよりも、日頃たわいもなくしている雑談で、ふざけたりしながらいろんな話題を縦横無尽に移動する中で、「これってそもそもこういうことじゃない?」とか「これとこれを組み合わせたら面白そう」みたいな気づきから、自然にコンセプトが浮き上がってきて、それが結果として「ワタシたちの百科事典」になるような仕組みあったとしたら、それが本当に自分たちがやりたかったこと(必要だと思ったこと)の種になるんじゃないだろうか?

例えば、

みたいな会話が、

という感じで「煽り運転」というテーマに辿り着く。

さらに、そのテーマにつなげる形で、あちこちで見つけてきた情報を投稿していく。

このテーマについて、もっと深く掘り下げる場所を作っておきたいということであれば、

みたいな感じで、発言内容から新しいチャットルーム(ここではコトノマと呼んでいる)を作る。このコトノマが、会話に参加している人たちにとっての百科事典の項目になる。

さらに何日か経って、

ここで キラーン✧ とひらめくTakeo氏、

その勢いで新しいコトノマを作る。

これはあくまでも例なので内容については深く考えないで頂きたい。

「チャットでウィキペディアをつくる」ということの雰囲気は感じて頂けただろうか? こんな感じで、発想にリミットがかからない普段の会話からコンセプトをすくい上げて辞書化するというのが、Cotoami の一つの活用例である。

予めこういうことをやろうとか、こういう情報が必要だろうという予断を持つよりも、興味のまま、話を面白そうな方向へ転がす時に出てくるキーワードを拾い上げたり、それらの間にネットワークを作ったりすることによって、本当に自分たちが必要とするオリジナルなアイデアを生み出す。それが「チャットでウィキペディアをつくる」という実践である。

Elixir から Elm の流れで、いよいよオブジェクト指向に対する懐疑心が無視できないレベルに達した2017年冬。

このエントリは Elm2 Advent Calendar 2017、2日目の記事になります。


Disclaimer: 勢いで書いてしまった後に改めて読み返してみると、Elmの中身には全く触れてないような気もしなくはない感じになってました… その辺を期待している方はブラウザ(のタブ)をそっ閉じして明日の記事にご期待下さい。


東京都港区の会社でインフラの仕事をしているフリをしながら、Elixir や Elm での関数型プログラミングに四苦八苦しつつ、Cotoami というよく分からないアプリケーションを作ったりしています。

今回は、まだ駆け出しの関数型プログラマーである筆者が、関数型プログラミングの洗礼を受けたことによって、長年慣れ親しんできたオブジェクト指向に対する見方が変わってきたという話について書いてみたいと思います。たとえて言えば、外国語を勉強することで、初めて日本語というものを客観的に見る機会を得たという体験に似ているかもしれません。

90年代からゼロ年代の中盤ぐらいまでにオブジェクト指向でプログラミングを始めた人間にとって、その考え方はプログラムデザインの共通言語のようになっていて、それ自体を疑うということには、なかなかなりづらい状況が長く続きました。Paul GrahamJeff AtwoodLinus Torvalds のような著名な人たちがオブジェクト指向に対する批判や懐疑を表明しても、「使い方の問題だよね」という感じで、オブジェクト指向そのものの問題ではないというのが多くの支持者の反応だったように思います。

あの TDD(Test-Driven Development)に対しても「Faith-based TDD」として同じ構造の批判がなされています(参考:「TDD再考 (8) – 凝集性(cohesion)とは何なのか?」)。このような議論の際によく見られる「◯◯が機能しないのは、◯◯のやり方を間違えているからだ」のような論法は、「No True Scotsman fallacy(本当のスコットランド人なら◯◯などしない論法)」だとの指摘もあります。

そもそもオブジェクト指向への批判が、関数型プログラミング界隈から行われることが多かったということもあり、その筋の人たちにとっては自明のことでも、オブジェクト指向しか知らない人たちにとっては、その指摘自体をうまく理解できないという非対称な構造がありました。

Information hiding vs. Explicitness

そのような状況の中で、オブジェクト指向への敬虔な信仰を残したまま、関数型プログラミングの門を叩いたわけですが、そこでいきなりオブジェクト指向の中心的な価値を全否定されるという事件が起こります。

オブジェクト指向では、状態というものがインタフェースの向こう側にあって、どのように実現されているか見えないようになっており、それがカプセル化、あるいは情報隠蔽と呼ばれる、複雑さを扱う技術の核心になっています。

ところが、関数型プログラミングでは状態の遷移を隠さずに、関数の入出力として表現しようとします(状態の遷移が入出力で完結している時、この関数を「純粋な関数」と呼ぶ)。

状態遷移が関数の入出力に限定されている時、プログラムの動作を把握するのは飛躍的に楽になります。一方で、オブジェクト指向ではプログラム上は簡潔に見えても、水面下に沢山の状態が隠されているので、何か問題が起きた時に状況を把握するのは容易ではありません。


The problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle. — Joe Armstrong(関数型プログラミング言語 Erlang の作者)

「欲しかったのはバナナだけなのに、それを持ってたゴリラどころか、ゴリラがいたジャングルごとついてきた」って、分かりやすくてなかなか面白い表現ですが、それを言ったら、関数型だってバナナだけじゃ済まないだろっていう話もあるような・・・


ところが、なぜオブジェクト指向が状態遷移を隠蔽していたかと言えば、インターフェースに対してプログラミングすることによって、プログラムが実現すべき要求だけを簡潔に表現できるということがあったと思います。そうせずに、単純に状態遷移を全て入出力で表現しようとすると、プログラムはとても読み辛いものになってしまいます。この関数型特有の問題に対応するため、いくつかの関数型言語ではモナドという「本来の計算とおまけを切り離す」ための仕組みが導入されており、これはオブジェクト指向でやっていた情報隠蔽が形を変えて現れたと言えるのかもしれません。

興味深いのは、オブジェクト指向と関数型で、状態の扱いに関して言えば、全く逆の考え方になっているところです。オブジェクト指向では複雑性を扱うための核心となっていた考え方が、関数型プログラミングでは悪として扱われている。

ここでぼんやり、オブジェクト指向と関数型のハイブリッド言語ってどうなのよ? という疑念が立ち上がってくることになります。

A paradigm is a mindset

そんな思いを抱いたまま、8月に開催された Elm Tokyo Meetup #4 に参加して、そこで教えて頂いた、Elmアプリケーションのプログラムデザインについての動画を見ている内に、オブジェクト指向と関数型プログラミングはそもそも両立しないのではないかという印象はさらに強くなっていきました。

この動画を見る限り、Elmのコミュニティには、オブジェクト指向からの編入組が結構いるのではないかという印象を受けます。というのも、そこで語られていたのは、コンポーネントという、オブジェクト指向的なデザインを導入したみたけれど、どうもうまく行かなかったので、一度その辺の考え方をリセットして、もっと原理的なところからプログラムデザインを考え直してみようという話だったからです。

動画の中で、オブジェクト指向と関数型というのは、プログラミング言語の問題というよりもマインドセットの問題なのだという話が紹介されています。つまり、言語のパラダイムとは関係なく、プログラマがオブジェクト指向のマインドセットでコーディングしていれば、たとえElmのような純粋関数型の言語であっても、オブジェクト指向的にデザインされてしまうということです。

そのように考えると、マルチパラダイムの言語では、良く言えば、プログラマのマインドセットによって多彩なプログラムデザインが実現できるということになるけれど、悪く言えば、互いに相容れない複数のマインドセットを想定している場合は、単に混乱の元になるだけではないかという感じがして来ます。

Prefer duplication over the wrong abstraction

そのモヤモヤ感が強まった中で、さらに追い討ちをかけて来たのが、Cindy Sridharan 氏による「小さな関数は有害だと考えられる」というタイトルの記事です。

この記事で彼女は、一般的には名著とされている「Clean Code」に書かれているような、オブジェクト指向時代に生まれた設計指針は、むしろ過剰な構造化を誘発して、可読性や柔軟性を欠いたコードになることが多いのではないかという、いかにもその筋で炎上しそうな指摘をしています。

この指摘の背景には「そもそもオブジェクト指向が想定する抽象化が容易ではない」という問題意識があります。抽象化が容易でないのに、オブジェクト指向の設計指針には、その分割が本当に必要だと確信できるより前に、プログラムの分割を進めさせてしまうような圧力があります。

SRP(Single Responsibility Principle)

SRPでは、「1つのクラスは1つの責務を持つ」を原則とします。複数の責務を 1つのクラスに持たせないこと。1つの分かりやすい役割をクラス分割の境界 とすること。1つのクラス内に入る要素(属性や操作)が、1つの目的に向かっ て凝集していること。これが原則です。

クラスに変更が起こる理由は、一つであるべき。
A class should have only one reason to change.

– ソフトウェア原則[3]

 

ISP(Interface Segregation Principle)

クライアントは自分が使わないメソッドに依存することを強制されない。
Clients should not forced to depend on methods that they don’t use.

クライアントが本当に必要としているインターフェイスのみ が、クライアントから見えるべきで、他のメソッドには依存したくない。依存 を最小にして、変更の伝播を最小限に食い止めたい。Segregationとは分割、分 離、という意味です。つまり、ISPは「インターフェイスをクライアント毎に分離しよう」という原則なのです。

– ソフトウェア原則[4]

この問題について、Elmの作者である Evan Czaplicki 氏も同じような話をしています。上の動画と同じ「Elm Europe 2017」にて行われた発表によれば、

JavaScript での開発では、モジュールを細かく分けて、小さなファイルを沢山作る傾向があるが(”Prefer shorter files.”)、何故そのようなことになるかと言えば、

1) 一つのモジュールが大きくなると、その内部で何か想定外のことが起こる可能性が高くなる(想定外の状態共有や変更)
2) Static type のない JavaScript では、リファクタリングのコストが高くなるので、早い段階で分割を進めてしまう

という理由があるからではないかと指摘しています。Elmでは、1) に対しては、副作用がない純粋関数型であること、2) に対しては、強力な型システムがあることによって、これらの懸念を払拭できるため、先走りのモジュール分割を避けることが出来るというわけです。実際に、Elmでは一つのファイルやモジュールが大きくなることについて、他の言語(特にオブジェクト指向言語)よりも寛容であるということがよく言われています。

そもそも適切な抽象化が難しいのに、その抽象化が有用であるという証拠が揃わない内に分割を進めてしまうと、より不適切な分割をしてしまう可能性が高くなってしまいます。先走って分割したモジュールが、後々の状況変化に対応できなくなって、例外条件に対応するコードが増えて行き、そしてスパゲッティ化していく過程というのはシステム開発の現場で働く多くの人が目撃しているのではないでしょうか。

duplication is far cheaper than the wrong abstraction, and thus to prefer duplication over the wrong abstraction

「間違った抽象化よりも、コードの重複の方を好む」ということで、長らく信奉されてきたDRY原則への挑戦がここでは行われています。

Leaky abstraction

そして、偶然なのか何なのか、これも同じ8月に、オブジェクト指向における抽象化がなぜ難しいかというのを良く表している大変興味深い記事を見つけることになります。

カプセル化することが自己目的化していて、何のためにカプセル化するのかという視点が極めて希薄なことです。その結果、「うまいカプセル化」「まずいカプセル化」の区別がない、という状況に陥っています。

本来は、値引き判定のロジックをどのオブジェクトに配するかを決めるにあたって、どのような知識を隠蔽すべきか、あるいは裏返して言えば、どのような知識は開示して構わないかという点に思いをめぐらすべきでした。

解決策は、「データとロジックを一体に」という、どちらかというとゲームのルールのような具体的で単純なルールから視点を引き上げ、「情報隠蔽(=知識隠蔽)」のような、より本質的な、目的志向的な設計原則に立ち帰って考えることです。

この記事の要旨は、増田亨氏の「現場で役立つシステム設計の原則」という書籍で紹介されているオブジェクト指向のコード例について、「データとロジックを一体に」というオブジェクト指向の表面的なルールに囚われ過ぎて「何を隠蔽して何を表に出すのかという設計判断」を蔑ろにしているという指摘です。

しかし、この記事を読んで個人的に思ったことは、設計判断の根拠となる「スコープが適切でない」ということを、後から文脈をズラすことでいくらでも言えてしまうというのが問題の本質じゃないか、そこに「データ・ロジック一体型設計」の限界があるということなのではないか、ということでした。

うまく抽象化したつもりでも、どこかに必ず漏れが出てきてしまうという話は、「Joel on Software」の「The Law of Leaky Abstractions」という、2002年に書かれた記事に出てきます。

TCPプロトコルが、下位のネットワークをうまく抽象化しているように見えて、実際はいくつかの例外ケースで、その隠蔽しているはずのネットワークの存在が漏れて出してしまう(Abstraction Leak)。そうなった時にかかるコストというのは、抽象化がなかった時よりも高くついてしまう可能性があります。隠蔽された部分の知識も結局のところは必要なのだとなれば、抽象化された部分と隠蔽された部分の両方の知識が必要になるからです。

Objects bind functions and data structures together

オブジェクト指向の問題点を指摘する場合、一番厄介なのは、オブジェクト指向に定まった定義がないという事実です。このブログでは以前、オブジェクト指向の歴史を遡って、あれってそもそも何だったのかということについて検討したことがあります。

歴史的な経緯から言えば、オブジェクト指向を発案したアラン・ケイ氏が言うところの「メッセージング」が、オブジェクト指向の本質だということなりそうですが、一般に普及した「オブジェクト指向言語」と呼ばれるもので、メッセージングをサポートしているものはほとんどありません。メッセージング、あるいはそれが実現する動的結合(late binding)だけを考えると、それは今、オブジェクト指向と呼ばれるものよりも遥かに広い範囲で利用されていますし、実際にはオブジェクト指向言語じゃなくても実現できることを考えると、C++ をきっかけに流行した「抽象データ型」を起源とする流れが、一般的に認識されているオブジェクト指向だと考えて差し支えなさそうです。

ちなみに、オブジェクト指向信者の反論を「No True Scotsman fallacy」だと指摘した Lawrence Krubner 氏によれば、一般的にオブジェクト指向の強みだと思われているほとんどの要素はオブジェクト指向固有ではなく、オブジェクト指向固有の強みなど、実際には一つもないそうです。

オブジェクト指向固有でないものを除外していくと、最後に残るのが「データとロジックを一体に」という先ほどのルールです。そして、どうもこのルールがオワコンになりつつあるのではないかというのが、この約1年間、Elmでプログラミングをしていて実感するようになったことです。

関数型プログラミングをしていると、データとロジックが分かれていることのメリットを実感する機会が度々あります。アプリケーションにはアプリケーション固有の複雑さというものがあって、それらは多くの場合、必要な複雑さである場合が多いような気がしています。オブジェクト指向では、データとロジックを一緒にしなければならないという制約のために、それらの例外的だと思われるケースを捨象して、現実に即さないモデルに(強引にでも)落とし込むことになります。必要な複雑性を無理に捨象しようとするから、Abstraction Leak が起こります。

オブジェクトに関数が結びついているからこそ「このメソッドはこのオブジェクト構造を処理するためのものだ(他の用途には使えない)」という風に専門化できていたんであって、データと関数を個別のものと扱う以上は、「この関数はこのオブジェクトだけを扱う」という前提を置けないのです(当たり前です)。あるオブジェクトと別のオブジェクトが、型であったりクラスであったりが異なったとしても、関数は、そのオブジェクトが、関数の処理できる構造であれば、処理できるべきなんです。関数とオブジェクトが独立しているというのはそういう意味であるべきです。であれば、すべての関数にまたがるような、共通の汎用データ構造があって、すべてのデータはその汎用性を担保してたほうがいい。 Clojureの世界観 – 紙箱

Oscar Nierstrasz 氏が、彼のオブジェクト指向批判の中で、「オブジェクト指向とは、つまりモデリングなのだ」と喝破していますが、複雑な事象を分かりやすい用語(ターム)の集合に落とし込めるという先入観が、アプリケーションレベルの複雑性を扱う時に明らかな障害となって現れるケースが多くなっているような気がします。

メッセージングが今のオブジェクト指向と関係ないのだとすれば、「データとロジックを一体に」がオブジェクト指向の核心になりますが、そうだとすれば、オブジェクト指向自体がオワコンだという結論になってしまいます。そして、それはどうもそうっぽいという感じがしているのです。

Solve problems of its own making

ここからは少し余談になりますが、以上のような気づきを得た上で、過去のオブジェクト指向批判の文章を読むといちいち首肯できることが多くて困ってしまいます。

オブジェクト指向というのは、オブジェクト指向にしかない問題を作り上げて、それを解決するためのツールを作るというマッチポンプ的なことをしてお金を稼いでいるという話があります。

If a language technology is so bad that it creates a new industry to solve problems of its own making then it must be a good idea for the guys who want to make money. Why OO Sucks by Joe Armstrong(さっきのバナナの人)

あるいは、オブジェクト指向ではそもそも過剰な複雑性を作り込んでしまう傾向があるという批判があります。何故かといえば、オブジェクト指向には、インターフェースに対してプログラミングするという考え方があるので、プログラムは自然にレイヤー構造になっていくからです。もう古典と言っても良いかもしれない、Martin Fowler 氏の「リファクタリング」にも、

「コンピュータサイエンスは、間接層(indirection)を設けることであらゆる問題が解決できるという信念に基づいた学問である。― Dennis DeBruler」

とあったりしますが、

しかし、間接層はもろ刃の剣であることに注意しなければなりません。1つのものを2つに分割するということは、それだけ管理しなければならない部分が増えるということなのです。また、オブジェクトが、他のオブジェクトに委譲を行って、その先もさらに委譲を繰り返すような場合、プログラムが読みにくくなるのも事実です。つまり間接層は、最小限に絞り込むべきなのです。

とは言え、実際には過剰なレイヤー構造になっていることが多いような気がします。アジャイルという考え方が出てきて、「Just enough」や「YAGNI」なんてことが言われるようになりましたが、今を思えば、これはオブジェクト指向の側にそもそも過剰な複雑性を生む性質があったために、わざわざ言わなければならなくなったことのようにも思えてきます。

オブジェクト指向特有の問題として指摘されている中で、ああこれはと思ったのは、オブジェクトをどう作るかという問題、すなわち Dependency Injection の問題です。

OOP was once seen as the silver bullet that was going to save the software industry. Nowadays we need a silver bullet to save us from OOP, and so, we are told (in the next paragraph), the Inversion of Control Container was invented. This is the new silver bullet that will save the old silver bullet. Object Oriented Programming is an expensive disaster which must end | Smash Company

これはまさにオブジェクト指向にしか存在し得ない問題を、比較的大掛かりに解決しようとした例の代表だと言えそうです。今改めて考えると、この枠組みには二つの問題があって、一つはインタフェースベースのポリモーフィズムの問題(あらかじめ想定したインタフェースの範囲の柔軟性しか持てない)、そしてもう一つは、本当に複数の実装を必要とするケースがどれだけあるのか? という問題です。後者は仮に統計が取れれば面白い数字が出てきそうですが、少なくともテスト時にモックオブジェクトに置き換えられるという主張は、テスト容易性はすなわち良いデザインではないと、Ruby on Rails の作者である David Heinemeier Hansson 氏に批判されています。

最後に

なんか、「もうやめて!オブジェクト指向のライフはゼロよ!」みたいな感じになってしまいましたが、これは完全に Elm のせいです。

オブジェクト指向と関数型プログラミングは両立するのか?

オブジェクト指向と関数型プログラミングは両立すると言われる。実際に両方のパラダイムを持つプログラミング言語も存在し普及もしている。確かに言語というレベルでは両立しているように見えるが、果たしてプログラムデザインのレベルではどうなのだろうか? 関数型プログラミングに入門してから一年余り、どうもこの二つの考え方は両立しないのではないかという思いが強くなってきた。あるいは、これらが両立するという前提が混乱の元になっているのではないかと思うようになった。

例えば、「関数型つまみ食い: 関数型プログラミングの何が嬉しいのか?」では、オブジェクト指向と関数型での、状態に対するアプローチの違いについて触れた。オブジェクト指向では状態とその遷移を隠蔽することによってシステムを単純化する一方、関数型では(多少冗長になっても)状態遷移を明確にすることによってシステムの動作を予測しやすくするという対比だ。つまり両者は、状態に関しては真逆のアプローチを取っている。

オブジェクト指向でも、不可変なデータを扱うことによって意図しない状態遷移を防ぐことができるし、それによってオブジェクト指向でなくなってしまうわけではないと考えることもできる。あるいは、副作用を避ける(不可変なデータを扱う)場所と、副作用を扱う場所を分けることによって、両者の考え方をうまく共存させることができるのではないかと。

しかし、先月開催された Elm Tokyo Meetup にて教えて頂いた、Elm Europe 2017 のアーカイブ動画を見ていたら、オブジェクト指向と関数型はそもそも根本的に異なる思想なのだという思いを新たにした。ちなみに Elm は Web のフロントエンド開発に特化した、純粋関数型のプログラミング言語である。

この発表では、プログラムデザインの流れをもう一度基礎から再考すべきであるということが語られている。まずは一枚岩で構造化されてない状態から初めて、「Build -> Discover -> Refactor」というサイクルを回しながら、構造化すること自体を目的化せず、今行おうとしているそのリファクタリングによって「そもそも何を保障したいのか?」を問う。

ここでの狙いは、過去の経験によって培われた「構造化の先走り」を防ぐことにある。「構造化の先走り」とは何か? Elm で有名な問題に「コンポーネント・アンチパターン」というものがある。

以前の Elm のチュートリアルでは、UI をコンポーネントの集まりとして表現する、というそれまで当たり前に考えられてきた文脈に沿って、状態とその変更ロジック、そしてその状態を表示するためのビューをひとまとめにしたコンポーネントというものを定義し、それらを組み合わせることによってアプリケーションを構築するという手法を紹介していた。しかし、このデザインでは、アプリケーションが大きくなるにつれてコンポーネント同士を連携させるためのボイラープレートが膨大になったり、柔軟性に欠けるケースや無駄な重複が現れるとして、その後、コンポーネント化はできるだけ避けるべきだと明言されるようになった。

Elm Europe の動画では、コンポーネントとは結局のところ、状態と操作をひとまとめにする、オブジェクト指向の構造化手法であることが指摘されている。長らくオブジェクト指向に親しんできたプログラマーが Elm に同様の手法を適用した結果、思ったような柔軟性を得られなかったという流れだ。

このブログでは過去に「オブジェクト指向とは何だったのか?」という記事で、オブジェクト指向をその起源に遡って解釈しようと試みたことがある。

ケイ氏は、オブジェクトを、ネットワークを形成してメッセージを送り合うコンピュータのメタファーとして捉えており、インターネット上のサーバーのように、リクエストをメッセージとして受け取り、そのメッセージをサーバー側で解釈して何らかの処理を行うというモデルを想定していた。

この全てがオブジェクトであるという原則と、大きなシステム(オブジェクト)は小さなシステム(オブジェクト)の組み合わせで作られるという「再帰的デザイン」によって、どんな複雑なシステムをデザインするときでも、覚えなくてはならない原則は少なくて済むようになる。

オブジェクト指向では上のような再帰的構造を持つものとしてシステムを捉える。これがアプリケーションに応用されると「アプリケーションはより小さなアプリケーションの集合によって表現される」ことになる。凝集性の観点から言えば、これはとても魅力的なアプローチに思える。アプリケーションを構成する、より小さなサブアプリケーションは各々が独立していて、変更の影響も局所化される。

しかし、関数型の世界(少なくとも Elm の世界)では、このモデルに疑いの目が向けられることになった。

件の動画では、アプリケーションはアプリケーション固有の(そもそも必要とされる)複雑さを持つのだから、その複雑さに対応するために、データから操作を切り離し、両者をより柔軟に組み合わせることによってその複雑性に対応すべきだということが示唆されている。その結果として、Elm ではグローバルな状態やロジックを集めたコードが長くなることについて、他の言語よりも寛容である、ということが説明される。これはあるいは Elm の持つ強力な型システムの支えもあるかもしれないが。

アプリケーションの再帰的構造というオブジェクト指向のモデルは、昨今のフロントエンドのような複雑なシステムを表現するには単純に過ぎるという問題は、オブジェクトとリレーショナルモデルをマッピングするときに問題視されたインピーダンスミスマッチの問題に似ているかもしれない。

最近、この問題に関係していると思われる大変興味深い記事に遭遇した。Cindy Sridharan 氏による「小さな関数は有害だと考えられる」というタイトルの記事だ。

タイトルが若干ミスリーディングであることは本人も認めているが、彼女が言わんとしていることはこういうことである。名著「Clean Code」などに書かれていたり、あるいはソフトウェア開発における著名人が喧伝するような、今では当たり前とされる、オブジェクト指向時代に生まれた設計指針が果たして今も本当に有効なのかどうか今一度確認してみるべきではないのか、と。

彼女の問題意識は、適切な抽象化というものがそもそも難しいのだというところから出発して、DRY原則などによって無条件に構造化を推し進めようとする既存の設計手法は、多くの場合に失敗に終わる抽象化によって、可読性や柔軟性を欠いたコードになることが多いという発見に辿り着く。オブジェクト指向時代の多くのプラクティスは抽象化が適切に行われることを前提にしているため、物事が複雑に揺れ動く現実の世界では、そもそもそのメリットを享受することが難しい。

duplication is far cheaper than the wrong abstraction, and thus to prefer duplication over the wrong abstraction

そもそも完璧な抽象化というものは存在しない。抽象化された概念というのは、ある文脈を前提にした主観的なものに過ぎないからだ。そしてのその文脈はあらゆる要因によって常に揺れ動いている。Sridharan 氏は、既存のコードが持つ影響についても指摘している。すでに書き上がったコードはある種の成果であるため、たとえ文脈が変わってしまっても、そもそもの文脈を廃棄することには心理的な抵抗が働くのだと。その結果、いびつな形で増改築が行われた建築物のように、構造化はされているが、全体像を捉えるのは難しいプログラムが出来上がってしまう。

より速くそして複雑に変化するシステムを捉えるためには、無条件な構造化については慎重にならなければならない。下手に構造化するぐらいであれば、多少関数が長くなっても実直に書かれている方が遥かに可読性が高く修正もしやすい。そして、過剰な抽象化を避けるためにコードのライフサイクルについても思いを馳せるべきだと Sridharan 氏は指摘する。これまでの設計指針では、コードを追加したり、削除したりする際は Open-Closed Principle(開放/閉鎖原則)に則るべきだと言われてきた。しかし、その原則がうまく働くのは抽象化がうまく行われた時だけである。であれば、「修正しやすい」こととは何かということを今一度考え直すべきなのかもしれない。

さて、この議論を呼びそうな記事に対しては、当然のことながら、既存の設計指針を支持する陣営から反論が行われている。以下はその一例である。

反論の要旨は以下のような感じだ。

  • 大きな関数は変更が局所化されないので脆弱である
  • ユニットテストが書きづらくなるため、結果としてテストされないコードが増える
  • 適切に抽象化されれば、やたらと長い名前問題はそもそも起こらない
  • コードは Open-Closed Principle に従って修正されるべきである
  • 大きな関数は副作用を持つ可能性が高くなる

こういう反論はいかにもありそうだという印象を個人的には受ける。「あなたはそもそも適切に抽象化できていないから」小さな関数が有害だと感じるのだという、オブジェクト指向界では比較的馴染みのあるものだ。「そもそも抽象化が難しい」というところには応答しておらず、議論はすれ違っている印象を受ける。

「そもそもやり方が悪いんだ」問題は、以前このブログで紹介した「Is TDD Dead? 会談」にも現れていた。

DHH氏の想定するようなTDDは、Beck氏やFowler氏の想定するTDDとは異なり、単にやり方が悪いからうまく行かないだけで、DHH氏の発言は「わら人形論法」に過ぎないというのが、TDD肯定派の反論である。それに対して「うまく行かないのはやり方が間違えているからであって、正しく実践すればいつかは正解に辿り着ける」というのは「faith-based TDD」という信仰であって、そのようなものは認められないというのが DHH氏の立場であり、ここがすれ違いの中心になっている。

そして、「ユニットテストが書きづらくなる」という主張も、関数型(特に Elm のような静的型付けの)ではユニットテスト の役割が相対的に小さくなること、あるいは「TDD再考」でも取り上げたように、ユニットテストそのものの価値の問題を考えると、それほどクリティカルな反論であるとは言えないように思える。

Cotoami コンセプト考: 発想を支援するツール

仕事の合間に Cotoami というシステムを開発している。

Cotoami は、それ以前に開発していた Oinker というシステムの後継に当たる。

これはこれで便利に使っていたのだけど(例えば、この「ゆびてく」の記事の多くは、一度 Oinker で素材を作ってから書いている)、どうしても身内に閉じてしまいがちなので、さらに色々な展開を望むならオープンに開発した方が良いのでは? という考えに至った。

そのような経緯で、Oinker をオープンソースとして一から作り直そう、ということで始めたのが Cotoami プロジェクトである。

2017年5月現在、完成にはまだ程遠い状態であるが、中途半端な状態でもとりあえずサービスとして公開して、興味を持って頂いた方々からのフィードバックを募っている。

断続的に開発して5ヶ月程経ったが、すでに Oinker とは大分異なるシステムになってきている。最終的にどのようなシステムにするかもぼんやりとしか決めておらず、思いついたアイデアを Twitter に投げて、それに対していろんな意見や提案を頂いて考え直したりと、そのようなプロセスを経て、最終的には当初の想像とは全く違うシステムが出来上がれば良いなと期待している。

現段階でぼんやりとしながらも頭の中にあるアイデアを、全て俎上に載せて、色々な方々からフィードバックを頂く機会を作れないかということでこの記事を書いている。とりあえず今回は第一回ということで、中心となりそうなコンセプトについて紹介してみたい。


Cotoami で何を作ろうとしているのか? 大きな括りで言えば「発想を支援するツール」ということになると思う。

ここで注意しないといけないのは、そもそもパーソナルコンピュータやインターネット、そしてWeb自体が「発想を支援するツール」になっているという事実である。そういう基盤がすでにある上に「発想を支援するツール」をわざわざ作る意味については常に考えておかないといけない。

パーソナルコンピューティングの主題は、コンピュータを使っていかに人間の能力を高めるか(「Amplify human reach」)ということであったが、これは言い換えれば「クリエイティビティ(創造性)」の追求である。 – 「オブジェクト指向とは何だったのか?」 

もっと具体的に言えば、パソコンやスマートフォンを買えば普通に付いてくるテキストエディタやその他の基本的なツール、こういったものを押しのけてまで必要なものになり得るのかという問いである。結局テキストエディタでいいんじゃない? とならないかどうか。テキストエディタは思っている以上に柔軟で強力である。

というわけで、まずは、最も単純なツールであるテキストエディタを出発点に考えてみたい。

テキストエディタで簡単に実現できないことは何か? それはコンテンツに構造を持たせることである。インデントなどを駆使して擬似的な構造を表現することはできる。しかしそれはあくまでも見た目上の構造である。構造を構造として扱うことはできない。

ここで一つ目の要件が出てくる。

要件 1) コンテンツに構造を持たせる

複雑な発想を表現するためには、コンテンツに構造を導入する必要がある。そのような構造をサポートするツールで普及しているものと言えば、最も基本的なところでファイルシステム(ファイルシステムを駆使すればテキストエディタでもある程度構造を扱えるようになる)、そしてアウトライナーやマインドマップ、さらには Web 上での共同作業を支援するあらゆるツールの基礎となっている WikiWikiWeb がある。

これらのツールが採用する構造のタイプに目を向けると、大体以下の二種類に分かれる。

  1. ツリー型: ファイルシステム、アウトライナー、マインドマップ
  2. ネットワーク型: WikiWikiWeb

ツリーとネットワークを比較した時に最も分かりやすい違いは、ある一つのコンテンツがたった一つの場所に所属するか、あるいは複数の場所に所属するかという違いである。ツリー型だと一つのコンテンツは一つの場所にしか所属させることができない。つまり、コンテンツの文脈は常に一つになるが、ネットワーク型だと一つのコンテンツを複数の文脈の中に配置することができる。

アウトライナーやマインドマップのようなツールは、一つのテーマ(文脈)を出発点に、そこから連想したり知識を細分化したりするのが基本になる。一つのテーマを俯瞰したり、まとめたりするのには便利なのだが、発想を広げようとするとツリー構造が制限になる。

ツリー型だと知識の俯瞰はしやすいが、本来の知識が持つ多様な文脈を表現できない。ネットワーク型だと知識の全体像を掴むのは難しくなるが、複数のテーマを横断するような知識の表現が可能になる。

Cotoami としては、「発想を広げる」という観点から、一つのコンテンツをいろんな文脈に置くことのできるネットワーク型を選択したい。

要件 2) コンテンツはネットワークを構成する

そして、ここまでに挙げたツールの多くが個人向けのツールである。より発想を広げたいと考えた時、やはり他の人と何らかの形で連携できた方が良さそうである。それも少人数で流動性の低いチームだけでなく、不特定多数の人とコラボレーションできればもっと発想は広がるだろう。

要件 3) 個人から、複数人のチーム、さらには不特定多数の人とのコラボレーションをサポートする

ここまでに挙げたツールの中で、これら三つの要件を満たすのは WikiWikiWeb だけである。この WikiWikiWeb が、Cotoami から見たときの重要な参照点になる。

WikiWikiWeb は、プロダクトそのものよりも、コンセプト自体が永遠に生き残るような「発想を支援するツール」の傑作だと思う。Cotoami としても、結果としてそのようなコンセプトを残せればと思うのだけど、それはちょっと高望みし過ぎかもしれない。

さて、「発想を支援する」という観点から考えた場合、この WikiWikiWeb に足りないものは何だろうか?

それは、どんな小さなアイデアでも気軽に投稿できるような仕組みがない、ということではないだろうか。

思いついたことを気軽に書き込める Inbox 的な Wiki ページを用意すれば良いのではないかと思われるかもしれない。しかし、Wiki の場合、一つのページ内に書き込まれた内容はテキストエディタと同様に見た目上の構造しか持たせることが出来ない。ページとして登録するにしても、思いつきの断片でわざわざページを作りたくないというケースも多いだろう。ユーザーインタフェース的な問題もあって、投稿障壁が最小限になっているとは言い難い部分もある。

発想の可能性を広げるためには、投稿障壁を最小限にして材料となる情報を出来るだけたくさん集めること、そして、どんな小さなアイデアでも構造の単位としてネットワークを構成できるように、そしてそれを柔軟に操作できるようにしたい。というわけで、以下の二つの要件が導き出される。

要件 4) どんな小さなアイデアでも気軽に投稿できる(投稿障壁を最小限にする)

要件 5) どんな小さなアイデアでも構造の単位となれる

Cotoami では、投稿障壁を最小限にするために、コンテンツの入力部分はチャットアプリと同じ形になっている。

Twitter が情報発信の障壁を劇的に下げて、それ以前までは考えられなかったような発言を利用者から引き出したように、情報の入力を Twitter や、さらに障壁が低いと考えられるチャットの形式にすることによって、どんな些細な情報でも発想の材料として利用できるようにしたい。

投稿障壁をチャットのレベルまで下げると、発想の材料としては使えないようなノイズも多く含まれるようになる。そこで、以下のような要件が出てくるだろう。

要件 6) ネットワークに参加させるコンテンツを取捨選択できる

さて、この辺からいよいよ Cotoami の核心に近づく。

チャット形式で入力されたアイデアは、思いつきで投稿されるため、その内容は比較的ランダムになりやすい。特に複数人で会話をしているようなケースだとそのような傾向が強くなるだろう。ある程度テーマを決めて話していても、その周辺となるような話題に足を踏み入れたりすることも少なくないはずである。あるいは個人で利用しているケースでも、思いついたことは何でも記録するという形で利用していれば、そこに様々なテーマが現れてくるはずだ。それらのテーマを事後的に発見して、構造を立ち上げていけるような仕組みが欲しい。

要件 7) コンテンツの中から事後的にテーマを発見して構造を立ち上げていける

ここが既存の発想支援ツールと一線を画すところだと言えるかもしれない。アウトライナー、マインドマップ、そして WikiWikiWeb も同様、基本的には「連想」をベースにした発想を支援するツールである。ツリーの場合、その連想はどちらかと言えば、上から下というように細分化する方向に向かう。ネットワークの場合は、より自由な連想を可能にするが、隣接する領域にしか発想が伸ばせないという意味では同じである。

ここで Cotoami として考えていることは、一見相互に関係ないと思われるコンテンツの中につながりを発見して、それを新しい発想の拠点とすることである。

具体的にどのように実現するか? 以下の図を見て欲しい。

まず、チャット形式で投稿される小さなコンテンツ、これを Cotoami では Coto(コト) と呼んでいる。その Coto は、Cotonoma(コトノマ) という、チャットルーム的な場所で投稿される。「Stage 1 – Posting to timeline」の部分は、まずは Cotonoma を作って、そこに Coto を投稿して行く(チャットする)という段階を表している。

そうして集めたランダムな Coto の中につながりを探して、そのつながりの中心となるような Coto を選び(あるいはつながりを表す名前を新たに Coto として投稿して)、そこから関係する Coto に向かってリンクを作成する。これが二段階目の「Stage 2 – Connecting」である。

そのようにつながりを作って行くと、Cotonoma の中にいくつものグループが出来上がってくる。そして、その中からこれは重要だと思えるものが出てくるはずである。その重要だと思われるグループの中心となっている Coto を Cotonoma に変換することによって、その新しく生まれたテーマを、専用の場所で掘り下げて行くことができる。これが「Stage 3 – Convert a category coto into a cotonoma」である。

その後はまた最初のステージに戻って同じ流れを繰り返して行く。このようにして生まれた Cotonoma(テーマ)のリストは、発想のプロセスを開始する前に想定していたものとは異なるものになっている可能性が高い。これを「発想」の肝として考えるのが Cotoami のコンセプトである。


というわけで、今回は Cotoami の中心となるコンセプトについて書いてみた。第一回ということで、比較的長い間維持されそうな核となるコンセプトに絞ったのだが、ツッコミどころは色々とありそうな予感もある。あるいは、これらのコンセプトからどのような展開があり得るのかということも開発を進める上で明らかになってくると思う。このような話題、あるいはツールに興味のある方は、是非是非 Cotoami プロジェクトをウォッチして頂いて、思いついたことがあったら何でも、Twitter やコメント欄などで気軽に話しかけて頂けたら幸いである。

関数型つまみ食い: 関数型プログラミングの何が嬉しいのか?

 

「モナド会」とは、モナドをまともに使ったことがない人間が、モナドどころか関数型プログラミングの経験もない人間に、モナドについて解説するという恐ろしい会である。

実は以前、モナドについての記事を書いたことがある。

モナドについての知識が全くない頃に(今でもかなり怪しいが)、Philip Wadler 氏の論文を読んで、少し分かった気になったので軽い気持ちで書いた記事だ。しかしその後、何の間違いなのか、「モナド」でググるとこの記事が1ページ目に表示されるようになってしまった。本当に申し訳ない気持ちでいっぱいである。

この責任ある立場としては、「モナド会」なるものを開催し、分かったつもりの勢いで初心者に解説を試みて、そしてその成果をここで紹介することでより混乱を深めていくしかない、そのように決意した次第である。

というわけで、今回は「モナド会」で説明を試みた話題の中から、最も根本的な話である「そもそもなんで関数型プログラミングが必要なのか?」というお題について紹介してみたい。

 

オブジェクト指向と不確定性

関数型プログラミングのメリットは、これまでの主流を占めていたオブジェクト指向プログラミングとの比較で考えると分かりやすい。

一言で言えば、オブジェクト指向と比較して関数型は「原因と結果を局所化するので、システムの動きが分かりやすくなる」。

どういうことだろうか?

以下の図を見て欲しい。

オブジェクト指向では、システムに何か動きがあったとき、その動きの原因となる箇所と、結果となる箇所が分散しているため、システムの動作(状態遷移)が把握しづらくなる。上図で言うと、青い部分が原因になる箇所で、赤い部分が結果として状態変更の行われる可能性のある箇所だ。

まず、青丸に Arg と書かれている method の引数が動作の入力になるというのは、比較的すんなりと理解できる。ところが、図をよく見ると、Devices と書かれた箱も青い線で囲まれていて、入力の一部になっていることが分かる。Devices は、プログラムの外部にあるサービスを表している。単にオブジェクトを利用するときには意識に上らないことが多いが、実は Devices の状態も事前条件として、動作に影響を与える「原因」の一部になっている。

さらに、結果の方を見てみると、動作に関係しているオブジェクトそれぞれの状態が変更される可能性がある上に、Devices にも状態変更が起こる可能性があることが分かる。

Devices を操作するのがプログラムのそもそもの目的なのだから、当たり前と言えば当たり前の事態なのだが、オブジェクト指向言語でユニットテストを書いたことがある人なら、なんとなくこれらの厄介さが分かるのではないだろうか。

とあるメソッドのテストを書く場合、単純に引数を渡して実行すればOKというわけには行かず、依存オブジェクトやシステムについて、何らかの準備が必要になることが多い。そして、結果を検証する際にも、オブジェクトの境界だけを確認するか(Mockist Testing)、あるいは分散したシステムの状態を確認するか(Classical Testing)といった選択に悩むことになる。

このように、プログラムから直接接続された Devices(外部サービス)は、プログラムの挙動を予測しづらくする諸悪の根源なのである。

 

純粋関数 – 原因と結果の局所化

関数型では、この原因と結果を、関数の入出力として局所化するため、システムの動きが格段に分かりやすくなる。

この原則が徹底されているとき、つまり、システムで発生し得る状態遷移の全てが関数の入出力として表現されてるとき、これらの関数を純粋な関数と呼ぶ。

純粋な関数だけで構築されたシステムでは、その入出力として表現されている以外の出来事は起こらない。つまり、関数の入出力を見ればシステムがどのように動くかを完全に把握できるということだ。

そのようなシステムを図にしたのが以下である。

システムが動くときの原因と結果が、関数の入出力として局所化されていることが分かる。

これでシステムの状態遷移がシンプルになったし、めでたしめでたしと言いたいところだが、オブジェクト指向で Devices にアクセスしてた部分はどうなったのだろうか? Devices を操作できなければ、まともなプログラムは作れないはずである。

実はそこに純粋関数型プログラミングのトリックがある。

図をよく見ると、関数は入力を得て出力を返すのみで、Devices へ直接アクセスすることはないものの、入出力を受け渡しするレイヤーとして Runtime というものが Devices との間に挟まっている。

純粋関数型は、Devices を直接操作できない代わりに、Devices への命令をデータとして出力し(図中の出力に Command が含まれていることに注目)、それを Runtime に渡すことによって、間接的に Devices を操作する。

このようなややこしい迂回をすることによって、外部サービスをプログラムから切り離して、関数の純粋性を保つわけである。

Devices への命令をデータ化して、プログラム全体を純粋関数にしようとするのは、関数型プログラミングの中でも最もハードコアな部類になるとは言え、基本的に関数型プログラミングは、純粋関数を出来るだけ多く導入することによってシステムから不確定性を取り除こうとする考え方だと言って良いのではないだろうか。

純粋関数にはメリットがある。しかし、それを徹底しようとすると「命令をデータとして表現する」というややこしい方法を取らざるを得ない。その結果、命令型の言語のような簡潔さは失われることになる。その失われた簡潔さを取り戻すために「モナド」のような仕組みが活躍することになるのだが、これはまた続きの記事で紹介したいと思う。