2012年3月30日金曜日

DCI (Data Context Interaction)

3月19日(月)に要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』を行いましたが、説明を端折ったところを中心にスライドの回顧をしています。

今回は「Data Context Interaction (DCI)」として用意した以下のスライドを説明します。

オブジェクト・モデリングの問題点の一つに、ユースケースからドメイン・オブジェクトへ真面目に責務の分散配備をしていくと、ドメイン・オブジェクトの実現が非常に重たくなってしまうという問題があります。

ドメイン・モデルそのものに由来するドメイン・ロジックをドメイン・オブジェクトで実現するのは、本来のオブジェクト指向の趣旨にも則っており問題ありません。

論点となるのはユースケース由来のアプリケーション・ロジック。このアプリケーション・ロジックを、(1)どのようにして適切な責務に分解して、(2)どのオブジェクトに分散配備していくのか、というのがオブジェクト・モデリングのホットスポットで、オブジェクト・モデリング手法を比較する上でのポイントとなるところです。

大きく、ドメイン・オブジェクトに配備していく派と、アプリケーション・ロジックを別オブジェクトで実現する派に分かれますが、理論的な主流派は前者、実際の開発者は後者を選ぶというねじれ状態になっています。

それはともかく、責務をドメイン・オブジェクトのみに配備していくと、システムを取り巻く文脈ごとのユースケースで必要な責務を集積した、巨大な責務の集りをドメイン・オブジェクトがまとめて実現することになり、(1)太ったドメイン・オブジェクトになってしまうという問題が出てきます。これに加えて、(2)色々なユースケースの断片が分散配備されることは、モジュール化、疎結合、高凝集という観点からも問題です。また、(3)モデルのライフサイクルの違いの扱いも論点の一つです。(3a)比較的安定しているデータモデル、(3b)比較的ライフサイクルの短いアプリケーション・ロジック、(3c)比較的高頻度に更新されるUIロジックを同じモジュールに配備すると、(3a)のUIや(3b)のアプリケーション・ロジックに引きずられ(3c)のデータモデル(を扱う処理部)にも影響が出てしまいます。

(1)は集中することの問題、(2)は分散することの問題、(3)は集中することの問題なので、それぞれ相反する事象であり、全てを一度に満足することは困難です。程よく集中して、程よく分散するバランス点を見出し、プログラミング言語での自然な実装手法を確立していく必要があります。

この問題に対する解として一時期AOPが注目されましたが、あまりうまくいっていないようです。柔軟すぎるメカニズムは、プログラムの安全性という意味で問題がありそうです。静的型付けの枠組み内でこの問題を解決するのが、安全性を担保するひとつの目安だと思います。

この問題を解決するアプリケーション・アーキテクチャとして注目されているのがDCI(Data Context Interaction)です。

このスライドでは、OFPが提供するトレイト、型クラスという新しい言語要素(「OFP新三種の神器」)が、DCIといった新しいアプリケーション・アーキテクチャを可能にするという点の説明を行う予定でした。

モデリングの流れ

DCIのモデリングの流れとしては:

  1. ユースケース・モデルの作成
  2. コラボレーションとコラボレーションから呼び出すロールを抽出
  3. コラボレーションを実現するアプリケーション・ロジックを分解してロールに配備
  4. ロールをドメイン・オブジェクトに編み込み。コンテキストで全体を統合。

となるかと思います。ボクが確認した範囲では、アプリケーション・ロジック専用のオブジェクトを生成したり、コンテキスト側でアプリケーション・ロジック(の一部)を実現するのではなく、あくまでドメイン・オブジェクトに編み込むロールにロジックを分割するようです。

つまり、この理解が正しければユースケースの責務をドメイン・オブジェクトに分割配備する伝統的なオブジェクト・モデリングの手法を軸としながら、ユースケース由来の責務をコンテキストに紐づいたロール側に配備することで、モジュール化を実現しているのがDCIということになります。

実装技術

ロールをオブジェクトに編み込む実装技術として、トレイトと型クラスが有力です。

現時点では、書籍の「Lean Architecture: for Agile Software Development」がDCIについて最も情報量があるのではないかと思います。この本では、DCIについての主な実装方法として、CとRubyの2つの言語での実装が主に取り上げられており、付録としてScalaも取り上げられています。

Cの実現はマクロを使ったものでやや強引な印象です。Rubyの実現はmoduleを使った動的型付ならではのメタプログラミング的な手法で、プログラマに負担をかけないのが美点です。

ただ、普通の静的型付け&OOPでの実装技術となるとなかなか難しく、Javaで実現する場合は相当量のボイラープレイト、繋ぎコードが必要になりそうです。この問題に対する解として期待されるのがScalaのトレイトです。

トレイトによる解

DCIでは、Scalaのトレイトによる実装を選択肢の一つとして挙げています。たとえば、「Lean Architecture」で紹介されているプログラム例の一部を以下に引用します。

val source = new SavingAccount with TransferMoneySource
val sink = new CheckingAccount with TransferMoneySink
source.increaseBalance(100000)
source.transferTo(200, sink)

SavingAccountクラスのオブジェクトを生成する時に、ロールを実現したTransferMoneySourceトレイトをwith句で編み込んでいるのがポイントです。この(ユースケース由来の)アプリケーションロジック向けの機能を事前にSavingAccountクラス側で提供しておく必要はなく、アプリケーションロジックでの必要に応じてロールを編み込みます。ユースケースから派生したロジックを、直接ドメイン・オブジェクトに分散配備するのではなく、ユースケースに紐づいたロール側にまとめておけるのでモジュール化も保てるというわけです。

型クラスによる解

Scalaでは、トレイとに加えて、型クラスという新しい言語機能が提供されています。(正確には暗黙パラメタと暗黙変換によってライブラリで型クラスを実現することができます。)型クラスは、基本的にはジェネリックプログラミングのカテゴリの言語要素だと思うのですが、Haskellで実用化され、関数型の枠組みの中でうまく機能しているのでたので広義の意味では関数型の特徴的な言語要素ということも可能かと思います。

Scalaでの実現であるScalazを実際に使ってみると"OOPにも型クラスの導入が可能で、実用上も有益である"のかなと思いますが、プログラミング言語理論は素人なので、このあたりの事情はよくわかりません。

ひとつ言えるのは、Scalaでは型クラスは非常にうまく機能しており、Scalaの提唱するObject-Functional Programmingの重要な基盤の一つになるだろう、ということです。

DCIをトレイトで実装する場合の問題点は、with句を使うコーディングとなるのでロールをドメイン・オブジェクトに編み込むところがハードコーディングとなることです。コンテキストが複数のロールを扱うときに、これらのロールを束ねるメカニズムもないので、このあたりで拡張性やモジュール化で問題が出てきそうです。

型クラスの場合は、コンテキスト毎にアプリケーション・ロジック、ロール、ドメイン・オブジェクトの対応関係の組を柔軟に指定することができるので、拡張性、モジュール化の能力に優れているのではないかと思います。そういう意味で、トレイトではなく型クラスが、ScalaにおけるDCI実現の本命ではないかと考えています。

DCIの評価

「このスライドでは、OFPが提供するトレイト、型クラスという新しい言語要素が、DCIといった新しいアプリケーション・アーキテクチャを可能にするという点の説明を行う予定」だったわけですが、改めてじっくり検討してみると、DCIはとても魅力的なアプリケーション・アーキテクチャではあるものの、実際のシステム開発への展開はまだまだ難しそうというのが実感です。

また、アプリケーション・ロジック専用のオブジェクトは第一級のモデル要素としては考えていないというのが、DCIがボクの好みとずれがある点です。ボクとしては、アプリケーション・ロジック、ロール、ドメイン・ロジックの3階層で責務の分割配備を考えたいと思っています。(このようにして抽出したアプリケーション・ロジックは、ドメイン・ロジックそのものなので、ドメイン・ロジック&ドメイン・オブジェクトとしてドメイン・モデル内に還元する、という考え方であればDCIとは矛盾しません。どちらの考え方にした方が、実際のアプリケーション開発で有効なのか、もう少し様子をみて判断したいと思っています。)

以上がOFADについてひと通り通して考えてみた上での実感です。

EDA

アプリケーション・ロジック、ロール、ドメイン・ロジックの3階層で責務の分割配備を行うためには、DCIでいうところのコンテキストを、アプリケーション・ロジックを配備するモデル要素に格上げするような、アーキテクチャが有効ではないかと考えます。仮にこのモデル要素をコラボレーションと呼ぶことにしましょう。(「コラボレーション」という用語はミスリードのような気もするので、他の名前を編み出したほうがよいかもしれません。)

このコラボレーションを、実際のアプリケーション・アーキテクチャに組み込む場所ですが、クラウド・アプリケーションでは、「EDAとオブジェクトと関数」で取り上げたEDAをベースとしたアーキテクチャが有力ではないかと考えています。

このアーキテクチャにあるコラボレーションをイベント・コンシュマーとしてEDA内に組み込むわけです。この時に、コラボレーションの通信相手となるロールを型クラスの機能を用いてドメイン・オブジェクトに編み込みます。

この方式では、イベント発生に付随した複数の疎結合のユースケースが並行動作することを自然に扱うことが事ができます。また、EDAの特質上、非同期処理も自然に扱えるのという長所もあります。

そういう意味で、DCIというアーキテクチャを単独で考えるのではなく、EDAのアーキテクチャの中にDCI的なアプローチも取り込みながら、EDAの枠組みで全体のアーキテクチャ(モデリング・ビュー、コンポーネント・ビュー)を考えていくのが、クラウド時代のOFADを整備していく上で有力なアプローチではないかというのが現時点での考えです。

諸元

当日使用したスライドは以下のものです。(PDF出力ツールの関係で、当日は非表示にしたスライドも表示されています)

このスライド作成の様子は以下の記事になります。

まとめは以下の記事です。

回顧は以下の記事になります。

2012年3月29日木曜日

メタモデル

3月19日(月)に要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』を行いましたが、説明を端折ったところを中心にスライドの回顧をしています。

今回「メタモデル」として用意した以下のスライドを説明します。

以前からSimpleModelingというOOADの開発手法の一環でDSL駆動開発向けのOOADメタモデルを整備しています。この本の内容はクラウド・プラットフォーム登場前のものですが、クラウド・プラットフォーム向けにいくつか拡張を行ってきました。

また、今回スライドを作りながらOOADと関数型言語の関係を考えてきたわけですが、その考察の中で鍵となるモデルとして浮かび上がってきたデーターフローというモデル要素を追加することにしました。

まとめると、クラウド以降に以下のモデル要素が追加されたことになります。これらのモデル要素は図では白抜き文字にしています。

  • UX
  • サマリー
  • データフロー
  • メッセージフロー
  • データフロー

以下で順に説明します。

ここまでの拡張

このSimpleModelingに対してここまでクラウド向けモデリング向けに追加してきたモデル要素は以下のものです。

  • UX
  • サマリー
  • データフロー
  • メッセージフロー
UX

OFADの要素技術/関連技術」でも説明しましたが、Ajaxやスマートデバイスの興隆によるリッチなGUIのニーズの高まりを背景にUCD(User Centered Design)、UX(User Experience)という技法が注目されています。以前から要求仕様の一環として画面設計を行うケースはあったわけですが、UCD/UXはそのアプローチを利用者視点でより理論的に進めたものということができます。

UXは比較的最近の言葉ですが、UCDは「ユーザー中心設計」にもある通り、かなり古くからあるモデリング技術です。一般消費者向け商品のモデリングとして発展してきたものが、Webシステムによる一般消費者向けのサービスが大きなマーケットに育ってくる中で、ITシステムの要求仕様のためのモデリング技法として注目されてきているという流れだと思います。

