arainko / ducktape

Automatic and customizable compile time transformations between similar case classes and sealed traits/enums, essentially a thing that glues your code. Scala 3 only. Or is it duct 🤔
https://arainko.github.io/ducktape/
Other
402 stars 8 forks source link

New `F-unwrapping` example would not work in Scala 3.4+ #188

Open WojciechMazur opened 1 month ago

WojciechMazur commented 1 month ago

The example from release notes 0.2.3 and the first test case https://github.com/arainko/ducktape/blob/54d7942a0bc5a4fc310fd7bfdf129d7ff7528155/ducktape/src/test/scala/io/github/arainko/ducktape/fallible/FUnwrappingSuite.scala#L10-L25 would not work in Scala 3.4 and it's changes to better specification and less unsoundness in match types I would fail with the following error:

[error] -- [E191] Type Error: /Users/wmazur/projects/community-build3/repo/ducktape/src/test/scala/io/github/arainko/ducktape/fallible/FUnwrappingSuite.scala:21:37 
[error] 21 |      val actual = source.fallibleTo[Tuple.InverseMap[source.type, mode.Self]]
[error]    |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error]    |       The match type contains an illegal case:
[error]    |           case mode.Self[x] *: t => x *: Tuple.InverseMap[t, mode.Self]
[error]    |       The pattern contains a type alias `Self`.
[error]    |       (this error can be ignored for now with `-source:3.3`)
arainko commented 1 month ago

Hmm, that's disappointing - would it work with import language.betterMatchExtractors? Since Mode#Self is just a typealias I'd expect it to be alright (citation needed 😄 )

WojciechMazur commented 1 month ago

I've made a quick check and yes, it works with import scala.language.experimental.betterMatchTypeExtractors the only issue is that it's an experimental feature currently. Guess, we need to wait till it's stabilized https://github.com/scala/improvement-proposals/pull/84

arainko commented 1 month ago

That's alright then, as long as that gets stabilized in time for the next LTS we're alright, it's just going to need a couple of warnings in the docs.

Thanks for looking out!

arainko commented 2 weeks ago

So I did some testing and it's pretty weird:

//> using scala 3.5.0
//> using options "-experimental"

trait Test[F[_]] {
  final type Self[A] = F[A]
}

object Test {
  inline def current(using T: Test[?]): T.type = T

  trait Opt extends Test[Option]
  trait Either[E] extends Test[[a] =>> scala.Either[E, a]]
}

object testing {

  locally {
    given Test.Opt()

    val tup = (Some(1), Some(2), Some(3))

    // compiles just fine with no `scala.language.experimental.betterMatchTypeExtractors`
    val d: Tuple.InverseMap[tup.type, Test.current.Self] = ??? 
  }

  locally {
    import scala.language.experimental.betterMatchTypeExtractors

    given Test.Either[String]()

    val tup = (Right(1), Right(2), Right(3))

    // refuses to compile with or without `scala.language.experimental.betterMatchTypeExtractors`
    val d: Tuple.InverseMap[tup.type, Test.current.Self] = ???
  }
}

with the output being:

Compiling project (Scala 3.5.0, JVM (21))
[error] ./file.scala:35:12
[error] The match type contains an illegal case:
[error]     case given_Either_String.Self[x] *: t => x *: Tuple.InverseMap[t, given_Either_String.Self]
[error] (this error can be ignored for now with `-source:3.3`)
[error]     val d: Tuple.InverseMap[tup.type, Test.current.Self] = ???
[error]            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

is this expected? I'd expect these two examples to either not compile without betterMatchTypeExtractors at all or compile with betterMatchTypeExtractors