Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion: Codec for unions to help Akka serialization #161

Open
mushtaq opened this issue Jan 27, 2020 · 6 comments
Open

Suggestion: Codec for unions to help Akka serialization #161

mushtaq opened this issue Jan 27, 2020 · 6 comments

Comments

@mushtaq
Copy link
Contributor

mushtaq commented Jan 27, 2020

Akka has a pattern for specifying serializers: first specify the serializer in akka.actor.serializers setting and then bind it with the types in akka.actor.serialization-bindings.

To avoid doing this for each top-level ADT, we mark every top-level ADT with a marker trait. This allows us to write just one serializer per application.

I am wondering if borer can help simplify writing this serializer by providing a utility to write codec for this Marker trait (which can not be a sealed). One way will be by providing forUnion helpers like below.

//Marker.scala
trait Marker
object Marker {
 implicit val encoder: Encoder[Marker] = Encoder.forUnion2[Marker, Adt1, Adt2]
 implicit val encoder: Decoder[Marker] = Decoder.forUnion2[Marker, Adt1, Adt2]
}

//Adt1.scala
sealed trait Adt1 extends Marker
object Adt1 {
  implicit val codec: Codec[Adt1] = ???
}

//Adt1.scala
sealed trait Adt1 extends Marker
object Adt2 {
  implicit val codec: Codec[Adt2] = ???
}

// my naive impl, will be good to have them for all arities. 
// Ideally, if one could just do `Codec.forUnion2[Marker, Adt1, Adt2]` to get both, that will be great.

def Encoder.forUnion2[B, T1 <: B: Encoder: ClassTag, T2 <: B: Encoder: ClassTag]: Encoder[B] = Encoder { (w, value) =>
  value match {
    case x: T1 => w.write(Map(nameOf[T1] -> x))
    case x: T2 => w.write(Map(nameOf[T2] -> x))
  }
}

def Decoder.forUnion2[B, T1 <: B: Decoder: ClassTag, T2 <: B: Decoder: ClassTag]: Decoder[B] = Decoder { r =>
  val name = r.readString()
  if (name == nameOf[T1]) r.read[T1]() else r.read[T2]()
}

def nameOf[T: ClassTag]: String = scala.reflect.classTag[T].runtimeClass.getSimpleName
@sirthias
Copy link
Owner

I see what you mean.
Since the logic of the code generation for these forUnionX calls appears to be not very complex: Couldn't you simply write a small macro that generates the encoders, decoders and codecs?

Also, note that you can replace

w.write(Map(nameOf[T1] -> x))

with

w.writeMapOpen(1).writeString(nameOf[T1]).write(x).writeMapClose()

and avoid the intermediate Map and Iterator creation.

@mushtaq
Copy link
Contributor Author

mushtaq commented Feb 3, 2020

Given that macros will need rework after Scala-3, our team has decided to not implement any in our projects. (But library macros are kosher :-)

I thought this will be more of sbt-boilerplate kind work than a macro, no?

For now, we have take a simpler approach. Sharing an example of that pattern for those who may want to use borer with Akka:

If we see perf issues, we may use reflection to replace List to a Map as done on a branch version of CborAkkaSerializer

So, you can close the issue if adding a sbt-boilerplate based impls for all arities does not feel appropriate to be part of the borer.

@sirthias
Copy link
Owner

sirthias commented Feb 3, 2020

Very interesting! Thank you, @mushtaq, for the pointers to your solution!
Maybe your setup even warrants a blog post of some kind, to show, how the akka serializer infrastructure can be integrated with other libraries like borer.
I'm sure that your approach is interesting to other people as well!

Since the problem appears to be a more general one that isn't quite specific to borer, I'm indeed inclined to not generate more boilerplate on borer's side at this point.

However, I'm gonna keep this issue open for some time to maybe attract more feedback/comments from other users.

@mushtaq
Copy link
Contributor Author

mushtaq commented Feb 3, 2020

You are right about the blogpost, thanks. Our team will soon write it up.

@skvithalani
Copy link

skvithalani commented Feb 25, 2020

@sirthias

Based on the Akka Serializer pattern described in this issue, we have written a blog post - How to write your own Borer based Akka Serializer !!!. It covers our journey of evolving the Akka serializer pattern using Borer.

@sirthias
Copy link
Owner

Thank you for the pointer, @skvithalani!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants