Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save privateblue/6245339 to your computer and use it in GitHub Desktop.
Save privateblue/6245339 to your computer and use it in GitHub Desktop.
three solutions for the same problem: traits, pattern matching, type class
// our model
object model {
abstract class X
case class A extends X
case class B extends X
}
// some extra behaviour we want to add to our model objects, without polluting them
object extra {
import model._
trait ExtraBehaviour[T <: X] {
def doSomething: String
}
}
// downside of this is that we cannot use the companion object's apply method
// to instantiate a model object, as we need to mix in the extra behaviour. and
// also the extra behaviour needs to be mixed in for every instance individually.
object traits {
import model._
import extra._
trait ExtraBehaviourA extends ExtraBehaviour[A] {
this: A =>
def doSomething = "foo"
}
trait ExtraBehaviourB extends ExtraBehaviour[B] {
this: B =>
def doSomething = "bar"
}
// usage in client code:
(new A with ExtraBehaviourA).doSomething
(new B with ExtraBehaviourB).doSomething
}
// downside of this is that it is less extendible - adding a new implementation is
// more complicated.
object patternmatching {
import model._
import extra._
implicit class Wrapper[T <: X](x: T) extends ExtraBehaviour[T]{
def doSomething =
x match {
case A() => "foo"
case B() => "bar"
}
}
// usage in client code:
A().doSomething
B().doSomething
}
object typeclass {
import model._
import extra._
implicit class Wrapper[T <: X : ExtraBehaviour](x: T) {
def doSomething = implicitly[ExtraBehaviour[T]].doSomething
}
implicit object ExtraBehaviourA extends ExtraBehaviour[A] {
def doSomething = "foo"
}
implicit object ExtraBehaviourB extends ExtraBehaviour[B] {
def doSomething = "bar"
}
// usage in client code:
A().doSomething
B().doSomething
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment