Question

There are some frameworks that fully embraces the typeclass pattern. scalaz and shapeless would be good examples. So there are certainly some cases where typeclasses are preferable over normal java classes and polymorphism.

I awe implicit evidence expression power and I'm curious why this method suffer a shortage of practical applications. What reasons compel scala programmers to use basic classes. The typeclasses obviously cost in verbosity and run-time, but is there any other reason?

I came to scala without prior java experience and wonder if I've missed some essential benefits that classic scala-java classes may give.

I'm searching for some spectacular use cases showing areas where typeclasses are insufficient or ineffective.

Was it helpful?

Solution

Typeclasses and inheritance enable reuse in different ways. Inheritance excels at providing correct functionality for changed internals.

class Foo { def foo: String = "foo" }
def fooUser(foo: Foo) { println(foo.foo) }

class Bar extends Foo {
  private var annotation = List.empty[String]
  def annotate(s: String) { annotation = s :: annotation }
  override def foo = ("bar" :: annotation.map("@" + _)).mkString(" ")
}

Now, everyone who uses Foo will be able to get the correct value if you give them a Bar, even if they only know that the type is a Foo. You don't have to have anticipated that you might want pluggable functionality (except by not labeling foo final). You don't need to keep track of the type or keep passing a witness instance forwards; you just use Bar wherever you want in place of Foo and it does the right thing. This is a big deal. If you want a fixed interface with easily-modifiable functionality under the hood, inheritance is your thing.

In contrast, inheritance is not so great when you have a fixed set of data types with easily-modifiable interface. Sorting is a great example. Suppose you want to sort Foo. If you try

class Foo extends Sortable[Foo] {
  def lt(you: Foo) = foo < you.foo
  def foo = "foo"
}

you could pass this to anything that could sort a Sortable. But what if you want to sort by length of name not with the standard sort? Well,

class Foo extends LexicallySortable[Foo] with LengthSortable[Foo] {
  def lexicalLt(you: Foo) = foo < you.foo
  def lengthLt(you: Foo) = foo.length < you.foo.length
  def foo = "foo"
}

This rapidly becomes hopeless, especially since you have to hunt down all subclasses of Foo and make sure they are updated properly. You are much better off deferring the less-than computation to a typeclass which you can swap out as needed. (Or to a regular class, which you must always reference explicitly.) This kind of automatically-selected functionality is also a big deal.

You can't really replace one with the other. When you need to easily incorporate new kinds of data to a fixed interface, use inheritance. When you need a few kinds of underlying data but need to easily supply new functionality, use type classes. When you need both, you will have a lot of work to do whichever way you go about it, so use to taste.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top