Skip to content

An implicit val should not be available for defining itself? #6114

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
liufengyun opened this issue Mar 19, 2019 · 10 comments
Closed

An implicit val should not be available for defining itself? #6114

liufengyun opened this issue Mar 19, 2019 · 10 comments
Labels

Comments

@liufengyun
Copy link
Contributor

liufengyun commented Mar 19, 2019

The following code compiles in Scalac 2.12. However, if we give explicit type to x, it compiles neither in Scalac nor Dotty.

class Test {
  implicit val m: Int = 10
  def get(implicit x: Int): Int = ???
  def bar: Unit = {
    implicit val x = get
    // implicit val x: Int = get
  }
}

Error message:

5 |    implicit val x: Int = get
  |                             ^
  |               x is a forward reference extending over the definition of x

Such code exists in ScalaTest:

implicit val strEq = StringNormalizations.lowerCased.toEquality
es.contains("one") shouldBe true;
es.contains("ONE") shouldBe false

trait NormalizingEquality[A] extends Equality[A]

trait Uniformity[A] extends Normalization[A] {
    final def toEquality(implicit equality: Equality[A]): NormalizingEquality[A] = ...
}

@liufengyun liufengyun mentioned this issue Mar 19, 2019
66 tasks
@smarter
Copy link
Member

smarter commented Mar 19, 2019

Paging @milessabin since this reminds me of cachedImplicit in shapeless whose semantics I'm sure he can explain :).

@allanrenucci
Copy link
Contributor

Forward definitions, also are available to the implicit scope:

scala> def test: Unit = { implicitly[Int]; implicit val foo: Int = 1 }
<console>:11: error: forward reference extends over definition of value foo
       def test: Unit = { implicitly[Int]; implicit val foo: Int = 1 }

Some discussions about what should be and not be in the implicit scope happened here.

If I remember correctly, at the time @odersky said it was tricky to exclude some vals from the implicit scope and better to have the forward reference checker reports invalid implicitly inferred references.

@milessabin
Copy link
Contributor

You're seeing the forward reference error in this case because x is a local definition. If you move it to the top level it'll compile with the explicit type annotation.

Self references of this sort are unproblematic and should be accepted if the implicit argument they correspond to is by-name, ie. def get(implicit x: => Int): Int = ???.

@liufengyun
Copy link
Contributor Author

Self references of this sort are unproblematic and should be accepted if the implicit argument they correspond to is by-name, ie. def get(implicit x: => Int): Int = ???.

It has to do with initialization, if x is used inside get eagerly, then x will be an uninitialized default value 0, which is unsafe.

@milessabin
Copy link
Contributor

if x is used inside get eagerly, then x will be an uninitialized default value 0, which is unsafe.

Yes, but that's orthogonal to the implicitness or otherwise of the definitions and arguments here.

@liufengyun
Copy link
Contributor Author

@odersky suggested that a migration for the following code:

  def libMethod(implicit x: Int): Int = ???

  implicit val m: Int = 10
  def bar: Unit = {
      implicit val x = libMethod
      implicitly[Int]
  }

would be like the follows:

  def libMethod(implicit x: Int): Int = ???

  implicit val m: Int = 10
  def bar: Unit = {
      implicit def x(implicit x: Int): Int = libMethod
      implicitly[Int]
  }

I verified that it indeed works in Dotty. However, the code above doesn't compile in Scala 2.12.

@odersky
Copy link
Contributor

odersky commented Apr 2, 2019

I verified that it indeed works in Dotty. However, the code above doesn't compile in Scala 2.12.

Why does it not compile? Is it that you get a divergence error?

@liufengyun
Copy link
Contributor Author

Scala2 produces the following error:

examples/test.scala:6: error: ambiguous implicit values:
 both value m in object Test of type => Int
 and value x of type Int
 match expected type Int
      implicit def x(implicit x: Int): Int = libMethod
                                             ^
examples/test.scala:7: error: ambiguous implicit values:
 both method x of type (implicit x: Int)Int
 and value m in object Test of type => Int
 match expected type Int
      implicitly[Int]
                ^
two errors found

/cc: @adriaanm @milessabin

@OlivierBlanvillain
Copy link
Contributor

Looking back at the initial example it seams like it's exploiting a loop hole in scalac which which requires an implicit without explicit return type. Is there any action to be taken here or should we just close the issue?

@liufengyun
Copy link
Contributor Author

Close, the Dotty behavior is principled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants