2009年4月24日金曜日

技術者/プログラマのためのモナドと圏論 第1回 行ってきた

またまた行ってきた。前回(論理・ラムダ・圏)、タイトルに「圏」とありながら、実際は圏の話がほとんどできなかったことを受けてはじまった、新シリーズ。細かいところや背景知識なんてわかるわけがない(数学ほとんどやってないし)ので、わかった範囲で書きとめておく。間違いまくってる可能性大。

全体的に
教科書的に進めようかと思っていたそうだが、檜山さんが体調不良で方針転換。形式的なものより、お絵かきメインで進めることになった。

というわけで、第1回の予定はこうでした。
  1. まえおき/まえせつ
  2. いろいろな図の描き方
  3. 圏の定義と実例
  4. 休憩
  5. 行列の圏で遊ぶ
  6. コレクション・データ
実際は、3の途中まで(休憩はやったよ!)で終了しました:-D

まえおき/まえせつ
シリーズ全体の目標としては…
  1. 圏の基本概念を学ぶ
  2. モナドの実例をたくさん知る
  3. モナド法則に慣れる
  4. モナドのクライスリ圏の作り方を知る
  5. モナドがモノイドであることを理解する
こんなとこで、4くらいまではいきたいそうだ。自分としては、ここまでいければ当面満足なので、しばらくは独学であがかなくてもよさそうなのが非常に嬉しい。

今日の目標は…
  1. 圏の定義と直感的なイメージの両方を知る
  2. お絵かき実習する
  3. 圏の実例をいじって体になじませる
  4. 横道にそれて面白そうな話題を紹介する
モナドの意義
今のところさっぱりわかっていないモナドの意義。計算の観点からは、
  • コレクション・データ構造に統一的見方を与える、らしい
  • 関数計算を拡張・一般化できる、らしい
  • 文や式の構文論を整理できる
どれもこれも現段階では具体例がわからないのでいまいちピンと来ないんだけど、1番目と2番目はHaskellやらの関数型言語をさわる際に、(気持ち・理解的な)役に立ちそう。特に、関数計算に部分関数、エラー・例外、状態・副作用などを導入する形で拡張するのに出てくる、というあたりはとても興味深い。楽しみだ。
圏論の観点からの意義は、知らない語ばっかりでいまいちよくわからなかった。好奇心の観点からは、色んなものがモナドでっせーみたいな話のようで、もしかしたら後で出てくる「色んなものを圏の枠組みで見ると同じように見え、同じような問題が同じように解ける」とかいう話と関係しているのかもしれない。が、よくわからない。

回路とか関数とかの書き方
[ ボックス図 ]
関数をブラックボックスとして記述。


[ オダンゴ図 ]
ボックス図の省略版。箱を書くのがめんどうくさいので生まれたらしい。ボックス図もそうだが、ブラックボックスの内部を書くこともできる。


[ ストリング図 ]
オダンゴが小さくなった感じ。

さて、以下の3つの関数で…
  • f(x, y) = 3x + y
  • g(z) = z + 1
  • h(x) = x * x + x
f(x, y) と g(z) のComposition(結合・合成)をオダンゴ図で書いてみる。

きたねええええええ!
…h(x) をオダンゴ図で書いてみる。

h が少し面白いのは、普通に書こうとすると、x が1つしかないので2乗などができずに困ってしまう点。xを複製するようなワイヤリングをすればいいのだけど、このようにに、引数も消尽され得るリソースだと考えると、コンピュータによる計算のリソースセンシティブな部分をうまく表現できるようだ。これは、論理の世界で考えると線形論理に対応しているらしい。なんか前回そんなことを言っていた。この複製(や交差)なんかのワイヤリングを計算と考えることもできる。
テキスト表記(Symbolic Calculationというらしい)だとわからなかったことが、絵算(Pictorial Calculationというらしい)だとわかる、というのも面白いところ。これらは一長一短があるので、状況によって使い分けるといい、とのこと。

引数・戻り値の数
引数は複数あるが、戻り値は常に1つでないといけないのか?
そんなことはない。実際、先ほどの f と g の合成関数の例では、ボックス(ダンゴ)の位置を少し変えれば(合成関数自体は同じでも)簡単に戻り値が複数あるような関数に変更できる。


が、人間は多値(特に戻り値)を扱うのはあまり得意ではないので、1つにしていることが多い。複数必要になった場合には、タプルで対応する。

f: A, B => C

これはAとBの2引数を取り、Cを返す関数だが、

f': A×B => C

このような、A×B型のタプルを引数として取る関数とまぁ同じと考えて良い。掛け算記号の×は、タプル型をあらわす。タプルは色んな表記法があり、(特にプログラミング言語では)()だったり[]だったり{}だったりする。プログラミング言語から中立であることを示しつつタプルを表記するため、<>をよく使うらしい。

f'(<x, y>) = f(x, y)
g'(x, y) = g(<x, y>)

これは、絵算で言うと、ワイヤーを束ね(て1本にす)ることに相当する。フラットケーブルとかそういうやつ。こんな感じ。

ダンゴ図で書くときは、右下にちっちゃく書いてるように、ゴムみたいなので束ねるように書いたりする。

ストリング図とアロー図
今まで書いてきた、ボックス図・オダンゴ図は、どちらもストリング図という種類になるが、一般的に図(Diagram)というと、たいていはアロー図を指す。以下のような関数について考えてみる。
f: A => B

[ ストリング図 ]

関数が箱(または点)、引数や戻り値が矢印(アロー)になっている。

[ アロー図 ]

関数が矢印(アロー)、引数や戻り値が点になっている。
# なんか、圏っぽい。てか圏のセミナーでした。

この2つの違いは単に趣味的なものではなく、大きな差である、らしい。ちなみに、アロー図はペースティング(Pasting)図や球状(Globular)図とも言うらしいが、これは絵を2次~3次元に拡張した場合に円や球に見えるから、だそう。スライドには高次圏とか書いてあるし、とりあえず今はわからなさそうなので飛ばしてしまう。

矢印で何を表すのか
矢印は、色んな意味で使われるので注意が必要。違うことが同じ記号や同じ言葉で語られるため、混乱しやすい。これから出てくるものとしては…
  • 要素関の対応関係(集合の要素、元同士の対応関係)
  • 集合同士の対応関係(集合同士の対応関係、写像?)
  • 関手(どうやら圏と圏の対応のようなものらしい)
  • 自然変換(関手同士の対応のようなものに見えるが)
こんなものが、すべて矢印であらわされる。

圏、しりとり圏
はじめての圏論で書かれていた、しりとり圏をもとに圏を説明。以下、しりとり圏の構成要素の転記。
  1. 対象の集合: H -- ひらがな文字全体の集合
  2. 射の集合: HStr -- ひらがな文字列全体の集合
  3. 域 dom と余域 cod : first, last : HStr => H(最初と最後の文字)
  4. 恒等 id : unit : H => HStr(1文字からなる文字列)
  5. 結合(composition) : しりとり結合 s;t は last(s) = first(t) のときだけ定義される
まずは、圏のなんとなーくのイメージをつかむ。といっても、今まで出てきた話の延長線上にある。というか、もちろんそういう風に話を持っていっているんだと思います :-D

対象というのは、アローダイアグラムでの点みたいなもの。射というのは、アローダイアグラムでの矢印のようなもの。域というのは、矢印の出発点側にくっついている点のようなもの。余域というのは、アローダイアグラムの終点側についている点のようなもの。

結合は、(つなげられる)矢印同士をつなげること。

# f;g は、fとgの結合です。

恒等は…ものすごくぶっちゃけていえば、ある点から出て同じ点に帰ってくる矢印みたいなもの。


で、同じ点に帰ってくる矢印なんだから、他の矢印と結合しても、元の矢印(がたどり着く点)は変わらないでしょう、というようなもの。

なんかこんなイメージ。これを、しりとりにあてはめると、こんな感じになる。

# 実際には、点や線はもっとたくさんある。
以下、圏の構成要素がしりとり(圏)において何に対応しているのか考える。

[ 対象 ]
対象は、ここで言えばひらがな1文字1文字のことにしてある。'あ' とか 'い' とかそういうやつ。なので、対象の集合は、ひらがな文字全体の集合になる。

[ 射 ]
射は、対象、つまりひらがな文字の間に引く矢印みたいなもので、今回は(ひらがな文字を並べた)文字列、ということにする。射の集合は、ひらがな文字列全体の集合。

[ 域と余域 ]
域は、文字列の先頭の文字とする。"きまいら"の域は、'き'。余域は、文字列の末尾の文字とする。"きまいら"の余域は、'ら'。

[ 結合 ]
結合は、射(つまり文字列)同士をくっつけることで、定義では「1つめの射の余域」と「2つめの射の域」が同じでなくては結合できない。これを今までの定義で言い換えると、「1つめの文字列の末尾の文字」と、「2つめの文字列の最初の文字」が同じでなければ結合できない、となる。つまり、しりとりできる文字列しか結合できない。今回の結合では、(しりとり可能な)文字列同士を結合し、重複した文字を(1つ)取り除くこと、とする。

"きまいら";"らいおん" => "きまいらいおん"

こんな感じ。なんかしりとりっぽくなってきた。

[ 恒等 ]
恒等は、射と結合しても変わらない射なので、これまで決めてきたことから考えると、長さ1の文字列になる。"あ"とか"い"とか。例としては…

"きまいら";"ら" => "きまいら"
"き";"きまいら" => "きまいら"

こんな感じ。少し前のざっくりした説明だと、同じ点に帰ってくる矢印みたいなもん、って書いてたんだけど、同じ点に帰ってくる射(文字列)でも、"きつつき" なんかは恒等にはならない。これは、他の射と結合した場合に違う射になってしまい、定義を満たさないから。たぶん。
# 射が同じってのは、定義しなくても大丈夫なんだろうか?文字列が同じって考えていいんだよなぁ…?

恒等自体は射(矢印)なので、"あ"とか"い"とかは、文字ではなく文字列なのに注意。

圏の法則
ここで、圏の法則をば。下つき文字の書き方がわからないので unit はプログラミング言語っぽく書いちまいます。今作ったしりとりの圏が、この法則を満たしていることを確認する。
  1. first(unit(x)) = last(unit(x)) = x (x∈H)
  2. first(s;t) = first(s), last(s;t) = last(t)
  3. (s;t);u = s;(t;u)
  4. x = first(s), y = last(s) なら、unit(x);s = s;unit(y) = s
[ 1つめ ]
  • 対象xの恒等(射)の域は、対象xに等しい。
  • 対象xの恒等(射)の余域は、対象xに等しい。
と言っている。しりとりで文字をあてはめてみると…
  • 文字: 'あ'
  • 対応する1文字の文字列: "あ"
  • 文字列 "あ" の域: 'あ'
というわけで、きちんと法則を満たしている。当たり前か。末尾も同じ。

[ 2つめ ]
  • 射sと射tを結合した射の域は、射sの域に等しい
  • 射sと射tを結合した射の余域は、射tの余域に等しい
これもしりとりで文字をあてはめてみると…
  • 射s: "きまいら"
  • 射t: "らいおん"
  • 射sとtの結合: "きまいらいおん"
  • 射sとtの結合の域: 'き'
  • 射sの域: 'き'
  • 射sとtの結合の余域: 'ん'
  • 射tの余域: 'ん'
というわけで、成り立っている。

[ 3つめ ]
  • 射sと射tを結合し、それに射uを結合したものは、射sに射tと射uの結合を結合したものと同じ
全然わかりやすくなってないな…。しりとり。
  • 射s: "でんき"
  • 射t: "きまいら"
  • 射u: "らいおん"
  • (s;t);u: ("でんき";"きまいら");"らいおん = "でんきまいら";"らいおん" = "でんきまいらいおん"
  • s(t;u): "でんき";("きまいら";"らいおん") = "でんき";"きまいらいおん" = "でんきまいらいおん"
成り立っている。なんかむなしくなってきた。

[ 4つめ ]
  • 射sの域がxであり、余域がyであるならば…
  • xの恒等(射)unit(x) と 射sの結合は、射sに等しい
  • 射sとyの恒等(射)unit(y) の結合は、射sに等しい
しりとりで。
  • 射s: "きまいら"
  • 射sの域 x: 'き'
  • 射sの余域 y: 'ら'
  • xの恒等 : "き"
  • yの恒等 : "ら"
  • xの恒等と射sの結合 : "き";"きまいら" = "きまいら"
  • 射sとyの恒等の結合 : "きまいら";"ら" = "きまいら"
OK。しりとり圏はちゃんとした圏。らしい。

unitで混乱
セミナーの最後に、unitに関して質問があり、微妙に混乱した。内容は…
構成要素には、unit が「対象」から「射」への写像(って言ってたかなぁ…)だとされているけど、絵で見ると恒等というのは「対象」から「対象」への射になっているので、別のものですよね?というもの。
これはたぶん…
  • unit: unit(x) で「対象xに関する恒等(射)」をあらわすもの、つまり対象から射への写像
  • unit(x) : ある対象xに関する恒等(射)、unitに具体的に対象が与えられたもの
というものがごっちゃになって混乱した、という話だと思った。あってるのだろうか。

Map Finite Ordinals
2つめの圏の例。こんな対象を考えてみる。
  • [0] = {}
  • [1] = {1}
  • [2] = {1, 2}
  • [3] = {1, 2, 3}
  • [4] = {1, 2, 3, 4}
