2010年8月31日火曜日

現在の活動

8月も今日で終わり。今年も2/3に、今年度も半分に近づきつつあり、おおむね半分が過ぎたというところで活動の内容をまとめておくことにした。
現在行っている活動は大きくまとめると以下の3つである。

  • SimpleModeling - クラウドアプリケーション向けのメタモデル
  • SimpleModeler - SimpleModeling用のScala DSLモデルコンパイラ
  • g3フレームワーク - メッセージフローフレームワーク



元々SimpleModelingは、wakhok時代に教育用に整備したオブジェクトモデリング手法で、『上流工程UMLモデリング』や『マインドマップではじめるモデリング講座』という形で本にまとめることができた。従来型のオブジェクトモデリングをベースにwakhokで得られた知見などをもとに拡張している。

SimpleModelingのもう一つの大きな目的はモデル駆動開発で使用できるオブジェクトモデルのプロファイルである。DSLで記述することができ、かつプログラムに落としこむことができることを念頭にメタモデルを定めている。

このSimpleModeling用のモデルコンパイラとして開発したのがSimpleModelerである。
SimpleModelerはScalaをDSLのホスト言語として使用しており、SimpleModeler自身もScalaで開発している。
SimpleModelerでは、Scala DSLから仕様書とプログラムを生成する。仕様書はクラス図や状態機械図、状態遷移表を埋め込んだHTMLを生成することができる。

SimpleModelingとSimpleModelerが一通り目処がついたところで、クラウドがそろそろ来そうという感触を得たのが一昨年の秋。SimpleModelerでGoogle App Engine/Python用のコード生成が動き始めた頃、Google App Engine/Javaが公開された。
それ以降はGoogle App Engine/Java向けのコード生成を中心にSimpleModelerの開発を進めている。

Google App Engineを一通り経験した後、SimpleModelingをクラウド向けに拡張する本格的に開始したのが昨年の夏、秋。11月にはMicrosoftのPDCに参加させてもらってAzureの技術に刺激を受けた。

クラウド向けの拡張項目についてはいずれブログでも説明する予定だけど、今の所(1)ドメインモデルの拡張(2)メッセージフローの導入の2点。

アプリケーションの振舞いモデルを主にメッセージフローで記述してみようというアプローチなんだけど、このアプローチを取るためには現在利用できるクラウド・プラットフォームとは距離がありすぎる。

そこで、この距離を埋めるために開発を始めたのがメッセージフローフレームワークであるg3フレームワークである。g3フレームワークの開発は今年に入ってから。
g3フレームワークはScala DSLで記述したメッセージフローを実行する。

最近は、このg3フレームワーク上でメッセージフローモデルとドメインモデルを連携させるための仕組みづくりの開発を行っている。

以上の3つの活動はそれぞれ面白い論点があるので、ブログで順にまとめていきたいと思っている。

2010年7月12日月曜日

マインドマップ・モデリング

先週の木金に函館で久々にマインドマップ・モデリングの講習を行った。

組み込みエンジニアなど実装周りを担当されているエンジニアの再教育事業でのモデリング教育。
UMLの文法を講義して終わり、ということにはしたくなかったので、wakhok時代に開発したモデリング教育の技法であるマインドマップ・モデリングを行うことにした。


一日目にマインドマップ・モデリングの文法や簡単な演習を行った後、二日目に実際の雑誌記事を用いて演習を行った。この二日目の演習が本番である。日本語の文章とモデルのマッピングの勘所をつかんでもらうという狙いがある。

今回は旬の話題ということで週刊 東洋経済 2010年 7/3号の特集『激烈!メディア覇権戦争』から『電子書籍は「本」を救うのか』をテーマに選んでみた。

以下はボクがサンプルで作ってみたマインドマップ・モデル。
モデル作成の過程を三段階で示している。

まず最初に記事中から単語を抜き出してマインドマップ・モデリングが定義しているBOIの下に配置する。
それぞれの枝は通常のマインドマップの技法を用いて自由に情報を整理する。


次の段階ではマインドマップ・モデリングの文法に従って用語の整理を行う。
用語の名寄せ・統合と用語間の関係を整理して、基本的な構造を抽出するのが目的である。


そして、以下が最終型。時間の関係があって完成というところまではいけなかったけど、大体の構造まで持ってくることができた。
全段階で得られた基本構造をベースにして、モデルのリファインを行う。
ここで重要なのが物語(ユースケース=利用事例)と出来事(イベント)によって記事の意図をモデルに注入する点である。
物語と出来事によって、モデルに動的な側面が加わる。そして、モデルを動作させるのに必要な静的構造の文脈が確定する。


こういったモデリングをUMLでももちろん行うことができるけど、仕掛けが大掛かりになりすぎて、演習という限られた時間では実現が困難というのが経験則。
この問題を解決するために開発した技法がマインドマップ・モデリングというわけである。

マインドマップ・モデリングのモデルは一般的なOOをベースにしている(正確にはボクが開発したSimpleModelingというプロファイルをメタモデルにしている)ので、このまま半機械的にクラス図やユースケース図に変換することが可能である。

今回もUMLによるモデリングはほとんど経験のないという受講者の方が多数おられたが、適切なマインドマップ・モデルを作成されていた。
いずれUMLを使うようになり、UMLによるモデリングを行う必要が出てくれば、今回の講習の成果を活かしていただけると思う。

久々にマインドマップ・モデリングをやってみてモデリングは楽しいなぁ、と再認識。
プログラミングと直結する設計モデルの作成はどちらかというと無駄な作業だと思うけど(Scalaを使って直接コーディングするかDSL化するのがよい)、ふわふわとした状況を具体的な形にしていく概念モデリングという作業はクラウド時代になっても引き続き重要な技術であり続けるだろう。

現在は新しく登場したプラットフォームであるクラウドの基盤技術に業界の関心が集中しているけど、いずれモデリングの方にも関心が移ってくることになるはずである。
そういったことも意識しつつ、今はマインドマップ・モデリングをどのように改修すればクラウドに対応できるのかといったことを切り口にしてクラウド時代のモデリングについてつらつらと考えている。

2010年6月24日木曜日

ボクらのScala

明日6月25日に拙著のScala入門書『ボクらのScala』が出版されます。


