Question

Say we have the following two traits:

trait Foo[A] { def howMany(xs: List[A]) = xs.size }
trait Bar

And an implicit conversion from the second to the first:

implicit def bar2foo[A](bar: Bar) = new Foo[A] {}

We create a Bar and a list of integers:

val bar = new Bar {}
val stuff = List(1, 2, 3)

Now I'd expect the following to work:

bar howMany stuff

But it doesn't:

scala> bar howMany stuff
<console>:13: error: type mismatch;
 found   : List[Int]
 required: List[A]
              bar howMany stuff
                          ^

So we go to the spec, which has this to say (emphasis in bold is mine):

Views are applied in three situations.

  1. [Isn't relevant here.]

  2. In a selection e.m with e of type T, if the selector m does not denote a member of T. In this case, a view v is searched which is applicable to e and whose result contains a member named m. The search proceeds as in the case of implicit parameters, where the implicit scope is the one of T. If such a view is found, the selection e.m is converted to v(e).m.

  3. In a selection e.m(args) with e of type T, if the selector m denotes some member(s) of T, but none of these members is applicable to the arguments args. In this case a view v is searched which is applicable to e and whose result contains a method m which is applicable to args. The search proceeds as in the case of implicit parameters, where the implicit scope is the one of T. If such a view is found, the selection e.m is converted to v(e).m(args).

So we try the following, thinking it must be too absurd to work:

trait Foo[A] { def howMany(xs: List[A]) = xs.size }
trait Bar { def howMany = throw new Exception("I don't do anything!") }

implicit def bar2foo[A](bar: Bar) = new Foo[A] {}

val bar = new Bar {}
val stuff = List(1, 2, 3)

But it does (on both 2.9.2 and 2.10.0-RC2, at least):

scala> bar howMany stuff
res0: Int = 3

This leads to some really strange behavior, as for example in this workaround for this problem.

I have three (closely related) questions:

  1. Is there a straightforward way (i.e., one that doesn't involve adding fake methods with the appropriate name) to have the view applied correctly in the original case above?
  2. Can someone provide a reading of the spec that accounts for this behavior?
  3. Assuming this is the intended behavior, does it make any sense at all?

I'd also appreciate any links to previous discussions of this issue—I haven't been having much luck with Google.

Was it helpful?

Solution

For everyone's reference, this could only be a bug. The way you know that is the error message:

<console>:13: error: type mismatch;
 found   : List[Int]
 required: List[A]

List[A] is not a real type - it is List applied to its own type parameter. That is not a type which can be required since it is not a type which can be expressed.

[Edit - it's too early, who knows what I'm talking about. Ignore the above, but you can still follow the link.]

The relevant ticket for this is https://issues.scala-lang.org/browse/SI-6472 .

OTHER TIPS

This seems like a bug so my answers are:

  1. search for a simliar bug reported against the Scala compiler and if not found, report a new bug https://issues.scala-lang.org/
  2. that part of the spec doesn't seem to matter in this case as it doesn't talk about type inference
  3. doesn't make any sense to me

PS. In 2.8.1 your workaround of adding the dummy method to Bar doesn't make it compile.

Replacing your Foo with this:

trait Foo[_] { def howMany(xs: List[_]) = xs.size }

It works, which also makes quite a lot more sense to me because your absolutly not interested in A.

Your implicit conversion seems to be doing exactly what you told it to do.

implicit def bar2foo[A](bar: Bar) = new Foo[A] {}

Converts a Bar to a new Foo[A] object. So in turn

scala> bar howMany stuff
<console>:13: error: type mismatch;
 found   : List[Int]
 required: List[A]
              bar howMany stuff

It looks for an 'A' type.

In order to make this work the way you want it to you (I think), instead of defining the view on the trait you can do it on the function.

trait Foo { def howMany[A](xs: List[A]) = xs.size }
trait Bar
implicit def bar2foo[A](bar: Bar) = new Foo{}
val bar = new Bar {}
val stuff = List(1, 2, 3)

then it should give you the result you desire.

scala> bar howMany stuff
res0: Int = 3

or you can define the view on the implicit function

trait Foo[A] { def howMany(xs: List[A]) = xs.size }
trait Bar

implicit def bar2foo[A](bar: Bar) = new Foo[Int] {}

val bar = new Bar {}
val stuff = List(1, 2, 3)

Personally I think defining it on the function is cleaner.

This, while ugly, appears to work:

(bar: Foo[Int]) howMany stuff
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top