射の具体的な内容は、要素間の対応関係(の組み合わせ)になる。ここで、対象間の矢印と要素間の矢印は違うものをあらわしていることに注意。

具体的に域が[2], 余域が[3]であるような射について考えてみると、射の数は9つになる。これは、実際に書いてみればわかる(が省略!)。[1]も加えて射の数を考えてみると…

こんな感じ。一般化すると、[n] から [m]への射の数は、mのn乗になる。

# この先はハイスピードでとばしまくり。

部分圏
MapFOの中でも、こんなものは部分圏になる。
  • InjFO 単射の圏
  • IsoFO 同型射の圏
  • IncFO 包含写像の圏
IncFOよくわからなかった。

関手
スライド0.2秒くらい、説明ゼロでした :-D
例を見ると、HSiri => KSiri が関手になっているので、圏を「対象」として見たとき、つまり圏の圏を考えたときの射が関手、ということなんだと思う。けど、具体的なイメージが…。

同じに見える色んな問題
スライドに書いてある色んな問題たちが同じようにみなせるらしい。が、どれとどれが同じなのか、はたまた全部同じなのか、よくわからなかった。

時間切れ、終了。なんか飲み会行ってテンションあがったので頑張って書いてみた。他の出席者の方のように、「あー、なるほど、あれがあれね!」とか「じゃああれはこうですか?」みたいなつっこんだ話は全然できず、道のりの長さが感じられたんだけど、それはそれでよかった。わかってること勉強しにいってもしょうがないしね。もう少し初級者向け(幻想?)の圏論読書会も立ち上がるかもしれない雰囲気。

2009年4月18日土曜日

アーキテクトの審美眼

MS萩原さんのセッション。同名の書籍(元ネタはDBマガジンの同名の連載)が元になっていて、そのダイジェスト版+最新の動向の補足、という形。
とても示唆に富んでいるんだけど、抽象的な上に、1つ1つのトピックがめちゃめちゃ重たいので、きちんと理解しようとすると調べる量がとても多くなってしまう上に、そもそも理解しきるのが難しすぎる。とりあえずは現段階の自分の稚拙な理解をもとに、こんなことじゃないかなーと補足しながら書いていく。

アーキテクチャの原則
アーキテクトにとっては、アーキテクチャの原則を理解することが重要。アーキテクチャの原則は、インフラなどが変化しても変わらず、クラウドでも通用する。

アーキテクチャ先行定義の原則
アーキテクチャは先行定義せねばならぬ、という原則。アーキテクチャ設計を含むソフトウェア開発は、こんな風な流れになるはず。


モデリングとアーキテクチャ設計の双方のインプットとして、システムの機能要求・非機能要求があげられていた。

■ モデリング
この部分は、システム化対象(ドメイン)を何らかのパラダイム(DOA/OOAなど)でモデル化する作業だと思われる。ドメインモデリングであれば、主に機能要件がそのインプットとなりそうだ。

■ アーキテクチャ設計
アーキテクチャパターンやリファレンスモデルをもとに、機能/非機能要件を満たしうるアーキテクチャを整備していく作業だと思われる。アーキテクチャ設計では、例として以下のような項目を(仮)決めしていくようだ。
# メモしきれなかったので一部のみ ^^;
  • プラットフォーム
  • フレームワーク
  • データモデル
  • トランザクションモデル
  • セキュリティ
[メモ]
図では、モデリングとアーキテクチャ設計が並列になっているが、モデリングとアーキテクチャ設計を完全に独立で実施できるとは思えない。アーキテクチャ設計にフレームワークやデータモデルが入っていることから、お互いになんらかの関係があることを示唆している気もする。もしかしたら、順序関係や依存関係を定義した図ではないのかもしれないし、マッピング/設計のフェーズがそれを包含しているのかもしれないが、セッションからはよくわからなかった。

個人的にここで重要だと思うのは、システム開発にはなんらかの形での「システム化対象のモデリング」が存在することを明示している点。そして、DOAやOOAを包含して「モデリング」としている点。データモデリングやオブジェクト指向モデリングをしているんじゃなく、「システム化対象」を「データの観点」や「オブジェクトの観点」からモデリングしているんだ、というのは、個人的になんだかしっくりくる。
[/メモ]

データアーキテクチャの原則
データには、マスターデータとトランザクションデータが存在するが、それぞれのデータには特性があり、扱う方法にも原則がある、という話。

■ マスターデータ
  • 複数のアプリケーションで共通的に使われるデータ
  • 論理的にはアプリケーションの外に出すのが原則

■ トランザクションデータ
  • 属性として「時間」を持っている
  • 追加(新規作成)のみで、変更も削除もされない
  • ETLなどでDWHやDMにぶちこまれる


# なんかこんな感じだった気がする。
実際は、特にマスターデータに関してはこのようになっていないことが多い。これは、マスターデータ自体の共通性は高いのだが、ビューが異なると必要な属性が異なってくる(属性にバリエーションがある)ため、Factor outしづらいのが原因。
なお、マスターデータは論理的に一箇所で管理すればよく、必ずしも物理的に一箇所で管理する必要はない。

[メモ]
  • マスター、トランザクションの区分はデータモデリングの王道中の王道。THでもTMでも、定義の厳密性や名前の違い(イベント/リソースなど)こそあれ、基本的なスタンスは同じ。
  • マスターデータをアプリケーションにまたがって共通的に持つ、というのは、最近DOA界隈(と言ってしまっていいのだろうか)ではやりのMDM(Master Data Management)と同じですね。データ総研の椿さんも同じこと言ってました。
  • マスターデータがコンテキストごとに属性のバリエーションを持つ、という問題を解決する方法は明示されなかった気がするんだけど、どのように考えているんだろうか?「外だしする=共通性として認識する」、「コンテキスト(アプリケーションやビュー)ごとに持つ=可変性として認識する」と考えると、結局は共通性と可変性の認識の問題になる気がする。負の可変性とか。この境界は、時間の経過や事業領域の変更でも変わっていくのだと思われる。
  • 物理的に一箇所にする(大福帳DB?)のではなく、論理的に一箇所にすれば良い、というのは現実を考えると非常に重要だと思う。
[/メモ]

Entity(個体)識別の問題
Entityはそれ単独(そのものが持つ本質的な特性)では決まらず、他との相対的な関係でしか決定できない。Relationshipは関数従属性であり、ビジネスから決まる、とも言っていた。

[メモ]
これは、データモデリング(OOも)の永遠の課題である、個体(Entity)をどうやって認識するのか、という話だと思うが、CoddのRelationalモデルとChecnのEntity-RelationshipモデルはどちらもEntityの識別方法を与えてくれない。詳しくは忘れてしまったけど、そもそもRelationalModelにはEntityなんてなかったと思うし。ChenのE-Rモデルでは、Eのまともな定義がないため、抽出の役に立たない。
TMとTH、両方のセミナーを受けたことがあるんだけど、ChenのERモデルが工学的(実務的?)に問題があることまでは共通の認識として持っていて、それぞれに定義を補足した上で抽出手順を決めている。詳しくはそれぞれのモデリング技法をどぞ!

追記:
書籍の方では、データモデリングにおいては既存の帳票や画面からのボトムアップ+関数従属性による正規化によって、Entityは一意に識別できる。が、OOの場合はトップダウンにならざるを得ず、一意性を保証できない点が問題だ、という感じで書いてあった。
[/メモ]

アプリケーションの設計モデルは、データモデルと独立して定義可能。ビジネスモデルをそのまま反映することもできる。アプリケーション設計モデルのコアな部分は、ユースケースと独立して、先行して定義する。関係はユースケースが決まらないと確定しないので、ブレが大きくなる。

[メモ]
「アプリケーション設計モデル」が少しわかりづらいんだけど、自分はアプリケーションの構造(特に、PoEAAのDomain Logic Patternsに相当するもの)の選択と、それがドメインごとに具体化されたものだと理解した。そうすると、Domain Logicのモデルはデータモデルとはある程度独立になる話も理解できるし、アプリケーション設計モデルのコアがUCの定義に先立つ、という話も、勘定構造(Accounting Patterns)の導入などと絡めて考えれば理解しやすい。気がする。
[/メモ]

縦の構造と横の構造
アプリケーションの構造は、縦の構造(Entity)と横の構造(Relationship)から成ると考えることができる。縦の構造は比較的固定化されており、横の構造は比較的流動的。変更要求があった際に、それが縦の構造に影響を与えるか、横の構造に影響を与えるかによって、修正コストは大幅に違ってくる。
# よって、(このコンテキストでは)いかにEntityを安定なものにするかが重要になってくる。

[メモ]
この「縦の構造」と「横の構造」は、ソフトウェア(ひいてはビジネス)は視点によって複数の構造を取りうることを表している。
ただ、モデルとしては複数の構造を持っていたとしても、複数の構造をソフトウェアとして実装するのは単一のパラダイムでは難しい。
手続き型では階層的に詳細化された手続きを呼び出すような単純な構成になるし、オブジェクト指向であってもクラス構造は基本的に単一で、まったく違うクラス構造を重ね合わせて実装することはできない(多重継承やMix-inはある程度これを可能にするが)。
複数のパラダイムが形作る構造が対等な関係で関連し合うようなアーキテクチャも考えられるのかもしれないが、現段階では主たるパラダイムが主たる構造を構成し、それ以外のパラダイム(やメカニズム)が補足的な構造を形作ることが多い。この、主たる構造が縦の構造、補足的な構造が横の構造と呼ばれているのかなーと感じた。

補足的な構造を導入するためのパラダイムがアスペクト指向で、現状では主としてアスペクト指向プログラミングが、(ビジネス構造を表す主たるクラス構造に)非機能要求の実現を表す構造を導入する形で用いられている。ヤコブソンは、アスペクト指向による分析・設計を考えており、ユースケース(というアスペクト)を分析・設計段階で扱うことを考えているようだが…。
[/メモ]

パラダイムの進化
オブジェクト指向には限界がある。
  • 言語間の通信が困難
  • 動的なオブジェクトのブートやシャットダウン
  • 非機能の扱いが困難
[メモ]
言語間の通信とか、OOの問題ではなく、OOの典型的なプラットフォームが持つ問題のような気がするんだけど…。動的なオブジェクトのブートやシャットダウンも同じく。
[/メモ]

パラダイムは進化してきた。
手続き型 => オブジェクト指向 => コンポーネント指向 => サービス指向 => Web指向

[メモ]
コンポーネント指向は、言語間の通信が困難である点を解消する。サービス指向が改善するものはなんだろう?再利用可能性だろうか?
Web指向というのは聞いたことがなかったんだけど、分散化され、メッシュ上にリンクされたリソースをコードが扱う、というモデルらしい。
[/メモ]

これらの進化は、古いものを置き換えてきたというわけではない点が重要。新たに登場したパラダイムでの開発には、以前にパラダイムも必要になる。組み合わせて使うことが必要。

[メモ]
フレームワークやミドルウェアの登場で開発はどんどん簡単になっているはずなのに、なぜか難しく感じる理由は、これである程度説明できるのかもしれない。抽象の破れとあわせて説明すれば、直接開発に関わらない人が「うまく開発することが、なぜいまだに難しいのか」を理解する助けになるだろうか。
[/メモ]

モデルをどう作るか?
システムはさまざまな視点からのモデル化することになるが、どのようにすればうまく表現できるのか?
# モデルの話再び。個人的にはここが最重要トピック。
問題をうまく分類し、分割することが重要。単一のパラダイムではうまく対応できないため、マルチパラダイムで対応する。これから、いくつかのパラダイムとその問題点を見ていく。

■ オブジェクト指向
クラス決定に一意性がなく、属人的であることが問題とみなされる。このせいで、手続き指向への回帰が起こっているケースが多々見られる。
(個別UCの)要求によってクラスの構造を変えてはならない。要求の実現ではなく、保守性を目的としてクラス構造を取る。
OOには多くの制約があり、これを乗り越える・補完するためにさまざまなパラダイムが考案・導入されつつある。

■ アスペクト指向
可変性にフォーカスした技術。メモで前述。このあたりから全部、ものすごいさらっと紹介していく流れに。

[メモ]
可変性というのは、ドメイン工学やマルチパラダイムデザインの用語で、システム・もしくは概念の変わりうる部分のこと。AOPが可変性にフォーカスした技術である、ということは、システムの変わりうる部分をアスペクトとして実装することが意図されている、ということだろうか。あまりそんな風に使ってないような気がするが。
ユースケースをアスペクトとして扱うのであれば、この言説に合致するんだけど、あってるのだろうか。
[/メモ]

■ サブジェクト指向(Subject Oriented)
再利用にフォーカスした技術らしい。こちらもさらっとした紹介程度で終了。

[メモ]
どんなものかもわからないので、Webで調べてみた。

サブジェクト指向プログラミング(Wikipedia英語版):
サブジェクト指向プログラミングは、下記をサポートするようなプログラミング手法を指す。
  • サブジェクトの組み合わせによるオブジェクト指向システムの構築
  • システムの、既存サブジェクトおよび新規サブジェクトの組み合わせによる拡張
  • サブジェクトの組み合わせによる、システムの統合
サブジェクトの組み合わせがもたらす柔軟性によって、OOプログラムの開発やモジュール化に新たな機会がもたらされる。
広義のサブジェクト指向は、システムのサブジェクトへの分割や、サブジェクトを正確に組み合わせるためのルールの記述を含む。サブジェクト指向はOOPを補完し、以下のような問題を解決する。
  • OOPが大規模システム開発に用いられた際に発生する問題
  • OOPが、相互運用可能または統合されたアプリケーションスイート開発に用いられた際に発生する問題
