Type Classes
Type classes are a generic traits, hat allow attaching interfaces to types. Allows for
- ad hoc polymorphism
- type enrichment
Type Classes with Type Parameters
Generic Trait
trait ReversibleWithTypeParam[A] {
def reverse(x: A): A
}
Type Specific Implementations
implicit object IntReversible1 extends ReversibleWithTypeParam[Int] {
override def reverse(x: Int): Int = x.toString.reverse.toInt
}
implicit object StringReversible1 extends ReversibleWithTypeParam[String] {
override def reverse(x: String): String = x.reverse
}
Usage
Concrete implementation can be used via implicit parameters:
object Reverser1A {
def reverse[T](x: T)(implicit reversible: ReversibleWithTypeParam[T]): T =
reversible.reverse(x)
}
...
println(Reverser1A.reverse(42))
println(Reverser1A.revers("reverse me!"))
Or via Context Bounds:
object Reverser1B {
def reverse[T: ReversibleWithTypeParam](x: T) = implicitly[ReversibleWithTypeParam[T]].reverse(x)
}
...
println(Reverser1B.reverse(42))
println(Reverser1B.revers("reverse me!"))
Context Bounds
- Context Bounds Notation:
[T: ReversibleWithTypeParam]
- Meaning:
- there exists an implicit value
- of type
ReversibleWithTypeParam
- with a type parameter
T
implicitly[...]
gives the concrete implicit istance
Type Classes with Type Members
Type members are an alternative way for parametrizing abstract classes with types.
Generic Trait
trait ReversibleWithTypeMember {
type A
def reverse(x: A): A
}
Type Specific Implementations
implicit object IntReversible2 extends ReversibleWithTypeMember {
type A = Int
override def reverse(x: Int): Int = x.toString.reverse.toInt
}
implicit object StringReversible2 extends ReversibleWithTypeMember {
type A = String
override def reverse(x: String): String = x.reverse
}
Usage
object Reverser2A {
def reverse[T](x: T)(implicit reversible2: Reversible2 {type A = T}): T =
reversible2.reverse(x)
}
...
println(Reverser2A.reverse(42))
println(Reverser2A.revers("reverse me!"))
NB! Context Bounds cannot be used with type members
Type Enrichment via Implicit Conversion
Instead of invoking Reverser
explicitly, the parameter can be implicitly converted to a type that has a reverse
method.
implicit class FlipOps[T](val x: T) {
def flip(implicit reversible1: Reversible1[T]): T = reversible1.reverse(x)
}
...
println("foo".flip)
println(123.flip)