2

I've stumbled upon this thread and there @prolativ provided some fancy scala 3 syntax for creating extension methods for objects

extension (int: Int.type) def unapply(s: String): Option[Int] = s.toIntOption 

But this got me thinking if there is a way to do something similar in scala 2?

I've tried

implicit class IntOps(s: Int.type) { def unapply(s: String): Option[Int] = s.toIntOption } 

Which seemed just scala 2 translation, but it wont compile with error object Int is not a case class, nor does it have a valid unapply/unapplySeq member.

UPD In case of pattern matching there is a way to do this - just add new object and match it:

object IntOps { def unapply(s: String): Option[Int] = s.toIntOption } 

but the question remains - how to add extension methods to object?

1
  • 2
    Int.type is the right way to add extension methods to the companion object of the type Int - It just seems you can't add unapply as an extension in Scala 2. Commented Mar 30, 2023 at 13:13

1 Answer 1

3

An implicit class is a correct way to add extension method to an object

implicit class IntOps(obj: Int.type) { def parse(s: String): Option[Int] = s.toIntOption } Int.parse("123") // Some(123) 

And unapply can be added in the same way

implicit class IntOps(obj: Int.type) { def unapply(s: String): Option[Int] = s.toIntOption } Int.unapply("123") // Some(123) 

Just this will have no impact on pattern matching

"123" match { case Int(i) => ??? // doesn't compile: object Int is not a case class, nor does it have a valid unapply/unapplySeq member } 

The place where Scala 2 compiler throws object Int is not a case class, nor does it have a valid unapply/unapplySeq member is here: https://github.com/scala/scala/blob/v2.13.10/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala#L121-L122

else if (!reallyExists(member)) CaseClassConstructorError(fun, s"${fun.symbol} is not a case class, nor does it have a valid unapply/unapplySeq member") 

This is based on symbols (kind of typeOf[Int.type].decl(TermName("unapply")) in scala-reflect terms). Implicit conversions (extension methods) are not checked here.

This is modified in Scala 3: https://github.com/lampepfl/dotty/blob/3.2.2/compiler/src/dotty/tools/dotc/typer/Applications.scala#L1291-L1341 The compiler tries to typecheck x.unapply and this includes implicit conversions.

By the way, when Scala 2 compiler decides whether to generate unapply in the companion object of a case class (Int wasn't a case class) the compiler doesn't check extension methods either

How can I view the code that Scala uses to automatically generate the apply function for case classes?

case class MyClass(i: Int) MyClass(42) match { case MyClass(i) => println(i) // 42 } 
case class MyClass(i: Int) object MyClass { def unapply(mc: MyClass): Option[Int] = Some(100) } MyClass(42) match { case MyClass(i) => println(i) // 100 } 
case class MyClass(i: Int) implicit class MyClassCompanionOps(mc: MyClass.type) { def unapply(mc: MyClass): Option[Int] = Some(100) } MyClass(42) match { case MyClass(i) => println(i) // 42 } 

And this behavior is the same in Scala 3.


How to extend String to add new unapply function for using it in extracting?

enrich PartialFunction with unapply functionality

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.