2011年12月16日金曜日

Netty入門 - 「Nettyベース」の何かに着手する前に

12/15 の Akasaka.scala で Netty について簡単に説明した。

2011年10月8日土曜日

Play Scala 0.9.1 から Play 2.0 まで

10/5 の Daimon.scala で Play Scala 0.9.1 と Play 2.0 について簡単に説明した。

2010年2月13日土曜日

なぜ Lift に違和感を覚えるか

最近の私の仕事は Ruby on Rails 案件ばかりだ。そうなる以前、流行しつつある Rails を横目に見ながら Java 案件に取り組んでいた頃は、Java の融通の利かない言語仕様に苛立ち、一刻も早くJava から Ruby へ移行したいと思っていた。しかし、実際に Ruby で開発を行ってみると、そこには譲れないトレードオフがあった。Eclipse 上の Java コードは、死んだ文字列ではなく、呼びかければ応えてくれるオブジェクトだったが、Aptana RadRails 上の Ruby コードは、それとは程遠い代物だった。補完はまともに働かず、依存先へのナビゲーションや依存元の検索はインテリジェントでなく、リファクタリング支援は貧弱。これは、特定ツールの未成熟の問題というよりも、動的型付け言語のアイデンティティに関わる問題だろう。そして、Rails アプリケーションの実行速度は一般的に遅い。View のキャッシュで強引に解決できるかはドメイン次第だ。Ruby 1.9 対応や Merb 統合などの成果が意図通りに出るのかはよくわからない。

Java 案件時代に私が漠然と必要としていたのは、おそらく次のような特性を持つ言語だったはずだ。

  • 型推論を備えた静的型付け
  • 式言語が不要になる程度の表現力 (演算子的メソッドやプロパティ構文や名前付きパラメータなど)
  • ビルトインの動的プログラミング手段 (Java のリフレクション や Dynamic Proxy や バイトコードエンハンスメントなどよりも簡単に使えるもの)
  • クロージャ
  • Java と同等のパフォーマンス
  • Unix 系 OS でのまともな動作 (C# は残念ながらここで落選 - Mono ?)

そして、(今年予定の JDK7 でも Generics 限定型推論の導入とシンプルなクロージャの導入止まりな Java を差し置いて) 既に Scala がそこにある。Scala には、変態的な考え抜かれた言語仕様に基き上記のほとんどの特性が備わっているだけでなく、関数型パラダイムや Actor や Java 統合などの嬉しいおまけもついてくる。さらに、Scala には既に Rails 相当の Web フレームワーク、Lift がある。

だが、Lift の設計は、ざっと見た限りでは、大きな違和感を覚えさせるものだ。Rails は、いわば過去の MVC Web アプリケーションフレームワークのラディカルな書き直しであり、Java から Ruby へ (より詳細に言えば、Java の「Web アプリケーションフレームワーク + DI コンテナ + OR マッパー」の 3 点セットから、フルスタックの Rails へ) の移行は、少なくともアーキテクチャの理解の面では大きな困難を伴うものではなかった。Lift の場合は大きく事情が違う。

以下いくつか違和感のポイントを挙げてみよう。

POSO (Plain Old Scala Object) でない Domain Object
Domain Object の属性が object なのは不自然だ。クエリのタイプセーフ性という (唯一の ?) メリットは、払う代償と釣り合っているのだろうか。タイプセーフ性だけが目的であれば、メタデータクラスの自動生成などの別の解決策もあったのではないか。現状では Domain Object のライフサイクルはフレームワークに完全に握られ、アプリケーションスコープのロングキャッシュやセッションへの退避などはたぶん難しい。さらに、永続化部分を Lift ORM 以外のものに差し替えようとすると、Presentation 層のコードを大幅に書き換える必要がありそうだ。
Domain Object と Presentation 層の密結合
Lift の Domain Object には HTML フォーム出力機能までが癒着しているが、ひとつの Domain Object が複数の画面 (例えば、一般ユーザ向け画面と管理者向け画面や、PC サイト向け画面と携帯サイト向け画面) で違う View を持つことは普通にありうるし、また、HTML の View を伴わない RESTful API やバッチアプリケーションでも同じ Domain Object を使うはずだ。Domain Object が層をまたがっていること自体は構わない (Rails はそうして生産性を上げたし、それまで Java で層間分離と DTO の介在必須を主張していた人たちは Rails を見てあっさり宗旨替えしてしまった) が、View 固有の事情は、Scala であれば後付けの trait で mix-in できたりしないものだろうか。
View First Design それ自体
これまで Controller First なフレームワークに基づく開発で蓄積してきた設計ノウハウの一定部分が無駄になってしまう (そもそも、これまで Controller First であることの不都合を感じていない)。また、名詞を中心に HTTP メソッドに対応する動詞がぶら下がる RESTful なアーキテクチャでは、Rails のような Controller を中心にメソッドがぶら下がる Controller First のほうが自然な設計になるのではないか。非同期ページ要素更新が主な構成であれば View First の利点もあるかも知れないが。
リファクタリング耐性の低い Template View
Scala の表現力をもってすれば、Template View の式言語のポジションにそのまま Scala を置いて、Template View にリファクタリング耐性を持たせることができるはずだ。だが、Lift の取った選択肢は独自タグ (Snippet のメソッドコールに対応する) だった。テンプレートが一応 XHTML 的に valid になるというメリットはあるが、Tapestry や Wicket の Plain Old HTML Document バインディングほどは徹底していない。
責務の不明確な大クラス
とりあえず net.liftweb.http.Snet.liftweb.http.LiftRules を参照。例えば WebWork2 (後の Struts2) で慎重な Servlet API の隠蔽が行われていたのとは対照的だ。
ノイズの多い API
Domain Object の getSingleton は Convention over Configuration で解決すべきだ。RequestVar(Full("xx")) は冗長だ。Schemifier.schemify のパラメータには適切なデフォルト値があるべきだ。…など多数。一言で言うと、DSL 的でない。
独特すぎる命名
Rails の命名は、PofEAA に明示されているような慣習にある程度沿ったものだったが、Lift のそれはよくわからない。コアなクラスからして、上述の net.liftweb.http.S や、net.liftweb.mapper.Mapper (Data Mapper ではなく Domain Object) など、独特だ。さらに、ユーザの作成するクラスに関しては Rails の *Controller *Helper のような類型別の命名規約がないようだ (例えば、チュートリアルの ToDo モデルに関する Snippet の名前はなんと TD だ)。

Lift の生産性がある特定の分野にフォーカスして高いものであることに関してはその通りなのかも知れないが、幅広い分野に適用可能なスケーラブルな設計であるとは思えないし、少なくとも、初期学習コストを多く伴うハードランディングなソリューションであることは確かなようだ。Lift は、Scala による Web アプリケーションフレームワークの必然的な収束点だろうか ? おそらく違うと思う。よりソフトランディングな競合フレームワークが出現することを期待したい。

2010年2月10日水曜日

Wiki ページとリファクタリング

パターン、Wiki、XP』に、オリジナル Wiki の設計思想のひとつとして「ドキュメントはプログラムのように記述されるべきだ」(p145, 153) という言葉が挙げられている。しかし、現在主に使われている Wiki エンジン (ここでは主に Trac と Redmine の Wiki 機能を想定している) に至っても、そのページ編集におけるユーザエクスペリエンスは、例えば Java コードを Eclipse でリファクタリングするときのような自在な感覚からはほど遠い窮屈なものだ (あえてそこに制限を加えることで得られる何かがあるか ? という点についてはとりあえず問わないことにする)。

手始めに、一定以上の規模の開発プロジェクトなら必ず発生する、複数ページの用語の一括置換について考えてみよう。現状では以下のような手段がある。

バックエンドデータベース内容の置換
Trac や Redmine で Wiki ページの実データがどのような形式で保存されているかは比較的簡単に確認することができ、置換 SQL をその場限りで実行すれば一応目的は達成される。が、変更履歴は残らないし、変更履歴を残すようにするにはより複雑なデータ操作が必要だ。さらに、こうした対処を実行できるのは、(置換処理をプラグイン化でもしない限りは) データに直接アクセスできる権限を持つ者に限られる。
Web クライアントライブラリによる置換
Ruby の Mechanize や Watir のような Web クライアントライブラリを使えば、人のブラウザ上での操作をなぞるようにして、複数ページの置換を自動化することができる。が、処理速度や、履歴の各ページへの散逸や、排他制御などの問題は残る。

いずれにしても目的に対しての手順が不必要に煩雑であるし、用語の一括置換より複雑なレベルのリファクタリング (ページのマージや項目の階層移動) になるともう対応しきれない。

ここで前提条件をひとつ変えてみよう。Wiki エンジンのバックエンドデータベースが、Trac や Redmine のそれのような RDBMS ではなく、Subversion などのバージョン管理システムであったらどうだろうか。

バージョン管理システムベースのページソースの置換
1 ページにつき 1 個のソースファイル (例えば、reStructuredText 形式のファイル) がまとめて checkout / commit できれば、あとは一括置換なりページのリネームなりは自由に実行できる。さらに、複数ページの置換がばらばらの操作ではなくアトミックで意図の明確な一つのリビジョンアップとして記録されるし、排他制御における競合への対処はバージョン管理システムが得意とするところだ。

これは自然な選択肢であるように思える。単純なリファクタリングにとどまらず、(ページの名前空間がフラットである制約を外せば) 安定版バージョンのページを trunk で、開発版バージョンのページを branches で、それぞれ並行運用し時期を見てマージするといった、まさにソースコードと同じライフサイクルでのページ編集を実現できる。用語の一括置換より複雑なレベルのリファクタリングについても柔軟に対応可能だ。

パターン、Wiki、XP』にも、初期の Wiki クローンがバージョン管理システムをバックエンドにしていたとの記述がある (p156) し、例えば Hiki には Subversion をバックエンドとするモードがあるようだ。しかし、現在多く使われている Wiki エンジンでは、なぜこうしたアーキテクチャが採用されていないのだろうか ?