今後は、エンタープライズ・システムでも、ソーシャルメディアといった軸でビジネス的な意味での顧客とのコラボレーションを設計していかなければならないと考えられ、そのなかでUCD/UXが利用されていくのではないかと思います。

こういったモデリング技術の動向を取り入れるために、UX(User Experience)をユースケースの前段に配置しています。ここでのUXは、Ajaxやスマートデバイスを対象とした比較的狭い範囲のUCD向けを想定していますが、ソーシャルメディア上のコラボレーションという意味では、図の右側ビジネスモデルの所にUCDを持ってくることも考えられます。従来からあるビジネス・プロセス、ビジネス・ユースケースのアプローチとUCDの関係をどのように整合させていくのかはまだイメージが湧いておらず、ジャストアイデアというところなのでメタモデルの図には載せていません。

UXとユースケースは、「OFADの要素技術/関連技術」で説明した「(2)Ajaxやスマート・デバイスなどのリッチなGUI向けのUIデザイン(+要求仕様)」について、「従来のユースケースと部分的に技術がかぶってくるので、役割分担を考えていかなくてはなりません。利用者とのインタラクションはUXを用い、システム側はエッセンシャル・ユースケースに徹するという分担が考えられます。」というアプローチを考えています。たとえば、UXでペルソナを作成し、これをエッセンシャル・ユースケースに落とすという連携です。

ただ、通常の定型業務向けの業務アプリケーションは、ビジネス・ユースケース(や業務フロー上)でロールを抽出し、このロールの責務を明確にするという従来からあるOOADのモデリングの方が向いていると思われるので、必ずしもUXの導入がよいというわけでもなさそうと考えています。 その場合でも、ロール・モデリング後に、要求仕様としての画面設計(ちょっと言葉が変ですが、画面モックアップぐらいがよいかも)の中でUX的な技法を適用していくのは有効そうです。

サマリー

サマリーは、「要約エンティティ」という記事で説明したエンティティです。

従来から企業システムは、(1)同期型のオンラインシステムと(2)非同期型のバッチの2つのサブシステムから構成されています。ERモデルでは、summaryエンティティという非同期型バッチが計算した要約情報を格納するのに適したエンティティを使うこともあります。

クラウド・プラットフォームでは、こういったバッチ的な非同期処理(さらにストリームも入ってきます)が大前提になるので、サマリーというエンティティを第一級市民のモデル要素として扱っていくことにしました。

メッセージフロー

メッセージフローは、ユースケースから責務の抽出とオブジェクトへのアロケーションを行うために使用するロバストネス図のクラウド・アプリケーション向けの代替、発展形として導入したモデルです。また、クラウド・アプリケーションの全体像を簡潔に記述する目的にも使用できます。

クラウド・アプリケーションでは、非同期処理が重要な構成要素になります。また業務端末からのデータエントリ処理、夜間バッチといった定型ジョブの起動だけでなく、企業顧客や、消費者からの様々なイベントが高頻度にあがってくるようになるので、アプリケーション・アーキテクチャもイベント駆動型アーキテクチャ(EDA)に変わってくることになるでしょう。

アプリケーション・アーキテクチャがEDAになってくると、従来からあるロバストネス図では、機能不足のためモデルを記述するのが難しくなってきます。この問題を解決するために考案したのがメッセージフロー図です。

メッセージフロー図は、制御フローとデータフローを同じモデル内に記述するために考えたモデル図です。基本的にロバストネス図の発展形であり、宣言的、演繹的なモデルではなくて、実例ベースの帰納的なモデルです。このため、DSL駆動によってプログラムの生成を行うためのソースとして使用することは想定しておらず、ユースケースを実現(realization)するための補助線として使用するラフスケッチという運用になります。

今回の拡張

今回の関数型に関する一連の考察でデーターフローを新規に追加することにしました。

このデーターフローは、アプリケーション・モデルの実現モデルに入ります。

スライドで用いた図では、イベント→状態遷移→データフローのパスになっていますが、「EDAとオブジェクトと関数」で考えたように、実用的には状態遷移を飛ばしてイベント→EDA→データフローにしてしまう方式にしていこうと思います。

この場合、イベント処理エージェントやイベントコンシュマーの実装の中で、必要に応じて状態機械を使用することになります。また、現在OOADやOOPで用いられているDbCの事前条件、事後条件による状態遷移の記述を活用し、状態機械を間接的にモデル化していくアプローチも併用していくことになります。

イベント

従来からイベント・エンティティはSimpleModelingの中軸となる非常に重要なモデル要素です。永続的なビジネス・イベントはエンティティでもあるので、ドメイン・モデルとアプリケーションを接続するハブとして機能します。

今回の図には直接見えてきませんが、イベント・エンティティの実現方法として、「EDAとオブジェクトと関数」で説明したように、ちょっと実装よりになりますが、状態機械ではなくEDA的なアーキテクチャを導入するのが現実解ではないかと考えています。

何故このあたりを細かく考えているのかというと、メタモデルの精度がDSL駆動開発の基盤になるからです。イベント&EDA周りでどのようなDSLを定義していくのかは今後の課題ですが、クラウド・アプリケーションにおけるイベントの実現方式もかなり視界が開けてきました。

諸元

当日使用したスライドは以下のものです。(PDF出力ツールの関係で、当日は非表示にしたスライドも表示されています)

このスライド作成の様子は以下の記事になります。

回顧は以下の記事になります。

2012年3月28日水曜日

EDAとオブジェクトと関数

3月19日(月)に要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』を行いましたが、説明を端折ったところを中心にスライドの回顧をしています。

今回はセッションで説明したモデルの改良案として、EDA(Event-Driven Architecture)の導入を行ってみます。

元のスライド

オブジェクトと関数型の繋ぎのところを具体的に考えるために2つスライドを作成しました。

「イベント→データフローの流れ」は、OOPレベルの細かい処理の流れを示しています。

「ユースケースと関数」は、OOADの観点からユースケース・モデルからOOPを経て、FPに至る流れを示しています。

OOADを軸としたアプローチが検討対象にしているので、いずれも(協調→)状態機械→アクションというオブジェクト・モデルとして素直な連携を軸に考えています。

実際に図にして考えてみると、(協調→)状態機械→アクションという流れのモデリングはあまり便利そうに見えません。協調→状態機械のモデリングは難しいですし、仮にモデルをきちんと作ったとしても、モデルと実装の乖離が大きいため、実装が進むにつれモデルが朽ちていきそうです。

理論的には正しくても、便利でないものは使われないので、もう少し実装に近いモデルで再検討してみます。

EDA

クラウド・アプリケーションのアプリケーション・アーキテクチャでは、EDA(Event Driven Arachitecture)やEIP(Enterprise Integration Patterns)といったSOA、エンタープライズ系のアーキテクチャの技術が有効ではないかと考えています。

そこで、OOAD的な(協調→)状態機械→アクションではなくて、EDA上での実現を考えてみました。

イベント→データフローの流れ

スライド「イベント→データフローの流れ」の改良案です。

OOAD的には、イベント発生が状態機械に状態遷移を発生させ、状態機械のアクションによってプログラムの処理がキックされるというモデルになりますが、「状態機械→アクション」の部分をEDAに取り替えてみたのが以下の図です。


図に登場するEDAの構成要素は以下の通りです。

イベント
システム内で発生する事象
イベントプロデューサ
イベントを生成するエージェント
イベントチャネル
イベントプロデューサとイベントコンシュマーを結び付けるチャネル
イベント処理エージェント
イベントのルーティングを行うエージェント
イベントコンシュマー
イベントを受信するエージェント

イベントプロデューサがイベントを発生させると、イベントチャネルからイベント処理エージェントを経由してイベントコンシュマーにイベントが通知されます。

イベントコンシュマーは、元のOOADの図におけるアクションに対応します。ここで、イベントに対するアプリケーション・ロジックを記述します。

イベントコンシュマーの実装では、データフローと関数型でアプリケーション・ロジックの中核部分を記述し(データフローの実装技術)、イベントコンシュマー本体でエンティティの更新を行うという役割分担(オブジェクトと関数の連携(2))を採ることになります。

EDAの構成を採ることで、イベント発生とアプリケーション・ロジックを疎結合にし、アプリケーション・ロジックの追加や拡張を容易にします。複数のユースケース・コンテキストがドメイン・モデル上に重なってくるようなアプリケーションでは、ユースケース・コンテキスト(あるいはユースケース)毎にイベント・コンシュマーを用意することで、システムのモジュール化を促進します。

また、イベントチャネルを介在させることで、MOMやESBを使用した非同期処理での実装も可能になります。

実装

論理モデルとして、この構成でモデリングしておいても、設計/実装の段階ではイベントプロデューサからイベントコンシュマーを直接同期型で呼んでしまうという実現方法を選択することも可能です。

実装時のイメージとしては、右のようなものを想定しています。基本的な処理はイベントチャネルを介さず、同期型でイベントコンシュマーにイベントを通知し、処理結果を持ち帰ります。一方、それ以外の処理はイベントチャネル経由で非同期にイベントコンシュマーにイベントを通知して処理を行います。

ユースケースと関数

スライド「ユースケースと関数」の改良案です。

OOAD的な視点では、ユースケースから関数までの流れが重要になります。

ユースケースは自然言語で記述した物語ですが、システム開発につなげるためには、もう少しシステムよりのモデルを用意してユースケースから使用するような形に持ってくる必要があります。ここでは、タスクとサービスというモデル要素を用意しました。

タスクは、ユースケース・フローにおけるシステムの使用方法を定義したものです。システムが提供するサービスの使い方(パラメタなど)を定めます。

サービスはイベントプロデューサを呼出して(サービス自身がイベントプロデューサも可)、EDAのメカニズムの上で処理を進めます。ここから先は前節で説明した流れで処理が進んでいきます。(イベント処理エージェントは宣言的な定義で記述する事が多いと思われるので図ではOOPから外していますが、OOPやFPあるいはDSLによる実現が可能です。)

DSLによるモデル記述と実装への展開

EDA上でオブジェクト・モデルと関数型を連携させていく方法についてやや実装よりに細かく考えてきました。

このあたりは、g3フレームワークSimpleModelerで試行錯誤しながら色々と検討してきた点なのですが、ここでいっているような形では状態機械にはこだわらず、まずEDAベースでモデリングとフレームワークを構築するのがよさそうというのが最近の考えです。そのようなこともあり、今回ちょっと腰を据えて考えてみました。

今回考えたモデルをベースに、SimpleModelerとg3フレームワークでどのようにDSL化(モデルDSLとフレームワークDSL)していくのかというのが次の課題です。

諸元

当日使用したスライドは以下のものです。(PDF出力ツールの関係で、当日は非表示にしたスライドも表示されています)

このスライド作成の様子は以下の記事になります。

回顧は以下の記事になります。

2012年3月27日火曜日

オブジェクトの世界と関数の世界

3月19日(月)に要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』を行いましたが、説明を端折ったところを中心にスライドの回顧をしています。
「オブジェクトの世界と関数の世界」として用意した以下のスライドを説明します。


このスライドは元々、以下の2つの情報を表現するために用意していたのですが、いろいろ書き込んでいくうちに、OFADでのモデル変換の流れの側面が大きくなってしまいました。
  • オブジェクトの世界と関数の世界は併存するのが必然
  • アプリケーション・モデルの実装側はできるだけ関数型にしていく
このため、同じような情報を持つスライドをセッションでは2つ省略しましたが、逆に、このスライドでは、「OFADでのモデル変換の流れ」の説明になってしまい、肝心なことの説明ができなくなってしまったので、この記事で補足します。

オブジェクトの世界と関数の世界は併存するのが必然

