--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Type-classes are perfect for this use-case. Examples from Scala’s standard library are Ordering[T], Numeric[T] and Integral[T].
This is why for example you can do List(7,3,1,2).sum or List(7,3,1,2).sorted, even though in the definition of List[T] there is no restriction on what T is. SortedSet is another example that uses the Ordering[T] type-class. In order to instantiate a SortedSet with your own types in it, you have to define an implicit Ordering for them - which is much like implementing an interface, without the restriction that your type must inherit from it directly. Numeric[T] also has a zero property that implementations must define, which is clearly not a property of individual instances of Long or whatever numeric type you want, but a property of the whole class of numbers you’re working with. This is why in Scala you can define a generic sum function for sequences of any kind, which is not possible in Java btw, because in Java “+” on primitives doesn’t implement any generic interface that you could use:
def sum[T : Numeric](seq: Seq[T]): T = {
val num = implicitly[Numeric[T]]
seq.foldLeft (num.zero) { case (acc, elem) => num.plus(acc, elem) }
}
Type-classes in Scala are used by means of implicit parameters. When you’re using “context bounds” such as:
def leaf[T: Reader]: Parser[Node[T]]
That’s actually syntactic sugar for something like:
def leaf[T](implicit ev: Reader[T]): Parser[Node[T]]
Calling that function for a given T means that there has to be an implicit Reader[T] defined in scope, as Roman explained. This means that you either have to define or import the definition of this implicit in the scope you want (e.g. where you’re calling the leaf function), or you can define it in the companion object of either Reader[T] or that of the T you want to work with, to be globally accessible without explicitly importing it in scope.
Speaking of Numeric[T] in the above example, Numeric[T] is actually too specific and the above could be useful for String concatenation or Set unions as well and it would have been great for Scala’s library to have a Monoid type-class defined, but thankfully we can easily implement our own:
trait Monoid[T] {
def zero: T
def append(x: T, y: T): T
}
object Monoid {
// all Numeric[T] are monoids
implicit def NumericMonoid[T : Numeric] = new Monoid[T] {
val ev = implicitly[Numeric[T]]
def zero = ev.zero
def append(x: T, y: T) = ev.plus(x,y)
}
// Strings are monoids too
implicit object StringMonoid extends Monoid[String] {
def zero = ""
def append(x: String, y: String) = x + y
}
}
def sum[T : Monoid](sequence: T*): T = {
val num = implicitly[Monoid[T]]
sequence.foldLeft (num.zero) { case (acc, elem) => num. append(acc, elem) }
}
scala> sum(1,2,3,4)
res1: Int = 10
scala> sum("foo", "bar")
res2: String = foobar
Ain’t that nice? :)