5

Let's consider this code:

class A
object A{
  implicit def A2Int(implicit a:A)=1
  implicit def A2String(a:A)="Hello"
}

object Run extends App{
  implicit val a: A =new A

  import A.A2Int
  // without this import this code does not compile, why ?
  // why is no import needed for A2String then ?

  def iWantInt(implicit i:Int)=println(i)
  def iWantString(implicit s:String)=println(s)

  iWantInt
  iWantString(a)
}

It runs and prints:

1
Hello

Now, if we comment out the line

import A.A2Int

then we get a compilation error:

enter image description here

With the line commented out, why Scala cannot find A.A2String if it can find A.A2Int ?

How can this problem be fixed ?

Thanks for reading.

jhegedus
  • 20,244
  • 16
  • 99
  • 167

1 Answers1

3

The difference is that when you do iWantString(a), the compiler gets some starting point to work from: you are explicitly passing a, which the compiler knows is of type A. Given that iWantString takes a String and not an A, the compiler will search for an implicit conversion from A to String in order to insert it and make the call succeed. Implicit lookup rules state that the compiler must look (among other places) in the companion object of class A because type A is the source type of the conversion. This is where it finds the implicit conversion A2String. What you must get from this is that it is only because you passed an instance of A that the compiler knew to look for implicit conversions into the companion object of A.

When you just do iWantInt, the compiler has no reason to look into A, so it won't find your method A2Int (and as no other method/value in scope provides an implicit value of type Int, compilation then fails).

For more information about implicit lookup rules, see the See the scala specification at http://www.scala-lang.org/docu/files/ScalaReference.pdf (chapter 7.2). Here's the most relevant excerpt:

The implicit scope of a type T consists of all companion modules (§5.4) of classes that are associated with the implicit parameter’s type.

Régis Jean-Gilles
  • 32,541
  • 5
  • 83
  • 97
  • 2
    In addition, the compiler won't perform more than one implicit *lookup* in order to match types, whereas you have to implicit lookups for `iWantInt`: one implicit lookup to transform `A` to `Int` and one implicit lookup to find the param of `iWantInt`. In the case of `iWantString` there's only one implicit lookup, the one to convert `A` to `String`. – Ionuț G. Stan Mar 31 '14 at 14:55
  • 1
    This is incorrect. Have you noticed that the code DOES compile with just additional import statement? The compiler won't perform more than one implicit lookup **on its own**, that is true. But here the first conversion asks for an implicit value of type `A`, whih kicks the second implicit lookup. – Régis Jean-Gilles Mar 31 '14 at 15:08
  • You're right about that, what I meant is that in order to find an implicit `Int` param (without the import), the compiler would have to first find an implicit conversion from `A` to `Int`. These's a dependency between these two implicit lookups, in that you can't resolve the first without resolving the second. The two implicit lookups you're describing are not dependent one of another. It first resolves the `Int` implicit param (because `A.A2Int` has been imported), and then resolves the second implicit lookup, i.e., the implicit param of `A.A2Int`, which resolves because `val a` is implicit. – Ionuț G. Stan Mar 31 '14 at 15:39
  • The Scala compiler won't try to perform extra lookups in order to resolve another lookup. – Ionuț G. Stan Mar 31 '14 at 15:43
  • Thank you for the answer Regis. Now I have one remaining question: is there some fundamental reason why Scala does not look for implicit conversion functions in the companion objects of all implicitly available values ? If not, then perhaps that might be a possible improvement for Scala, what do you think? I have been thinking about this and did not see why looking for implicit conversion functions in the companion objects of implicitly available values would pose any real problems (aside from occosional ambiguity which can be resolved by explicit parameter passing like in iWantString(a)). – jhegedus Mar 31 '14 at 19:49
  • 1
    I would like to suggest one small change - `A2Int` is not in fact an implicit conversion at all, only an implicit value. I have an explanation of this in [an answer](http://stackoverflow.com/questions/22772575/why-implicit-parameters-cannot-be-passed-explicitly-when-they-undergo-an-implici) to another one of jhegedus's questions. – wingedsubmariner Mar 31 '14 at 23:35
  • You are totally right, this was misleading. I have updated this part. – Régis Jean-Gilles Apr 01 '14 at 08:37