Injection: Creating Custom Encoders

Injection lets us define encoders for types that do not have one by injecting A into an encodable type B. This is the definition of the injection typeclass:

trait Injection[A, B] extends Serializable {
  def apply(a: A): B
  def invert(b: B): A
}

Example

Let's define a simple case class:

case class Person(age: Int, birthday: java.util.Date)
// defined class Person

val people = Seq(Person(42, new java.util.Date))
// people: Seq[Person] = List(Person(42,Tue May 23 10:57:10 CEST 2017))

And an instance of a TypedDataset:

val personDS = TypedDataset.create(people)
// <console>:24: error: could not find implicit value for parameter encoder: frameless.TypedEncoder[Person]
//        val personDS = TypedDataset.create(people)
//                                          ^

Looks like we can't, a TypedEncoder instance of Person is not available, or more precisely for java.util.Date. But we can define a injection from java.util.Date to an encodable type, like Long:

import frameless._
// import frameless._

implicit val dateToLongInjection = new Injection[java.util.Date, Long] {
  def apply(d: java.util.Date): Long = d.getTime()
  def invert(l: Long): java.util.Date = new java.util.Date(l)
}
// dateToLongInjection: frameless.Injection[java.util.Date,Long] = $anon$1@6d6cc4bc

We can be less verbose using the Injection.apply function:

import frameless._
// import frameless._

implicit val dateToLongInjection = Injection((_: java.util.Date).getTime(), new java.util.Date((_: Long)))
// dateToLongInjection: frameless.Injection[java.util.Date,Long] = frameless.Injection$$anon$1@79ea3da5

Now we can create our TypedDataset:

val personDS = TypedDataset.create(people)
// personDS: frameless.TypedDataset[Person] = [age: int, birthday: bigint]

Another example

Let's define a sealed family:

sealed trait Gender
// defined trait Gender

case object Male extends Gender
// defined object Male

case object Female extends Gender
// defined object Female

case object Other extends Gender
// defined object Other

And a simple case class:

case class Person(age: Int, gender: Gender)
// defined class Person

val people = Seq(Person(42, Male))
// people: Seq[Person] = List(Person(42,Male))

Again if we try to create a TypedDataset, we get a compilation error.

val personDS = TypedDataset.create(people)
// <console>:32: error: could not find implicit value for parameter encoder: frameless.TypedEncoder[Person]
//        val personDS = TypedDataset.create(people)
//                                          ^

Let's define an injection instance for Gender:

implicit val genderToInt: Injection[Gender, Int] = Injection(
  {
    case Male   => 1
    case Female => 2
    case Other  => 3
  },
  {
    case 1 => Male
    case 2 => Female
    case 3 => Other
  })
// genderToInt: frameless.Injection[Gender,Int] = frameless.Injection$$anon$1@70fdc07e

And now we can create our TypedDataset:

val personDS = TypedDataset.create(people)
// personDS: frameless.TypedDataset[Person] = [age: int, gender: int]

results matching ""

    No results matching ""