2010年2月22日月曜日

Scalaでappengineのapiproxy hookを作る

Hackathonから帰ったあと、@marblejenkaさんがappengineのlower levelなネタを
作ってるのを聞いてたので、appengineのライブラリ以外をscalaで作ったらどうなるんだろ、
と思ってApiProxyのHookを書いてみた。

簡単だろ、と思ってたら意外なところでハマってしまった。
最初こんな感じで書いた。
01class HookDelegate(val delegate:Delegate[_ <: Environemnt])
02  extends Delegate[Environment] {
03 
04 def makeSyncCall(e:Environment,
05  s:String,m:String,r:Array[Byte]):Array[Byte] = {
06  delegate.makeSyncCall(e,s,m,r)
07 }
08 
09 def makeAsyncCall(e:Environment,
10   s:String,m:String,r:Array[Byte],
11     c:ApiConfig):Future[Array[Byte]] = {
12  delegate.makeAsyncCall(e,s,m,r,c)
13 }
14 
15 def log(e:Environment,r:LogRecord) = {
16  delegate.log(e,r)
17 }
18 
19}

アウト。
コンパイル時に型が違いますエラーをdelegate.makeXXX,logの呼び出しで喰らう。
Delegateが共変ではないってことになるのかな。

渡すdelegateはApiProxy.getDelegateで取得することを想定しているのだけど、
これ仮型引数が分からない。scalaのエラーによると、<? extends Environment>
ってのは分かってるんだけど、型として<? extends Environment>ってのは用意できない。
まあJavaで同じことしてもエラーになるんだけど、ApiProxy.getDelegateはraw type
、イレイジャ通って戻ってくるので、キャストして呼べるんだよね。

1日悩んで、どうしたらいいんだろと考えてひらめいた。

01class HookDelegate[E <: Environment](val delegate:Delegate[E])
02  extends Delegate[E] {
03 
04 def makeSyncCall(e:E,
05   s:String,m:String,r:Array[Byte]):Array[Byte] = {
06  delegate.makeSyncCall(e,s,m,r)
07 }
08 
09 def makeAsyncCall(e:E,s:String,m:String,r:Array[Byte],
10   c:ApiConfig):Future[Array[Byte]] = {
11  delegate.makeAsyncCall(e,s,m,r,c)
12 }
13 
14 def log(e:E,r:LogRecord) = {
15  delegate.log(e,r)
16 }
17 
18}

これならOK。
コンストラクタの引数を元に仮型引数を決定できるので渡されたdelegateを
元に推論すればいいじゃん!とひらめいたのでした。
・直接このHookのmakeSyncCallは呼び出せない
・Hook内でEnvironmentをいじれない
けれども。
多分Javaだとタイプセーフにはできないような気がする。

0 件のコメント:

コメントを投稿