副題は「次世代Java徹底入門」となっています。
題と副題は編集のYさんがつけたものですが、なかなかよい感触です。
少し冒険的な副題ですが、二年ほどScalaをいろいろと触ってみてのボクの実感とも一致しています。
内容をみてYさんもそのように感じられたのだと思います。

Scalaは色々な言語のよいところを絶妙のバランスで配合しており、プログラミング効率も高く、高い実用性を備えています。その上で、DSL、マルチコア、クラウドといったこれからの応用を実現するために必要な機能が整備されているのが美点です。
今すぐ便利であるのはもちろん、これからの10年を乗り切るための基盤ともなる言語といえるでしょう。

ただ、言語仕様やライブラリの使い方を一度にすべて把握しようとすると初学者には敷居が高いので、普通のプログラミングをする上で普通に必要な言語仕様やライブラリを、学習段階に合わせて説明していくというアプローチを取りました。

400ページに収めるために、大幅に原稿を削りました。
原稿を書く側からすると辛いところですが、逆に読者の側からは内容がコンパクトになって読みやすくなったのではないかと思います。
割愛した原稿は、このブログで随時アップしていこうと思っています。

2010年5月4日火曜日

Scala DSL

クラウド時代に向けて:

  1. SimpleModel:オブジェクト・メタモデル。クラウド・アプリケーション向けの拡張中。
  2. SimpleModeler:SimpleModelのモデルコンパイラ。
  3. g3:メッセージフロー・フレームワーク。

の開発を行っているわけだけれど、これらの技術のキーテクノロジとなるのがDSL(Domain Specific Language)である。

SimpleModelはメタモデルだけれど、Scala DSLで簡潔に記述できることが要件の一つになっている。そして、SimpleModelのモデルコンパイラがSimpleModeler。SimpleModelerはSimpleModelを記述したScala DSLの集まりをソフトウェアリポジトリとして使用する。

またg3は、Scala DSLで記述したメッセージフロー・モデルに基づいて動作する。

このように、このところはScala DSLを中心に活動しているのだけれど、ここにいたるまで色々と紆余曲折があった。

1998年に登場したXMLがJavaと分散環境のミッシングリンクを埋めるキーテクノロジーであると感じ、Java&XMLを中心に活動していた時期がある。

分散環境では、分散ノード間でのオブジェクトの共有や移送は理論的には不可能ではないにしても相当ハードルが高く、当面は実運用的には不可能と考えてよい、というのが分散OSや分散オブジェクトの教訓。

このため、分散アプリケーションが取るべき現実解は、異機種間での疎結合のサービスを構造化された値の送受信で連携させるということになるのではないか。そしてこの「異機種間で送受信する構造化された値」を記述する技術が当時のJavaには欠けており、ここにXMLはすっぽりとはまるのではないか。

そういったブレークスルーの予感もあって、XMLアプリケーションであるSmartDoc(1998)とRelaxer(2000)を開発したのだけれど、これらのアプリケーションを通して、いわゆる「one source multi use」の有効性を体感することができた。

当時はまだ、DSL(Domain Specific Language)という用語はなかったか、あるいは一般的ではなかったのだけれど、用途ごとの専用言語をXML上に構築して「one source multi use」の運用を行うこと、つまり今の言い方ではDSLが今後の技術の主流になると思えた。

ただ、XMLをDSLのホスト言語として使用することを試行錯誤する中で、XMLは文書をマークアップするにはとても良いのだけれど、定義ファイルやプログラムといった用途に使用するのには向いていないことが分かってきた。

そこで、プログラミング言語をホスト言語にすることを考えたのが次の段階。最初に候補となったのはGroovy。これは2004年頃。Javaベースであり、メタな操作も可能なのでDSLのホスト言語としてうってつけであると思ったわけである。

しかし、いくつか問題があってGroovyを採用するのは断念した。

当時のGroovyは、非常に遅かったのが一点。ただ、こういった問題は時間が解決するので、DSLの用途ではそれほど大きな問題ではない。

木構造を扱う抽象構文的な拡張の柔軟性が低い、という問題もあった。これは、他に良いところがあれば我慢できる範囲。(なにぶん古い話なので今は違った状況になっていると思う。)

しかし、これは大きな問題だと思ったのは、動的言語であるためにDSLの構文にコンパイラ相当のエラーチェックをかけることができない、ということ。これはDSLを使ってモデルを作成していく際の効率を著しく低下させる。

たとえば、フレームワークの設定ファイルに識別子のミススペルがあることが原因で、フレームワークが意味不明のエラーで落ちる、というような事が起きる。

DSL処理系の開発も難しくなる。DSL処理系をクイックハックで作る分には楽でよいのだけれど、本格的なものにしようとするとコンパイラ相当のエラーチェックを自分で実装しなければならなくなる。

そんな中で、アノテーション技術が登場したこともあり、JavaをDSLのホスト言語とすることに決めて開発を進めていた。

しかし、JavaをDSLのホスト言語として使用した場合、メタな情報はjava.lang.reflectionとアノテーションで部分的に取り出すことができるだけなので用途が限られるという問題がある。Javaの文法を構文木として取得するのが非常に難しい。EclipseのASTを使うという案もあったのだけれど、Eclipseに依存してしまうので断念した。

以上のようなこともあり、Javaでは木構造のデータ構造を簡潔に記述するための文法を定義するのが難しい。Javaで書いたロジックをそのまま取り出すことも難しい。

もちろん、GWTのような荒業もあるけれど、これはハードルが高すぎて一般的な技術には成り得ない。

また、Javaではテキスト情報をDSLの一部として記述するのが一苦労。コメントに記述したものを、自作パーサで読み込むといったようなことをやったりしていた。

そして最後に行きついたのがScalaということになる。これが、2008年の7月。

Scalaは、高階関数と字句上の様々なトリックを併用することで、簡潔で強力なDSLを簡単に構築することができる。また、生文字リテラルやXMLリテラルがあるのでテキスト情報の記述も簡単。ケースクラスや抽出子によるリテラル追加機能もよい。しかも、型推論のついた静的型付け言語。

まさに、DSLのために生まれてきた言語である。

たとえば、以前にご紹介したg3フレームワークが使用する以下のScala DSL。Enterprise Integration PatternsのSplitパターンとAggregateパターンを使用したメッセージフローを記述している。これをXMLやJavaで記述しようとすると、大変な事になりそうだ。実装もややこしくなる。

Split.scala
class Split extends G3Application {
  start(List(1, 2, 3, 4, 5)) split() agent {
    case x: Int => x + 100
  } aggregate()
}

しかし、Scalaだととてもすんなりいくのである。実装も、簡単というわけではないけれど、XMLやJavaをDSLにした時のことを考えるとはるかに容易になる。

いずれにしても、こういったDSLの構築技術が、クラウドに限らずこれからのソフトウェア開発のキーテクノロジーになると思っている。よほどの本格的な用途でない限り、ホスト言語の上に構築する内部DSLを使うことになるので、内部DSLが最重要技術ということである。

内部DSLのホスト言語としては、現在のところRuby、Groovy、Scalaが有力だと思われるけれど、やっぱりDSLは静的型付けがよいかな、ということでボクの選択はScalaということになる。もちろん、これは各自の好みで決めてよいところ。DSL処理系の開発効率も重要なので、プログラミングに慣れている言語を選択するのがよいでしょう。

Scala DSLアプリケーションであるSimpleModelerやg3の開発を通して、二年近くScala DSLプログラミングしてきて、多少ノウハウも蓄積されてきた。そのようなこともあり、Scala DSLに関するノウハウを5月18日に行われるJJUG CCCで『Scala DSLの作り方』というセッションで発表させていただけることになった。

興味のある方はぜひどうぞ。

2010年5月3日月曜日

クラウド・モデリング

5月14日に行われる『Hadoopを中心とした分散環境での開発方法論・モデリング・設計手法等についての座談会』という座談会に参加させていただくことになった。

よい機会なので、クラウドをターゲットにして取り組んできたモデリング手法、ツール、フレームワークなどをつらつらと整理しているところ。

オブジェクト指向モデリングは、本来、振舞いモデルを記述する能力が高い所が特徴であり強みでもあるはずだけれど、今のところ有効活用されているとは言い難い。

伝統的な基幹システムも、新興のWebシステムも基本的には画面とデータベース間の転記が基本アプリケーション・アーキテクチャとなっていることが多く、この場合には従来型のデータモデリングで十分だったということかもしれない。

三段(3 tier)構成のシステムアーキテクチャといった設計レベルでのモデリングもあるけれど、アーキテクチャレベルの鳥瞰図があれば、あとは直接プログラミングした方が話が早い。

しかし、クラウドでは故障、遅延、規模といった問題が従前より重要な課題となって浮上するため、今までのようにはいかなくなるだろう。

故障、遅延、規模の問題をデータベースシステムを中心としたミドルウェア層で吸収することはもはやできなくなるわけで、性能と一貫性のバランスを取るために、アプリケーションの用途に応じて、アプリケーション側で調整を行う必要が出てくる。いずれこういった問題を吸収するクラウド・ミドルウェアが出てくることになるとは思うけど、これは10年スパンで考えていくことである。

利用者側への見せ方、エクスペリエンスにも影響が出てくる。たとえば、今までだったらフォームの完了ボタンが完了したらデータベースには確実に書かれているはずだったけど、クラウドでは後で書いときます、という約束が行われるだけとなることが多くなる。そうなると、利用者側のアクションとして、書かれたことの確認や、実は書き損なっていたのでリカバリを利用者自身にお願いするというようなユースケースが普通に出てくる。

こういった問題を取り扱うには並列、分散、非同期といった要因を上位のモデリング段階で扱わなくてはならなくなるはず。そのモデリングのためのメカニズムとしてオブジェクト指向が再評価されることになるだろう、というのが最初のアイデア。そして、システム構築という観点から見るとMQ的なキューを中心としたシステムアーキテクチャ、アプリケーションアーキテクチャになるだろう、というのが二つ目のアイデア。

当時考えていたこれらのアイデアを昨年の1月にクラウド研究会でお話をさせていただいた流れから、UNIXマガジン誌に寄稿した記事を『クラウドの技術』の一編として再録していただいた。

この2つのアイデアを、従来から考えているモデリング手法に取り込む方針で作業を進めている。

従来システムに対するオブジェクト指向のモデリング手法はSimpleModeling、MindmapModelingという形でまとめ、一昨年の夏に出版させていただいた。

SimpleModelingとMindmapModelingは、内部的には共通のメタモデルSimpleModelを使用しており、相互運用可能である。MindmapModelingでラフなドメインモデルやユースケースモデルを抽出し、SimpleModelingで本格的なモデリングに入っていくという流れを想定している。

SimpleModelでは、イベントを軸に静的モデルと動的モデルを相互連携させる点がポイントとなっている。SVOがモデリングの基本になる。Vがイベント。イベントがアクター(S)とResource(O)の状態遷移を束ねる。ユースケースも物語をイベントの列として記述することで、静的モデルと動的モデルの両方にシームレスに連結できる。

クラウド・アプリケーションの場合でも、この枠組は引き続き有効ではないか、というのが今のところの考え。とはいえ、静的モデル、動的モデルの双方に相応の拡張が必要になる。

静的モデルでは、いわゆるBASEトランザクションに対応したデータモデリングが必要になってくる。ここでSVOの枠組みを自然に拡張していくことで対応できるのではないか、と考えている。具体的には、関連の各種プロパティの解釈の精度を上げることと、イベント・エンティティにBASE向けの属性を取り入れるといったことである。

動的モデルでは、新しくメッセージ・フローという概念の導入がミッシングリンクを埋める鍵になるのではないかと考えている。オブジェクト指向に限らず、コントロール・フローやデーター・フローをモデル化する図は色々と用意されているのだけれど、コントロール・フローとデータ・フローを包含して記述するための図というのが案外ない。しかし、クラウド・アプリケーションのモデリングでは、コントロール・フローとデータ・フローを同じ文脈上で記述することが、有効なのではないか仮説を立てているところである。

この仮説に基づいて、メッセージフロー図の文法、メッセージフローのDSL、そしてメッセージフローの実行系としてのフレームワークを開発しているのが現在のステータス。メッセージフローのDSLとフレームワークが一段落したら、一昨年から昨年にかけて開発したScala DSLコンパイラSimpleModelerを、メッセージフロー対応する構想となっている。

先は長そうだけど、ぼちぼちやっていくつもり。

2010年4月10日土曜日

(仮称)クラウド研究会@札幌

4月4日に「第1回(仮称)クラウド研究会@札幌」で、以下の3つのテーマでお話をさせていただきました。幹事の中村さん(@nakayoshix)には大変お世話になりました。どうもありがとうございました。
  • Android + Mule ESB + Twitter + OpenX + Scala + EC2
  • SimpleModeler + AppEngine
  • g3フレームワーク
昨年の秋ぐらいに、SimpleModelerのAppEngine/JDO周り、静的モデル周りが一段落。まだまだ完成というわけではないんだけど、バランス的にそろそろ動的モデルを攻めてみようということで、ここ半年ほどはクラウド・アプリケーションにおける動的モデルの位置付けと実現方法について検討と試作を行ってきた。
11月にはPDC09に参加する機会があり、Windows Azureについて学ぶことができたのも大きな収穫だった。
システムの動的モデルでは、エンティティの状態遷移モデルがひとつの基本になるけど、システム全体の挙動をモデル化する手法は確立されていない。ユースケース・モデルとシステム実装を繋ぐ部分は依然として手作業になる。
もちろん、手作業といってもモデリング側も色々なモデルを用意しているし、システム側もJavaEEやSpringといったアプリケーション・フレームワークがかなりの仕事をしてくれるので、相当手間を省けるようにはなっている。
しかし、クラウド時代に入ってモデリング側の道具立ても、システム側のアプリケーション・フレームワークもゼロベースで再構築しなければならない状況になっているのは明らか。クラウド時代には非同期、並列、分散、故障、遅延を正面から取り扱わないといけない。アプリケーション・モデルではこれらの要因を捨象して、実装時に頑張るというわけにはいかないだろう。
そういった時代認識もあり、次世代のコンピューティング環境、その土台となるモデル体系とシステム基盤、アプリケーション・フレームワークについて、微力ながら色々と考えた結果到達した結論が「メッセージ・フロー」である。
メッセージ・フローを軸にしてユースケースからエンティティをつなぐ動的モデルの体系を構築し、できるだけインピーダンスミスマッチが発生しない形でDSLで記述しフレームワーク上で動作させる方法について、朧気ながら形がみえてきたような気がしており、その実現のためのDSL、DSLコンパイラ、フレームワークの開発が最近のボクのテーマになっている。
まだまだ、仕掛り中の状態なんだけど、今回はよい機会をいただいたので中間報告という形で発表させていただいた。

Android + Mule ESB + Twitter + OpenX + Scala + EC2

「Android + Mule ESB + Twitter + OpenX + Scala + EC2」ではボクが試行アプリケーションとして作成したMule ESBベースのアプリケーションを素材にメッセージ・フロー・アーキテクチャによるアプリケーションの考え方などを説明した。
ポイントとなるのは、以下のメッセージ・フロー図。コントロール・フローやデータ・フローを記述するための図はあるけど、メッセージ・フローを記述するための図が案外ないので、ボクのニーズに合わせて文法を定義した。詳細はいずれこのブログでも紹介しようと思っている。



Visioで作った図なんだけど、OpenOfficeにはない矢印を使っているためOpenOfficeでは描けないことが最近判明した。OpenOfficeでかける記法に修正する予定である。

SimpleModeler + AppEngine

「SimpleModeler + AppEngine」は、現在開発中のScalaベースのDSLモデルコンパイラSimpleModelerの紹介。
現在のところエンティティ・モデルからUMLクラス図とAppEngine/Javaのプログラムを生成できる。
動的モデルは、状態遷移図をUMLステートマシン図と状態遷移表に変換するところまで。
1月22日に開催されたAppEngine Ja Night #4で発表させてもらった時の資料「クラウド・アプリケーション:DSL駆動アプローチ:SimpleModelerの試み」を使って説明を行った。AppEngine Ja Night #4では、時間的な問題で半分まで行かなかったのだけど、今回は最後まで通して説明することができた。

g3フレームワーク

「g3フレームワーク」は、現在開発中のメッセージ・フロー・ベースのアプリケーション・フレームワークである。専用のScala DSLで記述したアプリケーションを直接実行する。
メッセージ・フローの実行基盤としてはApache CamelのScala DSLに期待していたんだけど、内部構造がやや弱いような印象もあり、進捗もはかばかしくないようにみえる。また、Apach CamelそのものもAppEngineで動かすのは無理があるような感じでもあるので、Apache Camelの利用は断念して独自にDSLとフレームワークを開発することにした。
g3フレームワークとDSLは、以下の3つの用途をカバーすることを念頭に動作セマンティクスとDSLを設計している。
  • Google AppEngine Java上で動作する
  • Mule ESBなどのESB上で動作する
  • シェルからコマンドとして実行できる
今の所、コマンドとして実行するという機能範囲を実装している。
ある程度形がついたところでAppEngine上に載せる予定。こちらは、TaskQueueを用いて直接実行させることを想定している。
並行してMule ESBへの対応も行いたいと考えている。こちらはDSLからXMLの定義ファイルを生成する形を想定している。Mule ESBを用いてEC2とWindows Azure上で動作させることができるようになることを期待している。
最終的には、DSLで記述したモデルから、用途に応じてAppEngine, EC2, Windows Azure向けのプログラムを生成し、適材適所でAppEngine, EC2, Windows Azureを選択できるようにするのが目標である。AppEngine, EC2, Windows Azureの混在環境でも有効に機能するはず。
説明/デモで用いたDSLは以下のもの。
リスト1[TwitterScan3.scala](ユーザ名、パスワードのところは改変)はTwitterのTLをAtomFeedで読んできてg3フレームワークのAtomFeedオブジェクトとして受け取るというもの。Scalaのcase sequence(PartialFunction)を使っているのがポイント。この構造にすることでリアクティブなアプリケーションを簡単に記述することができる。
TwitterScan3.scala
package org.goldenport.g3.app

import org.goldenport.g3._
import org.goldenport.g3.atom._

class TwitterScan3 extends G3Application {
service('demogon, "http://[user]:[passward]@twitter.com/statuses/user_timeline/demogon.atom")

  start invoke("demogon") agent {
    case AtomFeed(feed) => feed.toString
    case _ => "???"
  } agent {
    x => println("==> " + x)
    x
  }
}
リスト2[Split4.scala]は、Enterprise Integration PatternsのSplitterとAggregatorを記述したもの。現在の実装はSplitterがListの各要素ごとに分割したものを送信して、Aggregatorが集約してListに再構築する。Splitterは関数でカスタマイズできるようにする予定。
Split4.scala
package org.goldenport.g3.app

import org.goldenport.g3._

class Split4 extends G3Application {
  agent('compute) {
    case x: Int => x + 100
  } aggregate()

