2011-04-23
Akka の作者として益々注目を集めている Jonas Bonér が 2008年に書いた “Real-World Scala: Dependency Injection (DI) ” を翻訳しました。翻訳の公開は本人より許諾済みです。翻訳の間違い等があれば遠慮なくご指摘ください。
2008年10月6日 Jonas Bonér 著
2011年4月22日 eed3si9n 訳
さて、実戦での Scala シリーズ第二弾の今回は、Scala を用いた Depenency Injection (DI) の実装をみていきたい。Scala は、備わっている言語機構だけを用いても何通りかの DI を実現できる非常に豊かでディープな言語だが、必要に応じて既存の Java DI フレームワークを使うこともできる。
Triental では、一つの戦略に落ち着くまで三つの異なる方法を試した。以下のように話を進めていく。まず、現行の DI の実現方法を詳しく説明した後で、試した他の方法も簡単にカバーする。
Cake パターンを用いる
私たちが用いている現行の戦略は、いわゆる Cake パターンに基づいている。このパターンは、Martin Odersky の論文 Scalable Component Abstractions において、Ordersky と彼のチームが Scala のコンパイラを構成した方法として最初に発表された。このパターンがどのようにして DI を実現するのかということを日本語で説明する事を試みるよりも、(私たちの実際に使っているコードに大まかに基づいた)愚直なサンプルコードをみてみよう。
注意:
順を追って、最終バージョンに向けてリファクタリングしながら説明していくので、最終バージョンを読んで理解するまでは、「ダメじゃん!」と叫ぶのは待ってほしい(もちろん、読了後にどうしてもと言うなら批判、賞賛、意見、アイディアなどを送ってもいい)。また、このサンプルコードは、このようなサンプルの例にもれず、非常に複雑な方法で取るに足りない事を行っているようにみえるが、我慢して大規模システムでの実際のサービスを想像して、どのように応用できるかを想像して欲しい。
まずは、UserRepository (DAO、Data Access Object) を実装しよう。
// 実際の永続化は何もしていなくて、画面にユーザを表示するだけのダミー。
class UserRepository {
def authenticate( user: User ) : User = {
println( "authenticating user: " + user)
user
}
def create( user: User ) = println( "creating user: " + user)
def delete( user: User ) = println( "deleting user: " + user)
}
trait インターフェイスとその実装に分けて実装することもできたが、話を簡単にするために、敢えてここではそうしなかった。
2011-01-03
次々とヤバいコードを紡ぎ出し NY Scala シーンの中心的存在であり続ける @n8han が二年前に書いた “Weaving tweed with Scala and Json ” を翻訳しました。翻訳の公開は本人より許諾済みです。翻訳の間違い等があれば遠慮なくご指摘ください。
2009年5月27日 n8han 著
2011年1月2日 eed3si9n 訳
抽出子 は、Programming in Scala の 24章にのみ記述されている Scala の秘密機能で、この春の大ヒットだ。今までは皆 case Some(thing) => kerpow(thing) で満足だったのが、今では抽出子を書けなければ #scala freenode にも入れてもらえない。ズルをすれば Scala チャンネルの硬派な常連たちは君のコンピュータをハックして驚くほど馬鹿げたドールハウスを連続再生して(ただし混浴シャワーシーンを除く)、番組の「アクティブ」のように意味のあるタイピングを諦めなければいけない。
訳注。以下 Draco より抜粋
ロッサム・コーポレーションは人間の人格、経験をすべて消し、新しい人格を植え付ける技術を開発した。彼らはこの技術を用いて、戦闘用の人形アクティブを生み出した。
人形たちはドールハウスという施設で生活している。誰かの人格を移植され、任務に挑む。
アクティブの一人、エコーは記憶のリライト中にエラーが起こり、完全には記憶が消されていなかった。
この抽出子はちょっとチクっとしますからね
命からがら逃げ出した僕は抽出子をありとあらゆる状況に適用するよう努めた。例えば、JavaScript インタプリタが理解できるお洒落な文字列、Json オブジェクトだ。抽出子を使えば case 構文でこのように処理することができる:
import dispatch.json.Js
val echo = Js("""{"acting": "無表情で前を見ている"}""")
object Echo extends Js { val acting = 'acting ? str }
echo match {
case Echo.acting(hmm) => hmm
case _ => "pshaw"
}
res0: String = staring blankly ahead
Symbol 'acting に対して ? を呼び出すことで抽出子が作成される。Symbol には ? メソッドはないが暗黙の変換でそれをもつオブジェクトに変換されている。Echo はいつも演技してるということが分かっているので、この抽出子を使って直接代入することもできる:
2010-12-30
Scala をやりながら他の IDE も試しましたが、結局 TextMate に戻っています。
今回巷で話題の IntelliJ IDEA に便乗するにあたって、Twilight theme を作りました。
2010-12-07
/ scala / scalaxb
結局の所,scalaxb のユーザはエンティティ・オブジェクトが表現する現実の問題に興味があるのであって,それがどう XML に永続化されるかといったことではない.だから,いつかデータバインディングの実装をシングルトン/コンパニオン・オブジェクトから追い出さなければいけないことは分かっていた.つい最近までデータバインディングの実装は以下のように生成されていた:
object Address extends rt. ElemNameParser [ Address ] {
val targetNamespace = "http://www.example.com/IPO"
def parser( node: scala.xml.Node ) : Parser [ Address ] =
...
def toXML( __obj : Address , __namespace : String , __elementLabel : String , __scope : scala.xml.NamespaceBinding ) : scala.xml.NodeSeq =
...
}
つまり,scalaxb は Address そのものとは関係の無い XML データバインディングのために一等地をハイジャックしてしまったのだ.
adapter
まず最初に以下のような adapter オブジェクトに追い出すことを考えた:
object DefaultXMLAdapter {
object AddressAdapter extends rt. ElemNameParser [ Address ] {
val targetNamespace = "http://www.example.com/IPO"
def parser( node: scala.xml.Node ) : Parser [ Address ] =
...
def toXML( __obj : Address , __namespace : String , __elementLabel : String , __scope : scala.xml.NamespaceBinding ) : scala.xml.NodeSeq =
...
}
}
この方法にはいくつかの問題がある.まず,scalaxb のランタイムである DataRecord が,今まではコンパニオン・オブジェクトの暗黙性を使ってたどっていた toXML にたどり着けないということだ.コンパニオン・オブジェクトの興味深い一面として「コンパイラは,暗黙 (implicit) の定義を変換元の型と変換先の型のコンパニオン・オブジェクトにも探しにいく」(Programming in Scala, p. 441) というものがある.
第二の問題は,アイデンティティー問題だ.ユーザのコードの邪魔にならないようにしようとしているのに,Address オブジェクトのためには DefeaultXMLAdapter.AddressAdapter,Item のためには DefeaultXMLAdapter.ItemAdapterなどと, かえって目に障るものを導入してしまった.ユーザが知っている必要があるのは Address が XML に変換できるという事実だけあって,それがどう行われているかというのは余計な詳細でしかない.
第三の問題として,拡張性の問題がある.例えば,Address を定義する ipo.xsd と,Address を使う purchaseReport要素を定義する report.xsd の二つのスキーマがあるとする.問題は,report.DefaultXMLAdapter.PurchaseReportAdapter は ipo.DefaultXMLAdapter.AddressAdapter を参照するため,ipo.DefaultXMLAdapter を拡張してカスタムのデータバインディングをすることができないということだ.
2010-11-06
Debasish Ghosh さん (@debasishg ) の “sjson: Now offers Type Class based JSON Serialization in Scala ” を翻訳しました.
元記事はこちら: http://debasishg.blogspot.com/2010/07/sjson-now-offers-type-class-based-json.html
(翻訳の公開は本人より許諾済みです)
翻訳の間違い等があれば遠慮なくご指摘ください
長い間 sjson のシリアライゼーション API はリフレクションに依存するものだった.この方法の長所としては,縁の下ではリフレクションによる実装が頑張っていても API は使いやすくすることができたということだ.
しかし,JSON 構造と Scala のオブジェクトでは型情報の豊かさに大きな違いがあることを忘れてはいけない.Scala から JSON にいくときに何らかの形で型情報をシリアライゼーションプロトコルの一部として保存しないかぎり,可逆変換させるのは場合によってはとてもトリッキーで難しいことになる.特に JVM では type erasure のせいで JSON構造にシリアル化した Scala オブジェクトの中には元に戻すのがほぼ不可能なものもあるだろう.
ver 0.7 より sjson は元のものに加えリフレクションを使わない JSON シリアライゼーションプロトコルを用意した.これはユーザが任意のオブジェクトから JSON へシリアル化する自分のプロトコルを規定できるようになった.リフレクションによる JSON シリアライゼーション ではアノテーションで行っていたものをカスタムプロトコルを自分で実装することで実現することができる.
sjons の型クラスによるシリアライゼーションは David MacIver による素晴らしい sbinary (現在は Mark Harrah によりメンテ されいる) にインスパイアされており,同じプロトコルを使いまた実装レベルでも色々と盗ませてもらった.
型クラスの基礎的概念への入門,Scala での実装,そして型クラスを使ったシリアライゼーションプロトコルが Scala でどう設計できるかについては,数週間前に書いた以下の blog 記事を参照してほしい:
組み込み型の JSON シリアライゼーション
これは sjson でデフォルトのシリアライゼーションプロトコルを使った REPL セッションの一例だ…
2010-11-03
Debasish Ghosh さん (@debasishg ) の “Scala Implicits : Type Classes Here I Come ” を翻訳しました.
元記事はこちら: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here-i.html
(翻訳の公開は本人より許諾済みです)
翻訳の間違い等があれば遠慮なくご指摘ください.
先日 Twitter 上で Daniel と Scala での型クラスについて論議していると,突然このトピックに関する書きかけだった記事を発見した.これを読んでもあなたは特に目新しい事を発見するわけではないが,型クラスに基づいた思考はあなたの設計の幅に価値を与えることができると思う.この記事を書き始めたのはしばらく前に設計の直交性についての記事 (原文) を公開したときのことだ.
まずは GoF の Adapter パターンから始めよう.委譲型の Adapter はよく勧められる合成(composition)というテクニック用いて抽象体(abstraction) どうしをバインドする.
設計の直交性のときと同じ例を使うと,
case class Address ( no: Int , street: String , city: String ,
state: String , zip: String )
これを LabelMaker というインターフェイスに適合させたいとする.つまり,我々は Address オブジェクトを LabelMaker として使いたい.
trait LabelMaker [ T ] {
def toLabel( value: T ) : String
}
インターフェイス変換を行うアダプターは…
// Adapter クラス
case class AddressLabelMaker extends LabelMaker [ Address ] {
def toLabel( address: Address ) = {
import address._
"%d %s, %s, %s - %s" . format( no, street, city, state, zip)
}
}
// この Adapter は Address オブジェクトに LabelMaker のインターフェイスを提供する.
AddressLabelMaker (). toLabel( Address ( 100 , "Monroe Street" , "Denver" , "CO" , "80231" ))
さて,上の設計で我々が副次的に導入してしまった複雑さはなんだろう?
2010-11-03
Debasish Ghosh さん (@debasishg ) の “Refactoring into Scala Type Classes ” を翻訳しました.
元記事はこちら: http://debasishg.blogspot.com/2010/07/refactoring-into-scala-type-classes.html
(翻訳の公開は本人より許諾済みです)
翻訳の間違い等があれば遠慮なくご指摘ください.
二週間ほど前に Scala の暗黙の(implicit)パラメータを用いた型クラスの実装 について書いた.型クラスはある抽象体(abstraction)についての直交した関心事を,抽象体そのものに直接組み込むことなくモデル化することができる.これでコアな抽象体から余計なものを取り去って,別々の独立したクラス構造に変えていくことができる.最近 Akka actor のシリアライゼーションをリファクタリングして型クラスの恩恵に関する実地的な知見を得ることができたので,ここに報告したい.
最初は継承と trait でうまくいくと思った…
… しかし,それは長続きしなかった.Jonas Boner と筆者の間で actor のシリアライゼーションに関して面白い論議があり,以下のような設計が生まれた …
trait SerializableActor extends Actor
trait StatelessSerializableActor extends SerializableActor
trait StatefulSerializerSerializableActor extends SerializableActor {
val serializer: Serializer
//..
}
trait StatefulWrappedSerializableActor extends SerializableActor {
def toBinary: Array [ Byte ]
def fromBinary( bytes: Array [ Byte ])
}
// .. 以下続く
このような trait はシリアライゼーションという関心事をコアな actor の実装と結合(couple)させすぎてしまう.様々なシリアライズ可能な actor があるため,良いクラス名が足りなくなってきていた.GoF本が教えてくれる知恵の一つにインターフェイスを用いたクラスの命名に困るとしたら,間違ったことをやっている,というものがある.関心事をより意味のある方法で分割する別のやり方を探ろう.
型クラスだ
コアの actor 抽象体からシリアライゼーションに関するコードを抜き出し,別の型クラスにした.
/**
* Actor 直列化のための型クラス定義
*/
trait FromBinary [ T <: Actor ] {
def fromBinary( bytes: Array [ Byte ], act: T ) : T
}
trait ToBinary [ T <: Actor ] {
def toBinary( t: T ) : Array [ Byte ]
}
// クライアントはそれぞれの Actor のための Format[] を実装する必要がある
trait Format [ T <: Actor ] extends FromBinary [ T ] with ToBinary [ T ]
actor をシリアライズ可能にするためにクライアントが実装する必要がある FromBinary[T <: Actor] と ToBinary[T <: Actor] という二つの型クラスを定義した.これをさらに Format[T <: Actor] という二つを合わせた trait に組み合わせた.