0
votes

I have posted this question in scala-user forum,

https://groups.google.com/forum/#!topic/scala-user/xlr7KmlWdWI

and I received an answer which I am happy with. However, at the same time I want to make sure if that is the only conclusion. Thanks

My question is, say, I have,

trait Combinable[A] {
  def join[B](l: List[A]): B
}

When I implement this trait with A as String and B as Int, for example,

class CombineString extends Combinable[String] {
  override def join[Int](strings: List[String]) = string.size
}

Obviously, Int, next to join method, is not the Scala integer class and compiling this code will fail. Alternatively, I could rewrite my trait as

trait Combinable[A, B] {
  def join(l: List[A]): B
}

or

trait Combinable[A] {
  def join[B](l: List[A])(f: List[A] => B): B
}

My question is, how can I implement the trait as defined in the first example as it is? If the first example has no practical use because of the way it is defined, why does the compiler not complaints? Thanks again.

2
Compiler won't complain since it does not know what are being passed for A and B. It's upto developer to maintain discipline.Ravindra babu
I understand that but from a human point of view (me and the other person who answered) there is only one outcome to this, throwing an exception, it is good that the compiler throws a warning. Unless there are other outcomes that I am not aware ofthlim
There is no such term\entity as "practical use" in today compilersOdomontois

2 Answers

2
votes

You can not expect for compiler to understand what type combinations make sense for you, but you can specify this sense as relation between types in term of multiparameter implicits.

Result is basically combination of two of your rejected approaches, but with desired syntax.

First of two rejected forms became implicit type:

trait Combine[A, B] {
  def apply(l: List[A]): B
}

Next you can define appropriate type combinations and their meaning

implicit object CombineStrings extends Combine[String, String] {
  def apply(l: List[String]) = l.mkString
}

implicit object CombineInts extends Combine[Int, Int] {
  def apply(l: List[Int]) = l.sum
}

implicit object CombinableIntAsString extends Combine[Int, String] {
  def apply(l: List[Int]) = l.mkString(",")
}

Finally we modify second rejected form that hides f argument for implicit resulution:

trait Combinable[A] {
  def join[B](l: List[A])(implicit combine: Combine[A, B]): B = combine(l)
}

Now you can define

val a = new Combinable[String] {}
val b = new Combinable[Int] {}

And check that

a.join[String](List("a", "b", "c"))
b.join[Int](List(1, 2, 3))
b.join[String](List(1, 2, 3))

runs nice while

a.join[Int](List("a", "b", "c"))

makes compiler cry until you could provide evidence of practical use of relation between String and Int in form of implicit value

0
votes

My question is, how can I implement the trait as defined in the first example as it is?

class CombineString extends Combinable[String] {
  override def join[B](strings: List[String]) = null.asInstanceOf[B]
  // or, for that matter, anything and then .asInstanceOf[B]
}

How could the compiler know that's not what you want?