Skip to content

Inconsistent resolution of implicits #10376

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

Open
charpov opened this issue Jun 16, 2017 · 9 comments
Open

Inconsistent resolution of implicits #10376

charpov opened this issue Jun 16, 2017 · 9 comments
Labels
Milestone

Comments

@charpov
Copy link

charpov commented Jun 16, 2017

object Base {

  import scala.language.implicitConversions

  implicit def conv[A, B](x: Base[A])(implicit f: A => B): Base[B] = new C[B]()
}

abstract class Base[A] {

  def +(n: A): Base[A] = this
  def +(that: Base[A]): Base[A]
}

case class C[A]() extends Base[A] {
  override def +(that: Base[A]): Base[A] = that
}

The idea is to lift an implicit conversion from A to B into one from C[A] to C[B]. Let A be Int and B be BigInt:

import Base._

  val x = new C[Int]
  val X = new C[BigInt]

The expression X + x compiles fine: argument x is lifted into a C[BigInt]. However, x + X does not compile (x is not lifted as a target). It seems to me that it should work.

Moreover, the expression x + X can be compiled if:

  • the first + method is removed from class C (no overloading), or
  • the second + method is given an implementation in the Base class instead of being abstract, then overridden in class C.

I don't think the specs say anything about overloading or abstract/concrete having such an impact on how implicits are resolved, making this behavior buggy (or at least very mysterious).

This is observed in 2.12.2.

@charpov
Copy link
Author

charpov commented Jun 27, 2017

No one thinks this is a bug? I had no replies on the Scala user list either.

@milessabin
Copy link

The reason that the first compiles and the second doesn't is that the standard library provides an implicit conversion from Int => BigInt, but not vice versa.

Welcome to Scala 2.12.2-20170419-092530-unknown (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_131).
Type in expressions for evaluation. Or try :help.

scala> implicitly[Int => BigInt]
res0: Int => BigInt = $$Lambda$1836/347136295@10823d72

scala> implicitly[BigInt => Int]
<console>:12: error: No implicit view available from BigInt => Int.
       implicitly[BigInt => Int]
                 ^

Thus the precondition for your implicit def conv[Int, BigInt] is met, but it's not for conv[BigInt, Int].

@charpov
Copy link
Author

charpov commented Jul 4, 2017

No. I expect x to be converted to C[BigInt], using the implicit conversion from Int to BigInt.

How do you explain that, if I replace def +(that: Base[A]): Base[A] in Base with def +(that: Base[A]): Base[A] = ???, then everything works?

@charpov
Copy link
Author

charpov commented Sep 11, 2017

See also discussion at: https://users.scala-lang.org/t/implicits-resolution/981

@schlichtanders
Copy link

I experienced similar brokeness with the following simple example:

import scala.util.Try
implicit class MyOption[+A](opt: Option[A]){
  def orElse[B >: A](alternative: => Try[B]): Option[B] = opt.orElse(alternative.toOption)
}
Option(1) orElse Try(2)

Intellij IDEA linter says everything is all right, however the compiler gives

Error:(5, 88) type mismatch;
 found   : scala.util.Try[Int]
 required: Option[?]
def get$$instance$$res0 = /* ###worksheet### generated $$end$$ */ Option(4) orElse Try(2)
                                                                                      ^

(same happens when analogously overwriting flatMap)

According to language specs http://scala-lang.org/files/archive/spec/2.12/07-implicits.html#views point 3) this should work, but doesn't apparently. So this is a bug, isn't it?

Is this the same issue?, or should I open a new one?

@charpov
Copy link
Author

charpov commented Jan 5, 2018

Agreed. It looks like the same behavior, inconsistent with the specs.

I had another compiler bug, somewhat related, recently resolved by @som-snytt . He seems to know his stuff. I wish he could have a look at this one too.

@Jasper-M
Copy link

Jasper-M commented Jan 5, 2018

@schlichtanders I think you encountered this other related issue: #9523

@SethTisue
Copy link
Member

Is it fixed in Dotty?

@SethTisue SethTisue added this to the Backlog milestone Feb 1, 2021
@som-snytt
Copy link

➜  snips cat t10376.scala

object Base {

  import scala.language.implicitConversions

  implicit def conv[A, B](x: Base[A])(implicit f: A => B): Base[B] = C[B](s"cv $x")
}

abstract class Base[A] {
  def +(n: A): Base[A] = this
  def +(that: Base[A]): Base[A]
}

case class C[A](tag: String) extends Base[A] {
  override def +(that: Base[A]): Base[A] = that
}

object Main extends App {
  import Base._

  val x = new C[Int]("x")
  val y = new C[BigInt]("y")
  println((x + y, y + x))
}
➜  snips scalac -version
Scala compiler version 3.0.0-M3 -- Copyright 2002-2020, LAMP/EPFL
➜  snips scalac -d /tmp t10376.scala && scala -classpath /tmp Main
(C(y),C(cv C(x)))
➜  snips

but

➜  snips scalac -d /tmp t10376b.scala && scala -classpath /tmp Main
-- [E007] Type Mismatch Error: t10376b.scala:7:24 ------------------------------
7 |    Option(1) orElse Try(2)
  |                     ^^^^^^
  |                     Found:    scala.util.Try[Int]
  |                     Required: Option[Any]
1 error found

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