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