Scala 型クラスへのリファクタリング
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 に組み合わせた.