Thanks to Jorg for his answer, which, in conjunction with this blog post helped me figure out what is going on here. Hopefully my additional answer will help others who have been confused by this.
My mental picture of type classes is that they are a means by which an author of a library would imbue his/her
library with the desireable trait of retroactive extensibility.
On the other hand, there is yet another technique for ad hoc polymorphism: implicits with wrapper classes.
You would use this latter technique when you are the consumer of a library which has some type
which is missing methods that you find useful.
I am going to try to extend Phillipe's example a bit to illustrate my understanding of how
type classes could be used by a library designer. I am not that experienced with Scala... so if
anyone notices something that is not correct in my understanding please do correct me ! ;^)
// 'Default' defines a trait that represents the ability to manufacture
// defaults for a particular type 'T'.
//
trait Default[T] { def value : T }
// Define a default for int.. this is an object default
//
implicit object DefaultInt extends Default[Int] {
def value = 42
}
// Define a default for Lists that contain instances of a 'T'.
// Interesting that this is method, not an object implicit definition. I
// guess that is to enable the 'on the fly' creation of default values for
// lists of any old type.... So I guess that's why we have
// a 'method implicit' rather than an 'object implicit' definition.
implicit def listsHaveDefault[T : Default] = new Default[List[T]] {
def value = implicitly[Default[T]].value :: Nil
}
// Here is the meat of the library... a method to make a message based of
// some arbitrary seed String, where that method is parameterized by 'T',
// a type chosen by the library user. That 'T' can be
// types for which implicits are already defined by the base library
// (T = Int, and List[T]), or an implicit defined by the library user.
//
// So the library is open for extension.
//
def makeMsg[T](msg: String)(implicit something: Default[T]) : String = {
msg + something.value
}
Given the above code, if I can create a message for the type List[List[Int]], or for Int's
makeMsg[List[List[Int]]]("moocow-")
makeMsg[Int]("dogbox")
And I get this result:
res0: String = moocow-List(List(42))
res1: String = dogbox42
If I want to override the default implicit value for a given type, I can do so like this:
makeMsg[Int]("moocow-")(something=new Object with Default[Int] { def value = 33344 } )
And I get this result:
res3: String = moocow-33344
object
forDefaulInt
, you could also have a value and an anonymous class, e.g.implicit val defInt = new Default[Int] { ... }
. What matters is that there's something of typeDefault[Int]
in the environment. 2) the signature ofmakeMsg
could bemakeMsg[T : Default]
, which is syntactic sugar for theimplicit
arg. The only difference is that now, since you don't have a name for the arg, you need to useimplicitly[Default[T]]
to retrieve it. – Prelate