Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the motivation for type-classes in Scala?

I am having some trouble motivating the use of type classes in Scala when comparing to upper bounds on types.

Consider the following code:

  case class NumList[T <: Complex](xs: Complex*) {
    def sum = (xs fold new Complex(0, 0))(_ + _)
    def map[U <: Complex](f: Complex => U): NumList[U] = NumList(xs.map(f): _*)
    override def toString = "[" + xs.mkString(", ") + "]"
  }

  case class GenList[T](xs: T*) {
    def sum(implicit num: Numeric[T]) = xs.sum
    def map[U](f: T => U) = GenList(xs.map(f): _*)
    override def toString = "[" + xs.mkString(", ") + "]"
  }

  val r = new Real(2)
  val n = new Natural(10)
  val comps = NumList(r, n, r, n)

  println(comps)
  println("sum: " + comps.sum)
  println("sum * 2: " + comps.map(x => x + x).sum)

  val comps2 = GenList(4, 3.0, 10l, 3d)
  println(comps2)
  println("sum: " + comps2.sum)
  println("sum * 2: " + comps2.map(_ * 2).sum)

While these two lists solve similar problems, one uses the numeric type-class and the other uses an upper bound on the type parameter. I understand the technical differences quite well, but I'm having a hard time getting to the core motivation of type-classes. The best motivation I found so far is the following:

While subclassing or implementing interfaces allows you to do mostly the same designs, type-classes allow you to specify features of a type on a per-method basis, whereas a generic class with type T and upper bound U constrains T everywhere where it is used. With this in mind, type-classes provide more fine-grained control over features of T in generic classes.

Are there any very clear examples motivating the pattern?

like image 522
Felix Avatar asked Apr 10 '13 13:04

Felix


1 Answers

Trying to simplify one major aspect, typeclasses try to collect the behavior independently of your class hierarchy.

Suppose you need to define a new numeric type MetaNum (with standard numeric operations) but you can't or won't make it a subclass of your Complex type, for whatever reason.

With Numeric typeclass, you just need to provide an appropriate instance for your MetaNum, providing the needed operations.

Then you can create a GenList[MetaNum] and sum over it.

You can't do this with the NumList, because MetaNum is not a Complex. The implementation choice you made when defining NumList will stab back at you when you try to generalize your operation/data-structure in a second moment.

Conclusion
Typeclasses gives you more freedom to extend your behavior independently from hierarchical considerations, at the cost of some additional complexity and boilerplate.

I can't tell if you meant the same in your question.

like image 164
pagoda_5b Avatar answered Sep 21 '22 09:09

pagoda_5b