Skip to content

def unapply(implicit ev: T) no longer works in pattern matching #23499

Open
@WojciechMazur

Description

@WojciechMazur

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:pattern-matchingitype:bugregressionThis worked in a previous version but doesn't anymorestat:needs decisionSome aspects of this issue need a decision from the maintainance team.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions