lihaoyi / scala.rx

An experimental library for Functional Reactive Programming in Scala
984 stars 79 forks source link

Strange compilation error when using existentials within Rx #209

Open steinybot opened 3 years ago

steinybot commented 3 years ago

There is a weird problem where using an existential type with an Rx causes the compiler to get confused.

I have minimal reproduction here: https://github.com/steinybot/bug-reports/tree/scalarx/existentials

trait Foo[A]

case class Bar(name: String, foo: Foo[_])

Rx {
  val bar: Bar = ???
  val foo: Foo[_] = ???
  bar.copy(foo = foo)
}

Output:

❯ amm
Loading...
Welcome to the Ammonite Repl 2.3.8 (Scala 2.13.3 Java 11.0.8)
@ import $ivy.`com.lihaoyi::scalarx:0.4.1`, rx._
import $ivy.$                           , rx._

@ trait Foo[A]
defined trait Foo

@

@ case class Bar(name: String, foo: Foo[_])
defined class Bar

@

@ Rx {
    val bar: Bar = ???
    val foo: Foo[_] = ???
    bar.copy(foo = foo)
  }
cmd3.sc:4: type mismatch;
 found   : ammonite.$sess.cmd1.Foo[_$1(in value x$1)] where type _$1(in value x$1)
 required: ammonite.$sess.cmd1.Foo[_$1(in value res3)] @scala.reflect.internal.annotations.uncheckedBounds where type _$1(in value res3)
  bar.copy(foo = foo)
                 ^
Compilation Failed
steinybot commented 3 years ago

Removing the name field from Bar causes the problem to go away.

steinybot commented 3 years ago

Sometimes this can be worked around by putting the body inside another method. Such as:

  Rx(x)

  def x: Bar = {
    val bar: Bar = ???
    val foo: Foo[_] = ???
    bar.copy(foo = foo)
  }

There are times where this doesn't work, such as when the existential type is coming from outside as in:

  val y: Rx[Foo[_]] = Rx(x)

  def x: Foo[_] = {
    ???
  }

In this case you have to use Rx.build:

  val y: Rx[Foo[_]] = Rx.build(x)

  def x(owner: Owner, data: Data): Foo[_] = {
    ???
  }