オブジェクト指向技術は、元々はシミュレーション技術に根っこがあり、その最も重要な軸は、(経験則的な)人間の認知モデル、メンタルモデルをそのままモデルとして使用している点です。
このため、要求モデルを記述する際に、普通の人が把握できる範囲のモデルにおさまることが期待できます。さらにオブジェクト指向言語を使うことで、要求仕様から実装まで同じセマンティクス、インピーダンス・ミスマッチなしで一気通貫に記述できるというのがOOADの最大のアピールポイントです。(OOの重要なメリットである情報隠蔽による安全性やポリモーフィズムによる拡張性は、その次ぐらいに重要な項目といえます。)
たとえば、DDDのUbiquitous Languageで、用語集からドメイン・モデル、さらにOOPでの実装まで一気通貫に共通化できるのはこのような理論的な背景があるからですね。
一方、振舞いの記述、アルゴリズムの記述は、情報科学や数学のバックグラウンドを持つ関数型に一日の長があります。(関数型はモデルの種類の名称としては一般的ではないので、以下では数理モデルという用語を使うことにします。)
とはいえ、数理モデルは、以下の問題があるので汎用的な意味で要求モデルを記述するモデルに採用するのは困難です。
  • 問題を数理モデルで記述できるとは限らない
  • 数理モデルは情報科学や数学のスキルがないと作成できない
  • 数理モデルは情報科学や数学の素養がないと内容を理解できない
要求モデルとして数理モデルで記述できる範囲のモデル、顧客が数理モデルの素養がある、という条件がうまくハマった場合には、一気通貫に要求モデルから実装コードの生成まで持っていけるので、極めて強力です。
このため、大枠はオブジェクト・モデルで考え、サブシステムまたはモジュール単位で数理モデルで要求モデルを併用するようなアプローチが有効でしょう。
いずれにしても、数理モデル一本ということは現実的には不可能なので、オブジェクト・モデルは必須です。全体の大枠は清濁併せ呑むオブジェクト・モデル、ぴったりとハマったところは数理モデル、ハマらなかったところはオブジェクト・モデルで記述して併用・併存するのが現実解です。

アプリケーション・モデルの実装側はできるだけ関数型にしていく

要求モデルでは、大枠はオブジェクト・モデルが必須で、一部条件を満たしたケースで関数型/数理モデルを併用することになります。エンタープライズシステムの場合、現実的には当面関数型/数理モデルを使うことはほとんどないでしょう。つまり、事実上要求モデルはオブジェクト・モデルということになります。
しかし、オブジェクト・モデルを実装する過程の中で、関数型を使えるポイントが色々とあります。一つは関数型言語を使った関数型的なプログラミングですし、より抽象的なモデルとしてはデーターフローが有力です。
図中のオブジェクトの世界では、現実世界→ドメイン・モデルのラインとやりたい事の物語→ユースケース・モデル→協調→状態遷移→アクションのラインがありますが、前者のドメイン・モデル・ラインはオブジェクト・モデリングがよいでしょう。
一方、後者のユースケース・モデル・ラインは、オブジェクト指向的によい実装方法がないので、可能なかぎり関数型にしていくのが望ましいところです。たとえばデーターフローで記述できるところを見つけて、これをデータフローモデル→関数型またはDSLで記述していくというアプローチですね。この部分をいかに関数型の方向に倒していくのか、というのが今後のプログラミング技術やモデリング技術の方向性を見ていく上で重要な視点ではないかと思います。
この場合、関数型で記述したアルゴリズムが扱う事実の情報、いわゆるファクトモデルとして、オブジェクトのドメイン・モデルを使うのがよい組み合わせです。

省略したスライド

内容が重複するので省略したのは以下の2つのスライドです。

上側は「 オブジェクトと関数の連携(3)」として用意していた図です。オブジェクトと関数の接点として、当面はデータフローが現実解かな、という内容です。


下側は「Domain-Driven Design (DDD)」で説明したとおり、OOADの中でのDDDの位置付けを説明する内容です。

OOPは不要?

昨日たまたま某所で教えてもらったのですが、以下のような流れもあるみたいです。(注意:一年前の記事です)
情報科学やアルゴリズムの勉強にOOPは不要でFPのみでよい、というのは一理あります。
現状ではエンタープライズ向けにはOOPが支配的な言語パラダイムである(COBOLがあるので"新規開発では"という注釈つきかもしれません)、システム、サブシステム粒度のモデルの記述方式に何を用いるのか、といった点は置いておくとしても、情報システム構築の要求モデルにオブジェクト・モデルが必須である以上、長期的に見てもエンジニアのスキルとしては引き続きOOPは必要と思います。
とはいえ、OOPが圧倒的、絶対的という訳ではなく、条件付きで有効というように相対化していく流れを象徴する出来事であるのは確かです。

諸元

当日使用したスライドは以下のものです。(PDF出力ツールの関係で、当日は非表示にしたスライドも表示されています)
このスライド作成の様子は以下の記事になります。
回顧は以下の記事になります。

2012年3月26日月曜日

Domain-Driven Design (DDD)

3月19日(月)に要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』を行いましたが、説明を端折ったところを中心にスライドの回顧をしています。

「Domain-Driven Design (DDD)」として用意した以下のスライドを説明します。



セッションの全体構成は以下のようになっています。

  • 関数型プログラミング
  • Object Functional Programming (OFP)
  • Object Functional Analysis and Design (OFAD)
  • 応用

この中で4番目「応用」は、今OOADやクラウド・プラットフォームで話題となっている技術がOFADでどのような影響を受けそうなのかということを考えてみる趣旨のセクションです。

具体的に考えてみることで、OFADへのイメージがより明確になるかなと思ったのですが、残念ながら時間の関係で省略することになってしまいました。「応用」は33枚目からなのですが、50分だと30枚ぐらいが目安ですね。

上記スライドは、その「応用」の一つとしてDomain-Driven Design (DDD)を取り上げたものです。DDDはいうまでもなく、エリック・エヴァンス氏の以下の書籍によるドメイン層の設計技法です。

OOADの中での位置付け

まず、OOADの中でのDDDの位置付けについて確認しておきましょう。以下は、当日は他のスライドと内容がかぶるので非表示にしていたスライドです。


この図ではOOADを、大きくクラス図を中心としたドメイン・モデリングとユースケース/協調を中心としたアプリケーション・モデリングの2つに分けています。

さらに、ドメイン・モデリングは、ドメイン・モデルそのものを作成する業務モデリング&要求モデリングとドメイン・モデルの実装方法をモデル化するシステム・モデリング&設計の2つに分けることができますが、DDDがカバーするのは後者システム・モデリング&設計の所です。

セッションのテーマであるOFADでは、"関数型の所は主にユースケース、協調に関するモデリング技術と関係を持ってくるのでDDDは直接影響を受けないだろう"、というのがこのスライドのDDDに関する部分の趣旨です。

このスライドを使っていた場合は、以下のようなことを口頭で補足する感じです。

まず、ドメイン・モデルという観点では関数型はルール・モデルに影響を与える可能性があります。とはいえ、ルール・モデルは関数型をさらに発展させた論理型と対応させるべきモデルなので、関数型の段階の影響はそれほどないのではないかと思います。

それより、関数型言語で実現するDSLでルール・モデルを記述するようなアプローチはありそうなので、その点は継続して動向を見ていく必要はありそうです。

[参考]業務モデリングと要求モデリング

ちなみに前者の業務モデリング&要求モデリングは、以下のような本が役に立ちます。

(翻訳は分かる範囲で載せていますが、翻訳品質は未チェックです。今回調べてみて分かったのですが、翻訳の方は良い本でもすぐに絶版になってしまいますね。早く電子出版に移行して欲しいです。)

DDDによる設計と関数型言語

OFADによってDDDの位置付けが変わったり、DDDによってOFAD側に影響が出るようなことはなさそうですが、DDDの設計・実装技法と関数型言語の関係はまた別です。

最初のスライドに戻ると、DDDのパターンの中でいくつか関数型的な手法が見られるので、それをピックアップしています。

まず、重要なのは:

  • Declarative Design - Domain Specific Language
  • A Declarative Style of Design - Composite Specification
  • Declarative Style

という形で宣言的な設計スタイルを推奨していることです。宣言的にすることでDSLを適用できたり、仕様の合成もできるようになるわけですね。関数型と相性がよい設計スタイルです。手続き型的な手法はこういったことができずに、どうしても手作業が多くなってしまいます。

OFAD的に翻案すると「いかに、手続き型な部分を減らして、関数型的な部分(DSLを併用)を広げていくのか」、というのが今後のソフトウェア設計の軸になるのではないかと思います。

直接関数型を意識しているのは以下の項目。

  • Side-Effect-Free Functions
  • Closure of Functions

Context Mapは、複数のドメイン・モデルを併用するときの技法です。最近だとLensのような代数的/圏論的な手法も登場してきているので、こういった手法を適用できる可能性があります。

Value Objectは、Entityの内容を持ちまわったり、協調の中で発生する揮発性の情報を持ちまわるためのオブジェクトで、DTO的な用途のオブジェクトを、モデリング上の一級市民に格上げしたものです。定義的にはID(がモデル上の意味)を持たないオブジェクト、オブジェクトの同一性の比較がIDではなくて、格納されている値の比較で行われるオブジェクトということです。

DDDでは、Value Objectとしていますが、基本的な値を表現するオブジェクトと、XML的な構造を持ったオブジェクトは分けたほうがよいと思うので、ボクは前者をValue、後者をDocumentと使い分けています。

Value Objectは、不変オブジェクトであることが推奨されており、関数型の代数的データ型や永続データ構造(関数型言語の技術マップ)といった技法で実現するのがぴったりはまります。(とはいえ、性能問題やプログラミングの手間もあるのでDDDには「Special Cases: When to Allow Mutability」というノートも書いてあります。実際問題として、Value Objectを完全に不変オブジェクトにするとプログラミングの手間が大変ですので、プログラマの習性としては分かっていても可変オブジェクトにしてしまいがちです。そこで、不変オブジェクトとこれをビルドするためのBuilderを自動生成しようというのが拙作のSimpleModelerのアプローチです。)

DDDは、筋のよいOO設計、OOP実装技法の経験則という側面が大きいと思いますが、DDDが出版された2003年当時OOP/OOAD全盛の中でも、関数型(的なアプローチ)の存在感はなかなか大きい感じがします。

関数型言語が実用化された今となっては、できるだけDSL&関数型的な手法で実現する範囲を大きくしていくことが、新しい経験則になっていくのではないかと思います。

具体的には、以下に書いたようなことですね。

諸元

当日使用したスライドは以下のものです。(PDF出力ツールの関係で、当日は非表示にしたスライドも表示されています)

このスライド作成の様子は以下の記事になります。

回顧は以下の記事になります。

2012年3月23日金曜日

DSL指向プログラミング

3月19日(月)に要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』を行いましたが、説明を端折ったところを中心にスライドの回顧をしていきたいと思います。

「アプリケーションの階層と役割」として用意した以下のスライドを説明します。

なんだかんだいっても関数型言語はやはり難しいし、さらにオブジェクトと合体したオブジェクト関数型言語となると、使うのがさらに難しいというのが実際のところでしょう。

ScalaはBetter Javaとして使う道もあるので、それに徹すれば普通のOOプログラマにとって、習得はそれほど難しくはありませんが、プロジェクトで使用する第一プログラミング言語を選定する際に、Better Javaという理由だけでプログラミング言語を定番言語から新興言語にスイッチするのはリスクが大きすぎます。

また、現状Scalaの入門/解説の書籍、記事の多くは「関数型」の華やかなところにスポットライトを当てているので、Better Javaのための情報はなかなか入手することができません。この点でも、Better Java効果を期待してScalaを導入するのは難しくなっています。(拙作の入門書はBetter Javaにフォーカスしています、と宣伝。Amazonでは評判は悪いみたいですが(笑)。)

