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?
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
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?