Эффективная сериализация классов - PullRequest
8 голосов
/ 13 октября 2011

Для библиотеки, над которой я работаю, мне нужен эффективный, удобный и типобезопасный метод сериализации классов scala. Идеальным было бы, если пользователь может создать класс case, и, пока все члены сериализуемы, это, похоже, тоже должно быть. Я точно знаю тип как на этапе сериализации, так и на стадии десериализации, поэтому нет необходимости (и я не могу себе этого позволить) иметь какую-либо информацию «схемы» как часть формата сериализации (например, сериализации Java-объекта).

Я играл с несколькими идеями, и эта, кажется, подошла довольно близко. Главная проблема, которую я вижу здесь, заключается в том, как пользователь должен указать функции класса «применять» и «не применять». Так как это действительно статические функции, мне интересно, можно ли заставить компилятор найти его.

Вот отдельный пример:

trait InOut[T] {
  // just keeping things simple, for illustration purposes
  def toWire(x: T): Array[Byte]
  def fromWire(v: Array[Byte] ): T
}

object InOutConversions {
  // Pretend these are implemented properly

    implicit def Int = new InOut[Int] {
      def toWire(x: Int): Array[Byte] = Array[Byte]()
      def fromWire(v: Array[Byte] ): Int = 44
    }

    implicit def String = new InOut[String] {
      def toWire(x: String): Array[Byte] = Array[Byte]()
      def fromWire(v: Array[Byte] ): String = "blah"
    }

    // etc... for all the basic types
}

И тогда мне нужна такая функция:

def serialize2[T, A1 : InOut, A2 : InOut](unapply : T => Option[Product2[A1, A2]])(obj : T) : Array[Byte] = {
  val product : Product2[A1, A2] = unapply(obj).get 
   implicitly[InOut[A1]].toWire(product._1) ++ implicitly[InOut[A2]].toWire(product._2)
}

Что позволит пользователю использовать его довольно легко. например,

case class Jesus(a: Int, b: String)
val j = Jesus(4, "Testing")
serialize2 (Jesus.unapply(_)) (j)

Но, как вы можете видеть, эта последняя строка была действительно противной. Конечно, должно быть возможно улучшить это? (Учитывая Иисуса, я определенно могу найти «неприменимый» статический метод)

1 Ответ

3 голосов
/ 13 октября 2011

Поскольку у вас нет общего способа получения объекта-компаньона для данного класса дел, вы добавите для выполнения дополнительной работы. Я вижу три варианта:

  1. Вы можете использовать структурную типизацию, но вы потеряете производительность
  2. Вы можете использовать класс малого типа для неявного разрешения правильного unapply метода.
  3. Вы можете добавить toTuple метод к экземплярам класса дел.

Например:

trait ToTuple2[A1,A2] {
  def toTuple: (A1,A2)
}

case class Jesus(a: Int, b: String) extends ToTuple2[Int,String] {
  val toTuple = (a,b)
}

def serialize2[T <: ToTuple2[A1,A2], A1 : InOut, A2 : InOut](obj : T): Array[Byte] = {
  val product : Product2[A1, A2] = obj.toTuple
  implicitly[InOut[A1]].toWire(product._1) ++ implicitly[InOut[A2]].toWire(product._2)
}

Пример кода для варианта 2:

case class Jesus(a: Int, b: String)

trait Unapply2[T,A1,A2] {
  def asTuple( t: T ): (A1,A2)
}

implicit val UnapJesus = new Unapply2[Jesus,Int,String] {
  def asTuple( j: Jesus ) = Jesus.unapply(j).get
}

def serialize2[T, A1, A2](obj : T)
(implicit unap: Unapply2[T,A1,A2], inout1: InOut[A1], inout2: InOut[A2])  : Array[Byte] = {
  val product : Product2[A1, A2] = unap.asTuple(obj)
  inout1.toWire(product._1) ++ inout2.toWire(product._2)
}

Вы должны взглянуть на SBinary , оно выглядит аналогично.

...