SOPは、サブジェクトを作る事で、プログラマの認知能力にまつわる問題も解決しようとしている。サブジェクトはその出自からして、アスペクトと大きくは違わない。アスペクトは初期に、主としてコンパイルタイムに先立ってソースを組み立てるようなコードウィーバーのコンセプトに労力を向けていたが、SOPパラダイムはクラスの組み立てに多くを依存している。

なんのこっちゃ。雰囲気からすると、オブジェクト指向システムをクラスとは別の構造(サブジェクト)に分解し、サブジェクトを組み合わせることでオブジェクト指向システムを構築する、というコンセプトらしい。これも、補完的な構造(横の構造)を導入するためのパラダイムのようだ。

Hyper/J
IBMによる、サブジェクト指向開発環境のJava実装らしい。暇があれば使ってみようかな。

DCIA:
講演の中で出てきた、サブジェクト指向アプローチか製品のようだけど、調べてもよくわからなかった。
[/メモ]

ビジネスのモデルをそのままソフトウェアクラスの構造に反映する。ユースケースの要求はサービスとして実現することで、ビジネスのモデルを安定にできる。

[メモ]
DDDによる本来のサービスの定義は、ビジネスで認知されている純粋な手続きや、複数のドメインクラス(Entity/ValueObjet)にまたがる処理をサービスに記述することになっている。
結果的にユースケース要求がサービスに実装されることになることもあるかもしれないが、本来のDDDの意図とは少し違っているように思える。DDD本で紹介されている本でも、ユースケースによって徐々にドメインクラスを変更していっているしなぁ。このあたりはできれば直接質問したかったんだけど、できずじまい。
[/メモ]

■ フィーチャ指向
可変点をフィーチャとして分析する。フィーチャ指向は、静的・コンパイル時バインディングが基本だが、これを動的にバインドできるようにしたものとして、コンテキスト指向というものがある。

かなりさらっとした紹介で終了。もう少し掘り下げて調べてみようと思ったけど、それはそれで膨大になりそうなので今回は省略。それにしても、フィーチャ指向のフィーチャって可変点だけだっただろうか?システム自体を概念とフィーチャで分析していく手法で、フィーチャの分析として可変かどうかを調べていたような記憶があるんだけど、間違っているかもしれない…。

■ セル生産方式
プロセスの抽象化と再利用らしい。さらっと終了シリーズ。

[メモ]
セル生産方式の解決する問題が、プロセスの再利用だとは知らなかった。簡単にしか紹介されなかったので、詳しいことはわからずじまい。書籍に期待。
[/メモ]

■ 関数型パラダイム
すべてが関数。クラウドの普及によって、中〜長期的には重要になってくるはず。


以上紹介してきたように、パラダイムは多数存在しているが、いずれにしろドメインに特化したパラダイムを選ぶのが重要。

妥協のない意思決定の方法
目的-手段の階層を意識するのが重要。手段のレベルでトレードオフが発生した場合には、同じレベルでは選択できない。目的−手段階層をさかのぼり、上位の階層でトレードオフを解消できないか(まったく別の手段を取る事ができないか、目的レベルで考えると手段の1つは不要になるのでは、などなど)を考えることで、妥協せずに意思決定することができる。

[メモ]
これ重要っすよね。事業戦略もまったく同じで、経営レベルから自分へいたる意思決定のパスが明示されていないと、現場レベルでは判断つかないことが多い。
[/メモ]

萩原さんの夢とメッセージ
プログラミングやインフラなどの実装に近い部分や、ビジネスや要求よりの部分は注目され、色んなことが試されているが、その間の設計の部分は意外と注目されておらず、軽視されているとも言える。ここにはまだまだ向上の可能性があるので、ここをきちんとやっていくということを夢として持っている。

みなさんへのメッセージとしては、ユニーク性を持つ、ということをすすめたい。ユニーク性のない仕事は、どんどん代替可能な人材に置き換えられ、価値も下がってしまう。ユニーク性を持てば代替は不可能となり、価値を発揮できる。ぜひユニーク性を持ってください。

雑感
個人的には、一番ストライクゾーンに近かったセッション。ここ1年ほど興味を持って調べていた内容に非常に近く、自分の考えを確認しつつ新たな試みに触れることができた。もちろん、より良い整理のヒントも得ることができた。
内容は全体に浅く広くだったとはいえ、モノの見方を紹介するという意味ではどんな人にとっても刺激になるセッションだったんじゃないだろうか。書籍「アーキテクトの審美眼」も発注したので、届いたら改めて深く掘り下げてみたい。

目次:
QCon Tokyo 2009に行ってきた

2009年4月17日金曜日

Yコンビネータにトライ

via おとうさん、ぼくにもYコンビネータがわかりましたよ!

きしだんさんすごいっす。自分も学習のために、ラムダの復習も兼ねつつJavaScriptでトライしてみる。独学で適当にやってるので間違いも多数あると思いますが、ご容赦を。

ラムダ
ラムダ式は、関数の表現形式みたいなもので、おおよそfunction式に相当する。引数が複数あるのが違うところだけど、この話は後で解決する。(非形式的な)ラムダ式ってこんなもの。
λx.x*2
このラムダ式は、xを受け取ったら2倍して返す関数を表している。
最初は読むのに(特にλ記号が複数になった場合に)とても苦労したんだけど、引数を「λ」と「.」で挟み込む形で表現した関数だ、と考えるとずいぶん読みやすくなった。これをJavaScriptで表すと…
function(x) { return x * 2 }
こうなる。ラムダ計算の体系では、計算のすべてをラムダ式であらわす。ラムダ式は関数なので、計算のすべてを関数であらわすということだ。ということで、JavaScriptから余計なものをそぎ落としていきましょー。

カリー化
さっきも少し出てきたけど、もともとのラムダ計算体系ではすべての関数は1引数関数として扱う。一般にラムダ計算体系では…
f(x, y) = f(x)(y)
が成り立つ(理屈は省略)ので、多引数の関数はすべて1引数の関数に変換できる。こうすれば、多引数のことあんま考えなくてもいいから、世界がシンプルになる。たぶん。
で、カリー化とは、上記左辺の f(x, y) から f(x)(f) への変換のことを言う。Haskellとかで関数の部分適用の話が出てくるけど、Haskellの関数はすべてカリー化されている(1引数関数である)ので、部分適用といっても単に、順番に関数適用しているだけなんだと思う。

JavaScriptの関数は引数を複数取ることができるが、上記の法則によって、必ず1引数の関数に変換できる。関数や引数が特定された形であれば、特別な機構がなくてもカリー化はできる。
function func(x, y) { return x + y }
この関数を引数3でカリー化(+部分適用)したければ…
curried_func = function(x) { return func(x, 3) }
こうすればいい。
# これをカリー化とか部分適用というかどうかは甚だ疑問だが、とりあえずイメージってことで…。
でも、これだと関数や引数が変わるたびにガリガリコードを書かなければいけないので、嬉しくない。どんな関数や引数でもカリー化できる機構があれば楽ちんだ。
JavaScriptには関数をカリー化する標準的な機構は用意されていないんだけど、カリー化する関数を作る事はできる。カリー化関数の実装にはいくつかバリエーションがあるんだけど、ここではわかりやすさを重視して nanto_vi さんのカリー化関数を使わせていただくことにする。
function curry(f) {
if (f.length == 0) return f;
function iterate(args) {
if (args.length >= f.length)
return f.apply(null, args);
return function () {
return iterate(args.concat(Array.prototype.slice.call(arguments)));
};
}
return iterate([]);
}
これを使えば、どんな関数でもカリー化できる。元ネタの例を拝借して、2引数の以下のような関数をカリー化してみる。
function add(x, y) { return x + y }
curry関数を使って…
var curried_add = curry(func(x, y))
これで、1引数関数になった。
curried_add(1)(2)
> 3
OK。間にごちゃごちゃはさまるものの、関数は1引数だけ考えればよくなった。また1つ、世の中が単純になった。本当か?

ラムダで条件分岐

じゃ、次は1引数関数で条件分岐ができるようにしてみる。これができれば、if文なんていう特別な構文を用意しなくても if文相当のプログラムが書けるわけで、世界は単純なまま進行する(JavaScriptで言うなら、if文を忘れ去ることができる)わけだ。よーし、じゃあif関数みたいなものを作るぞ!こんな風に動けばいいよね。
myIf (b)(exp1)(exp2)
…しかしmyIfで全部やろうとすると…
var myIf = curry(function (b, exp1, exp2) {
return b ? exp1 : exp2
}
結局は内部に if文相当のもの(ここでは3項演算子だけど)が出てきてしまう。余分なものをそぎ落とすのが基本方針なので、やはりきしださんと同じアプローチを取る。
関数はすべて1引数なので、if関数が条件として真偽値をとらなければならないなら、真偽値自体が実行する関数を選ばなくてはならない。とうわけで、真偽値を関数として定義する。ラムダ式で書くとこんな感じ。
true=λx.λy.x
false=λx.λy.y
trueは、xとyを順に適用すると、xを返す関数。falseは、xとyを順番に適用するとyを返す関数として定義している。JavaScriptで書くと、こんな風になる。
var myTrue = curry(function(x, y) { return x })
var myFalse = curry(function(x, y){ return y })
// trueやfalseは予約語だから使えない
さて、条件分岐を書いてみよう。せっかくだから右の扉を…じゃなくて if っぽい関数を書いてみよう。全然型安全じゃないのがアレな感じだけど、とりあえず気にしない。
var myIf = function(x) { return x }
myIf(myFalse)(0)(1)
> 1
条件分岐できた。一応 (my)True も試しておく。条件分岐後に関数が実行されるようにもしてみる。
myIf(myTrue)(curried_add(1, 2, 3))(10)
> 6
OK。なんかカッコだらけなんですけど…。言語はシンプルだけど、世の中はどんどん複雑になっていっている気も。

さて、今のままだと、JavaScript の bool値はまったく使えない。ifを忘れ去るんだし、別になくても問題ない(というかなくしたい)んだけど、一応 JavaScript の bool と myTrue/myFalse/myIf をブリッジするための関数も用意してみる。
var bool = function(b) { return b ? myTrue : myFalse }
myIf の JS boolean バージョン。
myIf(bool(true))(curried_add(1, 2, 3))(10)
> 6
OK。振り返ってみると、真偽値自体を
  • Trueの場合は1番目の式を評価する関数
  • Falseの場合は2番目の式を評価する関数
このように定義することで条件分岐を可能にする、というアイディアだったわけですね。たぶん。

ラムダでループ
やっと本題にきたよ!さて、世界をシンプルに保つために、今度は関数でループできるかやってみよう。関数だけでループするためには、再帰を使えば良いよね。
フィボナッチを考えてみよう。とりあえずはプレーンなJavaScriptで。
function fib(n) {
if(n < 2) { return n }
else { return fib(n - 1) + fib(n - 2) }
}
ここでは、fib関数の中で自分自身(fib)を呼び出す事で再帰を実現している。普通に関数で計算を表現するだけなら、別にこれで十分だと思う。myIfで書き直したいんだけど、自然数とか比較とか(+とかも)作ってないからとりあえずこのまま ^^;

ただし、ラムダ計算の世界では自分自身を含む関数を定義することができない。せっかくなので、自分自身への呼び出しを取り除いて、ラムダ計算の体系に沿うように考えてみよう。

自分自身を呼び出すように記述できないのなら、自分自身を表す関数を引数に取って、その関数を呼び出せばいいのでは?と一瞬思ったが、結局それ、自分自身を呼び出してるよな…。

じゃあ、自分自身みたいだけど自分自身じゃない関数(fake_fibとでもしよう)が引数としてわたってきて、その関数を呼び出すことで間接的に自分自身が呼び出されるようにするのはどうだろう?JavaScriptで書くと、こんな感じ。
function fib(fake_fib, n) {
if (n < 2) { return n }
else { return fake_fib(n - 1) + fake_fib(n - 2) }
}
fake_fib(n - 1)呼び出しが、さらにfib(fake_fib, n - 1)を呼んでくれれば再帰が実現できることになるのでは?fibを g に、fake_fib を f にして簡潔に書きなおすと…
function g(f, n) {
if (n < 2) { return n }
else { return f(n - 1) + f(n - 2) }
}
こんな感じ。で、f(n - 1) が g(f, n - 1) に(1引数の世界で言えば、f が g(f) に)展開されれば再帰が実現できるはず。というわけで、展開するとg(f)になるような f、つまり
f = g(f)
となるような f を見つける、という問題に帰結する。このような条件を満たす f は一般的に「不動点」と呼ばれており、以下のようなラムダ式で表すことができることが知られている。
Y = λg.(λx.g(xx))(λx.g(xx))
これがいわゆるYコンビネータってやつらしい。
# このラムダ式がさきほどの条件を満たすことは、きしださんのところに書いてあるとおり、試してみればわかる。これをJavaScriptで書いてみると、
function Y(f) {
return function(x) {
return f(x(x))
}(function (x) {
return f(x(x))
});
}
# ナンダコレハ…。
こうなる。これを使う形で、あらためてフィボナッチをJavaScriptでちゃんと書くと、こんな感じになる。
var fib2 = curry(function(f, n){
if(n < 2) {return n }
else {return f(n - 1) + f(n - 2)}
})
実際に呼び出すには、Yを使ってこんな感じで呼べばいい。
Y(fib2)(10)
> InternalError: too much recursion
しかし、こちらも同じく再帰エラー。まねっこしてZコンビネータを作ってみよう。
function Z(f) {
return function(x) {
return function(m) {
return f(x(x))(m)
}
}(function(x) {
return function(m) {
return f(x(x))(m)
}
})
}
…なんかもう慣れてきた。呼び出し。
Z(fib2)(10)
> 55
えええ!動いちゃったよ!そりゃ動くように作ったけどさ!

で、これで何ができるかっていうと…自分自身を参照しなくても、再帰が書ける。だから、無名関数でも再帰が書ける。他にどんな良いことがあるかというと、相変わらずよくわからない ^^; ラムダ計算の体系に沿って計算を組み立てておくことで、何か良いことがあるのだろうか。

チャーチ数とかもやってみようと思ったけど、力尽きた。続く…かもしれない。

2009年4月16日木曜日

Google App Engineの料金体系が面白い

社内でGoogle App Engineのミニ勉強会をやることになったので、技術者らしくAPIとかBigTableまわりとかやろうかなーと思って色々ドキュメントをあさっていたところ、思いがけず非常に面白いものを見つけてしまった。

それは、料金体系。たいていのGAE紹介サイトには、〜まで無料、以降〜みたいな形でさらっと書いてあるんだけど、思った以上に奥が深い。DoCoMoやソフトバンクモバイルより奥が深い。しかも、この料金体系によってアプリケーションの作り方にまで影響が出てくる可能性がある。こうなってくると、JPAとかJDOとか開発環境とか紹介するのなんて馬鹿馬鹿しくなってしまった。Google App Engine勉強会、って名目で人呼んどきながら、ずーっと料金の話ってありだろうか?なしだろうなぁ。

基本的な概念、リソースとクォータ
さて、Google App Engineには利用可能な資源、リソースが定義されており、リソースにはクォータ(割当)が設定されている。逆に言えば、クォータが設定されているものがリソースであるとも言える。リソースには2種類存在し、固定的に割り当てられていて基本的にはクォータの上限が変更できないもの(Fixed)と、支払いによって上限を増やせるもの(Billable)がある。
ややこしいことに、Fixedのリソースのクォータは、Account保持者が支払い可能状態になると上限が増える(一方Billableなリソースのクォータは、実際に上限を増やすように設定しないと増えない)。

また、クォータにも2種類あり、日ごとの上限値と、分ごとの上限値が設定できる。分ごとの上限値は、日ごとに割り当てられたリソースが急速に消費されつくし、日のうち大部分が稼働不可能になる...といった状況を避けるために設定できるようになっている。

リソースの枯渇と補充
リソースは、クォータの上限値から消費されていき、Pacific Timeの0時にリセットされてクォータの最大値まで補充される。
リソースが枯渇した状態で枯渇したリソースにアクセスしようとした際には、例外が発生する。リソース枯渇に適切に対応しようとする場合、適切にこの例外をハンドリングしなければならない。たとえば、ユーザーにそのサービスが利用不可能であることを通知したり、管理者にメールで通知する等の事後処理が考えられるだろう。これが、アプリケーション設計に影響を与える要素の1つである。

リソースの種類
以下でリソースを紹介するが、支払いによって上限を増やせるものは、オリジナルのドキュメントに従って[B(illable)]と表記する。また、参考のために、無料かつ支払い可能でない状態での日ごとの上限値を並記する。

■ リクエスト系
  • リクエスト数(HTTPSを含む): 1,300,000 requests
  • 出力帯域総数[B](HTTPSを含む): 10 gigabytes
  • 入力帯域総数[B](HTTPSを含む): 10 gigabytes
  • CPU時間[B] : 46 CPU-hours
CPU時間はデータストアで使った時間も含む。外部サービス呼び出し等による待機時間は含まない。また、CPU消費時間の目安として、以下のようなものがあげられている。
  • データストアへの書き込みは、読み込みに比べておよそ5倍のCPU時間を使う
  • インデックス更新が必要な書き込みは、そうでないものよりCPU時間を使う
  • Entityのプロパティが多いほど、読み込みでも書き込みでもCPU時間を使う
  • Queryは常にIndexを使うため、Queryはほとんどの場合に同じくらいのCPUしか使わない
  • ただし、フェッチが必要な場合は追加のCPU時間が必要になる
リクエスト系のリソースの特筆すべき点は、これらのリソースが不足した場合にはリクエストのハンドリングを開始すらできないため、ユーザーにはHTTP Status403が返されてしまう、という点だ。

■ データストア系
  • データストアAPI呼び出し回数 : 10,000,000 calls
  • 格納データおよび関連インデックスのサイズ[B] : 1 gigabyte
  • APIに送信されたデータのサイズ : 12 gigabytes
  • APIから取得したデータのサイズ : 115 gigabytes
  • データストアに使ったCPU時間 : 60 CPU-hours
■ メール系
  • メールAPI呼び出し回数 : 7,000 calls
  • メール送信先アドレスの総数[B] : 2,000 recipients
  • 送信メール総数 : 5,000 mails
  • メッセージボディのデータサイズ総数 : 60 megabytes
  • 送信添付ファイル総数 : 2,000 attachments
  • 送信添付ファイルデータサイズ総数 : 100 megabytes
■ URLフェッチ系
  • URLフェッチAPI呼び出し回数 : 657,000 calls
  • URLフェッチデータ送信サイズ総数 : 4 gigabytes
  • URLフェッチデータ受信サイズ総数 : 4 gigabytes
■ Image操作系
  • Image操作API呼び出し回数 : 864,000 calls
  • APIへのデータ送信サイズ総数 : 1 gigabytes
  • APIからのデータ受信サイズ総数 : 5 gigabytes
  • 変換実行回数 : 2,5000,000 transforms
■ Memcache系
  • MemcacheAPI呼び出し回数 : 8,600,000 calls
  • APIへのデータ送信サイズ総数 : 10 gigabytes
  • APIからのデータ受信サイズ総数 : 50 gigabytes
■ デプロイ
  • デプロイ回数 : 未定義
リソース[B]の単位あたりコスト
  • 出力帯域総数:1 gigabytes あたり 約12円
  • 入力帯域総数:1 gigabytes あたり 約10円
  • CPU時間:1 CPU時間 あたり 約10円
  • 格納データサイズ:1ヶ月あたり1 gigabytesで約15円
  • メール送信先アドレスの総数:1アドレスあたり約0.01円
安…!いよね?

アプリケーション開発への影響
こんな感じで、明確に上限回数やリソース追加のコストが明示されたら、アプリケーション開発にはどんな影響があるだろう?まず、テストが変わるんじゃないだろうか。従来も行われてきたであろう、リソースの上限を突破しないか、という観点での試験に加えて、費用対効果を考慮した試験がより厳密に行われるようになる可能性がある。なにせ、無駄にCPUリソースを消費した処理を作ると、明確に費用として跳ね返ってくるようになるのだ。そうなると、富豪プログラミングにかわり、効率の良いアルゴリズムが再び脚光を浴びるようになるかもしれない。つまり、(今後の料金変動や景気、規模にもよりそうだが)今よりも省エネな設計が求められるようになる可能性があるんじゃなかろーか。

その他雑感
こうして、使用リソースに応じた料金表を見ていると、携帯電話料金表や電気料金表を見ながら最安のプランを考えているときと同じ感覚をおぼえて「あぁ、本当にユーティリティコンピューティングの時代が来たんだなぁ」と思いがけず実感してしまう。
と同時に、CPU消費時間で費用が変わるような世界は、よくよく考えると、本や口伝えでしか見聞きしたことがないような、コンピューターリソースが貴重だった時代に似ているような気もしてくる。マシン使用時間に応じて料金がかかっていたらしい、自分の知らない時代。
トレンドは螺旋を描いて戻ってくるとはよく言ったものだ。

2009年4月15日水曜日

GAE/Jにいまいち惹かれない

QConでのGregorのセッションを受けて、Google App Engine熱が再度高まってきた。ってわけでGAE for Javaをさわってみたのだが、イマイチぐっと来ない。
  • Webインフラが素のServletまで退行。
  • Webフレームワークが手探り状態。もう少ししたら一通りそろってくると思うけど。
  • 久しぶりにさわったJavaが、予想以上に柔軟性がなくて萎えた。
  • 現状AOPも使えないんじゃあ、のっぺり感満載…。Moduleとかtraitとかないんすか。
  • GAE/Jの永続化メカニズム(JDO/JPA)がどっちもいまいち好きになれない。
  • Scala使おうかと思ったけど、JDO/JPAのエンハンスとか考えると開発環境を快適にするのがめんどくさそうで萎えた。Actorは当然使えない。
  • JDO/JPAみたいな形だと、BigTableの柔軟性(行ごとに違うデータ項目を持っていてもOK)が生きてこない気がした。
てな感じ。これなら、(少なくともしばらくは)Python版でよくないか?てかPython版のがよくないか?お下がりとして頂いた「初めてのPython(第2版)」をぱらぱら読んでると、関数系が結構充実してる(lambda式とかリスト内包表記とかジェネレータとか)みたいで、使ってて面白そうだし。でも、なんか世の中に取り残されているみたいで寂しい。もしかしてSIer社員失格か?

しばらくはPythonでGAE使っていこうかな。世の中が落ち着いてきたらJavaも使ってみればいいし。

2009年4月14日火曜日

JavaScript: The Good Parts

今更だけど見てみた。


1時間以上あるのに軽く引くが、本当に重要なのは30分程度。残りはこの人の経験談や、JavaScript界隈の近況、質疑応答。
この人のプレゼンは、皮肉がきいていて面白い。とりあえず、わかった範囲でメモっておく。

JavaScriptの現状
  • いまだに最も誤解された言語
  • 誤用しては怒っている人多し
  • 最もすばらしいアイデアから、もっともひどいアイデアまで含まれている
  • こんなに幅の広い言語はほかにない
  • コンピュータ科学者からカット&ペースト厨まで、どの言語よりも幅広いスキルの人が使っている
JavaScriptへの不満
  • ブラウザプログラミングはひどいもんだ。 => まったくその通り
  • 十分な速度が出ない。 => 現在ではすでにJSではなくDOMの問題。今はランタイムは十分速い。
  • 失敗の積み重ねだ。 => Javaが完全に失敗した環境でうまくやりつづけているだろ?

影響を受けた言語
Self:
  • prototypal inheritance
  • dynamic objects
Java:
  • syntax
  • conventions
Scheme:
  • lambda
  • loose typing
Perl:
  • regular expressions
Schemeさんパネェッス。
loose typingを問題視する人もいるが、テストすればよい。テストは他の言語でも必要 型チェックだけじゃわからないことは多い。Javaのシンタックスはあんまよくないね。

Bad Parts
  • グローバル変数。
  • + をaddと結合に使う。Javaもやってるが、Javaは強い型つけを持ってるから問題ない。
  • セミコロン。初心者向けにCのシンタックスを導入したが、予期しないエラーがおきたりする。後述。
  • typeof。役に立たない。なんでもObjectになってしまう。
  • with と eval。evalは一番誤用されている。evalを使ってるときは、なんかまずいことをやってる。
  • まがいものの配列。本来はメモリを区切ってすばやくアクセスできるが、JSのはこういうのじゃない。実際はハッシュテーブル。
  • == と !=。Cから受け継いだ問題の多いシンタックス。
  • false, null, undefined, NaN。たくさんありすぎ。
どうなるかわかるだろうか?
'' == '0' // false
0 == '' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
false == undefined // false
false == null // false
null == undefined // true
"¥t¥r¥n" == 0 // true
常に ===を使うべき。==は(暗黙の型変換がカオスすぎるので)使ってはいけない。本当は間違いだけど、偶然うまくいくケースもある。
// 間違い
var obj = myObject[name]
if (obj == null) {
....

// 正解
var obj = myObject[name]
if (obj == undefined) {

単独では良い機能が、関連するとがうまく動かないケース
  • Objectは他のオブジェクトを継承できる
  • 関数はオブジェクトのメンバになれる
  • for..in文は、継承した関数とデータメンバを区別しない
=> 全然嬉しくない挙動

実際、for .. in は問題が多い
// 浅いとか深いとか、よくわかんなかった

Bad Heritage
ブロックのないステートメント:
if (foo)
bar();
expression statement:
  foo;
Floating point arithmetic:
  0.1 + 0.2 !== 0.3
// JSの問題というわけではないんだが
++ and --:
バッファオーバーランとか色々問題があるらしい。コードもトリッキーになる。

switch:
Javaと同じ問題。falling through

Good Parts
Lambda:
パワフルで安全

Dynamic Objects:
非常にパワフルで使いやすい

Loose Typing:
良い。クラスとかいらない。

Object Literals:
とてもよい。JSONとか。

Inheritance
コードの再利用。クラスベースとプロトタイプベースがある。
よく理解されていないが、プロトタイプベースはとてもパワフル。

Prototype Inheritance
Classは不要、オブジェクトから継承。
他のオブジェクトへのリンクを持ち、委譲する方法がある。
var newObject = Object.create(oldObject);

if (typeof Object.create !== 'function') {
Object.create = function(o) {
function F() {}
F.prototype = o;
return new F();
};
}
new はクラスベースっぽい記法を提供しているが、あまりよろしくない。コンストラクタ関数を呼ぶときにはnewが必要だが、忘れるとグローバルオブジェクトが変更されてしまう。そして、その際にはまったく警告が出ない。私は危険だから使わない。

Global変数いくない
var names = ['one', 'two'....]

functionでスコープ制限したらどうだろう?
function hoge(n) {
var names = ['one', 'two'...]
return names[n]
}
=> 遅い。呼び出しごとに配列が初期化されてしまう。

クロージャを使おう!
var digit_name = function() {
var names = [...]

return function(n) {
return names[n]
};
}
=> 無名関数のスコープ内で一度だけ初期化される。遅くない。これは、コンストラクタパターンとして一般化できる。といいつつ、次のスライドがModule Patternなのはなぜだ。

Module Pattern
var singleton = function() {
// 無名functionのスコープにとじこめられる
var privateVariable;
function privateFunction(x) {
..privateVariable...
}
// Objectリテラルで無名関数のスコープを使うように定義
return {
firstMethod: function (a, b) {
...privateVariable...
},
secondMethod: function(c) {
...privateFunction()....
}
};
}();
これは、(ようやく)コンストラクタに応用できる。

Power Constructor
1. Make an object.
  • Object literal
  • new
  • Object.create
  • call another power constructor
2. Define some variables and functions
これがプライベートメンバになる
3. 特権メソッドでobjectを複製する
4. 作ったobjectをかえす

コードで見てみると...
function myPowerConstructor(x) {
var that = otherMaker(x);
var secret = f(x);
that.priv = function() {
.... secret x that ....
};
return that;
}

コーディングスタイル
return {

}

return
{

}
どちらがいいか?これは、感情的な宗教論争ではない。少なくともJSに関しては。
return
{
ok: false
}
このように書いた場合、何も返らない。これは、returnの後ろに暗黙的にセミコロンが挿入されるから。
return;
{
ok: false
}
JavaScriptにはブロックスコープない。
ok: false
はラベルだとみなされる。
return;
{
ok: false;
};
こうなるということ。どのみち、return以降は到達しない文。

ビューティフルコード
JSLint使いなさい。Bad Partsを避けるように強制してくれる。注意しておくが、これを最初に使うと凹むことは請け合い。

他の良い点
安定してるぜ!1999からデザインがかわってないのだ。

標準化動向とか質疑応答とか省略。あー…うん。1度Youtubeの動画を貼ってみたかっただけなんだ…。

2009年4月13日月曜日

大規模ウェブサイトのベストプラクティス -eBayでの事例-

分量がめちゃめちゃ多かったので、あまりメモってない ^^;
ほとんど「大規模サイト構築のノウハウ全部見せます!-全部で20以上あるよ!-」みたいなノリだったので、とても全部メモることはできなかった。プレゼン資料欲しいです。

eBayシステムのプロファイル概略
  • トランザクションデータは2ペタバイト
  • DWH用データは50ペタバイト
  • 1日あたり48億回のSQL実行
ペタバイトとか、もう多いか少ないかもわからん。

大規模システムの原則
ということで、eBayのシステム構築の原則。
  • Partition Everything
  • Asynchrony Everywhere
  • Automate Everything
  • Remember Everything Fails
  • Embrace Inconsistency
なんだか清々しいです。

Partition Everything
分割方法は、データ、負荷、使用方法の特徴など、色々なものがあげられる。分割の動機としては…
  • Scalability
  • Availability
  • Manageability
  • Cost
このような感じ。PartitionとAvailabilityとは、Partitionによって故障箇所を局所化することでAvailabilityを向上できる、という点で関連する。PartitionとCostとは、Partitionによって問題を細分化することで、price/performance比がベストの点を選択できるようになる、という点で関連する。特に、Availabilityを重視している印象を受けた。

eBayでは、いくつかの方法でシステムを分割している。
■機能による分割(処理)
  • Selling
  • Search
  • ViewItem
  • Bidding
  • Checkout
  • Feedback
■機能による分割(データ)
  • User
  • Item
  • Transaction
  • Product
  • Account
  • Feedback
■水平的な分割
  • ノード分割+ロードバランシング
  • データをアクセスパスにしたがって分割(RDBのパーティションのことだと思う)
また、Partitionのため、セッションステートはアプリケーションティアに保持しないようにしている。CookieやURL、DBに持っているらしい。この辺は王道。

Asynchrony Everything
非同期化のメリットは…
  • ピークロード(最大負荷)ではなくアベレージロード(平均負荷)をターゲットにリソースを配分することができる点
  • ユーザーが許容可能な時間を越えて、リクエストを処理することができる点
# 他にもいくつかメリットがあったけど、個人的に重要だと思ったのだけなんとかメモ ^^;

非同期化の方法には、キューイングやマルチキャストメッセージがある。
eBayで採用しているキューでは順序性は保証していないが、メッセージ自体にはデータは入れず、イベント通知を受けた側がその時点で正しいデータを読みに行く(Read Back)ことで整合性の取れた処理を行えるようにしているらしい。

マルチキャストメッセージは、商品情報などのデータがアップデートされた際に、サーチエンジンのインデックス更新を通知する場合などに使われている。Feed Daemonというプロセスが定期的にデータ更新をポーリングしており、更新があった場合(eBayの場合、ほぼ確実にあると思うが)に各サーチエンジンにマルチキャストメッセージを送信する、という形を取っている。

Automate Everything
興味深い例として、ユーザーの振る舞い(サイト上でどのように行動したか)情報を収集し、それに応じてシステムを最適化する、という一連のアクティビティを自動化している例が紹介された。
収集したデータをもとにメタデータを更新し、システムがその振る舞いを変えるところまで自動化されているのが面白い。データ収集や、分析のネタ作りの自動化くらいまでは結構やってると思うんだけど、ここまでやっているのは珍しい。いいアイデアのヒントをもらった。

Remember Everyghint Fails
システム上のすべての変更は、すべてもとに戻せるようにしているらしい。これは、データだけではなく、システム機能に対する変更にもあてはまるようで、すべての機能はコンフィグでON/OFFが切り替えられるそうだ。データ更新に対するロールバックをどう扱っているかに興味があるが、それは次の「Embrace Inconsistency」で扱われるトピックなのだろう。
また、Graceful Degrationも可能らしい。まぁ、このレベルだとある意味当たり前と言える。

Embrace Inconsistency
ここで、満を持して(?)CAP Theoremが登場。このセミナーで3回目。はじめて、CAPの正確な定義がスライド上に登場する。が、はやすぎてメモれず ;-(
  • C : all clients see the same data even if system ...
  • A : all clients will get a response even if system failure exist...
  • P : 時間切れ ;-(
# おそらく、AmazonのCAPの定義と大差ないはず。でも、誰か知ってたら教えてください… ;-(

さて、ここでの基本的な主張は、もうおなじみ「Consistencyは"あり"か"なし"かの2者択一ではなく、Immediate ConsistencyとNo Consistencyの間に多数のConsistencyレベルがスペクトラムのように存在する」というもの。
-----------------------------------------------------------
   Immediate <------- Eventually -------> No Consistency
 Bids/Purchase         Search Engine/            Preferences
                              Billing System
-----------------------------------------------------------
金融システムにおいてすら、必ずしもImmediate Consistencyが必要とは限らない。ちなみに、eBayでは分散トランザクションはまったく使っていない(これが噂のTransactionlessか)。そのかわり、DB操作の順序を厳密に決定することで、システム全体の整合性を高めている。
# ステートマシンを使っている、という発言もあったけど、分析に使っているんだろうか?

最後にもう一度、重要なことを繰り返す。
  • Partition Everything
  • Asynchrony Everywhere
  • Automate Everything
  • Remember Everything Fails
  • Embrace Inconsistency
清々しいですね。

雑感
このセミナーで一番中身が濃かったのではないだろうか。大規模システム構築のパターンとして、本が一冊出せそうなくらいの内容だった。それだけにメモも難しく、セミナー資料の公開が待たれるところだ。

ビューティフルコード

Matzのビューティフルコード。講演内容は、少し前に開催された「まつもとゆきひろが語る『ビューティフルコード』×『プログラマ35歳定年説』」と同じだと思われる。
というわけで、包括的な内容は以下を見ればとてもよく書いてあるとオモイマス。
雑感
全体的には、教科書的な内容を集めた話になっているなぁ、という印象。以下のような話はなかなか興味深かった。
  • Brooksの生産性不変の法則(どんな言語でも、プログラマが生産できる単位時間あたりLOCは変わらない)から考えても、表現力の高い言語が求められている、ということ。
  • コードはアート、プログラマはアーティストとしての自覚を持つべき。アーティストの条件とは、自覚、自発である。
  • アートはビジネスとして成立しないという意見があるが、デザイナなど、アートとビジネスを両立させている職業はいくらでもある。
一方、疑問点も多数。
  • ソフトウェアは一品モノだし、プログラミングは設計なので、ソフトウェア工場というアイデアは間違っている、という話があったが、ソフトウェアの性質は一品モノか大量生産かの2択ではなく、この間の多様な中間点も取りうるはず。自分の経験上、大量生産に比較的近いようなソフトウェア開発も確かに存在するし、プログラミングが設計(=コードが設計書)だとしても、設計書同士が似通っている場合にはより効率的なアプローチがあり得るだろう。設計書同士の類似点が大きければ大きいほど、工場のメタファが有効になるかもしれない。ソフトウェアの種類を限定せずにこのような主張を行うのは、ポジショントークにしてもやりすぎであるように思える。
  • プログラミング言語は水鳥のようであるべきで、現実の複雑さは言語が引き受ければいい、という話があったが、現実の複雑さの多くは汎用言語では引き受けられないのではないか?複雑な現実を分類し、それぞれの分類に特化した言語を作ることで複雑さに対応する、というアプローチ(つまりDSL)が主流になりつつある。個人的には、現実の複雑さに対応するために汎用言語が目指す方向は、言語が複雑さを引き受ける方向ではなく、言語の表現力を増す(=複雑な現実を表現できる可能性を増やす)方向が良いのではないかと思う。まんま内部DSLのホスト言語のイメージだけど ^^;
  • Rubyは(動的型付けやクロージャ、オープンクラスやメタクラス、Mix-Inなどによって)かなり表現力が高い。これが、内部DSLのホストとして好まれる大きな理由の1つじゃないかと思う。
  • アルゴリズム記述用擬似言語みたいなものを目指す、と言っていたけど、それはアルゴリズムに特化しているだけで、アルゴリズムは書きやすくなるかもしれないけど他の部分に影響が出る可能性があるのでは?
自分のような言語のシロウトがMatzに疑問を持つのもアレなんですが、このあたりの疑問は解消されないまま今に至る。

Programming the Cloud -the Internet as Platform-

イケメンGregor Horhpeによるクラウドセッション。この人は、Martin Fowler SignitureのEnterprise Integration Patternsの著者の一人でもある。今回はEIPではなく、BASE〜CAP Theorem〜Google App Engineといったあたりのお話。

クラウドの良い点、悪い点
アーキテクト(の関心事)にとっては、夢を実現するコンセプト。
  • 疎結合
  • 拡張性
  • 標準への準拠
  • 耐障害性
  • 無制限のコンピューティングパワー
  • ユビキタス
しかし、開発者にとっては悪夢の発現。
  • No Call Stack
  • No Transaction
  • No Promises
  • No Certainty
  • No Ordering Constraint
# No Certaintyがよくわからず、訳せなかったので全部英語 ^^;

[メモ]
このあたりは、パネルディスカッションでGregorが話していた、ランタイムを考慮したテストの重要性が増すだろう、という話と対応しているようだ。
(ACID)Transactionがない点は、システム特性や方針レベルでは(CAP TheoremやBASEモデルとして)わりと整理されてきているんだろうけど、アプリケーションレベルでの対応方法は別の課題として(少なくともしばらくは)残ることになりそうだ。この話は、少し後にスタバの例つきで出てくる。
順序性の保証がない点は、同じくプログラミングモデルの変革に関わる部分。この話も最後あたりにStatelessとStatefulの分離の話と絡めて出てくる。

ACIDコンセプトの移り変わり
以前のACID
  • Atomic
  • Consistent
  • Isolated
  • Durable
以前のACIDに合うように作ってみた、今日のACID
  • Associative(結合性)
  • Commutative(交換可能性)
  • Idempotent(羃等性)
  • Distributed(分散)
[メモ]
結合性や交換可能性は、並列化をすすめる上で非常に重要になってくる。関数型パラダイムにも通じる概念(ですよね?)。分散は並列化のために必要になってくるのだが、分散をすすめるとノード故障への対応が難しくなってくる。この際に効率よく回復するために、計算の羃等性が重要になってくる。計算が羃等であれば、同じメッセージを再送信するだけで回復できるからだ。

分散システムにおけるトランザクション
分散システムにおいて、特定のノードと通信できなかった場合、どうするか?2フェーズコミットが必要か?スターバックスを例に考えてみよう。

結論から言うと、スターバックスは2PCをやっていない。つまり、注文を受けてからコーヒーを作り、清算して、これらすべてを1トランザクションに…というような仕事のやり方をしていない。スループットを出すために、注文を受け付けたら、清算前にコーヒーを作り始めたりしている。
さて、この一連の流れで以下のようなマズいことが起きたらどうすればいいだろう?
  • この飲み物は嫌だと、顧客に却下/返品される
  • コーヒーメーカーが壊れる
  • 顧客が支払えないことに気づく
顧客に却下された場合には、飲み物を作り直す(Retry)。コーヒーメーカーが壊れてコーヒーが提供できない場合、代金を払い戻す(Compensation)。顧客が支払えなかった場合、飲み物を捨てる(Write-off)。
このように、最初から完全を求めるのではなく、事後的に対処することで最終的な一貫性を保つようなやり方は、スタバだけでなく銀行のようなシステムにも当てはめることができる。例えば、日銀とバンク・オブ・アメリカが決済をするような場合、エントリを互いに送信しあう際には完全性を求めず、どこかのタイミングで突き合わせをすることで、エントリ送信の誤りを修正するようなことをしている。
# らしい。もちろん、本当かどうかは知らない。
ちなみに、このWrite-offというオプションは、システム開発でも特に忘れられがちな事後対処のオプションなので、もっと考えてもいいんじゃないか、というようなことも言っていた。

Googleの並列化への取り組み

みたいなタイトルで、MapReduceとかGFSとかBigTableとかの話が続いた。このあたりは有名な話ばっかりだったのでメモってない。Sawzallだけ知らなかったんだけど、これらのインフラを効率よく使うためのDSLらしい。

並列性を高めるための方法
いかにStatefulな部分をStatelessな部分から切り離すか、が重要。
例として、ファイル読み込みのようなものを考えてみる。ファイルを先頭から順に読み、一行ずつ処理していく場合、処理は何行まで進んでいるかの情報を利用可能で、処理のたびに位置情報が更新されていく形になるため、状態を持つことになる。これは、処理が位置情報を利用できるという意味で、Listに対する処理と考えても良い。
ここで、状態を持っている部分(ファイル読み込みの部分)をインフラ(ここではMapReduceだろうが)にまかせ、処理の部分はListではなくSet(集合は順序持っていないので)に対する処理とすることで、Statefulな部分とStatelessな部分を切り離すことができる。

こうすることで、Statelessな部分は並列実行できるようになる。しかし、Statefulな部分をデータ処理と切り離したことで、できないことが増えることには自覚的になる必要があるだろう。例えば、データ処理が状態を持っていれば可能な、連続して同じデータ内容の行を読み込んだ場合に誤入力としてスキップするようなことはできなくなってしまう。どんな妥協をしたのかを自覚しておくのは非常に重要。

Google App Engineのデモ
Java版の紹介とデモ。Hello, 東京!デモってやっぱり楽しいね。

雑感
拍手とともに終了。Gregorさんは話すのが好きなのか、セッションも楽しかった。
ホスト「それではいつものように、よかったと思った人は緑、まぁまぁかな、という人は黄色、いまいちかな、という人は赤を入れて...」
Gregor「Green!」
会場「ハハハハハ!」

内容的には、丸山先生の話のGoogle版といった感じかな。スタバの例がすごくわかりやすかった。自分が見たセッションで、唯一デモがあったセッションでもある。緑入れときました。

目次:
QCon Tokyo 2009に行ってきた

2009年4月12日日曜日

Spring in a Changing World

Rod Johnsonのセッション。なんかマーケ色がわりと強かったので、あまりメモっていない。メモった範囲で。

ビジネス、技術の両面で変わる世界
  • JavaEEの失敗
  • よりシンプルな技術への指向
  • 世界的な景気後退
今は、リーンなソフトウェア(目的にフィットしており、シンプル)が求められている。

技術の変化がとてもはやいため、マネージャは開発者に頼らざるを得なくなっている。開発者は今、かつてない力を持っている。

今まではSilicon Valleyがすべてのルールを決めていたが、これからは世界中に主導権が分散する。言語の問題はあるが、徐々に解決していく。

OSSはシンプルであり、リーンな開発につながる。今後のソフトウェアはこうなる。
巨大ベンダー => OSSコミュニティ
パワーポイント上でうまく動くソフトウェア => 開発者に支持されたソフトウェア

JavaEEは、単独ですべてを解決しようとしているが、これはリーンではない。

日本でのSpringの利用率は、他国と比べて高くないが、これはSeasarというプロダクトがあるからだと聞いている。しかし、長期のトレンドで見れば、他国と同じようになっていくだろう。

Next Spring
未だに残る、JavaEEの複雑性を取り除く。例えば、プロジェクトの開始時にプロダクトを選択し、動作可能なまでに整えるのはいまだに面倒。
新たな挑戦者(RoR, Django, Zend...)もいる。
.NETも色々やっている。.NETの利点は、MSが全体像を考えてドライブしている点。Javaにはそのような役割を果たすものがかけている。Sunは、EnterpriseJavaの方向性を示すのに失敗している。ただ、MSは独占企業なのが問題。
Spring Sourceがその役割を果たす。MSとは違い、オープンなアプローチで、統合された、アプリケーションのライフサイクル全体をカバーするものを提供する。

具体的には...
  • Tomcatへの支援を継続する
  • ツールスイートを作る
  • クラウド製品を提供する
雑感
開発者のエンパワーメント、という流れはいいなぁ、と感じたが、それとSpring Sourceの取り組みの結びつきがあまりぐっとこなかったな。個人的には、インフラや言語コミュニティの話より、アプリケーションやアプリケーションモデルの話がききたかった。

「技術の変化が早いから、マネージャは開発者に頼らざるを得ない」という下りは、必ずしもそうはなってない面があるように思う。技術の変化が早いことに、古い技術に固執することで対応しようとしたり、完全に無視することで対応する人は結構いる気がするんだなぁ。

目次:
QCon Tokyo 2009に行ってきた

クラウドの技術的な特徴について

丸山先生のジェネラルセッション。内容的にはEric Brewerの資料(CAP Theorem/Eventually Consistent)をクラウドの立場から眺める、といったもの。

ScalabilityとAvailability
Scalabilityは、Scal-outによって達成できるが、Scale-outには技術的な裏打ちが必要であり、ノウハウの蓄積が必要。
Availabilityは、故障に対する適切な対処によって達成できるが、これには複製を作成する戦略(Replication)が基本になる。複製を作ってしまうと、同期の問題、すなわち一貫性に関する問題が発生する。

[メモ]
CAP定理で言うと、ScalabilityがおおよそPartitionに、AvailabilityがそのままAvailabilityに、一貫性がConsistencyにあたると考えてよいと思う。上記は、クラウドにおけるCAPの関係をわかりやすく説明してくれていると言える。

Partitionが必須となる背景
High-endコンピュータとCommodityコンピュータの価格性能比は、約1:33。経済的な選択をすると、必然的にPartitionを選択することになる。安いハードウェアはそれなりの能力しか持たないが、障害に強いソフトウェアが、安いハードウェアを役に立つものに変える。

[メモ]
ただし、よくよく考えると、Partitionと、Partition度合いを容易に増減できるかどうか(=Scalability)は、違った特性のようにも思える。セミナーでも、一口にクラウドといっても、リソースを動的に増減できるもの(Hot Scalable)とできないものとできないもの(静的にのみ可能、Scalable)がある、と言っている。

CAP定理とクラウド
これは、共有データを持つシステムにおいては、Consistency, Availability, Partitionの3つのうち、最大でも2つの特性しか持つことができない、というもの。
クラウドではPartitionは必須になるため、ConsistencyかAvailabilityのどちらかを選ばなければならない。…が、この選択はシステム全体に一律に適用しなければならないわけではない、というのが重要な点。単一のシステム内でも、カートに商品を入れる段階ではAvailabilityを優先し、注文の段階では一貫性を優先する、といったように、部分ごとに必要となる特性は変わる。

また、上記でAvailabilityを「優先する」と書いたように、AとCはどちらかを取ればもう一方が完全に失われる、というものではなく、AとCの配分には中間的な選択肢が数多く存在する。特に、Consistencyにはいくつかのレベルがあり、これには名前もつけられているようだ。以下、結城さんの翻訳サイトからの引用。
  • 強い整合性(Strong consistency): 更新が完了したら, 続くアクセス(A, B, C いずれかによる) は更新された値を読み出す
  • 弱い整合性(Weak consistency): システムは続くアクセスが更新された値を読みだすことを保証しない. 更新した値が返される前には様々な条件を満たす必要がある. 多くの場合, その条件は経過時間である. 更新された時刻から更新した値の読みだしを観測者に保証するまでの間隔を 不整合ウィンドウ(inconsistency window) と呼ぶ.
  • 結果整合性(Eventual consistency): ストレージシステムは, オブジェクトに(不整合ウィンドウが閉じたあとも)最後まで変更が加えられなかったなら, 全てのアクセスが最後の更新値を読みだせると保証する. 結果整合性を実装する最もよく知られたシステムは DNS だ. 名前の更新は分散しており, 設定のパターンやキャッシュ制御の組合せで振舞いがかわる. クライアントは最終的に更新された値を見ることになる.
さらに結果整合性にも細かなバリエーションがあるらしい。
  • 因果整合性(Causal consistency): プロセス A がプロセス B と通信を行い, その B がデータ要素を更新したとき, B が続けてアクセスをすると更新された値が戻る. また書込みはその前の書込みを上書することが保証される. プロセス A と因果関係をもたないプロセス C は通常の因果整合性に従う.
  • Read-your-wrie consistency: このモデルは重要だ. プロセス A が書込みをしたとすると, そのあと A は常に更新された値にアクセスできる. 古い値は決して見えない. これは因果整合性の特別な場合だ.
  • セッション整合性(Session consistency): 上のモデルの実用的なバージョン. プロセスはあるセッションの下でストレージシステムにアクセスする. セッションのある間, システムは Read-your-wrie consistency を保証する. 何らかの故障シナリオでセッションが破棄された場合は新たに作りなおす必要がある. また保証はセッションをまたがない.
  • 単調読み出し整合性(Monotonic read consistency): もしプロセスがオブジェクトからある値を読みだしたら, それ以降にはより古い値を読み出さない.
  • 単調書き込み整合性(Monotonic write consistency): この場合はシステムが同じプロセスからの書込みが直列化されるよう保証する.このレベルの保証をもたないシステムでプログラムを書くのは難しいことで悪名高い.
クラウドの場合にConsistencyが問題になるのは、複数ノードへの書き込み(失敗)のケース。書き込み失敗時の対処としては、リトライや、失敗したノードの切り離しがあげられる。一定回数リトライし、それでもダメだった場合にはノードを削除することで、一定時間後にはシステム内は整合性が保たれた状態になる。これが、(クラウドにおいて)Eventually Consistentレベルの一貫性の実現方法の概要。

このような、リトライやノード削除の機能は、従来データベース製品が担っていたのだが、これらは本来OSが担っても良い部分。現段階でこの部分をOSが担っていないのは、単に歴史的な経緯によるものだと思われる。少なくともクラウドの分野では、OSとデータベースの統合がはじまっている。

このような、厳密にACIDを満たさないような新たなトランザクションモデルをBASE(Basically Available, Soft-state, Eventually-Consistent)と言う。ACIDはデータベース製品などが担当する比較的局地的なトランザクションモデルであるのに対し、BASEはもう少し広い、システムレベルのトランザクションモデルらしい。

Soft-State

分散システムにおいて、状態を完全に守りきるのは不可能。丸山先生の説明はよくわからなかったので、ネットで調べてみると、こんなのがでてきた。ステートと状態がかぶっているのが気になるが、丸山先生よりわかりやすかった。

ハード・ステート状態管理
 データ通信を管理する方法の一種。一般に、通信中のコンピュータが通信プログラムを終了する際に、「デー タ通信の終了」を通知しないと、それが正常に終了したのかどうか相手にわかりません。このため、通信プログラムの動作には高い信頼性がもてません。これに 対して、「データ通信の終了」を相手に通知してから終了するようなプログラムであれば、そのコンピュータの動作には高い信頼性が期待できます(終了通知な しに終了すれば事故だとわかる)。したがって、このようなプログラムに対しては、通信を始めるときにだけ状態を確認しておけば、以降の動作には信頼性がも てます。
 このように、通信の開始のときだけ相手プログラムの動作を確認するデータ通信管理手法を、ハード・ステート状態管理と言います。既存の 電話システムが、この例です。反対語は、通信中に絶えず相手の動作を確認するソフト・ステート状態管理で、インターネットがこれに当たります。

Eventually Consistency by Example
さて、ここで、2つのシステム要素(A, B)を持つ分散システム上で、システムAが自身の状態を更新し、その状態をもとにBの状態を更新するケースを考えてみる。
極端な例として、システム要素AとBが銀河をまたいで離れているようなケースを考えると、光速度の制約があるため、Aの状態変更とBの状態変更をACIDに行おうとすると、使い物にならなくなってしまう。この場合、まずはAの状態を(ACIDに)変更し、その後でBに状態変更を通知するのが現実的になるだろう。この際、Aが通知を送ってからBに届くまでの間、システムは不整合な状態となる。Bに通知が届いた後は整合性を回復したと考えることができ、Eventually Consistentとなる。逆にこうした動作を前提にすると、ACIDトランザクションをコーディネートするようなTransaction Managerは不要になる。

こうした状況を現実の分散システムにあてはめて考えてみると、ACIDトランザクションはBASE(Eventually Consistent)トランザクションの特殊な形態(Consistencyをほぼ完全に満たすことができるケース)とみなすことができる。

クラウドにおける排他戦略
悲観ロックより楽観ロックだよね、という話。Scalabilityのためには楽観ロック。ある意味当たり前?

クラウドと永続化

クラウドのようなシステムのノードは、落ちることなく動き続ける。であれば、メモリもPersistencyを担うことができるのではないか?という発想が出てくる。となると、DBのインデックスのようなものはハッシュテーブルとして実装されることになり、自然に分散ハッシュテーブル(Distributed Hash Table)への注力へとつながっていくことになる。

現実のEnterprise SystemへのEventually Consisitency概念の応用
システム全体を単一のトランザクションモデル(特にACID/Immediate Consistency)でカバーするのではなく、ケースに応じて一貫性レベルを選択することで、PartitionやAvailability、特にPartitionを増すことができる。こうした形で応用すると良いのではないか、みたいな話だった。
Eventually ConsistentやSoft Stateは、情報システムにおける物理的な限界点を示したものであり、基本原理となるため、考慮するのは必然、といった話もちらっと最後に出てきていた。

目次:
QCon Tokyo 2009に行ってきた

    2009年4月11日土曜日

    CAP Theorem(定理)

    QCon Tokyoでは色々なところで「CAP Theorem」という定理が出てきた。が、eBayの人をのぞけば(たぶん)正確な定義は提示されておらず、不明な点がいくつか残っている。これからのインフラを考える上でも、QConセッションの内容をより良く理解するためにも、このあたりをまずきちんと理解しておきたい。主な疑問点は以下のとおり。
    • C, A, Pの特性の正確な定義は何なのか
    • CAP定理が前提にしているシステムのモデルはどのようなものなのか
    • Theoremとなっているが、単なる経験則なのか、それとも数学的な定理なのか
    頼みの綱?のWikipediaを調べてみたが、日本語版はおろか英語版にも載っていない。概観をまとめた海外の記事を結城さんが訳してくれているようなので、これを読んでみる。

    CAP Theoremそのものは、Eric BrewerがPODCカンファレンスのキーノートで提唱したものらしい。CAP Theoremには形式的な証明があるらしいので、数学的に証明された、文字通りの定理であるようだ。証明の論文はACMの有料会員にならなければ見られないらしく、無料のWeb会員では見ることができなかった。残念。Eric Brewerの資料はWeb上で公開されているので、これを見るのが一番良さそう。

    Eric Brewerのスライドでは、availabilityやgraceful degradation, performanceのためにはCとIが犠牲になるが、これは根本的なトレードオフである、という認識が出発点になっており、丸山先生も紹介している BASE(Basically Available, Soft-state, Eventural consistency)の概念の提示に続いている。さらに、ACIDとBASEはどちらかを選択しなければならないような排他的なものではなく、スペクトラム のようなものではないか、とも書かれている。

    C, A, Pの定義
    さて、CAP Theoremだが、CAPは以下のように紹介されている。
    • C:Consistency
    • A:Availability
    • P:Tolerance to network Partition
    QCon(少なくとも同時通訳で)では、Partitionについてはネットワークとの関係は言及されておらず、並列処理可能性とほぼ同等に扱われていた気がする。が、ここでは明確に、ネットワーク分断への耐性、と書かれている。
    AmazonのCTOによる、2007年InfoQの発表では....

    - Strong Consistency: all clients see the same view, even in presence of updates
    - High Availability: all clients can find some replica of the data, even in the presence of failures
    - Partition-tolerance: the system properties hold even when the system is partitioned

    となっているらしい。Partition-toleranceは、システムが分割された場合でもシステムが保持する特性、と定義されている。つまり、 Partition-toleranceが低いシステムは、分割すると色々な特性が失われる、ということだろうか。この定義からすると、並列可能性というのもあながち間違った表現ではないように感じる。

    CAP定理
    さて、以上のように簡単な定義がなされた上で、CAP定理は以下のように定義される。

    You can have at most two of these properties for any shared-data system

    すなわち、「共有データを持つシステムは、CAPとして定義された特性のうち最大でも2つの特性しか持つことはできない」。以降、例をもとに説明してあるんだが、例自体があいまいなので、例の技術に詳しくない自分にはなかなかわかりづらい。想定している状況の単純なモデルを先に確認した方が話が早いと思う。

    想定するモデル
    2つのモジュールと、その間のインタフェースについて考える。クライアントとサーバーでも、ピアとピアでもなんでも良い。典型的には、プロシージャコール の形を取るが、この際インタフェースをはさんだどちらの側も同一のアドレス空間にいる。スレッドはこのインタフェースを超えていく。

    しかし、インタフェースをはさんだそれぞれの空間が同じアドレス空間ではない場合、Call by Referenceは使えず、データをコピーしなければならなくなる。こうした状況になった場合のCAPについて議論しているようだ…うーん。それでもやっぱりわかりづらいな。例を確認してみる。

    C+Aを持つケース
    • 単一サイトのデータベース
    • クラスタデータベース
    • LDAP
    • xFS file System
    一貫性と可用性を持つかわりに、ネットワーク分断に対する耐性を失っている例。このようなシステムは、2フェーズコミットや、キャッシュ検証のプロトコルを持つ。
    単一サイトのデータベースを考えてみると、確かに一貫性は得られている。ネットワーク分断で失われる特性が多いというのは、何をさしているのだろうか?少なくとも、 ネットワーク分断をすすめていくと、性能が犠牲になるのは間違いなさそうだが。
    可用性が得られている、というのはさらによくわからない。2PCの例では、TMがACID特性を保証しているので、データのコピーは発生せず、常にすべてのデータが得られるため可用性が高い、ということだろうか?少なくとも、クラスタでは可用性は高そうだが。

    C+Pを持つケース
    • 分散データベース
    • 分散ロック
    • 主要なプロトコル
    一貫性とネットワーク分断への耐性を持つが、可用性を失っている例。悲観ロックを使ったりしているらしい。
    分散データベースを考えると、ネットワーク分断への耐性はかなり高そうなのはわかる。また、システムが一部故障した際にはすべてのデータを得られなくなる だろうから、可用性が低いというのも理解できる。分散データベースの一貫性については、実はよくわかってないのだが、きちんと保証できているのだろうか?本当にきちんとできているなら、2pcとあまり変わらない気がするのだが。

    A+Pを持つケース
    • Coda
    • Webキャッシュ
    • DNS
    可用性とネットワーク分断への耐性を持つが、一貫性を失っている例。リース/期限切れのアーキテクチャパターンや、競合解決、楽観的同時実行制御などを用いるようだ。
    DNSについて考えると、確かにネットワーク分散しても失われる特性は小さく、可用性も非常に高いが、一貫性を失っていると言える。

    この後もスライドとしては細かい話が続いていくんだけど、結局CAP定理が前提としているモデルがどのようなものであるのかは、正確にはわからないままだ。
    例えば…
    • ネットワーク分断というのは、どの要素とどの要素の分断のことを言っているのか(あらゆるシステム要素間の分断なのか、特定の部分の分断なのか)
    • 分断によって失われる特性、失われない特性とはどのようなものなのか
    • 高い可用性が意味するものは何なのか
    • などなど
    これらがわからないと、きちんと理解できた気もしないし、分散システムにおけるアーキテクチャ設計で自信もって決断を下せないと思うんだけど、これらの資料から完全に理解するのは難しそうだし、どれくらい時間がかかるのかも見当がつかない。なので、一旦はここで終え、QConの個別セッションを見ていきたい。当面は、結城さん翻訳資料にある以下の説明を、例による便宜的な定義として使っていく。
    ネットワーク分断への耐性を捨てたシステムなら, データの整合性と可用性を実現できる. これには普通トランザクション・プロトコルを使う. 実現のためにはクライアントとストレージ・システムが同じ環境にある必要がある. これだとシナリオによってはシステム全体が故障してしまうし, クライアントは分断を検知できない. 重要な洞察がある: 大規模な分散システムではネットワークの分断が所与のものであり, 従って整合性と可用性は両立しない. どちらか一方は捨てる必要がある. 整合性を妥協すればシステムは分断のある状況でも高可用性を実現できる. 整合性を優先すればシステムの使えない状況を生む.

    2009年4月10日金曜日

    パネル・ディスカッション「エンタープライズ・ソフトウェア開発の動向」

    QCon Tokyo最後のセッション。以下のような豪華な参加者陣でした。
    • Floyd Marinescu (InfoQのエラい人)
    • Dylan Schiemann(Dojoのエラい人)
    • Randy Shoup(eBayのエラい人)
    • Gregor Hohpe(Googleの中の人、EIPの共著者)
    • Henrik Kniberg(Agileなコンサルタント)
    一緒に行ったメンバーとの会話で、なんとなく自分が担当になった雰囲気がしたので、わかる範囲で書き起こしつつ、自分の感想も(発言とは区別がつく形で)書いていきます。メモが中途半端なので、話をつなげるために微妙に補足してたりします。会話口調は、そのときの雰囲気から適当に作ってます。敬称はすべて省略してます。発言者とか間違えてるかもしれないので、その辺ふまえつつ適当に見てやってください。

    [まずは、エンタープライズソフトウェア開発の動向について全員が2分ずつ発言。]

    Henrik:
    プロセスにかかわる立場からの話をしたいと思う。
    北欧ではAgileの導入がかなり進んできているけど、Agile導入に際してはScrumをエントリポイントとして採用しているところが多い。こうして、特に従来よく見られていたような、技術チームと業務チームのコミュニケーション上の問題を解決することに成功してきている。
    ただし、Agileでこうした問題が解決されるにつれて、その他の問題、例えばコードの品質などの技術的な問題もあらわれてくる。このような問題を解決するために、今後はXPで採用されているようなプラクティスが重要になってくるだろう。
    # Scrumは他のAgileアプローチに比べてプロジェクト管理に強いが、技術上のプラクティスはあまり多くないことを受けての話だと思う。

    また、企業では、ソフトウェアの開発が終わったあとも色んなプロセスが続いていく。リーンの手法が重要になってきており、ソフトウェア開発だけでなくバリューチェーン全体を改善することが重要になってきている。
    これまで企業は、変革を口にするだけでなかなか実行に移そうとしなかった面もあったが、今回の経済危機の影響を受け、話だけではなく実際に実行にうつしつつある。
    無駄なプロセスを続ける余裕があった企業も余裕がなくなってきており、組織的な変革がはじまろうとしている。

    Gregor:
    あんまりネタを考えてこなかった。
    立ってのパネルディスカッションってはじめてっす。
    立ち飲みみたいっす。
    残念なことに、飲み物がアルコールじゃないです。
    ってことは、これは立ち話ってうんですかね。
    ビールがあると最高だったんですけどね。
    とかジョークをいいながら、話のネタを考えていました。

    大きなトレンドの話としては、知っていることが何度もまわってくる、ということがある。知っていることが何度もトレンドとしてやってくるのだが、そこには新しい考えが注入されていく。
    SOAやクラウドはほんとに新しいのか、との疑問がよくあるのだが、新しいとも言えるし古いとも言える。古いコンセプトに新しい考えが注入されているのだ。こうしたトレンドのサイクルは、ファッションと似ている。色んなものはやがて戻ってくる。これは当然で、実証済みの概念というのは古くても役に立つ。

    さて、これは開発者にとってはどんな意味があるのだろうか?開発者はどうすればいいのか?
    まずは概念を理解することをすすめたい。サイクルする技術は、詳細はかわってしまうかもしれないが、多くの概念が転用できる。開発者は、転用できるアイディアにフォーカスすべき。重要な概念を概観できることが大事。

    Henrik:

    [難しくて曖昧な話はわかったから、といった調子で :-)]
    結局、具体的にはどういう動向が重要になってくると思う?

    Gregor:
    人間は未来を予想できない、予想できなくても僕のやり方なら大丈夫なはずなんだけどね! :-)
    あえて新しいトレンドを推定するなら、ランタイムの重要性の高まりかな。
    今までランタイムは見えないもので、みんなはモデルや言語なんかの表面的な部分に注力してきたけど、クラウドの登場などでランタイムが重要になってきている。
    ソフトウェアを書くのは全体の一部、という話がHenrikからあったけど、組織プロセスの観点だけでなく、ソフトウェア実行の観点でも同じことが言える。ソフトウェアを書き終わったからといって、全体のほんの一部しか終わったことにならない。

    Randy:
    トレンドとしては、3つあると思う。

    まず1つは、大きな企業のプラクティスが小さな企業に浸透してきているということ。分散化、パーティショニングのような課題を、小さい企業も意識しはじめている。

    2つめは、アプリケーションの開発方法。
    たいていの企業では色んな言語やプラットフォームでシステムを作ろうとしており、ヘトロジニアスな環境が多くなってきている。多様な選択肢があるが、最適なものを見極めることが重要だ。

    3つ目は、デプロイメント、つまりアプリケーションの展開に関するもの。
    クラウドが一番明らかな例で、これまではコンシューマ向けにしか使われておらず、クラウドのプレーヤーも少ないが、これからは銀行や大企業、政府機関などが内部にクラウドを持つようなことも起きてくるのではないか。また、クラウドが浸透していくと、中小企業にまでその利用が広がっていくだろう。

    Dylan:
    2つの大きな相反する動き、統合と細分化に注目したいと思う。
    例えばJavaを見ていくと、90年代から統合化がどんどん進んでいった。
    # この辺よくわからなかったっす
    # 統合化されて肥大化し、使いづらくなると、小さなプレイヤーにチャンスが来るとかそんな話をしていたと思う

    OSSでは細分化が進んでおり、ものすごくたくさんのプロジェクトやソフトウェアが生まれている。一方でOSS同士のアライアンスも増えてきており、統合化も同時に展開されているという興味深いことが起きている。これは非常にエキサイティングだ。

    Floyd:
    FowlerとRod Johnsonのかわりにきました。彼らはもういなくなっちゃったからね。
    Martinは、プロジェクトに常に1つの言語を使うのではなく、目的にマッチした言語を組み合わせて使う、というようなことを言っていた。Rodは、リーンなツールが必要になってきているという話をしていた…と思う。なんかこんな感じだったよね?北京ではこんな話をしていたと思うんだけど、あってる?

    Dylan:
    ともあれ、すべての根底にある流れは、Webがアプリケーションのプラットフォームになってきている、ということだ。クラウドコンピューティングもRIAのようなものも、すべてこの流れから出てきたものだ。

    ところで、Javaのコミュニティってつまらなくなってない?あきてきた人?
    [手をあげて賛成者を探す。結構手があがる]

    これは、Javaを使うタスクが安定化してきたということだ。2000年頃は、色んなWebのフレームワークが出てきていた。これはつまり、Javaを使ってWebアプリを作るのが新しかったということだ。

    技術がかわるとまたいろいろとエキサイティングなものが出てくるだろう。クラウドもその端緒になると思う。Javaで標準かが次々進んだように、クラウドのプログラミングモデルを統一化する動きがはじまるかもしれない。みんなベンダーにロックインされたくないだろう?

    クラウドコンピューティングってのはすごく重要な流れだ。これによって、中小企業が効果的に色々な仕事ができるようになるだろう。Linuxやコンフィグファイルを気にしなくてもよくなるんだ。低コストで色々できるようになる。

    Gregor:
    ここいらで、今まで出た話を統合してみたい。
    フラグメンテーションや統合の話、クラウド、開発者のエンパワーメント、これらはつながっていると思う。
    # 開発者のエンパワーメントは、Rod Johnsonがセッションの前半であげたキーワード
    開発者の力が増すという流れと、クラウドは同じ方向を向いていると思う。開発者がまたガレージで開発を始められるということだ。ここのところあがってきていたWebに対するインフラの要求レベルに、やっとおいついてきた。

    こういう、土俵が平らになるような時期が訪れることがあるが、こういう時にはイノベーションが次々とおこる。

    Floyd:
    ここで、Rodが持っていた、少し違う意見を紹介したいと思う。Rodは、GrailsはJavaコミュニティにおける重要な進展だといっていて、Javaはまだまだエキサイティングだと言っている。
    SpringSourceもクラウド対応機能みたいなものをもうすぐリリースするそうだ。

    Gregor:
    ちゃんと機能するものを作るのが生活の糧であるなら、Javaにとって安定はいいことかもしれない。安定することによって、新しいことをする余裕が出てくる。細かなバグをおいかけたりしなくてもよくなる。こういう意味で、Javaの安定にはいい面も悪い面もあるだろう。

    Dylan:
    今や、Javaは世界のFortlanやCOBOLのようになってきているよね。

    [ここで質問タイム、外国の方が英語で質問]

    質問者:
    Javaコミュニティというと、Webアプリ開発者ばかりが対象として語られることが多いけど、JavaコミュニティはWebだけじゃない。WebではないJavaコミュニティの人がこういう場にきていないのは残念なことだ。

    Randy:
    eBayはフロントエンド以外はWebとは関係ない処理をたくさんやっているよ。

    Gregor:
    自分もそう思う。実際のシステムではWebは一部のみ。Webの定義にもよるけど、HTMLを扱っているのがWebだとすると、システムの大半はHTMLとは関係ない。
    バックエンドでは、SOAや コンポーネント化、RESTのような技術を扱っているのだろうけど、これらもWebと似たような課題を持っているのではないか?Web的な考え方がシステム全体に広がってきているのではないか?

    Randy:

    たしかに分散システムという意味では同じような特性を持っていると思う。とはいえ、銀行や政府のシステムとeBayのようなシステムでは要件はかなり異なってくる。

    Floyd:
    Webは新しいプラットフォームなのでしょうがない面もある。デスクトップJavaはずいぶん前に死に絶えてしまったし。

    質問者:

    確かにそうかもしれない。JavaがWebを捨ててしまえば生き残れないだろうから、仕方ないとは思う。

    Dylan:
    ヘテロな環境が増えてきたので、最適なものを選ぶことができるようになってきた。
    # これ、なんだったか思い出せない...

    [次の質問。]

    質問者:

    Scalaが好き!Scalaは分散処理などの面で面白いと思うけど、どう思う?
    # すごいはしょってます...

    Randy:
    実際に使ったことはないが、Scalaは良いと思う。正しいツールを正しい仕事のために使う、という話で考えると、すばらしいツール。Erlangにも興味深いアプリケーションができてきているし、特定のタイプの作業に特化した言語を適切な場所に使うのが重要になってくる。

    Gregor:
    一貫性・分散性を考慮すると、もっといい言語がほしいという気持ちはある。Javaはこの辺りを考えると、あまり適していない部分がある。
    # このあたり、C#がLINQや関数的な要素を入れていろいろやっている話をしていたが、どうつながっているのかよくわからない。

    僕はツールに興味があって、コンパイラとかエディタとかIDEとか、そういうものが生産性を高めてくれることを重要だと思っている。言語を切り替えるとツールも切り替えることになってしまうので、新しい言語は5倍くらいの表現力がないと、ツールの変更で損なわれるものを埋められないんじゃないかと思う。

    Dylan:
    僕はviだから大丈夫!

    Gregor:
    私はNotepadだ!

    Language Workbenchはこういうのをうめてくれるのかもしれないが、うまく動くようになるまでにはもう少しかかりそうだ。

    Randy:
    .NET開発者とかJava開発者とかいわなくなったのが、最近の状況を表している。1つのシステムを構築するにも、色んな言語やツールを使わなければならなくなっているということだ。Scalaについて言えば、Scalaの専門家とまではいかなくても、色々知ってるジェネラリストが必要。どの分野にどの言語を使えばいいか、判断できる人が必要だろう。そこで、より深く専門的な知識が必要になれば、言語の専門家が必要にもなってくるだろう。

    Gregor:
    賛成でもあるし反対でもある。どういうアイデアを転用できるかを知るのが大事。デザインコンセプトを知るということ。
    ところが実際に、言語を使いこなす方法をツール・パターン・ベストプラクティスを含めて理解するまでには時間がかかる。Javaで我々は10年かかった。
    あまりに多くの言語があふれると、開発者は大変になる。現代では、最大の制約は人間の学習能力になっている。現段階で存在する大量の言語は、既に一人の人間が理解できる範囲をこえている。色々な言語やツールに触れていると、深く使いこなすことができなくなるかもしれない。これはもしかしたら問題にならないかもしれないが、考える必要はあると思う。

    Dylan:
    ツールがやってくれるんじゃね?

    Others:
    ツールを選ぶためのツールがいるのか!
    そうなったらコンサルタントいらないね!
    ハハハハ!

    [次の質問。]

    質問者:
    言語・アーキテクチャやプロセスに色んな変化がおきているが、テストについてはあまり変化がないように思える。テストの変化についてはどう考えている?また、どうなっていくべき?

    Dylan:
    テスト用のクラウドサービスやブラウザモックもある。社内でできないようなテストを行うサービスもできてきている。AjaxのテストにはSeleniumやWindmilもある。テストツールはいつも少し遅れて出てくるが、だいぶ追いついてきた。

    Gregor:
    テストには2つの大きな目的がある。正しいかどうかを確認するためのテストと、開発の一環としてのテストだ。開発の一環としてのテストには、TDDやBDDなどの革新がある。
    これは、ツールの問題ではなく、使い方、使いこなし方の問題だ。JUnitが出てから8年たつが、このような新しい使い方が出てきている。

    もう1つ、ランタイムへのシフトがあげられる。ランタイムで設定される様々なプロパティや環境情報をテストする必要がある。

    職業としてのテストの輪郭はぼやけてきている。これは、アーキテクトなどのほかの職業にも言える。テスタの役割も変化してきていて、自動化できないところをやることが重要になっている。テストはかなり自動化されてきているので。実験的なテストなど、自動化できないテストをうまくできることが強みになってくるだろう。

    [次の質問。]

    質問者:
    典型的な業務(会計とかなんとかとか)の用語がほとんど出てこなかったが、このような業務に関わる人はあまりいないのだろうか?最初の話にも少しあったが、業務側と技術側がわかれているような気がするがこれはいいことか?エンジニアであれば、どちらもできるべきじゃないのか?
    こういう問題はあると思うか?あるとしたら、どういう風に解決しているか?

    Henrik:
    これは、問題だと思う。しかし、リーンやScrumを使うことでビジネス側とつながってきているので、どんどん解決に向かっている。

    Gregor:
    Fowlerはもう語り尽くしたから言わなかったんだと思うよ :-)
    問題はあると思う。これに対する解決策としては、自分はテスティングをあげるようにしている。仕様上の問題点などは、テストとして起こして、みんながそれを見ることができるようにしている。今の仕事上、業務というとHTTPヘッダとかそういうテクニカルな話になってしまうんだけど。

    Randy:
    eBayの経験からすると、一番うまくいったプロジェクトは、ビジネスチームと技術チームがタッグを組んだもの。これらの間の乖離がひどいものは成功していない。

    Dylan:
    私もCEOをしているのだが、ビジネス的なバックグラウンドを持つのはいいこと。なぜそういう要件が必要になるのかがわかるようになるし、自分たちのしていることがどのような収益を生むのか、なども理解できるようになる。事業側の立場にたって考えることは必要。

    Gregor:
    極論すると、ビジネスユーザーがVBマクロを書いたときに、一番うまくいっていた。マクロのスクリプティングは強力だ。
    しかし、(例えばVBが新しいバージョンになった時などに)これらをどうやって拡張するのか、再利用するのか、といった問題は解決されていない。

    [このあたりで時間切れ。ありがとうございましたー。]

    目次:
    QCon Tokyo 2009に行ってきた

    QCon Tokyo 2009に行ってきた

    QCon Tokyo 2009

    QCon Tokyoは2日間開催で、それぞれの日ごとに3つのトラック({1日目 => [Architecture, RIA, Ruby], 2日目 => [Cloud Computing/SOA, Agile, Case Study]})が設定されていて、トラックごとにセッションが4つ設定されている。また、各トラックに属する個別セッションとは別に、日ごとに2つのジェネラルセッションが設定されていて、大御所たちが話すことになっている。

    QConでは、良いと思ったセッションに緑、普通と思ったセッションに黄、いまいちと思ったセッションに赤のカードを入れることになっているんだけど、これに習って自分が出席したセッションを評価すると、こんな感じになる。

    [緑(よかった!)]
    • アーキテクトの審美眼 by 萩原正義
    • General Session 1 クラウドの技術的な特徴について by 丸山不二夫
    • クラウドのプログラミング − プラットフォームとしてのインターネット by Gregor Hohpe
    • 大規模ウェブサイトのベストプラクティス −eBayでの事例− by Randy Shoup
    • パネル・ディスカッション「エンタープライズ・ソフトウェア開発の動向」 by Floyd Marinescu
    [黄色(ふつう)]
    • General Session 1 ドメイン固有言語 −その役割− by Martin Fowler
    • General Session 2 ビューティフルコード by まつもとゆきひろ
    • Open Webの進展とその今後 by Dylan Schiemann
    • 実世界のRuby −3年間の経験から− by Martin Fowler
    • QCon Tokyo Conference Party
    • General Session 2 Spring Today and Tomorrow by Rod Johnson
    • アジャイル開発を始めるために −実例で得られた知見から− by Henrik Kniberg
    [赤(いまいち)]
    • Amazon Web Services in Action by Jeff Barr
    もともとはRod JohnsonとMartin Fowler目当てで行ったようなものなんだけど、Rod Johnsonの話はちょっとマーケティング色が強すぎて微妙だったので黄色。Martin Fowlerのジェネラルセッションは、既存の資料(DSL WIPやThought Works Anthology)で知っていた話が多かった。Rubyのセッションは興味深くはあったものの、Martin Fowlerの話というより、Thought WorksでRubyに取り組んでいる人たちの話をまとめた内容になっていて、Fowler自身の言葉があまり聞けなかったのが残念。

    上記のセッションを聞いてみて自分が持った感想は、「世の中や技術は複雑さを増し続けていて、単一の言語・製品・モデル・パラダイムだけでは(少なくとも効率的には)対応できない」ということ。Rod Johnsonは「JavaEEのようにすべてを包含しようとするのではなく、リーンな(特定の目的のために存在し、シンプルで無駄がない)仕様や製品を、適した場所に使っていくこと」を提唱していたし、Martin Fowlerは「特定のドメインに特化した言語を使い、それを場所に応じて使い分けることで、効率的にシステムを記述/開発できる」と考えているようだ。丸山先生やeBayのRandy Shoup、GoogleのGregor HorhpeはACIDとは異なる新たなトランザクションモデル(丸山先生はBASEモデルと呼んでいる)を提示して、ACIDトランザクション一色の既存のシステム開発の再考を促している。MSの萩原さんは、まんまマルチパラダイムへの流れを強調していたしな。
    Rubyは、DSLを記述するための高い表現力を持つため、内部DSL向けのホスト言語として重要な位置を占めることになる。
    # あれ…アジャイルは?^^;

    個別セッションのまとめは、内容的に興味が持てたものでかつ、かけそうなものを選んでちょこちょこ書いていく予定。たぶんInfoQの方にビデオ出ると思うし。とりあえずは、パネルディスカッションあたりから起こしていく予定。

    レポート:
    追記:
    発表資料公開されました。
    http://qcontokyo.com/tokyo-2009/schedule.html