Implementing Typeclasses

Note The syntax described in this section is currently under revision. Here is the new version which will be implemented in Dotty 0.19.

Given instances, extension methods and context bounds allow a concise and natural expression of typeclasses. Typeclasses are just traits with canonical implementations defined by given instances. Here are some examples of standard typeclasses:

Semigroups and monoids:

trait SemiGroup[T] {
  def (x: T) combine (y: T): T
}
trait Monoid[T] extends SemiGroup[T] {
  def unit: T
}
object Monoid {
  def apply[T] given Monoid[T] = the[Monoid[T]]
}

given as Monoid[String] {
  def (x: String) combine (y: String): String = x.concat(y)
  def unit: String = ""
}

given as Monoid[Int] {
  def (x: Int) combine (y: Int): Int = x + y
  def unit: Int = 0
}

def sum[T: Monoid](xs: List[T]): T =
    xs.foldLeft(Monoid[T].unit)(_.combine(_))

Functors and monads:

trait Functor[F[_]] {
  def (x: F[A]) map [A, B] (f: A => B): F[B]
}

trait Monad[F[_]] extends Functor[F] {
  def (x: F[A]) flatMap [A, B] (f: A => F[B]): F[B]
  def (x: F[A]) map [A, B] (f: A => B) = x.flatMap(f `andThen` pure)

  def pure[A](x: A): F[A]
}

given ListMonad as Monad[List] {
  def (xs: List[A]) flatMap [A, B] (f: A => List[B]): List[B] =
    xs.flatMap(f)
  def pure[A](x: A): List[A] =
    List(x)
}

given ReaderMonad[Ctx] as Monad[[X] =>> Ctx => X] {
  def (r: Ctx => A) flatMap [A, B] (f: A => Ctx => B): Ctx => B =
    ctx => f(r(ctx))(ctx)
  def pure[A](x: A): Ctx => A =
    ctx => x
}