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

Type is suprisingly inferred as Any due to its kind-polymorphism #9248

Closed
scabug opened this issue Mar 24, 2015 · 6 comments
Closed

Type is suprisingly inferred as Any due to its kind-polymorphism #9248

scabug opened this issue Mar 24, 2015 · 6 comments

Comments

@scabug
Copy link

scabug commented Mar 24, 2015

In the following example

// Main.scala
import scalaz._, Scalaz._

object Main {

	type WarnedT[F[_], A] = WriterT[F, Vector[String], A]
	type Warned[A] = WarnedT[Id, A]

	def mkW[A](a: A): Warned[A] = a.pure[Id].liftM[WarnedT]

	trait TYPE_CLASS[T[_]] {
		def buzz[A](a: T[A]): Int
	}
	val INSTANCE1 = new TYPE_CLASS[Warned] {
		def buzz[A](a: Warned[A]) = 5
	}
	def buzz[E[_], A](self: E[A])(implicit ev: TYPE_CLASS[E]): Int = ev.buzz(self)

	// The return type of mkW is incorrectly inferred as Any
	def foo: Int = buzz(mkW(5).map(a => a))(INSTANCE1)
}

Adding a type annotation after the map (using either the type alias or the expanded form) causes the compiler to work correctly.

The Build.scala for this is

// Build.scala
import sbt._
import Keys._

object BuildSettings {
   
   val buildSettings = Defaults.defaultSettings ++ Seq(
      version      := "0.0.1-SNAPSHOT"
    , scalaVersion := "2.11.6"
    , scalacOptions ++= Seq(
         "-unchecked"
       , "-deprecation"
       , "-feature"
       , "-language:higherKinds"
       , "-language:postfixOps"
       )
    )
}

object ErroneousType extends Build {
   import BuildSettings._

   lazy val root: Project = Project(
      "root"
    , file(".")
    , settings = buildSettings ++ Seq(
         libraryDependencies ++= Seq(
            "org.scalaz" %% "scalaz-core" % "7.1.0"
          )
       )
    )
}

and I have created a Github Repo illustrating the problem here

@scabug
Copy link
Author

scabug commented Mar 24, 2015

Imported From: https://issues.scala-lang.org/browse/SI-9248?orig=1
Reporter: Chris Neveu (ChrisNeveu)
Affected Versions: 2.10.4, 2.11.6

@scabug
Copy link
Author

scabug commented Mar 25, 2015

@retronym said (edited on Mar 25, 2015 2:46:35 AM UTC):
Here's a reproduction outside of Scalaz that shows what's happening.

object Test {
  trait Binary[A, B]

  type Unary[A] = Binary[A, A]

  def f[F[A], A](f: F[A]) = ???
  def test1(u: Unary[Any]) = f(u)

  def test2(u: Binary[Any, Any]) = f(u) // reports inference error, cannot unifiy Binary[Any, Any] with ?F[?A]

  def test3 = {
    implicit def b2u[A, B](b: Binary[A, B]): List[Int] = ???
    val b: Binary[Any, Any] = null
    f(b) // inference fails initially, but then we try to coerse the arguments 
    //
    // Under -Ytyper-debug, we see:
    //
    //    searching for adaptation to pt=Test.Binary[Any,Any] => ?F[?A] (silent: method test3 in Test) implicits disabled
    // |    |    |    |    5 eligible for pt=Test.Binary[Any,Any] => ?F[?A] at (silent: method test3 in Test) implicits disabled
    // |    |    |    |    [search #5] considering b2u
    // |    |    |    |    |-- b2u BYVALmode-EXPRmode-FUNmode-POLYmode (silent: method test3 in Test) implicits disabled
    // |    |    |    |    |    [adapt] [A, B](b: Test.Binary[A,B])List[Int] adapted to [A, B](b: Test.Binary[A,B])List[Int]
    // |    |    |    |    |    \-> (b: Test.Binary[A,B])List[Int]
    // |    |    |    |    solving for (A: ?A, B: ?B)
    // |    |    |    |    [adapt] [A, B](b: Test.Binary[A,B])List[Int] adapted to [A, B](b: Test.Binary[A,B])List[Int] based on pt Test.Binary[Any,Any] => ?F[?A]
    // |    |    |    |    [search #5] success inferred value of type Test.Binary[Any,Any] => ?F[?A] is SearchResult(b2u[Any, Any], )
    //
    // This leads to inference of ?F=Any, ?A=Nothing (this is kind-correct because Any/Nothing are kind polymorphic)
    // 
    // When we instantatiate the method type of `f` with this, the formal parameter type is now just Any[Nothing]
    // which is just Any.
    //
    // The provided argument of type `Binary[A, B]` now unifies with this, no implicit coercion required.
  }
}

The trouble comes from the fact that we don't have a way to mark an implicit conversion for use only as a provider of extension methods. So in Scalaz, a bunch of the ToXxxOps conversions end up compromising kind checking.

@scabug
Copy link
Author

scabug commented Mar 25, 2015

@retronym said:
I'll see about adding a lint warning: scala/scala#4401

@scabug
Copy link
Author

scabug commented Mar 26, 2015

Chris Neveu (ChrisNeveu) said (edited on Mar 26, 2015 12:31:02 PM UTC):
Thanks Jason, that will at least save us some debugging time in the future.

@scabug
Copy link
Author

scabug commented Apr 14, 2015

@adriaanm said:
I'm going to backlog polishing this PR. We first have some catching up to do with our feature work.

@scabug scabug added this to the Backlog milestone Apr 7, 2017
@joroKr21
Copy link
Member

Nowadays the original example is working in Scala 2.13 and in Scala 2.12 with -Ypartial-unification

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

No branches or pull requests

5 participants