Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala - Typeclass example. How to explain the benefits?

Tags:

So I was showing a coworker/friend an example of the typeclass pattern in Scala. It looks like this:

case class Song(name: String, artist: String)
case class Address(street: String, number: Int)

trait LabelMaker[T] {
  def output(t: T): String
}

object LabelMaker {
  implicit object addressLabelMaker extends LabelMaker[Address] {
    def output(address: Address) = {
      address.number + " " + address.street + " street"
    }
  }
  implicit object songLabelMaker extends LabelMaker[Song] {
    def output(song: Song) = {
      song.artist + " - " + song.name
    }
  }
  def label[T : LabelMaker](t: T) = implicitly[LabelMaker[T]].output(t)
}

Which can be used like this:

import LabelMaker._
println(label(new Song("Hey Ya", "Outkast"))) // Outkast - Hey Ya
println(label(new Address("Smithsonian", 273))) // 273 Smithsonian street

It's not the best example, and in retrospect I wish I'd come up with a better one. Upon showing him, he responded with a counter example, and asked what benefits the typeclass pattern actually brings to the table:

case class Song(name: String, artist: String)
case class Address(street: String, number: Int)

object LabelMaker {

    def label(address: Address) = {
        address.number + " " + address.street + " street"
    }

    def label(song: Song) = {
        song.artist + " - " + song.name
    }
}

import LabelMaker._
println(label(new Song("Hey Ya", "Outkast"))) // Outkast - Hey Ya
println(label(new Address("Smithsonian", 273))) // 273 Smithsonian street

I struggled to answer that properly, and it made me realise I don't quite understand the gains made 100%. I understand their implementation and very localized benefits when someone else uses them, but to actually succinctly explain them is quite difficult. Can anyone help me? And perhaps extend on my example to really show the benefits.

like image 404
Dominic Bou-Samra Avatar asked Dec 19 '12 23:12

Dominic Bou-Samra


1 Answers

Typeclasses capture the notion of retroactive extensibility. With static method overloads, you have to define them all at once in one place, but with typeclasses you can define new instances anytime you want for any new types in any modules. For e.g.

object LabelMaker {
  // ... your original cases here ...
  def label[T : LabelMaker](t: T) = implicitly[LabelMaker[T]].output(t)
}

// somewhere in future
object SomeModule {
  import LabelMaker._

  case class Car(title: String)

  implicit object carLabelMaker extends LabelMaker[Car] {
    def output(car: Car) = car.title
  }  
}

object Main extends App {
  import LabelMaker._
  import SomeModule._

  println(label(Car("Mustang")))
}
like image 200
aemxdp Avatar answered Sep 17 '22 15:09

aemxdp