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

Adding curly braces changes implicit resolution (involving type lambdas and partial unification) #10213

Closed
scabug opened this issue Feb 27, 2017 · 3 comments
Labels
Milestone

Comments

@scabug
Copy link

scabug commented Feb 27, 2017

Compile the attached file with -Ypartial-unification.

Focus on the difference between Test1 and Test2, which differ only in the extra pair of braces ({ }) around some code. That extra pair of braces helps implicit resolution, which otherwise fails. The claim of this bug report is that those braces should not be necessary.

A bunch of smaller working examples is added in Test3, which explains why the minimal failing case is so complex.

I inline the contents of the attached file for easy reference:

import scala.language.higherKinds

final case class Coproduct[F[_], G[_], A](run: Either[F[A], G[A]])

object Coproduct {

  sealed trait Builder {
    type Out[_]
  }

  sealed trait :++:[F[_], G[_]] extends Builder {
    type Out[A] = Coproduct[F, G, A]
  }

  sealed trait :+:[F[_], B <: Builder] extends Builder {
    type Out[A] = Coproduct[F, B#Out, A]
  }
}

trait Inject[F[_], H[_]] {
  def inj[A](fa: F[A]): H[A]
}

object Inject {
  import Coproduct._

  implicit def reflexiveInject[F[_]]: Inject[F, F] =
    new Inject[F, F] {
      def inj[A](fa: F[A]): F[A] = fa
    }

