Based on and extending Generic
and LabelledGeneric
, Lars Hupel (@larsr_h) has contributed the TypeClass
family of type classes, which provide automatic type class derivation facilities roughly equivalent to those available
with GHC as described in "A Generic Deriving Mechanism for Haskell". There is a description of an
earlier iteration of the Scala mechanism here, and examples of its use deriving Show
and Monoid
instances here
and here for labelled coproducts and unlabelled products respectively.
For example, in the Monoid
case, once the general deriving infrastructure for monoids is in place, instances are
automatically available for arbitrary case classes without any additional boilerplate
trait Monoid[T] {
def zero: T
def append(a: T, b: T): T
}
object Monoid extends ProductTypeClassCompanion[Monoid] {
def mzero[T](implicit mt: Monoid[T]) = mt.zero
implicit def booleanMonoid: Monoid[Boolean] = new Monoid[Boolean] {
def zero = false
def append(a: Boolean, b: Boolean) = a || b
}
implicit def intMonoid: Monoid[Int] = new Monoid[Int] {
def zero = 0
def append(a: Int, b: Int) = a + b
}
implicit def doubleMonoid: Monoid[Double] = new Monoid[Double] {
def zero = 0.0
def append(a: Double, b: Double) = a + b
}
implicit def stringMonoid: Monoid[String] = new Monoid[String] {
def zero = ""
def append(a: String, b: String) = a + b
}
object typeClass extends ProductTypeClass[Monoid] {
def emptyProduct = new Monoid[HNil] {
def zero = HNil
def append(a: HNil, b: HNil) = HNil
}
def product[F, T <: HList](mh: Monoid[F], mt: Monoid[T]) = new Monoid[F :: T] {
def zero = mh.zero :: mt.zero
def append(a: F :: T, b: F :: T) = mh.append(a.head, b.head) :: mt.append(a.tail, b.tail)
}
def project[F, G](instance: => Monoid[G], to: F => G, from: G => F) = new Monoid[F] {
def zero = from(instance.zero)
def append(a: F, b: F) = from(instance.append(to(a), to(b)))
}
}
}
trait MonoidSyntax[T] {
def |+|(b: T): T
}
object MonoidSyntax {
implicit def monoidSyntax[T](a: T)(implicit mt: Monoid[T]): MonoidSyntax[T] = new MonoidSyntax[T] {
def |+|(b: T) = mt.append(a, b)
}
}
The shapeless-contrib project also contains automatically derived type class instances for Scalaz, Spire and Scalacheck.
// A pair of arbitrary case classes
case class Foo(i: Int, s: String)
case class Bar(b: Boolean, s: String, d: Double)
import MonoidSyntax._
val fooCombined = Foo(13, "foo") |+| Foo(23, "bar")
fooCombined should be(Foo(res0, res1))
val barCombined = Bar(true, "foo", 1.0) |+| Bar(false, "bar", 3.0)
barCombined should be(Bar(res2, res3, res4))