Hash λ Bye

Haskell, Clojure, MLなどの話

プログラミングClojureにおける「データ」とは何か

プログラミングClojureは僕が読んだ二冊目のClojureの本で、 Clojureがどんな機能を備えているのか、どんなパラダイムなのかを 教えてくれるとても良い本。

読み終わった今でも時々、「あそこどう書いてたっけな」と気になってはすぐに読み返してしまう。

しかしながら、何度読んでも"具体的なオブジェクト"と"データ"という記述の違いが解らなかった。 その後、ひとりで悩んだり、人に相談してようやく理解できた。

この記事ではその理解をなんとか噛み砕いて説明しようと思う。

「データ」

どんなワークフローでプログラムを組んでいくのがよいか

プログラミングClojureではそれについて説明している箇所がある。 誰もが気になるところだ。文法や言語の機能が解ったところで、「実際どうやるの?」が解らなければ勇気をもって実装に踏み出せないものだ。

例えばこんな文章で説明されている。

Clojureでの設計の肝は、いつでもどこでも具体的なオブジェクトで溢れさせるのではなく、データそのものについて考えることだ。

引用元: 10.1 Clojurebreaker ゲームのスコアの計算 p.222

これが解らなかった

「データそのもの」とはなんだろうか。 具体的なオブジェクトとの違いは何だ?

さらにClojureプログラミングの原則として下記のようなことが書いてある。

・領域特有の具体物を持ち込まない(データはデータとして扱う)。

引用元: 10.1 Clojurebreaker ゲームのスコアの計算 p.224

これも解らなかった

「データはデータ」? データは具体物ではないのか? では何なんだ?

何が難しいのか

  • 具体的なオブジェクト/具体物
  • データ

この違いが解っていないから混乱する。もう一度文章をよく睨んでみよう。

Clojureでの設計の肝は、いつでもどこでも具体的なオブジェクトで溢れさせるのではなく、データそのものについて考えることだ。

データそのものについて考える。 それは具体的な何かの概念について考えることになるだろう。

そう思った。というか、そう連想した。

なので

データそのものについて考える=具体的なオブジェクトを設計する

ではないの?

・領域特有の具体物を持ち込まない(データはデータとして扱う)。

領域特有の具体物を持ち込まないプログラミング、というのが想像できなかった。

データが具体的でないのなら、いったいどんな形をしているのか、まったく解らない。 データは「扱われる」のだから何らかの意味で具体的になっているはずだろう。

教えてもらった

ずっと考えていても解決する兆しが無いので、直接訳者の方に質問してみた。

解ったこと

頂いた回答をもとにまとめると。。。。

具体物

{ :name "津島善子" :blood-type O :birth-day [Jul 13] }

データ

{ :name "津島善子" :blood-type O :birth-day [Jul 13] }

!?!?!?

同じっ!?

記法は同じ!!

しかし意味が異なる!!

どちらも同じMapだが、

  • 具体物としてそれを捉えた場合: それは津島善子という人の情報を表す
  • データとしてそれを捉えた場合: それはkey-value pairsを表す

「どう捉えるか」という解釈が問題。

それは確かに人を表す情報かもしれないけど、 それはいったん忘れて Map (key-value pairs) という データ として関数を適用しよう。

再咀嚼

Clojureでの設計の肝は、いつでもどこでも具体的なオブジェクトで溢れさせるのではなく、データそのものについて考えることだ。

・領域特有の具体物を持ち込まない(データはデータとして扱う)。

これらは何を言っているか。

対象の具体的な意味は忘れて、Map, Seq, Vectorとしてデータを変換するようなAPIを設計しよう、ということだ。 そして幸いにも clojure.core にそういった関数が膨大にある。 それを使えばいいじゃないか。

そう、それが再利用性ということ。

必要であれば自分で少し拡張しないといけないかもしれない。

具体的なオブジェクト

たとえばこれは明らかに具体物だ。

(defrecord AqoursMember [name blood-type birth-day])

だって AqoursMember と書いてある。 それが見たまんま ラブライブ サンシャイン に出てくるスクールアイドルを示す何かであることは疑いようもない。

データ

一方でそれは

IPersistentMap

だ。

それは key と value の対が収まった不変データそのもの。 歌ったり汗をかいたりはしない。

ただ assocmapreduce に渡せるインタフェースを満たすデータ構造だと考えることができる。

意味を忘れるとはつまり、それが僕達の仕事の中でどんな意味を持つかではなく、Clojureのプリミティブとして何であるかだけを考える、ということ。

データが何を意味するかは気にする必要ない。一般化して考えよう。

それがClojureにおけるボトムアッププログラミングのスタイルである、ということらしい。

現実的な問題に立ち向かう

そうは言っても僕達は具体物・具象から離れてはアプリケーションを作れない。 アプリケーションが特定の問題解くものである以上、絶対に避けられない。

そこで、Clojureのプログラミングではデータという抽象を扱うたくさんのAPI上に あるいは その外側 に領域特有(Domain Specific)のプログラムを構築する。

僕個人としてはこの考え方は結構衝撃的だった。

ドメインがいる場所

DDDを少しかじってみるとHexagonal Architectureというものに出くわす。

僕はアプリケーションの俯瞰図をそのイメージで考えることが多い。

Hexagonal Architecture

この図だと、ドメインつまり具体的なオブジェクトの集まりはアプリケーションの中核にいる。

でもClojureのワークフローに従うとこの中と外が 逆転 する。

f:id:ilyaletre:20180101120422j:plain

ドメイン固有のオブジェクトは外側になる。

代わりに中核に居座るのは抽象データを操作する関数だけになる。

ボトムアップでコアを作っていく

ボトムアッププログラミングと口にする時、それはこの抽象データ用の中核を作っていくことに他ならない。 定義する関数が Person だとか AqoursMember だとか、そういうことは一切気にしないでプログラミングする。

勿論REPLでテストデータを渡すために仮のレコードを定義して使うことはあるかもしれないが、 呼び出される関数はそれらをただの Map として認識して操作する。

この一般化された関数を"ボトム"として、その上に少しずつ具体的な"トップ"を作っていく。

あるいは

この一般化された関数を"内側"として、その"外側"に少しずつ具体的なオブジェクトを作っていく。

まとめ

プログラミングClojureにおける"具体"と"データ"の違いを見てきた。

  • 具体/具象: ドメイン固有オブジェクトとしてそれを捉えた時の呼び方
  • データ/抽象: ドメインをいったん忘れてただのClojureのデータ構造としてそれを捉えた時の呼び方

ボトムアッププログラミングとは、

  • データを操作する関数を最初に作り、
  • そこから具体的な意味を扱う関数をその上に作る

ようなワークフローである。

ということが解った。

謝辞

訳者の川合史朗さんの適切なアドバイスを受けてなんとかこの理解を得ることができました。 ありがとうございました。

ところで

原著の第三版 が出ている。

transducerとspecについての内容が追加されているらしく、もう原著で買うか、という気分になっている。