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での実現方式を付き合わせることで、クラウド・コンピューティングにおけるデータフロー・モデルと関数型プログラミングの接点が見えてくるのではないかと考えています。