Description
Based on OpenCB failure in scalaland/chimney
- fyi @MateuszKubuszok
Previously defining extractor using def unapply(implicit value: T)
allowed for dual usage:
- in pattern matching it worked as normal
def unapply(value: T)
method - in user code allowed to summon instance of extacted object if given implicit was in scope.
It was never possible to achive the same with def unapply(using value: T)
syntax becouse it requires an explicit using
keyword at callsite - it was never handled by the compiler in case of pattern matching.
The hidden feature of def unapply(implicit value: T)
works until Scala 3.7.0, in 3.7.1 it crashes (see #23022) and in 3.7.2-RC1 / nightly it fails to compile (see output)
The possible workaround is to define 2 unapply methods variants - taking normal and using
arguments and chaning the output name with @targetName
, however this solution is not as flexible as previously
Compiler version
Last good release: 3.7.1-RC1-bin-20250411-f4847cc-NIGHTLY
First bad release: 3.7.1-RC1-bin-20250412-e70ea84-NIGHTLY
Bisect points to b4a802a
Related to #23022
Minimized code
def summonsTest =
given Type[String] = ???
val opt1: Option[Wrapper[String]] = Wrapper.unapply[String] // ok
val opt2 = Wrapper.unapply[String]
opt2.map(_.getValue) // error when using workaround
def patternMatchTest =
Type[String] match
case Wrapper(v) => v.getValue
type Type[A] = Class[A] // any rhs would work here
object Type:
def apply[A]: Type[A] = ???
trait Wrapper[T]:
def getValue: T = ???
object Wrapper:
def unapply[T](implicit ev: Type[T]): Option[Wrapper[T]] = None
// Workaround:
// @annotation.targetName("unapplyValue")
// def unapply[T](ev: Type[T]): Option[Wrapper[T]] = unapply(using ev)
// @annotation.targetName("unapplySummon")
// def unapply[T](using Type[T]): Option[Wrapper[T]] = ???
Output
[error] ./my.example.scala:10:10
[error] Wrapper cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method with the appropriate signature
[error] case Wrapper(v) => v.getValue
[error] ^^^^^^^
[error] ./my.example.scala:10:24
[error] Not found: v
[error] case Wrapper(v) => v.getValue
[error] ^
With workaround:
[error] ./my.example.scala:6:3
[error] value map is not a member of Class[String] => Option[Wrapper[String]]
[error] opt2.map(_.getValue) // error when using workaround
[error] ^^^^^^^^
Expectation
To be decided if the old behaviour under unapply(implicit T)
should still be supported