Serialization of scala functions

2020-02-09 04:33发布

问题:

I know that there is Jerkson and scalaxb and even Java's XStream that successfully able to serialize scala's data. I know that they handle well Strings, Integers and even case classes pretty well. But if I have function as one of the fields?

e.g. if I have something like:

case class Foo(bar: (Int) => Boolean, baz: Int) 

Can these fields been somehow serialized to JSON or XML (actually, I don't care to which one, they should be human-readable, thats why I don't want to use SBinary)?

EDIT

Why would I want to do that? Right now I'm writing implementation of decision tree. I don't want to reconstruct that trees every time from training data hence I need to serialize them and that part could be done with SBinary. But additionally, it would be nice if as a humans we could look at serialized tree and analyze it.

It is not so wide task as I wrote in title, yes. What I thinking now, is to write a custom serializer (e.g. for Jerkson) with my own format, or write to string field and then parse this back.

But I though that there could be some insanely better way to perform that.

回答1:

No, a function is not necessary serializable... and serializable functions are unlikely to provide a human readable serialization.

If you want to be able to provide any kind of function, Im' afraid there won't be any solution. A possible workaround, if it can be used in your scenario, is to build a case class that implements Function[Int, Boolean] and thus to go back to a case class scenario.

For example, suppose that you have isolated that all your functions check whether a integer is exactly divisible by a given integer:

case class Mod(mod: Int) extends Function[Int, Boolean] { //the extends part is not even require
  def apply(x: Int) = x % mod == 0
}

case class Foo(bar: Mod, baz: Int)

It's clearly ultra-restrictive. But I'm afraid it's the best you can achieve.

According to your edit. A solution might be to have a kind of factory class:

case class IntTest(humanReadableDescription: String) {
  def apply(x: Int) = IntTest.fromString(humanReadableDescription)(x)
}

object IntTest {
  private val fromString = Map[String, Function[Int, Boolean]] (
     "mod2" -> {x:Int => x % 2 == 0},
     "is42" -> {x:Int => x == 42}
  )
}

case class Foo(bar: IntTest, baz: Int)

But here, you will lose type safety.



回答2:

Yes. It is possible and has just blown my mind:

import java.io._

def write[A](obj: A): Array[Byte] = {
  val bo = new ByteArrayOutputStream()
  new ObjectOutputStream(bo).writeObject(obj)
  bo.toByteArray
}

def read(bytes:Array[Byte]): Any = {
  new ObjectInputStream(new ByteArrayInputStream(bytes)).readObject()
}


read(write( {a:Int => a+1} )).asInstanceOf[ Function[Int,Int] ](5) // == 6

// And case class serialization
case class XF(f:(Int => Int)) extends Serializable

read(write(XF( {a:Int => a+1} ))).asInstanceOf[XF].f(5) // == 6 

NB. Doesn't work for classes defined within outer classes or function scopes. (which makes sense since you probably didn't mean to serialize the reference to the outer class).

Very cool, but note that the JVM requires the loaded class references to be available in the classpath when the object is loaded. (This is not a general-purpose serialization path as the reader also needs to have access to the compile class files being deserialized)



回答3:

Semantically speaking, how would you serialize a function? The only conceivable way would be to encode the actual implementation (i.e. bytecode), which is possible in both formats but is a world of pain in itself (classloading, versioning, performance...)

In short, without additional context your question makes very little sense.