このため、OFADの以前に、「OOPもうまく使いこなせていないのに、そもそも関数型言語が流行るの?」という疑問が出てくることが想定されるので、それに対する解として用意したのがこのスライドです。

従来から、アプリケーションをスクラッチで全部書くというのは稀で、普通は何らかのフレームワークを使用し、その土台の上にビジネス・ロジックを構築していきます。SIerや大手のソフトウェアハウスは、自分の事業ドメインに特化したフレームワークを持っているのが普通ですし、そうでない場合もOSSのフレームワークを使用するのが普通です。

データ設計は上流で行うとして、プログラマの仕事は用意されたデータモデルを操作してフレームワーク上にビジネス・ロジックやUI操作を宣言的あるいは手続き的に記述していくことに費やされます。この場合、OOPの知識はそれほど必要ではなく、フレームワークのしきたりの理解のほうがより重要になってきます。XYZという名前のフレームワークがあったとすると、OOプログラミングというより、XYZプログラミングというようなプログラミングスタイルになってくるわけです。

このXYZプログラミングをJavaで行うと、フレームワークとJavaの間にボイラープレートと呼ばれる繋ぎコードが沢山必要になって、生産性が今ひとつ上がりません。また、以前好まれていた宣言的な記述をXMLで行う手法は、(1)XMLの編集が大変、(2)XMLとJavaの繋ぎのところで静的型付けの効果がなくなる(プログラムが脆弱になる、デバッグが大変)、という問題があるため最近は回避する方向にあります。最近主流なのはアノテーションによる方法ですが、記述力が限定的なので、万能というわけではありません。

この問題の解として今後主流になると予想されるのが関数型言語によるDSLです。関数型言語は言語の自己拡張性によって内部DSLの実現に向いていますし、Scalaは各種文法糖衣で内部DSLの実現をさらに容易にしています。

DSLは(1)大きくプログラミング言語に埋め込む内部DSLと、(2)独立した専用言語の外部DSLの2種類に分けることができますが、前者はさらに(1a)モデルを記述するDSLと(1b)フレームワークのAPIとしてのDSLに分けることができます。

この(1b)フレームワークのAPIとしてのDSLが、今後最も注目される技術の一つと思います。今回のセッションでも、Hadoop向けDSLであるSpark、Scaldingと、ESB(Enterprise Service Bus)であるApache Camel&EIP向けのScala DSLを紹介しました。また、丸山先生の「エンタープライズ・クラウドの現在」ではScala DSLベースのWebアプリケーションフレームワークPlay 2.0が取り上げられていました。

DSL指向フレームワークとDSL指向プログラミング

図では、アプリケーション層、DSL層、フレームワーク層の3層でのアプリケーションアーキテクチャを示しています。(今考えてみると、アプリケーション層は、ビジネス・ロジック層あるいはアプリケーション・ロジック層の方がよかったような気がします。)

このDSL層とフレームワーク層は、極少数のエンジニアが作成することになるので、OOPでも関数型でも、高度な技術を持っていることを前提としてかまいません。逆に、このフレームワーク&DSLが、いかに高機能かつ使いやすいものになるのかが、SIerやソフトウェアハウスの核心的競争力になるので、関数型でも何でもいかに高度な技術であっても使えるものはきちんと使う、という事になるはずです。つまり、この用途にオブジェクト関数型言語が利用されるようになるのは、必然といってよいでしょう。

もちろん、フレームワーク層で使っている技術の難しさがアプリケーション層に出てしまってはいけません。

そこで登場するのがDSLです。Spark、Scalding、Apache Camel&EIP向けのScala DSLの例を見ても分かるとおり、下層のフレームワーク、ミドルウェアの難しさを完全に隠蔽して、プログラマには論理的なデータフローに見せることに成功しています。これまでは、これをJavaで実現しようとしたものの前述したような問題が出てきて今ひとつうまくいかなかったわけです。

アプリケーション層については、OOPや関数型の知識があればより効率的なプログラミングが可能になるのは言うまでもありませんが、DSLでビジネス・ロジックを記述していくだけであれば、OOPや関数型のスキルがあまりなくても大丈夫です。Scala DSLの場合は、DSLで記述するところはScalaで書いて、そこから先はJavaを使うという道もあるので、さらに導入の閾値は下がります。

アプリケーションはDSLつまり専用言語を用いたプログラミングを行うことになります。このように、従来的なAPIではなくて、専用言語であるDSLでの使用を前提としたフレームワークを今後、このブログではDSL指向フレームワークと呼ぶことにします。ある意味、前述したフレームワーク指向のXYZプログラミングをより進化させたものがDSL指向フレームワークによるプログラミングとうことになります。これはDSL指向プログラミングと呼ぶことにしましょう。

ボクが以前から取り組んでいるDSL駆動開発は、モデルをDSLで記述するのに加えて、DSL指向フレームワークによるDSLプログラミングも加えた、DSLを柱とした開発が、今後のソフトウェア開発の主軸になるのではないかという提案でもあります。このDSL駆動開発によって、モデリングとプログラミングが一体になる/できるというのがこのブログのタイトルである「Modegramming」の意図です。

関数型言語やオブジェクト関数型言語は、このDSL駆動開発という文脈で考えてみると、今後のソフトウェア開発における重要性がみえてきます。関数型言語は、メニーコアやクラウド・コンピューティングにおける並列、分散への対応でも必須の技術になりつつありますが、これに加えてDSL駆動という軸も合わせて視野に入れておく必要があるでしょう。ソフトウェア開発へのインパクトではDSLの方が、より広範囲で深いのではないかと思います。

諸元

当日使用したスライドは以下のものです。(PDF出力ツールの関係で、当日は非表示にしたスライドも表示されています)

このスライド作成の様子は以下の記事になります。

2012年3月22日木曜日

MindmapModeling「KDDIがO2O事業を検討、無印良品やファミマが参加し実証実験」

3月17日(土)に横浜モデリング勉強会を行いました。また、会場には(株)アットウェア様の会議室をお借りしました。参加された皆さん、アットウェア様、どうもありがとうございました。

この勉強会で、浅海が作成したモデルを紹介します。モデルはMindmapModelingの手法で作成しました。(勉強会で使用したチュートリアル)

今回から、モデル駆動開発をテーマにすることになりました。(1)MindmapModelingでドメイン・モデルの作成、(2)SimpleModelerでモデル駆動開発、の二回構成になります。今までは、雑誌記事などで未知の領域のドメイン・モデルを自然言語からの情報で作成することに主眼を置いていましたが、今回から実際に動作するドメイン・モデルを作成することが主眼となります。

モデリングの対象は、Internet Watch誌の記事「KDDIがO2O事業を検討、無印良品やファミマが参加し実証実験」です。この記事を素材にO2O向けのサイトのモデリングをテーマにしました。この記事は参考情報という位置付けで、O2O向けサイトの機能は各自で好きなように設定できるということにしています。

物語を作る

通常MindmapModelingの最初の作業は単語の抜き出しになるのですが、今回はある程度知識のある分野であり、システムを作るのが目的なので作るシステムの輪郭を早めに確定させるためには物語の作成から始めました。

「コンビニでクーポンで安く商品を買う」という一般客視点の物語を作りながら、この物語に登場する単語をドメイン・モデルの用語/エンティティとして抜き出しながら、登場人物、道具、出来事を中心にMindmapModelingの定めた分類に従って仕分けしていきます。

この結果、できた最初のマインドマップが以下のものです。(図をクリックすると拡大します。)

洗練

次の段階では、実装を想定して、登場人物、道具、出来事の洗練を行います。

通常のMindmapModelingでも、実装を意識したモデリングを行いますが、今回は実際にモデル駆動開発を行うので、モデリング中の意識もより具体的になり、実装よりのモデルになっています。

未知の領域のドメインモデルを作るときに、実装を意識しすぎるとXXXの販売システム、とかXXXの在庫管理システム、といったように自分の既知のシステムになってしまうことが多々あるので、純粋にドメイン・モデリングの練習の場合は、実装を意識しすぎないという方針にしています。(実装に落とし込めるモデルだけど、意識はしすぎないということです。)

そのため、今回のようにMindmapModelingでの実装前提のモデリングはかなり新鮮でした。実装を考えながらのモデリングは、思考が具体的になって面白いですね。

以上の作業を行った結果のマインドマップは以下のものです。

道具の構造を洗練させると同時に、システムの振舞いの中心となる出来事を整備しました。物語と一貫性を持たせながら、道具や出来事といったエンティティに肉付けしていくのがポイントです。

メタモデルの拡張

SimpleModelerの運用では、MindmapModelingでラフなモデルを作成した後、専用Scala DSLに変換して、これを編集してより詳細な実装レベルのモデルに洗練させることを想定しています。

ということで、MindmapModelingではブレインストーミング的な用途を想定した演劇のメタファの範囲でメタモデルを定義しています。

ただ、演習などではMinmapModelingでは最後までモデリングできた方がよいので、実装よりの拡張をするのがよいかなと考えています。そういう意図で、今回はモデルを作りながらBOI構造枝、「サービス」、「要約」とエンティティに対する構造枝「参照」を加えてみました。この構造枝を追加すると、"演劇のメタファ"からは少し外れてきますが、致し方のないところです。

次回

次回の勉強会は4月中旬に行う予定です。MindmapModel(各自で作ったもの、または浅海が用意したもの)を使ってモデル駆動開発の実践を行います。実装プラットフォームはPlay 2.0を予定しています。

SimpleModelerによるプログラムの自動生成は「自動コーディング」、「自動DDD」をキーワードにしています。

プログラムを生成するというより、ドメイン・ライブラリをプログラマに変わって自動的にコーディングしてくれるという意図です。また、自動的にコーディングされるコードはDDDに沿ったクラス構造(Value Object, Factory, Repositoryなど)になります。

アプリケーション・モデルはスタブの生成までで、自動生成されたドメイン・ライブラリを使用して機能を実装していくことになります。

モデル駆動開発に興味のある方はぜひご参加ください。

2012年3月21日水曜日

Object-Functional Analysis and Designまとめ

3月19日(月)に要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』を行いました。要求開発アライアンスの皆さん、多数お集まりいただきどうもありがとうございました。

当日使用したスライドは以下にあげておきました。(PDF出力ツールの関係で、当日は非表示にしたスライドも表示されています)

このスライド作成の様子は以下の記事になります。

要求開発アライアンス向けのセッションなので、OOADとOFPの関係というテーマ設定を行って考えて来たわけですが、現段階の材料ではびしっと決まらない…ですね。

Scala DSLを用いてデータフローを記述し、HadoopやESB上で動作させる技術が登場してきており、この技術を軸にOOADと関数型を組み合わせていくというのが当面の現実解として有効と考えられるので、セッションではこれにフォーカスした内容にしました。

OOAD的には一番普通のアプローチである協調(collaboration)を状態遷移経由でデータフローに結び付ける方法を中心に考えましたが、できあがったものを見るとあまり便利には見えないので、もう一段ブレークスルーが必要と思います。協調をイベント+データフローの集り+αで記述する方がエンタープライズ向けにはフィットしそうです。

プロセス計算

協調を記述する形式的な計算モデルの候補としてはプロセス計算が最右翼です。いわゆる形式手法のアプローチということになりますが、最終的にはこの方向とは思うもののエンタープライズ向けには現時点では研究段階というのがボクの認識なので、セッションではその点を軽く触れるにとどめました。

SOAの中核技術であるBPMN(のサブセット)が形式的だったと記憶しているのですが、Webを検索した感触だと、BPMNの説明で形式手法を全面に出している感じではなく、事実上はそういう使われ方はされていないのかなという印象です。

