Skip to content
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

Implicit conversion isn't applied for Id type alias #8049

Closed
travisbrown opened this issue Jan 21, 2020 · 3 comments · Fixed by #8082
Closed

Implicit conversion isn't applied for Id type alias #8049

travisbrown opened this issue Jan 21, 2020 · 3 comments · Fixed by #8082

Comments

@travisbrown
Copy link
Contributor

minimized code

import scala.language.implicitConversions

class Ops[A](a: A) {
  def bar: Unit = ()
}

implicit def toOps[A](a: A): Ops[A] = new Ops[A](a)

type Id[A] = A

class Foo[A](val value: Id[A]) {
  def same: Foo[A] = new Foo[A](value)
  def map[B](f: A => B): Foo[B] = new Foo[B](f(value))
}

val x: Int = 1
val foo: Foo[Int] = new Foo(1)

val res1 = x.bar
val res2 = foo.value.bar
val res3 = foo.same.value.bar
val res4 = foo.map[Int](_ + 1).value.bar
val res5 = foo.map(_ + 1).value.bar
Compilation output
-- [E008] Member Not Found Error: Id.scala:23:32 -------------------------------
23 |val res5 = foo.map(_ + 1).value.bar
   |           ^^^^^^^^^^^^^^^^^^^^^^^^
   |value bar is not a member of Id[B], but could be made available as an extension method.
   |
   |One of the following imports might fix the problem:
   |
   |  import Int.int2double
   |  import Int.int2float
   |  import Int.int2long
   |  import math.BigDecimal.int2bigDecimal
   |  import math.BigInt.int2bigInt
   |  import math.Numeric.IntIsIntegral.mkNumericOps
   |  import math.Numeric.BigIntIsIntegral.mkNumericOps
   |  import math.Numeric.IntIsIntegral.mkOrderingOps
   |  import math.Ordering.Int.mkOrderingOps
   |  import Long.long2double
   |  import Long.long2float
   |  import math.BigDecimal.long2bigDecimal
   |  import math.BigInt.long2bigInt
   |  import math.Numeric.LongIsIntegral.mkNumericOps
   |  import math.Numeric.FloatIsFractional.mkNumericOps
   |  import math.Ordering.Long.mkOrderingOps
   |  import Float.float2double
   |  import math.BigDecimal.double2bigDecimal
   |  import math.Numeric.DoubleIsFractional.mkNumericOps
   |  import math.Numeric.BigDecimalAsIfIntegral.mkNumericOps
   |  import math.Numeric.BigDecimalIsFractional.mkNumericOps
   |  import math.Numeric.LongIsIntegral.mkOrderingOps
   |  import math.Numeric.BigDecimalAsIfIntegral.mkOrderingOps
   |  import math.Numeric.BigDecimalIsFractional.mkOrderingOps
   |  import math.Numeric.BigIntIsIntegral.mkOrderingOps
   |  import math.Numeric.FloatIsFractional.mkOrderingOps
   |  import math.Numeric.DoubleIsFractional.mkOrderingOps
   |  import math.Ordering.BigDecimal.mkOrderingOps
   |  import math.Ordering.BigInt.mkOrderingOps
   |  import math.Ordering.DeprecatedFloatOrdering.mkOrderingOps
   |  import math.Ordering.Float.IeeeOrdering.mkOrderingOps
   |  import math.Ordering.Float.TotalOrdering.mkOrderingOps
   |  import math.Ordering.DeprecatedDoubleOrdering.mkOrderingOps
   |  import math.Ordering.Double.IeeeOrdering.mkOrderingOps
   |  import math.Ordering.Double.TotalOrdering.mkOrderingOps
   |  import math.Integral.Implicits.infixIntegralOps
   |  import math.Numeric.Implicits.infixNumericOps
   |  import math.Ordered.orderingToOrdered
   |  import math.Ordering.Implicits.infixOrderingOps
   |  import reflect.Selectable.reflectiveSelectable
   |  import implicits.Not.amb1
   |  import implicits.Not.amb2
   |         
   |
   |where:    B is a type variable with constraint >: Int
1 error found

expectation

This compiles on Scala 2. I'm running into this in the Cats tests, where we use ScalaTest's should matchers to tests methods on e.g. WriterT[Id, A, B], which return Id[C] where C is inferred.

If you change Id to something like type Id[A] = List[A] and edit Foo appropriately, it compiles fine on Dotty.

@odersky
Copy link
Contributor

odersky commented Jan 21, 2020

It also compiles if Id is covariant. I.e. if I change it like this

type Id[+A] = A

it compiles OK. What happens here is that A is not instantiated since it is nonvariant in the type of foo.map(_ + 1).value. Then since A only has a lower bound no member can be found on A.

A possible fix is to see "through" the alias to realize that A is covariant after all and should be minimized. That might also be a fix for #7993.

@odersky
Copy link
Contributor

odersky commented Jan 21, 2020

Btw, these are really useful minimizations! Thanks for putting in the effort.

@julienrf
Copy link
Contributor

I’m not sure whether it is the same issue or not, but the suggested imports contain duplicated entries:

  import Ordering.Implicits.seqOrdering
  import Ordering.Iterable
  import math.Ordering.Implicits.seqOrdering
  import math.Ordering.Iterable

Minimized code:

import Ordering.Implicits.seqOrdering

class Foo

object OrderingInstances {
  implicit def orderingFoo: Ordering[Foo] = ???
}

List(List(new Foo)).sorted

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

Successfully merging a pull request may close this issue.

3 participants