Matt Fenwick Matt Fenwick - 17 days ago 5
Scala Question

Why is no implicit view found, when eta conversion and specifying type parameters does allow an implicit view to be found?

The code:

object Test {
import scala.language.implicitConversions

case class C1() {}
case class C2() {}

implicit def c1ToC2(in: C1): C2 = C2()

def from[A, B](in: A)(implicit f: A => B): B = f(in)

def fails(): Future[C2] = {
val future: Future[C1] = Future.successful(C1())
future.map(from) // this line fails to compile!
}

def compiles1(): Future[C2] = {
val future: Future[C1] = Future.successful(C1())
future.map(x => from(x))
}

def compiles2(): Future[C2] = {
val future: Future[C1] = Future.successful(C1())
future.map(from[C1, C2])
}
}


In this example, only the
fails
method fails to compile. The error message is:


Error:(23, 16) No implicit view available from A => B.
future.map(from)


I'm confused about why no implicit view is found. Based on the
compiles1
and
compiles2
methods, which both successfully compile, it seems there is an implicit view available from A => B.

What is going on here, and why do the two
compilesN
methods work, but
fails
does not?

My background: I'm still learning Scala, so it could easily be the case that I'm missing something pretty obvious. :)

I'm on Scala 2.11.8.

Answer

The compiler attempts to resolve implicits before eta-expansion of from into a function, so the type parameters of from are not yet inferred when you call it like this:

future.map(from)

compiles2 obviously works because you supply the type parameters on your own. When you call future.map(from[C1, C2]), the compiler knows it will need an implicit C1 => C2, because that's what you've told it.

With compiles1, the difference is a little more subtle, but it stems from the fact that future.map(from) and future.map(x => from(x)) are actually very different things. The former uses eta-expansion, which fails for the aforementioned reasons. With future.map(x => from(x)), there is no eta-expansion happening. Instead, you have an anonymous function that simply calls from instead of eta-expanding it. Therefore, the compiler can infer the type of x, which tells us that x is a C1 (in this case), and it can find the implicit conversion c1ToC2 that satisfies the type parameters of from while resolving the implicit and the final return type of the method, Future[C2].

Comments