たとえば、InfoQでBPMを検索するとトップ記事が2010年の以下のものになります。

(サブセットが)形式手法であるというような記述も特に見られません。形式手法という観点では他の記事も大体似たような感じです。

BPMN&formalで検索すると以下のような記事(2008年)がヒットします。だいたい少し前の研究論文が頭に並んでくるのでSOA時代に一定の研究が進められていたのかな、という印象です。

データフローにフォーカスしたかったので、このあたりは省略したのですが、懇親会での材料投入にもなったと思われるので、入れておいたほうがよかったのかも、という気もします。

今考えてみると、BPMをSOAスケールで使うのではなく、局所的なデータフローの実現技術として使う方法もあるかもしれません。

関数型プログラミング

関数型プログラミングの最大の効用は、開発効率が高いことと思いますが、これだけだと実装技術として優れているということなので、OOAD的な開発手法との接点が出てきません。OOADで設計図を書いて、実装でScalaを使えば、という話になります。

そこで、セッションでは関数型プログラミングがシステム分析、設計に本質的に与える影響とは、という観点でまとめています。関数型プログラミングの最大の効用を省略しているので、念のためここに記しておきます。

そういう意味での、最大の効用は計算機科学、数学の成果物が、ちょっとした工夫でプログラミングの部品として活用できる道筋ができたことだと思います。直近では、並行プログラミングでそういった成果が活用できるようになるでしょう。

とはいえ、これは長期的スパンで漢方薬のようにじわじわ効いてくる効用なので、直近の実利としては説明しづらいのが難点です。

DSL

関数型言語の用途の一つにDSLがあります。関数型言語の自己拡張性というか言語を自然に拡張していける能力が内部DSLの実現に有効です。また、パーサコンビネーターといったメカニズムで外部DSLの実現も容易です。さらに、Scalaでは、さまざまな文法糖衣メカニズムによって、内部DSLの実現がより用意になっています。(こういった言語機能のScalabilityがScalaという名前の意図ですね。)

セッションでは、Spark、Scalding、Apache Camel Scala DSLの3つのデータフローDSLを紹介しました。それぞれHadoopやESBを利用するためのDSLとなっています。

モデリングとの接点で関数型言語によって実現されている実用技術を探すとこのあたりがアンテナに引っかかってきたわけです。

ここでは、モデリングとの接点なのでデータフローになりましたが、丸山先生の「エンタープライズ・クラウドの現在」で取り上げられていたPlay 2.0を始めScala DSLにはこの他にも様々な応用例が出てきています。

アプリケーションを構成する要素技術、ドメインごとにモデルをDSLで記述し、DSLの成果物を組合わせてアプリケーションを構築する...。以下の図はRelaxerの時代から使っているものですが、結局はこれかなという結論です。

この図を書いた当時はコンポーネントを生成して、これをスクリプトで結び付けるというアプローチを考えていたのですが、コンポーネント結合のDSLを使う手法が有力だと分かってきたことと、DI、AOPに加えてトレイト、型クラスといった新しい言語機能が加わってきたこともあって、実用化のハードルが低くなってきたと思います。

OFADより、DSL駆動というアプローチのほうが、当面の技術革新の方向にあっていると改めて感じました。

今後の方向性

懇親会で、萩原さんに最新の(クラウドスケールの)データベースの技術動向のお話を伺って非常に参考になりました。

一日寝かせて今朝思いついたのが以下のようなアイデア。(twitterより採録)

  • クラウドスケールの次世代DBの技術革新が、アプリ側に影響を与える一つはキー設計のところかな。GAEやAzureで議論になったキー設計はクラウド的に本質的。データフローを考えると、タプルの任意の冪集合をその段のキーとして使うようになるので、そのあたりも含めて。
  • SQLだと演算子が事前に決まっているけど、データフローは演算子を任意に追加できる。データフローを流れるデータと演算子の組について結合、可換をアプリレベルでどう記述していくのか、データベースやデータフローエンジンにどう情報を渡していくのかも論点。
  • ドメインモデル作成時に必要なこと(1)キー設計。ユースケースでドメインモデルのアクセスパターンを絞り込み、データ操作の局所性を抽出。同じ演算が適用されるデータが同じノード上に配置されやすいキーまたは複合インデックスを設計に織り込んでいく
  • ドメインモデル作成時に必要なこと(2)演算子設計。ドメインモデルでエンティティに加えて、データーフローに必要な代数的データ型(←エンティティをマップした物+中間データ)と演算子を定義。このために、ユースケースでデーターフローと演算子の抽出を行う。
  • エンティティに対する代数的データ型+演算子の組は、エンティティ操作に基本的に必要なものはドメイン・モデル、応用に特化したものはアプリケーション・モデル側で定義しておいて、後者はDCI的なメカニズムで後付けて編み込めるようにしておく。実装時には型クラスを使う感じ。

モデリングという方向では、ドメイン・モデル、ユースケース・モデル段階でこういった切り口でのモデリングを行うことが有効そうです。このあたりを今後の課題として考えていきたいと思っています。

2012年3月19日月曜日

OFADの要素技術/関連技術

要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』で使用するスライドについて背景説明を行っています。

今回は背景説明第14弾として、「OFADの要素技術/関連技術」のスライドを紹介します。



今回のスライドでは、関数型言語の実用化によるOOADへのインパクトという観点からOFADという名前にしていますが、実際のところは関数型言語以外のところでさまざまな技術革新が行われており、これらの要素を取り込んだ総合的な開発方法論になっていなければなりません。

スライドは、そういう観点でボクが意識している項目を上げたものです。OOAD、OFP(Object Functional Programming)は前提として、それ以外にクラウド・コンピューティング、アジャイル開発、UCD/UX(User-Centered Design, User Exprience)の3つがあります。

OOAD、OFPはセッション内の中心の話題ですが、それ以外の項目はクラウド・コンピューティングのアーキテクチャパターンについて触れることを除いてはセッションのスコープ外なので、ここで簡単に説明します。

クラウド・コンピューティング

関数型言語に対するニーズの高まりはクラウド・コンピューティングの興隆が大きな誘引になっています。

クラウド・コンピューティングによって、非同期、並列/並行、分散をアプリケーションが正面から取り扱う必要が出てきます。このニーズに対するプログラミング言語での解は関数型言語になるのではないかと思います。

ちょうと、GUIやWebでインタラクティブなシステム設計が必要となったことが、OOPの本格的な普及の誘引になったことに似ています。

セッションでは、クラウド・コンピューティングそのものには直接触れませんが、非同期、並列/並行、分散に対する現時点での関数型言語での現実解を中心に見ていく予定です。

アプリケーション・アーキテクチャ

クラウド・コンピューティングについては、前回ご紹介したModeling Forum 2009のDSL駆動によるクラウド・アプリケーション開発や、JJUG CCC 2010 Fallのクラウド・アプリケーション・アーキテクチャがボクの基本的な考え方になります。

この観点の上で、関数型言語とCQRS(Command Query Responsibility Segregation)、EDA(Event-Driven Architecture)、EIP(Enterprise Integration Patterns)といったアプリケーション・アーキテクチャの関係についても簡単に考察する予定です。

アジャイル開発

アジャイル開発は大きく(1)プロジェクト運営、(2)開発エコシステムというジャンルに分けることができると思います。

(1)のプロジェクト運営は人間系の話であり、開発方法論とは直交していると考えていて、OFADでは特別な考慮はしていません。高頻度の繰り返し開発が必須という点を押さえておけば大丈夫と考えています。

(2)の開発エコシステムは、CI(Continuous Integration)やTDD(Test-Driven Development)/BDD(Bheavior Driven Development)といった一連の開発・ビルド・テストサイクルを構築する開発ツール群によるエコシステムを指しています。これも、基本的には開発方法論とは直交していると考えています。

ただし、重要なポイントとして、(1)開発技法がより「コード中心主義」に移行してくる、(2)上流と下流で開発を分けるのではなく、同じエンジニアが要求定義から実装までセル方式的に行う、といった点は開発方法論でも取り込んでいく必要があります。

ソースコードを中心に、同じエンジニアが要求定義から実装まで高頻度に回すという運用イメージです。

このような運用では、テキストDSLによるコード生成をうまく利用しながら、必要部分のみ手作りで作っていくような開発スタイルになるのではないかと考えています。実は、このブログの名前である「Modegramming Style」はそのような開発スタイルを意味しています。

テキストDSLによるコード生成となると、関数型言語です。特にテキストDSL向けにチューニングされた文法を持つScalaが最有力と思います。

アジャイル開発と関数型言語は、基本的には直交した技術ですが、こういう感じで連携していくのかなと考えています。

UCD/UX

UCD(User Centered Design)/UX(User Exprience)は、(1)一般消費者向けの外部デザインを中心とした要求仕様、(2)Ajaxやスマート・デバイスなどのリッチなGUI向けのUIデザイン(+要求仕様)、という2つの側面があるのかなと考えています。

(1)については、従来からエンタープライズ向けの開発方法論で用いられているビジネス・モデリングとは別の、新しい応用に対するモデリング技法と考えています。そうすると、OOADの中核モデルであるシステム・ユースケース(以下ユースケース)の外側にあり、UCD/UXの成果物をシステム化する段階でユースケースに落としこんでいくというアプローチで対応することができそうです。

(2)については、従来のユースケースと部分的に技術がかぶってくるので、役割分担を考えていかなくてはなりません。利用者とのインタラクションはUXを用い、システム側はエッセンシャル・ユースケースに徹するという分担が考えられます。

エンタープライズ向けには、ビジネス・モデリングの段階で、UCD/UXの技法を用いて要求仕様の精度を上げるというアプローチも考えられます。この場合は、ビジネス・モデリングの成果物を従来どおりユースケースにに落としこんでいけばよいでしょう。

2012年3月16日金曜日

DSL駆動によるクラウド・アプリケーション開発

要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』で使用するスライドについて背景説明を行っています。

今回は背景説明第13弾として、「DSL駆動によるクラウド・アプリケーション開発」のスライドを紹介します。

色々と過去に作ったスライドを調べていたら、2009年のModeling Forum 2009向けに、今回のOFADの基盤技術でもあるクラウド・アプリケーションでのOOADによるモデリングとDSL適用について書いたものが見つかったのでご紹介します。ちょっと古いですが、今まで書いた中でこれが内容が一番まとまっています。(この後に作ったのは、セッション時間に収める目的で内容を省略したものが多くなるので。)

なぜかSlideShareに上げ忘れていたのでアップロードしました。

今回OFADについて考えていますが、クラウド・アプリケーションのアーキテクチャ、OOADによるモデリング、DSL駆動開発の適用方法についての認識は基本的にはこのスライドの当時と変わっていません。セッションでは、時間の関係でこのあたりの内容はあまり深く取り扱えないので、さらっと目を通していてもらえると、より突っ込んだ議論ができると思います。

このスライドを書いた時からの差分は以下のような感じです。

この他に、業界全体としてはHadoopによる分散並列処理、NoSQL(分散KVS、文書DB)といった技術が興隆してきました。先端応用での興味はストリーミングに移ってきています。また、アジャイル開発が大きなムーブメントになってきました。

クラウドアプリケーションは、サーバーサイドサービスとスマートデバイスの組合せで構成されますが、このクラウドアプリケーションの開発をDSL駆動で効率化したいというのが基本的な動機です。この目的で、いろいろな取組みをしてきました。

サーバーサイド側フレームワーク(g3)、スマートデバイス側フレームワーク(g4)と両プラットフォーム向けへのモデル生成(SimpleModeler)を実装してみて、ある程度クラウド・アプリケーション実装技術の把握ができたと思います。また、g3とSimpleModelerはScalaで開発しており、DSLと関数型プログラミングについて知見を得ることができました。

