Question

I am attempting to use implicits to try and override a serialize method that I have inside case classes for an ADT, however I am unable to get my head around how it should be done

// START OF API//

/**
 * Serialize methods defined here
 *
 */

object Serializer {
  def string(block: (String => String)) = block
  def int(block:(Int => String)) = block
  def double(block:(Double => String)) = block
}

/**
 * These are the DEFAULT serializers
 */

object Implicits {
  implicit val string = Serializer.string (s => s)
  implicit val int = Serializer.int (i => i.toString)
  implicit val double = Serializer.double (d => String.valueOf(d))
}

/**
 *
 * Our simple ADT defined here
 */

import Implicits._

abstract class Vehicle {
  def serialize:String
}

case class Car(s:String) extends Vehicle {
  def serialize: String = string(s)
}

case class Truck(i:Int) extends Vehicle {
  def serialize: String = int(i)
}

case class RocketShip(d:Double) extends Vehicle {
  def serialize: String = double(d)
}

// END OF API

// START OF USER CODE

object UserImplicit {
  implicit val string = Serializer.string(s => s.capitalize)
}

object Main extends App{
  val v = Car("some car")

  println(v.serialize)
  import test.UserImplicit._
  // This SHOULD print a capatilized version i.e. SOME CAR
  println(v.serialize)
}

// END OF USER CODE

Basically I want to monkey patch the default serializer methods (the ones contained inside object Implicits) so that users of the API can implement their own serializers

I have tried many combinations (such as having the implicits inside a trait instead of a object) however I havn't actually managed to get it working

Was it helpful?

Solution 2

I actually managed to figure it out using the pimp my library pattern, here is an example of the working code

object Implicits {
  def defaults(v:Vehicle):String = {
    v match {
      case Car(c) => "This is car " + c
      case Truck(t) => "This is a truct " + t
      case RocketShip(r) => "This is a rocketship " + r
    }
  }

  class VehicleSerializer(v:Vehicle) {
    def serialize:String = defaults(v)
  }

  implicit def vSerialize(v:Vehicle) = new VehicleSerializer(v)
}

/**
 * Our simple ADT defined here
 */
abstract class Vehicle {}
case class Car(s: String) extends Vehicle {}
case class Truck(i: Int) extends Vehicle {}
case class RocketShip(d: Double) extends Vehicle {}

// END OF API

// START OF USER CODE

class UserImplicit(v:Vehicle) {
  def serialize:String = {
    v match {
      case Car(c) => "This is a MASSIVE car " + c
      case _v => Implicits.defaults(_v)
    }

  }
}

object Test extends App {
  val c = Car("rawr")

  // This is the default serializer
  {
    import Implicits._
    println(c.serialize)
  }

  // This is our overwritten serializer
  {
    implicit def vSerialize(v:Vehicle) = new UserImplicit(v)
    println(c.serialize)
  }
}

// END OF USER CODE

which prints

This is car rawr
This is a MASSIVE car rawr

As intended

OTHER TIPS

I think you really want to use type-classes for this. Handily, in Scala you can supply your own (or 'override') as you see fit.

trait Serializer[A] extends (A => String)

object Serializer {
  def Serializer[A: Serializer] = implicitly[Serializer[A]]
}

trait SerializerInstances {
  implicit val StringSerializer = new Serializer[String] {
    def apply(s: String) = s
  }
  implicit val IntSerializer = new Serializer[Int] {
    def apply(i: Int) = i.toString
  }
  implicit val DoubleSerializer = new Serializer[Double] {
    def apply(i: Int) = i.toString
  }
  implicit def TupleSerializer[A: Serializer, B: Serializer] = new Serializer[(A, B)] {
    def apply(t: (A, B)) = s"(${Serializer[A](a)}, ${Serializer[B](b)})"
  }
}

Now, to use them, you simply declare that your code needs to take a thing that has a Serializer context bound:

def somethingThatDoesSerialization[A: Serializer](a: A) = {
  val serializedForm: String = Serializer[A](a)
  …
}

You can now import/manage whatever set of serializers are appropriate for your needs.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top