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

Predef.conforms chosen in preference to a locally defined implicit function for type (Int => Int) [2.8.0 regression] #2811

Closed
scabug opened this issue Dec 17, 2009 · 6 comments
Labels

Comments

@scabug
Copy link

scabug commented Dec 17, 2009

As discovered by Alexander: http://old.nabble.com/Puzzle-td26827682.html

Welcome to Scala version 2.7.5.final (Java HotSpot(TM) Client VM, Java 1.6.0_14)
.
Type in expressions to have them evaluated.
Type :help for more information.

scala> implicit def s2s(s: String) = "s2s"
s2s: (String)java.lang.String

scala> def implicitly[A](implicit a: A) = a
implicitly: [A](implicit A)A

scala> implicitly[String => String].apply("foo")
res1: String = s2s


E:\tools\scala-2.8.0.latest\bin>.\scala
Welcome to Scala version 2.8.0.r20024-b20091207020224 (Java HotSpot(TM) Client VM, Java 1.6.0_14).
Type in expressions to have them evaluated.
Type :help for more information.

scala> implicit def s2s(s: String) = "s2s"
s2s: (s: String)java.lang.String

scala> implicitly[String => String].apply("foo")
res0: String = foo

scala> implicitly[String => String].getClass
res4: java.lang.Class[_] = class scala.Predef$$$$anon$$1
@scabug
Copy link
Author

scabug commented Dec 17, 2009

Imported From: https://issues.scala-lang.org/browse/SI-2811?orig=1
Reporter: @retronym

@scabug
Copy link
Author

scabug commented Dec 17, 2009

@odersky said:
It's due to a change in Predef, because what was identity is now called conforms.
To make the test, if you rename s2s, or identity to conforms, it compiles:

scala> implicit def conforms = (s: String) => "s2s"
conforms: (String) => java.lang.String

scala> def implicitly[A](implicit a: A) = a
implicitly: [A](implicit a: A)A

scala> implicitly[String => String].apply("foo")
res0: String = s2s

The thing to keep in mind is that both the local and the Predef implicit are regarded as competing, unless they have the same name (in which case the local implicit will shadow the Predef implicit). In the example as given, the local implicit was a val and the Predef implicit was a def and the rules of overloading resolution pick the val over the def.

@scabug
Copy link
Author

scabug commented Dec 17, 2009

@odersky said:
Sorry, I got it wrong. The Predef implicit was the val, which got picked over the local def.

@scabug
Copy link
Author

scabug commented Dec 17, 2009

@retronym said:
Okay, I think I understand the overloading resolution. Predef.conforms returns a value the required type (String => String). Eta expansion of the the local implicit method s2s results in the same type, but this is not attempted because conforms matched already.

The change in Predef to return a (A <:< A) from conforms solves other problems, but there is no way to be backwards compatible without a special case in the compiler.

I played around with this some more and found a few more unexplained phenomona -- see the inline comments below.

object test1 {
  implicit def s2s(s: String): String = "s2s"
  assert(implicitly[String => String].apply("foo") == "foo")
  // Predef.conforms chosen here because it returns the required type (A => A),
  // so no need to try to eta transform the method s2s. Fair enough.
}
 
object test2 {
  locally {
    // s2s is a def inside a block. it seems to be automatically eta transformed to (String => String),
    // and is considered equally with conforms[A].
 
    implicit def s2s(s: String): String = "s2s"
    // assert(implicitly[String => String].apply("foo") == "foo")
 
    // UNCOMMENT LINE ABOVE FOR:
    // error: ambiguous implicit values:
    // both method s2s of type (s: String)java.lang.String
    // and method conforms in object Predef of type [A]<:<[A,A]
    // match expected type (String) => String
 
  }
}
 
object test3 {
  def s2s(s: String) = "s2s"
 
  locally {
    // here we ensure that (String => String) is implicit rather than a method. But why is this chosen now, rather than
    // getting the ambiguity error as in `test2`?
    implicit val _ = s2s _
    assert(implicitly[String => String].apply("foo") == "s2s")
  }
  locally {
    implicit def conforms(s: String) = "s2s" // shadowing Predef.conforms by name works. But pretty it ain't.
    assert(implicitly[String => String].apply("foo") == "s2s")
  }
}

@scabug
Copy link
Author

scabug commented Dec 18, 2009

@retronym said:
Here's an design that would be backwards compatible.

  sealed abstract class <:<[-From, +To] extends (From => To)
  object <:< {
    implicit def conforms[A]: A <:< A = new (A <:< A) {
      def apply(x: A) = x
    }
  }

  object Predef {    
    implicit def identity[A](a: A) = a
  }

According the the comments, the new design should have less scope for ambiguity. I need an example to understand this and weigh against this problem.

r18537
r19204

@scabug
Copy link
Author

scabug commented Dec 21, 2009

@adriaanm said:
(in response to private mail:)
the reason for replacing identity with <:< and conforms was the desire to be more precise in the encoding of generalised constraints such as (implicit w: T <:< Traversable[U]), if this were written as (implicit w: T => Traversable[U]) you get weird typing errors when you write something ill-typed, as the compiler will try to use other implicits that populate the overcrowded ? => ? space (such as the any2stringadd implicit, IIRC).

In hindsight, this design ignored at least one limitation of our implicit resolution scheme (see #2781)

Overall, I would not recommend using implicit values of function type, as that interacts strangely with implicit conversions (unless of course you want an implicit conversion and not the encoding of some witness to a typing constraint).

@scabug scabug closed this as completed May 18, 2011
@scabug scabug added the implicit label Apr 7, 2017
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

1 participant