  start(List(1, 2, 3, 4, 5)) split() publish("compute")
}

参考:デモ結果

参考のために(長くなるので最後に付録的に)デモの実行結果を載せておきます。スナップショットが表示されているだけなので、中身を知らないと意味が分かりませんが参考ということで。
以下、TwitterScan3とSplit4のデモの様子。TwitterScan3は当日はデモが失敗したので初公開。TwitterScan3のデモは表示が長いのでSplit4、TwitterScan3の順番に並べている。
Split4デモ結果
> run org.goldenport.g3.app.Split4
[info] 
[info] == compile ==
[info] Source analysis: 1 new/modified, 13 indirectly \
    invalidated, 0 removed.
[info] Compiling main sources...
[warn] there were unchecked warnings; re-run with -unchecked for \
    details
[warn] one warning found
[info] Compilation successful.
[info]   Post-analysis: 257 classes.
[info] == compile ==
[info] 
[info] == copy-resources ==
[info] == copy-resources ==
[info] 
[info] == run ==
[info] Running org.goldenport.g3.Main \
    org.goldenport.g3.app.Split4
G3Pipe.do_Process1(class \
    org.goldenport.g3.G3StartNode):List(WrappedArray(org.goldenport.g3.app.Split4)) \
G3StartNode.do_Process: \
    WrappedArray(org.goldenport.g3.app.Split4)
G3Publish.do_Process
G3Context.send
lookup = [compute], Some(org.goldenport.g3.G3AgentNode@1d01844a)
G3Pipe.do_Process1(class org.goldenport.g3.G3AgentNode):List(1)
G3Pipe.do_Process2(class org.goldenport.g3.G3AgentNode):1/List()
G3AgentNode.do_Process: 1
r = 101
terminal = List(1)
G3Publish.do_Process
G3Context.send
lookup = [compute], Some(org.goldenport.g3.G3AgentNode@1d01844a)
G3Pipe.do_Process1(class org.goldenport.g3.G3AgentNode):List(2)
G3Pipe.do_Process2(class org.goldenport.g3.G3AgentNode):2/List()
G3AgentNode.do_Process: 2
r = 102
terminal = List(2)
G3Publish.do_Process
G3Context.send
lookup = [compute], Some(org.goldenport.g3.G3AgentNode@1d01844a)
G3Pipe.do_Process1(class org.goldenport.g3.G3AgentNode):List(3)
G3Pipe.do_Process2(class org.goldenport.g3.G3AgentNode):3/List()
G3AgentNode.do_Process: 3
r = 103
terminal = List(3)
G3Publish.do_Process
G3Context.send
lookup = [compute], Some(org.goldenport.g3.G3AgentNode@1d01844a)
G3Pipe.do_Process1(class org.goldenport.g3.G3AgentNode):List(4)
G3Pipe.do_Process2(class org.goldenport.g3.G3AgentNode):4/List()
G3AgentNode.do_Process: 4
r = 104
terminal = List(4)
G3Publish.do_Process
G3Context.send
lookup = [compute], Some(org.goldenport.g3.G3AgentNode@1d01844a)
G3Pipe.do_Process1(class org.goldenport.g3.G3AgentNode):List(5)
G3Pipe.do_Process2(class org.goldenport.g3.G3AgentNode):5/List()
G3AgentNode.do_Process: 5
r = 105
terminal = List(List(101, 102, 103, 104, 105))
terminal = List(5)
[info] == run ==
[success] Successful.
[info] 
[info] Total time: 2 s, completed 2010/04/10 6:49:13
TwitterScan3結果
> run org.goldenport.g3.app.TwitterScan3
[info] 
[info] == copy-resources ==
[info] == copy-resources ==
[info] 
[info] == compile ==
[info] Source analysis: 0 new/modified, 0 indirectly invalidated, \
    0 removed.
[info] Compiling main sources...
[info] Nothing to compile.
[info]   Post-analysis: 257 classes.
[info] == compile ==
[info] 
[info] == run ==
[info] Running org.goldenport.g3.Main \
    org.goldenport.g3.app.TwitterScan3
G3Pipe.do_Process1(class \
    org.goldenport.g3.G3StartNode):List(WrappedArray(org.goldenport.g3.app.TwitterScan3)) \
G3Pipe.do_Process2(class \
    org.goldenport.g3.G3StartNode):WrappedArray(org.goldenport.g3.app.TwitterScan3)/List() \
G3StartNode.do_Process: \
    WrappedArray(org.goldenport.g3.app.TwitterScan3)
G3Invoke.do_Process
G3Context.invoke
lookup = [demogon], \
    Some(org.goldenport.g3.G3ServiceNode@53015c53)
G3Pipe.do_Process1(class org.goldenport.g3.G3ServiceNode):List()
G3Pipe.do_Process2(class \
    org.goldenport.g3.G3ServiceNode):List()/List()
G3ServiceNode.do_Process: List()
2010/04/10 6:44:21 \
    org.apache.http.client.protocol.ResponseProcessCookies \
    processCookies
?x??: Invalid cookie header: "Set-Cookie: guest_id=1270849461120; \
    path=/; expires=Sun, 09 May 2010 21:44:21 GMT". Unable to \
    parse expires attribute: Sun, 09 May 2010 21:44:21 GMT
terminal = List(<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" \
    xmlns:georss="http://www.georss.org/georss" \
    xmlns="http://www.w3.org/2005/Atom">
  <title>Twitter / demogon</title>
  <id>tag:twitter.com,2007:Status</id>
<link type="text/html" href="http://twitter.com/demogon" \
    rel="alternate"/>
<link type="application/atom+xml" \
    href="http://twitter.com/statuses/user_timeline/demogon.atom" \
    rel="self"/>
  <updated>2010-04-09T21:44:21+00:00</updated>
<subtitle>Twitter updates from \
    &#20986;&#33538;&#27177;&#22826;&#37070; / \
    demogon.</subtitle>
    <entry>
<title>demogon: \
    &#33258;&#30001;&#12364;&#19992;&#12395;&#12388;&#12356;&#12383;&#12290;&#12524;&#12473;&#12488;&#12521;&#12531;&#12364;&#12356;&#12356;&#12290;</title> \
<content type="html">demogon: \
    &#33258;&#30001;&#12364;&#19992;&#12395;&#12388;&#12356;&#12383;&#12290;&#12524;&#12473;&#12488;&#12521;&#12531;&#12364;&#12356;&#12356;&#12290;</content> \
<id>tag:twitter.com,2007:http://twitter.com/demogon/statuses/9554547226</id>
      <published>2010-02-24T01:56:05+00:00</published>
      <updated>2010-02-24T01:56:05+00:00</updated>
<link type="text/html" \
    href="http://twitter.com/demogon/statuses/9554547226" \
    rel="alternate"/>
<link type="image/png" \
    href="http://s.twimg.com/a/1270842741/images/default_profile_5_normal.png" \
    rel="image"/>
      <author>
        <name>&#20986;&#33538;&#27177;&#22826;&#37070;</name>
      </author>
    </entry>
    <entry>
<title>demogon: \
    &#12420;&#12387;&#12401;&#12426;&#33258;&#30001;&#12364;&#19992;&#12398;&#12452;&#12479;&#12522;&#12450;&#12531;&#12394;&#12435;&#12363;&#12393;&#12358;&#12290;</title> \
<content type="html">demogon: \
    &#12420;&#12387;&#12401;&#12426;&#33258;&#30001;&#12364;&#19992;&#12398;&#12452;&#12479;&#12522;&#12450;&#12531;&#12394;&#12435;&#12363;&#12393;&#12358;&#12290;</content> \
<id>tag:twitter.com,2007:http://twitter.com/demogon/statuses/9554480122</id>
      <published>2010-02-24T01:54:33+00:00</published>
      <updated>2010-02-24T01:54:33+00:00</updated>
<link type="text/html" \
    href="http://twitter.com/demogon/statuses/9554480122" \
    rel="alternate"/>
<link type="image/png" \
    href="http://s.twimg.com/a/1270842741/images/default_profile_5_normal.png" \
    rel="image"/>
      <author>
        <name>&#20986;&#33538;&#27177;&#22826;&#37070;</name>
      </author>
    </entry>
    <entry>
<title>demogon: \
    &#27494;&#34101;&#23567;&#26441;&#12395;&#30528;&#12356;&#12383;&#12290;&#19968;&#26479;&#12420;&#12429;&#12358;&#12290;</title> \
<content type="html">demogon: \
    &#27494;&#34101;&#23567;&#26441;&#12395;&#30528;&#12356;&#12383;&#12290;&#19968;&#26479;&#12420;&#12429;&#12358;&#12290;</content> \
<id>tag:twitter.com,2007:http://twitter.com/demogon/statuses/9554430782</id>
      <published>2010-02-24T01:53:26+00:00</published>
      <updated>2010-02-24T01:53:26+00:00</updated>
<link type="text/html" \
    href="http://twitter.com/demogon/statuses/9554430782" \
    rel="alternate"/>
<link type="image/png" \
    href="http://s.twimg.com/a/1270842741/images/default_profile_5_normal.png" \
    rel="image"/>
      <author>
        <name>&#20986;&#33538;&#27177;&#22826;&#37070;</name>
      </author>
    </entry>
</feed>
)
G3Pipe.do_Process1(class org.goldenport.g3.G3Agent):List(<?xml \
    version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" \
    xmlns:georss="http://www.georss.org/georss" \
    xmlns="http://www.w3.org/2005/Atom">
  <title>Twitter / demogon</title>
  <id>tag:twitter.com,2007:Status</id>
<link type="text/html" href="http://twitter.com/demogon" \
    rel="alternate"/>
<link type="application/atom+xml" \
    href="http://twitter.com/statuses/user_timeline/demogon.atom" \
    rel="self"/>
  <updated>2010-04-09T21:44:21+00:00</updated>
<subtitle>Twitter updates from \
    &#20986;&#33538;&#27177;&#22826;&#37070; / \
    demogon.</subtitle>
    <entry>
<title>demogon: \
    &#33258;&#30001;&#12364;&#19992;&#12395;&#12388;&#12356;&#12383;&#12290;&#12524;&#12473;&#12488;&#12521;&#12531;&#12364;&#12356;&#12356;&#12290;</title> \
<content type="html">demogon: \
    &#33258;&#30001;&#12364;&#19992;&#12395;&#12388;&#12356;&#12383;&#12290;&#12524;&#12473;&#12488;&#12521;&#12531;&#12364;&#12356;&#12356;&#12290;</content> \
<id>tag:twitter.com,2007:http://twitter.com/demogon/statuses/9554547226</id>
      <published>2010-02-24T01:56:05+00:00</published>
      <updated>2010-02-24T01:56:05+00:00</updated>
<link type="text/html" \
    href="http://twitter.com/demogon/statuses/9554547226" \
    rel="alternate"/>
<link type="image/png" \
    href="http://s.twimg.com/a/1270842741/images/default_profile_5_normal.png" \
    rel="image"/>
      <author>
        <name>&#20986;&#33538;&#27177;&#22826;&#37070;</name>
      </author>
    </entry>
    <entry>
<title>demogon: \
    &#12420;&#12387;&#12401;&#12426;&#33258;&#30001;&#12364;&#19992;&#12398;&#12452;&#12479;&#12522;&#12450;&#12531;&#12394;&#12435;&#12363;&#12393;&#12358;&#12290;</title> \
<content type="html">demogon: \
    &#12420;&#12387;&#12401;&#12426;&#33258;&#30001;&#12364;&#19992;&#12398;&#12452;&#12479;&#12522;&#12450;&#12531;&#12394;&#12435;&#12363;&#12393;&#12358;&#12290;</content> \
<id>tag:twitter.com,2007:http://twitter.com/demogon/statuses/9554480122</id>
      <published>2010-02-24T01:54:33+00:00</published>
      <updated>2010-02-24T01:54:33+00:00</updated>
<link type="text/html" \
    href="http://twitter.com/demogon/statuses/9554480122" \
    rel="alternate"/>
<link type="image/png" \
    href="http://s.twimg.com/a/1270842741/images/default_profile_5_normal.png" \
    rel="image"/>
      <author>
        <name>&#20986;&#33538;&#27177;&#22826;&#37070;</name>
      </author>
    </entry>
    <entry>
<title>demogon: \
    &#27494;&#34101;&#23567;&#26441;&#12395;&#30528;&#12356;&#12383;&#12290;&#19968;&#26479;&#12420;&#12429;&#12358;&#12290;</title> \
<content type="html">demogon: \
    &#27494;&#34101;&#23567;&#26441;&#12395;&#30528;&#12356;&#12383;&#12290;&#19968;&#26479;&#12420;&#12429;&#12358;&#12290;</content> \
<id>tag:twitter.com,2007:http://twitter.com/demogon/statuses/9554430782</id>
      <published>2010-02-24T01:53:26+00:00</published>
      <updated>2010-02-24T01:53:26+00:00</updated>
<link type="text/html" \
    href="http://twitter.com/demogon/statuses/9554430782" \
    rel="alternate"/>
<link type="image/png" \
    href="http://s.twimg.com/a/1270842741/images/default_profile_5_normal.png" \
    rel="image"/>
      <author>
        <name>&#20986;&#33538;&#27177;&#22826;&#37070;</name>
      </author>
    </entry>
</feed>
)
G3Pipe.do_Process2(class org.goldenport.g3.G3Agent):<?xml \
    version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" \
    xmlns:georss="http://www.georss.org/georss" \
    xmlns="http://www.w3.org/2005/Atom">
  <title>Twitter / demogon</title>
  <id>tag:twitter.com,2007:Status</id>
<link type="text/html" href="http://twitter.com/demogon" \
    rel="alternate"/>
<link type="application/atom+xml" \
    href="http://twitter.com/statuses/user_timeline/demogon.atom" \
    rel="self"/>
  <updated>2010-04-09T21:44:21+00:00</updated>
<subtitle>Twitter updates from \
    &#20986;&#33538;&#27177;&#22826;&#37070; / \
    demogon.</subtitle>
    <entry>
<title>demogon: \
    &#33258;&#30001;&#12364;&#19992;&#12395;&#12388;&#12356;&#12383;&#12290;&#12524;&#12473;&#12488;&#12521;&#12531;&#12364;&#12356;&#12356;&#12290;</title> \
<content type="html">demogon: \
    &#33258;&#30001;&#12364;&#19992;&#12395;&#12388;&#12356;&#12383;&#12290;&#12524;&#12473;&#12488;&#12521;&#12531;&#12364;&#12356;&#12356;&#12290;</content> \
<id>tag:twitter.com,2007:http://twitter.com/demogon/statuses/9554547226</id>
      <published>2010-02-24T01:56:05+00:00</published>
      <updated>2010-02-24T01:56:05+00:00</updated>
<link type="text/html" \
    href="http://twitter.com/demogon/statuses/9554547226" \
    rel="alternate"/>
<link type="image/png" \
    href="http://s.twimg.com/a/1270842741/images/default_profile_5_normal.png" \
    rel="image"/>
      <author>
        <name>&#20986;&#33538;&#27177;&#22826;&#37070;</name>
      </author>
    </entry>
    <entry>
<title>demogon: \
    &#12420;&#12387;&#12401;&#12426;&#33258;&#30001;&#12364;&#19992;&#12398;&#12452;&#12479;&#12522;&#12450;&#12531;&#12394;&#12435;&#12363;&#12393;&#12358;&#12290;</title> \
<content type="html">demogon: \
    &#12420;&#12387;&#12401;&#12426;&#33258;&#30001;&#12364;&#19992;&#12398;&#12452;&#12479;&#12522;&#12450;&#12531;&#12394;&#12435;&#12363;&#12393;&#12358;&#12290;</content> \
<id>tag:twitter.com,2007:http://twitter.com/demogon/statuses/9554480122</id>
      <published>2010-02-24T01:54:33+00:00</published>
      <updated>2010-02-24T01:54:33+00:00</updated>
<link type="text/html" \
    href="http://twitter.com/demogon/statuses/9554480122" \
    rel="alternate"/>
<link type="image/png" \
    href="http://s.twimg.com/a/1270842741/images/default_profile_5_normal.png" \
    rel="image"/>
      <author>
        <name>&#20986;&#33538;&#27177;&#22826;&#37070;</name>
      </author>
    </entry>
    <entry>
<title>demogon: \
    &#27494;&#34101;&#23567;&#26441;&#12395;&#30528;&#12356;&#12383;&#12290;&#19968;&#26479;&#12420;&#12429;&#12358;&#12290;</title> \
<content type="html">demogon: \
    &#27494;&#34101;&#23567;&#26441;&#12395;&#30528;&#12356;&#12383;&#12290;&#19968;&#26479;&#12420;&#12429;&#12358;&#12290;</content> \
<id>tag:twitter.com,2007:http://twitter.com/demogon/statuses/9554430782</id>
      <published>2010-02-24T01:53:26+00:00</published>
      <updated>2010-02-24T01:53:26+00:00</updated>
<link type="text/html" \
    href="http://twitter.com/demogon/statuses/9554430782" \
    rel="alternate"/>
<link type="image/png" \
    href="http://s.twimg.com/a/1270842741/images/default_profile_5_normal.png" \
    rel="image"/>
      <author>
        <name>&#20986;&#33538;&#27177;&#22826;&#37070;</name>
      </author>
    </entry>
</feed>
/List()
content = List()
content = List(demogon: ???R???u???????B???X?g???????????B)
xxx = demogon: ???R???u???????B???X?g???????????B
AtomContent = List(demogon: ???R???u???????B???X?g???????????B)
content = List(demogon: ???????R???u??C?^???A????????B)
xxx = demogon: ???????R???u??C?^???A????????B
AtomContent = List(demogon: ???????R???u??C?^???A????????B)
content = List(demogon: ????????????????B??t????B)
xxx = demogon: ????????????????B??t????B
AtomContent = List(demogon: ????????????????B??t????B)
G3Pipe.do_Process1(class \
    org.goldenport.g3.G3Agent):List(<feed><id>tag:twitter.com,2007:Status</id><title>Twitter \
    / demogon</title><updated>Sat Apr 10 06:44:21 JST \
    2010</updated><entry><title>demogon: \
    ???R???u???????B???X?g???????????B</title><link/><link/><content>demogon: \
 \
    ???R???u???????B???X?g???????????B</content></entry><entry><title>demogon: \
 \
    ???????R???u??C?^???A????????B</title><link/><link/><content>demogon: \
 \
    ???????R???u??C?^???A????????B</content></entry><entry><title>demogon: \
    ????????????????B??t????B</title><link/><link/><content>demogon: \
    ????????????????B??t????B</content></entry></feed>)
G3Pipe.do_Process2(class \
    org.goldenport.g3.G3Agent):<feed><id>tag:twitter.com,2007:Status</id><title>Twitter \
    / demogon</title><updated>Sat Apr 10 06:44:21 JST \
    2010</updated><entry><title>demogon: \
    ???R???u???????B???X?g???????????B</title><link/><link/><content>demogon: \
 \
    ???R???u???????B???X?g???????????B</content></entry><entry><title>demogon: \
 \
    ???????R???u??C?^???A????????B</title><link/><link/><content>demogon: \
 \
    ???????R???u??C?^???A????????B</content></entry><entry><title>demogon: \
    ????????????????B??t????B</title><link/><link/><content>demogon: \
    ????????????????B??t????B</content></entry></feed>/List()
==> <feed><id>tag:twitter.com,2007:Status</id><title>Twitter / \
    demogon</title><updated>Sat Apr 10 06:44:21 JST \
    2010</updated><entry><title>demogon: \
    ???R???u???????B???X?g???????????B</title><link/><link/><content>demogon: \
 \
    ???R???u???????B???X?g???????????B</content></entry><entry><title>demogon: \
 \
    ???????R???u??C?^???A????????B</title><link/><link/><content>demogon: \
 \
    ???????R???u??C?^???A????????B</content></entry><entry><title>demogon: \
    ????????????????B??t????B</title><link/><link/><content>demogon: \
    ????????????????B??t????B</content></entry></feed>
terminal = \
    List(<feed><id>tag:twitter.com,2007:Status</id><title>Twitter \
    / demogon</title><updated>Sat Apr 10 06:44:21 JST \
    2010</updated><entry><title>demogon: \
    ???R???u???????B???X?g???????????B</title><link/><link/><content>demogon: \
 \
    ???R???u???????B???X?g???????????B</content></entry><entry><title>demogon: \
 \
    ???????R???u??C?^???A????????B</title><link/><link/><content>demogon: \
 \
    ???????R???u??C?^???A????????B</content></entry><entry><title>demogon: \
    ????????????????B??t????B</title><link/><link/><content>demogon: \
    ????????????????B??t????B</content></entry></feed>)
[info] == run ==
[success] Successful.
[info] 
[info] Total time: 2 s, completed 2010/04/10 6:44:22

2010年3月26日金曜日

By-name Parameters

先々週に、メインで使っていたThinkPadがとうとう壊れてしまった。ファンの故障なので修理できないこともなさそうだったけど、5年程使っているマシンでディスクも50GBと少なく、メモリ2GBは今となってはEclipse&Scalaの組み合わせには到底足りないので、観念して新しいものを買うことにした。

最近、周りの人のMac率が非常に高いこともあり、スペックが魅力的だったのでiMacにしてみた。到着したのが先週の金曜。ちょうど一週間経ったけど、やっと作業環境が整備できてきた。(そういえば、ブログもこの記事がiMacからの初投稿になる。)

CPUはCore i7。4コアで、Hyper Threading機能により擬似的に8コアになる。今後、マルチコアを前提としたプログラミングモデルに移行することが予想されるので、検証の基盤として期待している。

ということで、8コアの振舞いを見たいのと、ScalaのActorを試したいということもあり簡単な並列ソートプログラムを作って動かしてみた。このプログラムについてはいずれ取り上げるかもしれないけど、今回は並列ソートプログラムの性能測定用に作った以下の小さなプログラムがテーマ。

Tmr.scala
object Tmr {
  def run(proc: => Unit) {
    System.gc
    val start = System.currentTimeMillis
    proc
    val end = System.currentTimeMillis
    println("elapse(ms) = " + (end - start))
  }
}

このプログラムはScalaの特徴的な機能であるBy-name Parameterを使っている。By-name Parameterは関数リテラル/クロージャと字句的なトリックを使って、メタな機構を導入せずに擬似的な制御構文の拡張を可能にした、まさにスケーラブルを目指すプログラミング言語Scalaの影の主役ともいうべき重要な機能である。大事なのはrunメソッドのパラメタ「proc: => Unit」の所。通常の関数パラメタだとパラメタがない場合「proc: () => Unit」となるところを、パラメタリストを省略している。この省略記法をScalaではBy-name parameterと呼んでいる。

Tmr.scalaは小さなプログラムなので、ひと目で何をしているか分かると思う。

でもできることはすごい。

基本的な使い方は次のようになる。この場合は小さなソートが5msで終了している。

scala> Tmr.run(List(2, 1, 3).sort(_ < _))
elapse = 5

何となく、見逃してしまうかもしれないけど、関数リテラルや高階関数を持たないJavaなどプログラミング言語ではこういう書き方はできない。なぜなら、Tmr.runメソッドに制御が移る前に、引数の式が評価されてしまうから。地味だけど、関数型言語ならではの機能なのである。

試しに1000msスリープする式を引数にTmr.runメソッドを実行してみると経過時間が1003秒となった。妥当なところである。

scala> Tmr.run(Thread.sleep(1000))
elapse = 1003

ここまでの機能だけだと「影の主役」はちょっと大げさ。本当に感心するのは、以下のように引数として制御構造のボディのような複数の文の列を書くことができることである。このことによって擬似的な制御構文が追加可能になっている。

scala> Tmr.run {
     |   val list = List(2, 1, 3)
     |   list.sort(_ < _)
     | }
elapse = 2

この記法を支えているのは2つの特例。まず、パラメタが1つのパラメタリストは「(」「)」だけでなく、「{」「}」が使えるという特例がある。また、先ほど説明したようにBy-name Parameterはパラメタがない関数リテラルでパラメタリストを省略することができるという特例である。この2つの特例が字句上の工夫で、組み合わせて使用すると擬似的に制御構文を追加することができる。

こういった字句上の工夫というかトリックが色々と入っているのがScalaの特徴で、一筋縄ではいかないところである。プログラミング言語は、こういった字句上の工夫が使い勝手に大きく影響するということが、Scalaを使ってみるとよくわかる。