typelevel/frameless

type inference for .opt no longer works without explicit type argument in Scala 2.13.x

tribbloid opened this issue · 3 comments

To reproduce, try the minimal example in latest Scala 2:

    val apartments = Seq(
      Apartment("Paris", 50, 300000.0, 2),
      Apartment("Paris", 100, 450000.0, 3),
      Apartment("Paris", 25, 250000.0, 1),
      Apartment("Lyon", 83, 200000.0, 2),
      Apartment("Lyon", 45, 133000.0, 1),
      Apartment("Nice", 74, 325000.0, 3)
    )

    val t1 = TypedDataset.create(apartments)

    {
      val t2 = t1.select(
        t1(Symbol("surface")).opt
          .map(v => v * 2)
      )

This will cause the error:

TypedDatasetDemo.scala:40:31: Cannot prove that Int <:< Option[X].

If you change .opt to .opt[Int] the error will disappear. The problem arise from summing proof of <:<, so even splain plugin won't give too much information:

!I x: Int <:< Option[X]
  Cannot prove that Int <:< Option[X].

Does it work in previous Scala versions? Is there a way to make X being inferred later than <:< to circumvent it?

version number is 2.13_0.15.0

is anyone reproducing this?

That is an expected and designed behavior.

.opt is intended for use with the optional columns; changing .opt to .opt[Int] with Scala 2.13 causes the same error. Int is not a subtype of Option[Int], the compiler error makes sense to me.

Also see tests:

test("opt compiles only for columns of type Option[_]") {
val ds = TypedDataset.create((1, List(1, 2, 3)) :: Nil)
"ds.select(ds('_1).opt.map(x => x))" shouldNot typeCheck
"ds.select(ds('_2).opt.map(x => x))" shouldNot typeCheck
}

To make it work make surface an optional field, i.e.

val apartments = Seq(
  Apartment("Paris", Some(50), 300000.0, 2),
  Apartment("Paris", Some(100), 450000.0, 3),
  Apartment("Paris", Some(25), 250000.0, 1),
  Apartment("Lyon", Some(83), 200000.0, 2),
  Apartment("Lyon", Some(45), 133000.0, 1),
  Apartment("Nice", Some(74), 325000.0, 3)
)

val t1 = TypedDataset.create(apartments)

val t2 = t1.select(t1(Symbol("surface")).opt.map(v => v * 2))

thanks a lot for explaining, case closed.

A similar function (probably called chain[T, R](fn: T => R)?) may come in handy when dealing with non-optional fields