Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serializable and AnyVal

Tags:

scala

I am search for the right signature of a method that takes a function func and an argument arg, copies them over the network to a remote computer and returns the result. Currently the signature looks like this:

def invokeRemote[A,B](address: String, func: A => B, arg: A): B

The problem with this is that the method throws a NotSerializable Exception if the arguments are not Serializable or one of Java's primitive types.

I came up with the following solution to catch this error at compile time ...

type Func = (A => B) with Serializable

def invokeRemote[A <: Serializable, B <: Serializable](address: String, func: Func, arg: A): B

... but now it is not possible anymore to pass arguments of type AnyVal like Int, Float or Double which do not explicitly implement Serializable.

How should the method signature look like such that it accepts only Serializable objects or objects of type AnyVal as argument?

like image 852
gruenewa Avatar asked May 14 '11 15:05

gruenewa


2 Answers

You can use a context bound implicit with a custom trait and provide implicit conversions for AnyVal and Serializable to that trait.

trait Ser[M]

implicit def toSer1[T <: AnyVal]: Ser[T] = new Ser[T] {}
implicit def toSer2[T <: java.io.Serializable]: Ser[T] = new Ser[T] {}

def f[T: Ser](a:T): T = a
f(1)
// res2: Int = 1
f("123")
// res3: java.lang.String = 123
f(new Object)
// could not find implicit value for evidence parameter of type Ser[java.lang.Object]

The compiler will look for an implicit parameter based on the type and because some are provided for T <: AnyVal and T <: java.io.Serializable it will compile in that case.

You can stick the implicit definitions in the companion object for Ser so that they are available where needed.

Then, your signature becomes:

def invokeRemote[A:Ser, B:Ser](address: String, func: A => B, arg: A): B
like image 121
huynhjl Avatar answered Oct 20 '22 00:10

huynhjl


Using Serializable as argument type does not work in practice, because you can have Serializable instances that are not in fact serializable:

class NotSerializable(val s: String)

// ArrayList inherits Serializable
val list = new java.util.ArrayList[NotSerializable]()

list.add(new NotSerializable("test"))

// will fail at run time
serialize(list)

Moreover, Scala collection traits do not inherit Serializable (the implementations do).

def f(s: Serializable) { println(s) }

// will fail to compile, because interface List does not inherit Serializable
f(List("a", "b"))

// will print true because list implementation is Serializable
println(List("a", "b").isInstanceOf[Serializable])

Being Serializable is a runtime property and cannot be enforced by types alone. Using Serializable as argument type will not save you from runtime Serialization errors. All you will accomplish is make your functions harder to call, as you are already experiencing with AnyVal (and that is just the tip of the iceberg).

like image 1
Lex Avatar answered Oct 19 '22 23:10

Lex