Loic
Loic

Reputation: 3370

Make a class extend a generic trait in Scala

I thought this should work to make a light alternative to type classes but it does not :

trait Summable[T] {
  def sumWith(other: T): T
}

implicit class StringSummable(s: String) extends Summable[String]{
  def sumWith(other: String): String = s + other
}

def sumAll[T <: Summable[T]](list: List[T]): T = {
  list.reduceLeft((sum, element) => sum.sumWith(element))
}

sumAll(List("1","2","3"))

I have this error : inferred type arguments [lang.this.String] do not conform to method sumAll's type parameter bounds [T <: Summable[T]]

Is there a trick to make this work?

Thanks

Upvotes: 1

Views: 1282

Answers (1)

Andrey Tyukin
Andrey Tyukin

Reputation: 44957

Yes, there is a "trick", it's literally a single-character change:

':' -> '%'

from subtyping to "implicit convertibility" bounds:

trait Summable[T] {
  def sumWith(other: T): T
}

implicit class StringSummable(s: String) extends Summable[String]{
  def sumWith(other: String): String = s + other
}

def sumAll[T <% Summable[T]](list: List[T]): T = {
  list.reduceLeft((sum, element) => sum.sumWith(element))
}

sumAll(List("1","2","3"))

Note that this tends to fall apart rather quickly. For example, as soon as you need a zero-ary function that returns an empty String, you are essentially out of luck with this approach, whereas it is trivial with a typeclass (as Monoid[T] shows). Without it, you can't even define sumAll properly, because it doesn't work on empty lists.

Note that the historical development so far has been away from implicit conversions, and towards the typeclasses, not the other way round.

Upvotes: 3

Related Questions