以上のような技術、情報をベースに改めて今風の開発方法論について考えてみたのが、今回のセッションの内容となります。

2012年3月15日木曜日

関数型とデータフロー(4)

要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』で使用するスライドについて背景説明を行っています。

今回は背景説明第12弾として、「関数型とデータフロー(4)」として用意した以下の図を説明します。

前回はモナドを結合してデータフローを実現する方法を説明しました。

これが一般的なモナドの使い方ですが、モナドを部品として使いまわそうとすると、若干問題が出てきます。

モナドの合成

前回使用したOptionモナドを使ったデータフローの配線です。再利用のためにp5m10という名前をつけました。

val p5m10 = (_: Int).some >>= plus5o >>= mul10o

以下のように動作します。

scala> 3 |> p5m10
res64: Option[Int] = Some(80)

ここで問題なのが、p5m10を合成して新しいデータフローを作ることが難しいことです。試してみると以下のようになりますが、エラーメッセージから分かるとおりp5m10の入力型と出力型が合っていないのが原因です。

scala> p5m10 >>> p5m10
<console>:17: error: type mismatch;
 found   : Int => Option[Int]
 required: Option[Int] => ?
              p5m10 >>> p5m10
                        ^

scala> p5m10 >>= p5m10
<console>:17: error: type mismatch;
 found   : Int => Option[Int]
 required: Option[Int] => Int => ?
              p5m10 >>= p5m10
                        ^

関数型とデータフロー(1)関数型とデータフロー(2)で説明したとおり、関数の場合は以下のように「>>>」などを用いて関数の合成を行うことで新しい関数を生成することができます。このことによって、関数をデータフローの部品として使用することが容易になります。

scala> val p5m10 = plus5 >>> mul10
p5m10: Int => Int = <function1>
scala> 3 |> p5m10
res0: Int = 80

モナドをデータフローの部品として使用することを考えると関数の合成と同様の使い勝手で利用できるモナドの合成が必要になってきます。

kleisli

モナドの合成で登場するのがクライスリ圏(kleisli category)です。クライスリ圏については以下のページが参考になりますが、理論的にはかなり難解でボクもきちんとは把握できていません。今の所、モナドを合成するにはクライスリ圏に持ち上げる、というように理解しています。

ただ、プログラミングのイディオムとしては慣れてしまえばそれほど難しくはありません。簡単に言うと、モナドを合成するためにKleisliという入れ物を用いる、ということです。プログラミングイディオムとしてのKleisliは以下のページが参考になります。

そこで、図に登場する以下の式です。

ScalazはKleisliのための一連の機能を提供しています。☆はモナドのbind演算に用いる関数をKleisliに入れる演算を行う関数です。

val plus5o = (a: Int) => (a != 5).option(a + 5)
val mul10o = (a: Int) => (a % 10 != 0).option(a * 10)
val plus5ok = ☆(plus5o)
val mul10ok = ☆(mul10o)

☆はkleisli関数の別名なので、以下のように書くこともできます。

val plus5ok = kleisli(plus5o)
val mul10ok = kleisli(mul10o)

☆(plus5o)によってKleisli化されたplus5oが、☆(mul10o)によってKleisli化されたmul10oが定義されます。それぞれにplus5ok、mul10okという名前をつけています。

関数plus5okとmul10okはそれぞれ以下のように動作します。Int型を引数にとってOption[Int]型を返します。

scala> 3 |> plus5ok
res3: Option[Int] = Some(8)

scala> 3 |> mul10ok
res4: Option[Int] = Some(30)

plus5okとmul10okは、いずれもKleisli化されている(Kleisliの容器に入っている)ので、以下のように演算子>=>で合成することができます。合成して作成した新しいモナド演算を行う関数にp5m10okという名前をつけます。

scala> val p5m10ok = plus5ok >=> mul10ok
p5m10ok: scalaz.Kleisli[Option,Int,Int] = scalaz.Kleislis$$anon$1@6a2a9a0e

以下のように期待通りに動作しました。

scala> 3 |> p5m10ok
res5: Option[Int] = Some(80)

Kleisli化されたOptionモナドの動作

Kleisli化されたOptionモナドを使ったデータフローの配線は以下のものになります。

plus5ok >=> mul10ok

Int型の値を受け取り、これをplus5ok関数、mul10ok関数で合成された関数に流していきます。概念的には、図にあるようにKleisliの箱の中にあるOptionの箱の中を左から右へデータが流れていきます。

以下ではそれぞれのパイプラインの動作についてみていきましょう。パイプライン内の動作は基本的に前回のものと同様です。

正常動作

データフローにSome(3)を流すと、データフローの計算は成功し、計算結果としてSome(80)が出力されます。

plus5oでエラー

データフローにSome(5)を流すと、データフローの計算は失敗し、計算結果としてNoneが出力されます。

5はplus5oでエラー判定されるため、plus5oから失敗側のパイプラインが動作します。

mul10oでエラー

データフローにSome(15)を流すと、データフローの計算は失敗し、計算結果としてNoneが出力されます。

15はplus5oでは正常な値なので、plus5oの結果は20となりますが、この20がmul10oでは10で割り切れるためエラー判定され、mul10oから失敗側のパイプラインが動作します。

ノート

「関数型とデータフロー」というタイトルで、関数とモナドを使ったデータフロー構築手法についてみてきました。

関数、モナドという軸と呼出し、合成という軸があります。それぞれの軸の組合せによる、関数/モナドの呼出しと合成の見取り図を表にしてみました。








関数/モナドの呼出しと合成
呼出し合成
関数g(f(x))f >>> g
モナドx.some >>= f >>= g☆(f) >=> ☆(g) |

簡単な応用であれば左側の「呼出し」で十分なのですが、データフロー的な利用方法を目指して部品化、再利用を考えると右側の「合成」が必要になってきます。

関数の合成は、ScalaでもcomposeメソッドやandThenメソッドとして実現されていますが、一連の記事ではScalazのArrowの方を利用してみました。(ScalazではArrowというメカニズムの上で関数合成を可能にしています。Arrowは圏論における射(arrow, morphism)の実現なので、適切に圏を設定すれば関数以外の射の合成にも適用できます。)

モナドの合成についてはScalaの標準機能にはないのでScalazのKleisliを利用する必要があります。

2012年3月14日水曜日

関数型とデータフロー(3)

要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』で使用するスライドについて背景説明を行っています。

今回は背景説明第11弾として、「関数型とデータフロー(3)」として用意した以下の図を説明します。



モナド

今回はモナドを使って、データフローのパイプラインの振舞いをよりリッチなものにしてみます。

今回使用するのは、成功パイプラインと失敗パイプラインの2つのパイプラインを持つモナドであるOptionモナドです。正常系の演算は成功パイプライン上で行われます。Optionモナドの異常系は、エラー発生時点で即失敗パイプラインに移行し、そのままデータフローを終了させます。エラー情報の通知もありません。

以下ではまずOptionモナドで使用する関数オブジェクトを説明した後、Optionモナドの動作について説明します。

モナドで使用する関数オブジェクト

図ではモナドで使用する関数plus5oとmul10oを定義しています。どちらも、Int型を引数に取って、Option[Int]を返す関数です。Scalazを用いて簡潔に記述しています。

plus5o

関数plus5oの定義は以下の通りです。

val plus5o = (a: Int) => (a != 5).option(a + 5)

前回、前々回に使用した関数plus5のOptionモナド版です。関数plus5では、引数にInt値を取り、返却値もInt値でしたが、モナド版のplus5oでは、引数にInt値を取るのは同じですが、返却値はOption[Int]となり、本来の返却値をOptionモナドに包んで返します。

Optionモナドを返すので、関数が成功したのか失敗したのかをOptionのSomeまたはNoneとして通知できるようになりました。引数が5の場合は失敗としてNone、それ以外は成功で引数に5を加えた値をSomeに包んで返します。

このようにOptionモナドを返す関数(正確には型Tを取りOption[T]を返す関数)は、Optionモナドのbind演算(ScalaではflatMapメソッド、Scalazでは>>=メソッド)に適用することができます。

さらに汎用的にいうと、型Tを取りモナドM[T]を返す関数はモナドM[T]のbind演算に適用することができます。今回の例はOptionモナドですが、他にもListモナド、EitherモナドなどScalaでは多数のモナドが用意されています。

普通にScala的に書くと以下のようになります。

val plus5o = (a: Int) => if (a != 5) Some(a + 5) else None
mul10o

関数mul10oの定義は以下の通りです。

val mul10o = (a: Int) => (a % 10 != 0).option(a * 10)

引数が10で割り切れる場合は失敗としてNone、それ以外は成功で引数を10倍した値をSomeに包んで返します。

普通にScala的に書くと以下のようになります。

val mul10o = (a: Int) => if (a % 10 != 0) Some(a * 10) else None
参考

Scala文法上の参考情報です。

関数オブジェクトplus5oを関数リテラルを用いて記述しましたが、これを普通の書き方で関数として定義すると以下のようになります。

def plus5od(a: Int): Option[Int] = {
  if (a != 5) Some(a + 5)
  else None
}

この関数を関数オブジェクトにするには、もう一段以下の処理を行います。

val plus5o = plus5od _

Optionモナドの動作

Optionモナドを使ったデータフローの配線は以下のものになります。

(_: Int).some >>= plus5o >>= mul10o

Int型の値を受け取りSomeに包んで、plus5o関数、mul10o関数に流していきます。概念的には、図にあるようにOptionの箱の中を左から右へデータが流れていきます。

前回、前々回のデータフローと違うのは正常系のパイプラインと異常系のパイプラインの2系統のパイプラインが用意されていることです。

以下ではそれぞれのパイプラインの動作についてみていきましょう。

正常動作

データフローにSome(3)を流すと、データフローの計算は成功し、計算結果としてSome(80)が出力されます。

plus5oでエラー

データフローにSome(5)を流すと、データフローの計算は失敗し、計算結果としてNoneが出力されます。

5はplus5oでエラー判定されるため、plus5oから失敗側のパイプラインが動作します。

mul10oでエラー

データフローにSome(15)を流すと、データフローの計算は失敗し、計算結果としてNoneが出力されます。

15はplus5oでは正常な値なので、plus5oの結果は20となりますが、この20がmul10oでは10で割り切れるためエラー判定され、mul10oから失敗側のパイプラインが動作します。

ノート

以上で説明したようにモナドを使うことで、データフローを構成するパイプラインに特殊な振舞いを付加することができます。

ここでは最も単純な(しかし強力なのでScalaプログラミングでは必須の)Optionモナドを使用しましたが、この他にも様々な種類のモナドが提供されています。

たとえば、条件によってパイプラインを切り替えながら動作させる場合にはEitherモナドが有効です。

また、複数の値を同時に流す場合はListモナドやStreamモナドを使います。この場合は、パイプラインを並行実行させ、並行処理を行うことも可能です。各パイプラインの動作の待合せも自動的に行ってくれます。

モナドやMonadicプログラミングというとかなり敷居が高い感じがしますが、ここで説明したようにデーターフローをパイプラインで実行するようにイメージするとずいぶん印象が変わってくると思います。

2012年3月13日火曜日

関数型とデータフロー(2)

要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』で使用するスライドについて背景説明を行っています。

今回は背景説明第10弾として、「関数型とデータフロー(2)」として用意した以下の図を説明します。


動作

図の動作は、入力されたInt値からデーターフロー的な処理を4段行い、最後に並行して計算した結果を足しこんで最終的な結果の値を計算しています。

これを、関数型の以下の記述方式で実現しています。

3 |> (plus5 &&& mul10) >>> (plus5 *** mul10) >>> plus5.first >>> plus5.second >>> join2