  implicit def injectLeft[F[_], G[_]]: Inject[F, (F :++: G)#Out] =
    new Inject[F, (F :++: G)#Out] {
      def inj[A](fa: F[A]): Coproduct[F, G, A] = Coproduct(Left(fa))
    }

  implicit def injectRight[F[_], G[_], H[_]](implicit I: Inject[F, H]): Inject[F, (G :++: H)#Out] =
    new Inject[F, (G :++: H)#Out] {
      def inj[A](fa: F[A]): Coproduct[G, H , A] = Coproduct(Right(I.inj(fa)))
    }
}

object Test1 {
  import Coproduct.{:++:, :+:}

  class Foo[A]
  class Bar[A]
  class Baz[A]

  implicitly[Inject[Baz, (Foo :+: Bar :++: Baz)#Out]] // error

  // expanded previous line, still doesn't compile
  implicitly[Inject[Baz, ({ type Out[A] = Coproduct[Foo, ({ type Out1[a] = Coproduct[Bar, Baz, a] })#Out1, A] })#Out]] // error
}

// Differs from Test1 only by extra braces.
// This compiles.
object Test2 {
  import Coproduct.{:++:, :+:}

  {
    class Foo[A]
    class Bar[A]
    class Baz[A]

    implicitly[Inject[Baz, (Foo :+: Bar :++: Baz)#Out]] // OK

    // expanded previous line
    implicitly[Inject[Baz, ({ type Out[A] = Coproduct[Foo, ({ type Out1[a] = Coproduct[Bar, Baz, a] })#Out1, A] })#Out]] // OK
  }
}

// A bunch of "smaller" examples that work without the extra braces.
object Test3 {
  import Coproduct.{:++:, :+:}

  class Foo[A]
  class Bar[A]
  class Baz[A]

  // injecting into Left(_) works
  implicitly[Inject[Foo, (Foo :+: Bar :++: Baz)#Out]]

  // injecting into Right(Left(_)) works
  implicitly[Inject[Bar, (Foo :+: Bar :++: Baz)#Out]]

  // injecting into Right(_) works
  implicitly[Inject[Baz, (Bar :++: Baz)#Out]]

  // define FooBarBaz equal to (Foo :+: Bar :++: Baz)#Out
  type BarBaz[A] = Coproduct[Bar, Baz, A]
  type FooBarBaz[A] = Coproduct[Foo, BarBaz, A]
  def proveTypeEquality[A] = implicitly[FooBarBaz[A] =:= (Foo :+: Bar :++: Baz)#Out[A]]

  // injectiong into Right(Right(_)) of FooBarBaz works,
  // as opposed to (Foo :+: Bar :++: Baz)#Out. But they are the same type!
  implicitly[Inject[Baz, FooBarBaz]]
}
@scabug
Copy link
Author

scabug commented Feb 27, 2017

Imported From: https://issues.scala-lang.org/browse/SI-10213?orig=1
Reporter: Tomas Mikula (tomas.mikula-at-gmail.com)
Affected Versions: 2.12.1
Attachments:

  • test.scala (created on Feb 27, 2017 10:11:40 PM UTC, 2554 bytes)

@scabug
Copy link
Author

scabug commented Feb 28, 2017

@milessabin said:
The extra braces make the difference between the definitions being member-level or block-local. Whilst I agree that this probably shouldn't make a difference here, there have been/are plenty of examples of different behaviour between these two scenarios.

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

@milessabin Yeah, I phrased this poorly. The main point of this bug report is that Test1 should compile. I added Test2 (with extra braces) just to show that, in some contexts, similarly looking implicit resolution already works, which should support the claim that it should work in Test1.

Also, in no way is adding extra braces a workaround, because it makes the types defined inside block local and thus invisible to the outside.

TomasMikula added a commit to TomasMikula/scala that referenced this issue Sep 12, 2017
Can cause ambiguous implicits, so is under a compiler flag
-Yhk-typevar-unification.

Fixes scala/bug#10197
Fixes scala/bug#10213
Fixes scala/bug#10238
Fixes scala/bug#10372
Presents an alternative fix to scala/bug#6895.
TomasMikula added a commit to TomasMikula/scala that referenced this issue Sep 14, 2017
Can cause ambiguous implicits, so is under a compiler flag
-Yhk-typevar-unification.

Fixes scala/bug#10197
Fixes scala/bug#10213
Fixes scala/bug#10238
Fixes scala/bug#10372
Presents an alternative fix to scala/bug#6895.
TomasMikula added a commit to TomasMikula/scala that referenced this issue Sep 26, 2017
Can cause ambiguous implicits, so is under a compiler flag
-Yhk-typevar-unification.

Fixes scala/bug#10197
Fixes scala/bug#10213
Fixes scala/bug#10238
Fixes scala/bug#10372
Presents an alternative fix to scala/bug#6895.
TomasMikula added a commit to TomasMikula/scala that referenced this issue Sep 27, 2017
Can cause ambiguous implicits, so is under a compiler flag
-Yhk-typevar-unification.

Fixes scala/bug#10197
Fixes scala/bug#10213
Fixes scala/bug#10238
Fixes scala/bug#10372
Presents an alternative fix to scala/bug#6895.
TomasMikula added a commit to TomasMikula/scala that referenced this issue Sep 27, 2017
Can cause ambiguous implicits, so is under the compiler flag
-Xsource:2.13

Fixes scala/bug#10185
Fixes scala/bug#10195
Fixes scala/bug#10197
Fixes scala/bug#10213
Fixes scala/bug#10238
Fixes scala/bug#10372
Presents an alternative fix to scala/bug#6895.
TomasMikula added a commit to TomasMikula/scala that referenced this issue Sep 27, 2017
Can cause ambiguous implicits, so is under the compiler flag
-Xsource:2.13

Fixes scala/bug#10185
Fixes scala/bug#10195
Fixes scala/bug#10197
Fixes scala/bug#10213
Fixes scala/bug#10238
Fixes scala/bug#10372
Presents an alternative fix to scala/bug#6895.
TomasMikula added a commit to TomasMikula/scala that referenced this issue Sep 28, 2017
Can cause ambiguous implicits, so is under the compiler flag
-Xsource:2.13

Fixes scala/bug#10185
Fixes scala/bug#10195
Fixes scala/bug#10197
Fixes scala/bug#10213
Fixes scala/bug#10238
Fixes scala/bug#10372
Presents an alternative fix to scala/bug#6895.
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

3 participants