N2 ToolBox(跡地)

跡地です。引っ越しました。http://d.hatena.ne.jp/nosen

Scalaによる64行のDependency Injectionフレームワーク

2009-10-14 01:58:33 | その他
機能のエントリで作ったDIフレームワークをもうちょっとちゃんとしました。
長いですけど引用します。
コンポーネントの循環参照を自動的に解決できるようになりました!


 1 import scala.collection.immutable.Map
 2 import scala.collection.immutable.Stack
 3
 4 trait Key[+T]
 5
 6 class Component(val entries:Map[Key[Any],(Injector)=>Any]) {
 7
 8     def define[T](key:Key[T], factory: (Injector)=> T) =
 9         new Component(entries + (key -> factory))
10
11     def define[T](key:Key[T], factory: => T) =
12         new Component(entries + (key -> (inj => factory)))
13
14     def apply[T](key:Key[T]):T = {
15         val inj = Injector.root(this)
16         inj(key)
17     }
18
19 }
20
21 object Component {
22     def define[T](key:Key[T], factory: (Injector)=> T) =
23         new Component(Map(key -> factory))
24
25     def define[T](key:Key[T], factory: => T) =
26         new Component(Map(key -> (inj => factory)))
27 }
28
29 object Injector {
30     def root(component:Component) = new Injector(component, Stack.Empty)
31 }
32
33 class Injector(val component:Component, resolving:Stack[Dependency[Any]]) {
34     def push(dep:Dependency[Any]) = new Injector(component, resolving push dep)
35
36     def apply[T](key:Key[T]):T = {
37         if(!component.entries.contains(key)) error("dependency not found:" + key + ",path=" 
38             + resolving.map(_.key).mkString("->"))
39
40         resolving.find(dep=>(dep.key == key)) match {
41             case Some(d) => new DependencyDependency(key, d)
42             case None => new ObjectDependency(key, this)
43         }
44     }
45 }
46
47 trait Dependency[+T] {
48     val key:Key[T]
49     def instance:T
50 }
51
52 class ObjectDependency[+T](val key:Key[T], inj:Injector) extends Dependency[T] {
53     private val injector = inj push this
54     private val factory = inj.component.entries(key)
55     val instance:T = factory(injector).asInstanceOf[T]
56 }
57
58 class DependencyDependency[+T](val key:Key[T], dep:Dependency[Any]) extends Dependency[T] {
59     def instance:T = dep.instance.asInstanceOf[T]
60 }
61
62 object Dependency {
63     implicit def dependencyToComponent[T](dep:Dependency[T]):T = dep.instance
64 }


使い方はこんな感じ。


 1 class A(val inject:Injector) {
 2    val b = inject(B)
 3    def sayHello = b.c.sayHello
 4 }
 5 object A extends Key[A]
 6
 7 class B(val inject:Injector) {
 8     val c = inject(C)
 9 }
10
11 object B extends Key[B]
12
13 class C(val inject:Injector) {
14     val a = inject(A)
15
16    def sayHello = println("hello, I am C")
17 }
18
19 object C extends Key[C]
20
21 val component:Component = Component
22   .define(B, new B(_))
23   .define(A, new A(_))
24   .define(C, new C(_))
25
26 val a = component(A)
27
28 a.sayHello
29


A -> B -> C -> A という循環参照が解決されてます。

Scalaによる19行のDependency Injectionフレームワーク

2009-10-12 10:32:09 | その他
お久しぶりです。

Scala でDIっぽいことをやる方法と申しますと、こういったアプローチがあるのですが、
これだと、inject用のTraitを山のように定義しないといけなくて、煩雑な気がしたので、自分で簡単なDIの仕組みを作ってみました。

こんな感じです↓


 1 import scala.collection.immutable.Map
 2
 3 trait Key[+T]
 4
 5 class Component(entries:Map[Key[Any],()=>Any]) {
 6
 7     def define[T](key:Key[T], instance: => T) =
 8         new Component(entries + (key -> (()=>instance)))
 9
10     def apply[T](key:Key[T]):T =
11         instance(entries(key)).asInstanceOf[T]
12
13     private def instance[T](factory:()=>T):T =  factory()
14 }
15
16 object Component {
17     def define[T](key:Key[T], instance: => T) =
18         new Component(Map.empty + (key -> (()=>instance)))
19 }


やってみたら、なんとたった19行で出来ちゃいました。
まぁ、エラー処理とかちゃんとやるともうちょい長くなりますけどね。。
あと、11行目とか、なぜこれでコンパイルエラーにならないのか自分でもわかってないです。
キャストしようとしても T がなにかわからないはずなのに。
まだまだ勉強がたりないですね。

使い方はこんなです↓


 1 //コンポーネント
 2 class A(inject:Component) {
 3     val b = inject(B)
 4
 5     def sayHello {
 6         b.sayHello
 7     }
 8 }
 9
10 object A extends Key[A]
11
12 class B(inject:Component) {
13     val c = inject(C)
14
15     def sayHello {
16       c.sayHello
17     }
18
19 }
20
21 object B extends Key[B]
22
23 class C {
24     def sayHello {
25       println("Hello, I am C")
26     }
27 }
28
29 object C extends Key[C]
30
31 //コンポーネントをくっつける定義
32 val component:Component  =
33     Component.define(A, new A(component))
34              .define(B, new B(component))
35              .define(C, new C)
36
37
38 //使ってみよう
39 val a = component(A)
40
41 a.sayHello
42


39行目で クラスA のインスタンスが生成されます。

A の sayHello が Bの sayHello を呼び出し、B のsayHelloが cのsayHelloを呼び出すので、
このコードを実行すると「Hello, I am C」と出力されます。

ポイントは、コンパニオンオブジェクトがコンポーネントのキーになってるとこですかね。

Componentクラスは、コンパニオンオブジェクトをキー、対応するクラスのインスタンスを生成する関数を値とするMap で、
他のコンポーネントに依存するコンポーネントは、このComponentクラスのインスタンスを保持して
自分を初期化するときに、Component クラスから必要なコンポーネントのインスタンスを取得しています。

Scala では、なるべくコンパイラに仕事をさせて、ランタイムエラーが発生しないようなコードを書くのが良いと
思っているのですが、この例では必要なコンポーネントが見つからなかったときにランタイムエラーが発生してしまいます。
というデメリットはあるのですが、これだけ単純な仕掛けでコードがぐっと簡潔になるので、
それを補うだけのメリットもあるのではないかと思います。


時代の流れに負けた

2009-07-27 23:50:13 | その他
完全に乗り遅れた感があって、半ば意地になって拒否していたのですが、
ついに世の中の流れに逆らえなくなって、Twitterを始めました。

@nosen

です。

これは誰かをフォローしないとつまんないものなんですね。。

ScalaからGAE/Jのデータストアを使う

2009-06-02 07:33:09 | その他
一旦途切れるとひどく長いこと中断してしまうのは、自分の悪い癖です。

別に忙しくもなかったし、書きたいこともあったのですが。。

というわけで、最近は Scala! Scala! と連呼している訳です。

一応 GAE/Jにも興味があるので、ScalaをGAE/J上で動かしてみているのですが、
ScalaのクラスをJDO経由で永続化できるようにするには、若干コツがいったので、それをメモします。

データクラスの定義は以下のようになりました。
package diet.entity
import javax.jdo.annotations.IdentityType
import javax.jdo.annotations.IdGeneratorStrategy
import javax.jdo.annotations.PersistenceCapable
import javax.jdo.annotations.PrimaryKey
import javax.jdo.annotations.Persistent

@PersistenceCapable {val identityType = IdentityType.APPLICATION}
class User(
  @Persistent
  var name: String
) {
  @PrimaryKey
  @Persistent {val valueStrategy = IdGeneratorStrategy.IDENTITY}
  var id: java.lang.Long = null
}



"id"をコンストラクタの引数にしていない理由は、プライマリキーの列に明示的に値を
指定したらAppEngineに怒られたからです。

でもこれだけだと、データクラスをEnhanceするときに、謎のjava.lang.VerifyErrorが発生します。

しらべたら、以下のようなバグがある模様。

http://www.datanucleus.org/servlet/jira/browse/NUCENHANCER-34

なので、GAE/J のSDKに同梱されているdatanuculeus のjarを最新版にいれかえたら、
何となく動いたっぽい。

ここまでやったところで、実はLow-Level APIをラップするクラスを書いた方がScalaっぽいのではないかと
思い始めた今日この頃です。

Scalaによるダイエット

2008-12-02 23:01:35 | その他
またしてもBlogを書くのをさぼってしまいました。
別に仕事が忙しかった訳ではないし、書くネタもそれなりにあったのですが。。

それはそうと、最近ものすごく太ってしまったので、またダイエットにチャレンジすることにしました。
以前RubyでBlogから体重を拾ってきてグラフにするスクリプトを書いたのですが、
それのScala版を書いています。

だいぶScalaにも慣れてきました。やっぱりScalaはものすごく面白いですね。
いちいちニヤリとしてしまう文法が満載です。
ただ、Javaをちゃんとわかった上でScala的な部分を理解しないといけないので、学習コストは高いのかもしれません。
自分も、先にHaskellを勉強してなかったら理解が遅くなっていた部分がかなりあるような気がします。

今とりあえず、グラフを画像で出すところまでは出来ていて、あとFTPでサーバにあげるところを
書けば完成です。

難しかったのはScalaというよりもむしろJFreeChart。初めて触ったけど、APIがややこしいです。

あと、うちのMacOS Xでは、ImageIOでgifもjpegもうまく出力できなくて、pngを試してやっとうまく行きました。
Windows上では普通にgifを出力できるのに。

ImageIO#getWriterFormatNamesというメソッドで、ImageIOがサポートする画像のフォーマットの一覧が
取れるのですが、gifはそこに入っていなかったので、まぁそれは私が悪いということにしましょう。

しかし、ImageIO#writeメソッドで画像をファイルに書き出そうとしたときに、
何の例外も投げずに、こっそりfalseとかを返して静かに失敗するのはいかがなものか。

あと、jpgについてはサポートされているはずなのに、なんでか真っ黒な画像が出力されてしまいました。
pngを試したらうまく行ったので、細かい原因は調べておりません。

とはいえ、やはり既存のJavaのライブラリが使えるというのは、大きなメリットだと感じました。
ランタイムから完全に新しい言語だと、JFreeChartのようなややこしいことをするライブラリは
なかなか出てこないでしょうから。。

最長ミーティング記録

2008-10-28 07:49:08 | その他
昨日、社会人になって以来最長のミーティング時間を記録しました。

8時間。

途中何回か休憩はあったものの。最後の方は意識が朦朧としていました。

自分の場合、普通頑張って2時間ですねー。

Perfumeとcapsule

2008-10-16 23:16:55 | その他
PerfumeのライブのDVDを買ってしまいました。
自分がアイドルのライブDVDをよろこんで買うような人間になるとは想像していなかったし、望んでいたことでもありません。いったいどこで道を踏み外したんでしょうかね?

プロデューサーである中田ヤスタカ氏の七光りみたいに言われることが多いPerfumeですが、中田氏自身のユニットであるcapsuleよりむしろ売れてるところからみると、あながちそうとも言えないのじゃないかと思います。

個人的な好みをいうと音的なかっこ良さはcapsuleの方が上だと思うんですが、Perfumeの方はむしろcapsuleより良く売れてますね。

中田氏の発言によると、capsuleっていうのは、自分のやりたいことをとにかく全部やるとこで、人のプロデュースをするときは、その人のイメージに合わせたものを作るというスタンスなんだそうです。
自分の趣味趣向を追求することと、人に合わせてよい成果を出すことを両立するのは困難なことだと思うので、両方を実現している中田氏はやっぱりすごい人なんだと思います。

自分の仕事にとっても参考になると思うのは、「自分のやりたいこととを追求する場」を確保して、それを他の仕事から切り離しているところです。自分のやりたいことを追求することで得られる成果と、人とのコラボレーションのなかで生まれる成果はきっと性質が違うもんです。

capsuleの音はただひたすらに「おされでかっこいい」ですが、Perfumeの音はどこかノスタルジックだっりリリカルだったり、そういうcapsuleでは隠れている部分が前に出ています。多分、Perfumeでcapsule的なおされかっこよさを目指したとしても、多分今より面白いものにはならない気がします。

自分にとっては会社の仕事はPerfumeで、プライベートで興味のあることを調べてブログに書いたりするのはcapsuleということになるのでしょうか?
会社の仕事っていうのは、お客さんがいて、プロジェクトのメンバがいてっていうコラボレーションの中で成果を出していくのに対し、ブログに書いてることっていうのは純粋に興味本位のことばかりです。平たくいうと、いくら自分が興味があるからっていきなり実プロジェクトに Scala つっこんでもいい成果はでないでしょってことです。
その2つの立場をわきまえて、それぞれに違う楽しみを見いだして生きてゆけたらと思います。実際、会社の仕事には自分が興味をもって調べているテクノロジーの5%くらいを落としていけたらそれで十分です。

そしていつか、Perfumeとcapsuleがお互いの知名度のシナジー効果で両方バカ売れ、みたいなところを目指していきたいとおもっております。

なんだか感覚的な話で、自分でも何を言っているのかわからなくなってきたところで、今日はおやすみなさい。


初音ミク習作: コンピューターおばあちゃん

2008-10-05 03:04:36 | その他
初音ミクの練習としてコピーした「コンピューターおばあちゃん」をこちらに公開します。mikuフォルダの中に入ってます。

よろしければコメント等くださいませ。

「コンピューターおばあちゃん」は昔NHKの「みんなのうた」で放送されていた曲で、実は坂本龍一がアレンジをしていることで有名です。子供のころすごいすきだった曲です。

今回は、耳コピをしようとして、途中で面倒くさくなって、結局記憶コピーになっています。
ところどころ手抜きの箇所がありますが、ミクさんの実力を知るには十分だと思います。