これは、Scalazの型クラスArrowで実現しています。Arrowのメカニズムは、以下のページが詳しいです。

構成要素

データフローの格段の処理を細かく見ていきましょう。


Fork
3 |> (plus5 &&& mul10)

「分岐」とすると条件分岐と紛らわしいのでForkとしておきました。値を2つのパイプラインにForkして、それぞれに関数plus5とmul10を適用します。結果は、タプルで返ってきます。

並列計算
(8, 30) |> (plus5 *** mul10)

タプルに格納された2つの値に対して並列演算します。結果は、タプルで返ってきます。

First
(13, 300) |> plus5.first

並列計算のパイプラインの1番目のパイプラインに関数を適用します。結果は、タプルで返ってきます。

Second
(18, 300) |> (plus5 *** mul10)

並列計算のパイプラインの2番目のパイプラインに関数を適用します。結果は、タプルで返ってきます。

結合

結合は、以下の関数で行っています。この関数では、タプルに格納された値を足しあわせています。

val join2 = { fs: (Int, Int) => fs.fold(_ + _) }

ノート

Scalazが提供するArrowまわりの部品を使って、それなりに複雑なデータフローを記述することができることが分かりました。もちろん、本格的なデータフローをここで紹介した部品だけで構築するのはちょっと難しそうですが、関数型の他の機能と合わせ技で色々なことが可能になりそうです。

2012年3月12日月曜日

関数型とデータフロー(1)

要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』で使用するスライドについて背景説明を行っています。

今回は背景説明第9弾として、「関数型とデータフロー(1)」として用意した以下の図を説明します。

オブジェクト・モデリングと関数型の連携ではデータフロー・モデルが重要な位置付けにあることを説明してきました。そして、前回「データフローの実装技術」では具体的な実装技術について取り上げました。

関数型言語でデータフロー・モデルを扱うための要素技術として、関数型言語上でデータフローをどのように記述するのかという記述方式が論点の一つとなります。

関数型言語上でデータフローモデルを記述する絶対確実な方法としてデータフローを構成する各ノードとノード間のリンクを集合的に記述する方法がありますが、可読性はあまりよくないので、あくまでも最終手段として考えておきたいところです。

そういう意味で、前回紹介したSparkでの以下の記述は、関数型言語として自然であり、とても魅力的です。

val file = spark.textFile("hdfs://...")
 
file.flatMap(line => line.split(" "))
    .map(word => (word, 1))
    .reduceByKey(_ + _)

この記述は、以下の処理をデータフロー的に繋ぐ表現を行っています。

  • テキストデータを各行毎に空白を区切り記号にして単語に分割
  • 書く単語毎に単語とカウンタの対のデータ構造に変換
  • 単語毎に値を加算で畳込み

このデータフローの実行の結果、テキストファイル内に存在する単語の単語数の一覧を得ることができます。

関数型言語を使うと、このように関数型言語的にもデーターフロー的にも自然な記述が可能になりそう、ということが分かりました。そこで、今回から数回に分けてScalaを例に関数型言語でデータフローを記述する方式について考えていきます。(セッションでは、ここで考えた内容を1,2枚のスライドに圧縮して説明することになると思います。)

基本

今回の図は、関数型言語でデータフローを記述する方法を考える基本形です。

まず、関数オブジェクトplus5とmul10を定義します。plus5は引数に5を加えたInt型の値を返す関数、mul10は引数に10を掛けたInt型の値を返す関数です。

val plus5 = (_: Int) + 5
val mul10 = (_: Int) * 10

この2つの関数を使ってInt型に5加え、さらに10を掛けるデーターフローについて考えます。このデータフローにInt値3を渡すと80が返ってきます。

普通の記述

この演算を普通に記述すると以下のようになります。これは、関数型言語でも手続き型言語でも共通のごく普通の記述方式です。ここでは関数呼び出し方式と呼ぶことにします。

mul10(plus5(3))

このように考えてみると、この基本的な記述方式も一種のデータフローと考えることができます。ただし、図で示したデータフローと記述方式のマッピングが直感的には分からないという問題があります。

関数合成

関数型言語では、関数合成によって複数の関数を合成して新しい関数を定義することができます。

以下では、関数オブジェクトのcomposeメソッドとandThenメソッドで関数合成しています。

(mul10 compose plus5)(3)
(plus5 andThen mul10)(3)

関数合成は、関数オブジェクトをプログラム的に操作して柔軟な結合ができるようになるのが魅力ですが、データフローの記述に用いるには、記述が冗長なのと、データフロー的な意味での可読性はあまり高くありません。

Scalaz

Scala用のクラスライブラリScalazを用いると、以下のような記述が可能になります。

3 |> plus5 >>> mul10

データフローの左側からInt値3が右側の方向に流れていくことが直感的に分かる記述方式になっています。

これは一種のDSLといえますが、Scalaの関数呼び出しに型クラスのメカニズムを用いて文法糖衣的な皮をかぶせているだけで、内部的にフレームワーク的な新しい演算メカニズムを導入しているわけではありません。つまり、関数型言語の持つデータフロー的なセマンティクスを活かす表現方式となっているわけです。

2012年3月9日金曜日

データフローの実装技術

要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』で使用するスライドについて背景説明を行っています。

今回は背景説明第8弾として、「データフローの実装技術」として用意した以下の図を説明します。



オブジェクトと関数の連携(3)では、オブジェクト・モデリングと関数型の連携ではデータフロー・モデルが重要な位置付けにあることを説明しました。

そこで重要になってくるのが、データフロー・モデルを関数型言語の世界でどのように実現してくのかという実装技術です。

このスライドでは、その具体例としてボクが注目している3つの技術を挙げています。

A Unifiying Framework for Structured Analysis and Design Models

A Unifiying Framework for Structured Analysis and Design Modelsは、SA/SDの代表的な技術であるYourdon法、DeMarco法、Jackson法でのデータフロー記述Structured Chart、Data Flow Diagram、Structure TextをInitial algebraという代数で記述して、圏論の枠組みで統一的に操作する試みです。

代数、圏論ベースの形式的な記述を行うので、関数型との相性はよいと思われます。

この書籍が当該研究分野でどのような評価を受けているのかはボクは知らないのですが、関数型とクラウドコンピューティングの接点として重要な位置付けになるのではないかと考えているデータフロー・モデルと、関数型をつなげる理論的なバックグラウンドとして面白そうなので、ぼちぼちチェックしているところです。

SA/SDのデータフロー図を関数型言語に変換したり、関数型言語でデータフローを記述するためのベースにしたり、といった方向に延ばせるのではないかと考えています。

AsakusaFW

データフロー的なモデルを専用Java DSLで記述してHadoop上で実行するためのバッチ実行フレームワークです。基幹業務で用いられてきたCOBOLバッチ的なバッチ処理をクラウドプラットフォーム上で超高速に実行します。ジョブ管理などバッチ処理のインフラがフルセットで揃っていることが特徴です。

データフロー的なモデル(AsakusaFWではDAGと呼んでいます)の詳細は「Asakusa DSL設計手法」が、データフローをベースにしたバッチ処理の実現技術は「Asakusa DSL詳細設計および実装技術」が参考になります。

現在はJava DSLベースですが、Spark的なアプローチで関数型のプログラミングモデルと統合するのも面白そうです。

Spark

SparkはApache Mesos上で動作するクラスタ計算システムです。Scala言語に統合されているのが特徴です。

具体的にどう統合されているのかというと、以下のプログラムを見れば一目瞭然です。これはHadoopでも例題によく用いられているワードカウントのプログラムですが、通常の関数型プログラミングの範疇で簡潔に記述できています。

val file = spark.textFile("hdfs://...")
 
file.flatMap(line => line.split(" "))
    .map(word => (word, 1))
    .reduceByKey(_ + _)

Sparkの並行処理計算インフラをモナドの計算文脈の裏側に隠蔽して、プログラマには、通常のMonadicプログラミングでの記述を可能にしているのです。クラウドコンピューティングにおいて関数型言語に期待されている機能をぴったり実現しているので、かなり有力なのではないかと思います。

Sparkの記述力はこれから調査する予定ですが、「A Unifiying Framework for Structured Analysis and Design Models」での代数で記述したデータフロー・モデル、ScalaのMonadic演算の記述力、Sparkでの実現方式を付き合わせることで、クラウド・コンピューティングにおけるデータフロー・モデルと関数型プログラミングの接点が見えてくるのではないかと考えています。

2012年3月8日木曜日

オブジェクトと関数の連携(3)

要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』で使用するスライドについて背景説明を行っています。

今回は背景説明第7弾として、「オブジェクトと関数の連携(3)」として用意した以下の図を説明します。

アプリケーション・アーキテクチャ

アプリケーション・アーキテクチャを考える場合、システムのレイヤ構造の視点が重要です。ここでは、システム、サブシステム、モジュール、コンポーネント、オブジェクトの5階層を考えます。具体的な実装を持っているのはオブジェクトで、オブジェクトをソフトウェア部品の単位で束ねたものがコンポーネント、コンポーネントを物理的な配備の単位で束ねたものをモジュールと呼ぶことにします。システムをドメインごとに分割した物がサブシステムで、サブシステムは複数のモジュールから構成されます。

システム、サブシステム、モジュール、コンポーネント、オブジェクトは、メタ・モデル的には同じ構造を持っており、オブジェクト・モデル上での運用で使い方を決めています。システム、サブシステム、モジュール、コンポーネント、オブジェクトを総称する場合は、分類子と呼ぶことにします。

モデル記述

図ではシステムの振舞いを記述するモデルの候補として以下の2つを挙げました。

プロセス計算

ボクの理解では、プロセス計算はオートマトンがベースで、モデルの記述方式として制御フロー的なアプローチと、状態機械的なアプローチがあり、理論的には相互に変換可能です。

制御フロー的なアプローチの代表が、有名なCSP(Communicating Sequential Processes)です。状態機械的なアプローチとしては、FSP(Finite State Processes)などがあります。

HoareのCSPが最初に発表されたのが1978年ですから、プロセス計算自体は相当昔からある理論です。しかし、今の所、エンタープライズ分野では普及しているとは言えません。普及していない理由は色々あると思いますが、実務での展開を考えた場合、特に以下の2つの問題が重要です。

  • オープンソースで無料で使える実装が見当たらない(知られていない)
  • 理論(使い方)が難解

プロセス計算系の技術を実現したものは色々とあるようですが、一般的な意味(たとえばApacheのトッププロジェクト級)で有名なものはないと思います。

また、研究者向けの文献は多数あるものの、プログラマ向け、(この分野の)初心者向けの平易な解説書がなく、一般のエンジニアが実務に適用することはかなり困難です。

個人的には、ひとつの目安はパーサーコンビネータによる構文解析だと考えています。Scalaのパーサーコンビネータは使い方も比較的簡単で機能も強力ですが、少なくてもこれと同程度の使いやすさのものが入門用の文書と込みでオープンソースで提供されてこないと、エンタープライズでの一般の開発に使うのは難しいと思います。

データーフロー

プロセス計算もいずれ実用化されてくると思いますが、短期的な観点でより実現の可能性が高いのがデーターフローです。(ここでは、データフローをデータフロー図的なモデルの意味で使います。)

データフローは、OOADが登場する前に支配的な開発方法論だったSA/SDで広く使われていた技術です。OOADでも、初期(1991年)に一世を風靡したOMTの機能モデル(function model)として採用されています。しかし、OOADはOOSE系のユースケース駆動が主流になったため、データフローはUMLでは(アクティビティ図の一部としてかすかに残っていますが)主軸のモデルからは外れており、事実上ロスト・テクノロジとなっています。(2004年のOMT第2版では、OMTもユースケース駆動になりデータフローはなくなっています。)

データフローは、ある意味でデータの計算過程を示しているので、関数型言語との相性がよいと思われます。そういう意味で、関数型言語が実用化されると、実績のある技術であるデータフローも再発見されることになるのではないかと思います。

オブジェクトと関数

システム、サブシステム、モジュール、コンポーネントは、考え方によっては実装技術にニュートラルな分類子です。オブジェクト・モデルの場合は、コンポーネントの実装技術にオブジェクトを用います。たとえば、コンポーネントの機能はファサード的なオブジェクトが代表として実現し、具体的な処理をプログラムとして実行します。

このため、システム、サブシステム、モジュール、コンポーネントでは実装独立の抽象度の高いモデルであるプロセス代数やデーターフローで、モデルを記述することになります。

一方、オブジェクトの場合はモデルを記述してもよいですが、その具体的な実現を関数で実装することも可能です。

ビジネスフローと状態機械

システム、サブシステム、モジュール、コンポーネントはビジネスフローを持つことができますが、これをプロセス代数で記述するアプローチができます。この場合は、制御フロー的な記述方式を用いることになります。

システム、サブシステム、モジュール、コンポーネント、オブジェクトは状態機械を持つことができますが、これをプロセス代数で記述するというアプローチも可能です。この場合は、状態機械的な記述方式を用いることになります。

どちらも有力なアプローチですが、プロセス計算自体のエンタープライズ・アプリケーションへの展開は、もう少し先の事になりそうなので、ここでは可能性を指摘するにとどめておきます。

メソッドとアクション

システム、サブシステム、モジュール、コンポーネント、オブジェクトのメソッドやアクションは、通常は手続き的に処理を記述しますが、ここにデーターフローを記述することが有益です。

つまり、状態遷移時のアクションや操作を呼び出されたときのメソッドの動作が、データの状態遷移を引き起こす場合、データは事前条件から事後条件へデータの更新が行われます。この更新データの計算にデータフローを使用することができます。

この処理が単純な場合には、もちろん関数でそのまま記述すればよいのですが、以下の場合にはデータフローのモデルで記述して、フレームワークなりモデルコンパイラなりに処理を委譲するのが有力な選択肢になります。

  • データフローの内容が複雑
  • 処理データ量が膨大

次のスライドでは、この辺について考えます。

2012年3月7日水曜日

オブジェクトと関数の連携(2)

要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』で使用するスライドについて背景説明を行っています。

今回は背景説明第6弾として、「オブジェクトと関数の連携(2)」として用意した以下の図を説明します。

オブジェクトと関数の連携(1)では、オブジェクトの世界と関数の世界を完全に分けてしまうのがよい、と説明しました。今回は、その具体的な連携方法について考えます。

Scalaでは関数もオブジェクトの一種なので、そういう意味ではすべてオブジェクトの世界で動いています。ここでは、純粋関数型言語の枠組みのみを使用しているものを関数型の世界、それ以外のものをオブジェクトの世界と呼ぶことにします。

図では、オブジェクトの世界を左側に関数の世界を右側に配置しました。

更新処理の流れ

オブジェクトの世界では、オブジェクトがグラフ構造で格納されています。このグラフ構造の一部を、関数を使って更新する流れについて見ていきましょう。

純粋関数型では副作用がありません。つまり、オブジェクトの内容や構造を更新することはできません。関数ができることは、計算をすることのみです。

とはいえ、純粋関数型言語でもインタラクションゲームデータベースアクセスを書くことができます。

副作用はないにもかかわらず、プログラムの外の世界の副作用は記述できるわけですね。この矛盾を解決するためのちょっとしたトリックがあります。このトリックがオブジェクトと関数を連携させる場合でも鍵となります。

基本データ構造

一般的な処理で関数が扱う構造は大きく以下の4種類です。

  • 値(構造は持たない)
  • リスト

この中で最も複雑な構造が木構造です。木構造までは再帰によって関数で自然に記述できます。これより複雑な構造はグラフ構造になりますが、これは特別な扱いを必要とするので、一般論を考える時は除外して考えるとよいと思います。(グラフ構造を扱うときは、木構造に参照を追加したり、表(行列)の形にしたり(ノードの集合として扱う)という操作の仕方になります。)

関数で自然に扱える最も複雑な構造が木構造で、表、リスト、(構造を持たない)値は木構造を単純化した構造と考えることができます。以上の点から、以下では木構造をベースに考えていきます。木構造でできることは表、リスト、(構造を持たない)値でも同様に適用できます。

データ構造の抽出

可変オブジェクトのグラフ構造から、処理に必要な情報を木構造として抽出します。この情報を、代数的データ型と永続データ構造を用いて表現します。どちらも不変オブジェクトで、変更を行うことはできません。

図ではグラフ構造としての情報は、木構造に参照を追加する形で表現しています。

データ構造のコンバージョン

関数での計算の基本は木構造のコンバージョンです。データ構造を更新することはできないので、複製しながら複製過程でその一部を変えることで、木構造のコンバージョンを行います。

更新指示書

関数では計算ができるだけなので、直接オブジェクト側のデータ構造の更新はできません。

単純なケースでは、関数で計算したデータ構造を直接オブジェクト側のデータ構造に上書きしてしまうことで、更新処理を完結させることができます。

問題は、そのようなことはできない複雑なデータ構造の場合ですね。

その場合、関数は木構造から「更新指示書」を計算します。ここが「トリック」です。

関数はあくまでも更新指示書の計算を行うのみで、更新処理そのものにはタッチしません。副作用という下世話な処理は、関数型言語の外側の誰かが更新指示書を見ながらやってくれる、という役割分担になっています。この役割分担によって、純粋関数型の純粋性が保たれているわけです。

計算結果の反映

オブジェクト側では、関数から返ってくる更新指示書に従って、可変オブジェクトのグラフ構造を更新します。

副作用はオブジェクト側で引き受けることで、関数の世界の純粋性を守りながら、オブジェクトと関数の連携を行うことができます。

ノート

更新指示書を使った更新処理はかなりややこしいです。

オブジェクト指向から関数型に入ってくるときに戸惑うのはこの点で、この(擬似)更新処理を円滑に行うためのテクニックが数多く存在します。モナドもその一つですね。先ほど紹介したゲームは名前がMonadiusですが、これはモナドを使っているのが名前の由来とのことです。

こういった、ややこしいメカニズムで関数型の純粋性を保つと何がよいのかというと、数学の理論の範囲内で処理を完結させる事ができるということです。これは、理論的にはプログラムが証明可能ということです。

もちろん現時点では、普通の関数型言語ではプログラムを証明することはできないのですが、関数型の枠組みでプログラミングすることによって間接的にバグの混入を大幅に防ぐことができます。

たとえば、並行処理を行う小さなモナドをたくさん用意して、このモナドを合成して一つの大きなサービスを記述するとします。各モナドは代数的構造デザインパターンで取り上げた代数的構造を持つ代数データ型を操作するとします。

このようなモナドの合成では、代数的に証明されたメカニズムでモナドを合成することができ、さらに代数データ型の枠組みの中で処理の最適化を行うことも可能になるはずです。

「小さなモナド」側で十分に検証してバグを取っておけば、これらのモナドを合成して作成したサービスは、問題なく動作することが期待できます。(モナドの合成メカニズムそのものは数学的に証明されたメカニズムが使用されるはずなので。)

並行プログラミングでは、このようなプログラムの作り方が非常に有益です。並行プログラミングでは、単純なケースを除いてはデバッガによるデバッグはほぼ不可能と考えてよいので、プログラミングの時点でバグが混入しづらいメカニズムの存在が大きな鍵になります。

このあたりの考えを進めていくと証明駆動や形式手法といった方面に進んでいくことになりますが、その土台は関数型プログラミングなので、まずは関数型プログラミングに習熟することが大事です。

SQL

「更新指示書」によるメカニズムはSQLを用いたデータベースプログラミングと同じことをしていると気づくと、処理の内容が腑に落ちると思います。(SQLでは木構造ではなく表構造になります。)

「更新指示書」としてSQL文を作る関数を書けば、オブジェクトの世界を飛ばして、状態はすべてデータベースに格納管理するというアプローチも可能です。こうすることで、データベースをバックエンドに使う一般的なWebアプリケーションを純粋関数型言語で記述できますね。

トランザクション

「更新指示書」によるメカニズムはトランザクション処理とも相性がよいです。

トランザクション処理では、処理がアボートした時には、データが処理前の状態になっていなければなりません。このため、データにロックをかけて随時情報を更新していくというアプローチでは、アボートした時にデータを戻す処理を行わなければなりません。

このような手間が必要なので、「更新指示書」方式のアプローチでも処理の複雑さは変わりません。関数型では言語の特性上「更新指示書」アプローチになるので、この上でトランザクション処理を考えていけばよいわけです。また、(ロックをかけない)楽観的アプローチは、「更新指示書」アプローチと相性がよいので、そういう意味でも「更新指示書」アプローチは有効です。

つまるところ、関数型言語はトランザクション処理と相性がよいということですね。

2012年3月6日火曜日

オブジェクトと関数の連携(1)

要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』で使用するスライドについて背景説明を行っています。

今回は背景説明第5弾として、「オブジェクトと関数の連携(1)」として用意した以下の図を説明します。


まず、プログラミング言語のスコープでオブジェクト指向と関数型の連携について考えます。オブジェクト指向言語と関数型言語のハイブリッドであるScalaを念頭に置いています。

Scalaでは、オブジェクト指向言語の機能と関数型の機能をシームレスに使うことができます。このため、特に意識しないでプログラミングを行うと、オブジェクト指向言語と関数型言語の機能が渾然一体となったプログラムになりがちです。

図の上側では、オブジェクト、手続き、状態、関数、値、データ構造が渾然一体となっています。

関数型言語を使用する最も重要なポイントは、数学理論の活用が期待できることです。現段階では、まだまだ活用できる数学理論は少ないですが、それでも群論のモノイド、圏論のモナドという数学概念が、具体的に利用できるプログラミング言語の機能として活用されていることを説明しました。実務の世界での関数型言語の本格活用は端緒についたばかりであり、今後大発展が期待できます。すでにある膨大な数の数学理論を、必要な部分だけ関数型プログラミングの世界に移し替えるだけで、完成度の高いさまざまな技法や応用が使えるようになるので、陸続と新しい技術が登場してくるのではないかと予測します。

この大きな流れに乗るためには、できるだけ純粋関数型言語的な使い方をすることで、型付ラムダ計算から各種の数学理論につなげていく方向で考えていくのが筋であり、長い目で見ていろいろな効用が出てくるでしょう。

方針

とはいえ、純粋関数型言語ですべてを構築するのは現段階ではリスクを伴います。少なくてもScalaを採用する場合は、そういう判断がある事が多いでしょう。

その場合でも、できるだけ関数型言語のメリットを享受したいわけです。その際のプログラミングの方針としては、オブジェクト指向で作成する部分と、関数型で作成する部分を明確に分離して、プログラミングしていくのが現実解です。

図の下側では、左側にオブジェクト指向の世界、右側に関数型の世界を配置しています。関数型プログラミングの基本中の基本は副作用がないことです。左側が副作用のある世界、右側が副作用のない世界となります。

関数型の世界では、副作用がない計算を実現するために、以下の3つの機能を使います。

  • 関数
  • 代数的データ型
  • 永続データ構造

オブジェクト指向で値オブジェクト(value object)として扱っていたものは、代数的データ型に、データ構造は永続データ構造に変換した上で使用します。

関数型の世界で記述する部分を増やしていくことで、先ほど説明したように技術革新の大波を受け止めることが期待できます。たとえば、関数合成による再利用性の向上や並行プログラミングといった技術で関数型プログラミングの恩恵を享受できるようになりそうです。