Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this explicit call of a Scala method allow it to be implicitly resolved?

Why does this code fail to compile, but compiles successfully when I uncomment the indicated line? (I'm using Scala 2.8 nightly). It seems that explicitly calling string2Wrapper allows it to be used implicitly from that point on.

class A {
  import Implicits.string2Wrapper
  def foo() {
     //string2Wrapper("A") ==> "B" // <-- uncomment
  } 
  def bar() {
    "A" ==> "B"
    "B" ==> "C"
    "C" ==> "D"
  }
  object Implicits {
    implicit def string2Wrapper(s: String) = new Wrapper(s)
    class Wrapper(s: String) {
      def ==>(s2: String) {}
    }
  }
}

Edit: thanks for the answers so far, which include a pointer to Martin Odersky's comment,

"An implicit conversion without explicit result type is visible only in the text following its own definition. That way, we avoid the cyclic reference errors."

I'd still be interested in finding out 1) what is the danger of "cyclic reference errors"?, and 2) Why does an explicit call make any difference?

like image 858
Matt R Avatar asked Apr 28 '10 16:04

Matt R


2 Answers

Explicitly ascribing the return type of string2Wrapper fixes the problem.

class A {
  import Implicits._

  def bar() {    
    "A" ==> "B"
    "B" ==> "C"
    "C" ==> "D"
  }
  object Implicits {
    implicit def string2Wrapper(s: String): Wrapper = new Wrapper(s)
    class Wrapper(s: String) {
      def ==>(s2: String) {}
    }
  }
}

Defining Implicits before bar also works:

class A {
  object Implicits {
    implicit def string2Wrapper(s: String) = new Wrapper(s)
    class Wrapper(s: String) {
      def ==>(s2: String) {}
    }
  }

  import Implicits._

  def bar() {    
    "A" ==> "B"
    "B" ==> "C"
    "C" ==> "D"
  } 
}

If you need to rely on a implicit conversion defined below within the current scope, make sure you annotate its return type. Pretty sure this has come up on the mailing lists before, and may be expected behaviour rather than a bug. But I can't locate it at the moment. I guess the explicit call in foo triggers the type inference of the return type of bar, which is then valid when typing the contents of bar.

UPDATE

What is the danger of the cyclic reference error?

The body of the implicit method may call the method that requires the implicit conversion. If both of these have an inferred return type, you're at an impasse. This doesn't apply in your example, but the compiler doesn't attempt to detect this.

Why does an explicit call make a difference?

The explicit call earlier triggers type inference of the return type of the implicit method. Here's the logic in Implicits.isValid

sym.isInitialized ||
      sym.sourceFile == null ||
      (sym.sourceFile ne context.unit.source.file) || 
      hasExplicitResultType(sym) ||
      comesBefore(sym, context.owner)

UPDATE 2

This recent bug looks relevant: https://lampsvn.epfl.ch/trac/scala/ticket/3373

like image 143
retronym Avatar answered Oct 10 '22 06:10

retronym


If you were just ooooone nightly later you would instead have seen the error message I added yesterday.

<console>:11: error: value ==> is not a member of java.lang.String
 Note: implicit method string2Wrapper is not applicable here because it comes after the application point and it lacks an explicit result type
           "A" ==> "B"
           ^
<console>:12: error: value ==> is not a member of java.lang.String
 Note: implicit method string2Wrapper is not applicable here because it comes after the application point and it lacks an explicit result type
           "B" ==> "C"
           ^
<console>:13: error: value ==> is not a member of java.lang.String
 Note: implicit method string2Wrapper is not applicable here because it comes after the application point and it lacks an explicit result type
           "C" ==> "D"
           ^
like image 33
psp Avatar answered Oct 10 '22 05:10

psp