Skip to content

ambiguous implicit object arguments for contravariant trait #14169

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
fdietze opened this issue Dec 24, 2021 · 3 comments
Closed

ambiguous implicit object arguments for contravariant trait #14169

fdietze opened this issue Dec 24, 2021 · 3 comments
Labels
area:implicits related to implicits itype:bug

Comments

@fdietze
Copy link

fdietze commented Dec 24, 2021

Compiler version

3.1.1-RC1, ScalaJS

Minimized code

import scala.scalajs.js

trait Render[-T] { def render(value: T): String }

implicit object StringRender extends Render[String] {
  def render(value: String): String = value
}

implicit object UndefOrRender extends Render[js.UndefOr[String]] {
  def render(value: js.UndefOr[String]): String = value.getOrElse("empty")
}

implicitly[Render[String]]

https://scastie.scala-lang.org/mVuFoqtXTbi89svihWkgeg

Output

ambiguous implicit arguments:
both object StringRender in object Playground
and object UndefOrRender in object Playground
match type Playground.Render[String] of parameter e of method implicitly in object Predef

Expectation

Should compile like in Scala 2.13

Notes

It works when making the trait Render invariant.
It also works when using implicit val instead of implicit object:

import scala.scalajs.js

trait Render[-T] { def render(value: T): String }

implicit object StringRender extends Render[String] {
  def render(value: String): String = value
}

implicit object UndefOrRender extends Render[js.UndefOr[String]] {
  def render(value: js.UndefOr[String]): String = value.getOrElse("empty")
}

implicitly[Render[String]]

https://scastie.scala-lang.org/mVuFoqtXTbi89svihWkgeg

@sjrd
Copy link
Member

sjrd commented Jan 21, 2022

This doesn't look related to Scala.js per se. It looks like a typer thing. I can reproduce the issue using Scala/JVM:
https://scastie.scala-lang.org/dENS4y3SReesiZGYKLrLDA

trait Render[-T] { def render(value: T): String }

implicit object StringRender extends Render[String]:
  def render(value: String): String = value
end StringRender

implicit object UndefOrRender extends Render[scala.|[String, Unit]] {
  def render(value: scala.|[String, Unit]): String =
    if value == () then "empty" else value.asInstanceOf[String]
}

implicitly[Render[String]]
// ambiguous implicit arguments: both object StringRender in object Playground and object UndefOrRender in object Playground match type Playground.Render[String] of parameter e of method implicitly in object Predef

@sjrd sjrd added area:implicits related to implicits and removed area:scala.js labels Jan 21, 2022
@odersky
Copy link
Contributor

odersky commented Mar 13, 2022

Seems nobody wants to take this on. I don't know enough about scala.I to figure out what goes on here. My guess is that it's the different handling of contravariant parameters, which would mean that Scala 3's behavior is as intended.

@odersky odersky closed this as completed Mar 13, 2022
@smarter
Copy link
Member

smarter commented Mar 13, 2022

I think it's just that neither object type is more specific than the other, if you use implicit vals or givens then it works out since we end up comparing the types Render[String] to Render[String | Unit] and one is more specific than the other:

trait Render[-T] { def render(value: T): String }

given StringRender: Render[String] with
  def render(value: String): String = value

given UndefOrRender: Render[scala.|[String, Unit]] with
  def render(value: scala.|[String, Unit]): String =
    if value == () then "empty" else value.asInstanceOf[String]

implicitly[Render[String]]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:implicits related to implicits itype:bug
Projects
None yet
Development

No branches or pull requests

5 participants