2013年9月30日月曜日

akkaのActorを抽象化する方法を試行錯誤する

akkaを使ってActorをゴリゴリ作ってると、当然Actorを抽象化したいという要求が出てくるよね。

例えばこんな方針で抽象化を試みる。

  • Actorを継承したtraitを作って、共通処理を抽象化する。
  • 作ったtraitは型パラメータを持たせて、具象クラス化するときに例えば受け取るメッセージの型を指定する。

import akka.actor._
trait AbstractActor[MesType] extends Actor {
override def receive = ...
}
case class MyMessage(mes: String)
class MyActor extends AbstractActor[MyMessage]
view raw 01.scala hosted with ❤ by GitHub


なんでわざわざ型パラメータなんて持たせてるかというと、これをやりたい。

import akka.actor._
import scalaz._, Scalaz._
trait AbstractActor[MesType] extends Actor {
implicit val MesShow: Show[MesType]
override def receive = {
case m: MesType => m.println // コンパイルエラーだけどこういうことがやりたい
}
}
case class MyMessage(mes: String)
class MyActor extends AbstractActor[MyMessage] {
override val MesShow = Show.showA[MyMessage]
}
view raw 02.scala hosted with ❤ by GitHub


要するに、送受信するメッセージは処理を内包しないただの型(case class MyMessage)にしといて、その型の挙動は型クラスのインスタンス(MesShow)で制御したいというわけ。

ところが、この実装には問題がある(実際に上記コードはコンパイルが通らない)。

  • akkaのActorにおいてメッセージはAny型で渡されるのでRuntimeにしか型判定できない
  • traitの型パラメータはコンパイル時に型消去されるのでRuntimeでの判定には使えない

=> つまり、上記コードの「case m: MesType」という判定はできない、となる。

これを解決するためには、

  • Anyで渡ってくるメッセージはobj.getClassを経由してRuntimeにクラス情報を取得する
  • 型パラメータの方は、TypeTagを渡すことでRuntimeまでクラス情報を引き継ぐ

とやるしかない、と思う。たぶん。

で、書いてみたのが以下のコード。

import akka.actor._
import scalaz._, Scalaz._
import scala.reflect.runtime.universe._
trait AbstractActor[MesType] extends Actor {
implicit val TTag: TypeTag[MesType]
implicit val MesShow: Show[MesType]
override def receive = {
case m if getType(m) <:< typeOf[MesType] => {
val mes = m.asInstanceOf[MesType]
mes.println
}
}
def getType(obj: Any): Type = {
val clazz = obj.getClass
val mirror = runtimeMirror(clazz.getClassLoader)
mirror.classSymbol(clazz).toType
}
}
case class MyMessage(mes: String)
class MyActor extends AbstractActor[MyMessage] {
override val TTag = typeTag[MyMessage]
override val MesShow = Show.showA[MyMessage]
}
view raw 03.scala hosted with ❤ by GitHub


TypeTagをわざわざ指定してやらないといけないところとか、asInstanceOfでダウンキャストしてるところとか、カッコ悪い。

というわけで、「こうやるともっと綺麗に書けるよ」とか、「そもそも方針としてこうやるべき」とかあったら教えてください!

0 件のコメント:

コメントを投稿