I want to be able to do this:
scala> val Int(i) = "1"
i: Int = 1
But Int
doesn't have an unapply
method.
I found this answer which gives instructions on how to implicitly add a method to an existing object, so I tried it out. The solution they gave works, but unfortunately not for pattern matching. Here's what I have:
object UnapplyInt {
val IntRE = """^(\d+)$""".r
def unapply(v: String): Option[Int] = v match {
case IntRE(s) => Some(s.toInt)
case _ => None
}
}
implicit def int2unapplyInt(objA: Int.type) = UnapplyInt
These test cases are all fine:
val UnapplyInt(i) = "1" // pattern matching with unapply is fine
val i = Int.unapply("1").get // implicit conversion is fine
But the one I want fails:
scala> val Int(i) = "1"
<console>:10: error: object Int is not a case class constructor, nor does it have an unapply/unapplySeq method
val Int(i) = "1"
^
If the implicit conversion works and pattern matching with unapply
works, why doesn't Scala put these two things together for implicit pattern matching?
edit So my original reasoning was no good. The real reason is from Section 8.1.8 of the Scala language spec
Syntax:
SimplePattern ::= StableId ‘(’ [Patterns] ‘)’
That is, the extractor object has to be stable, and an implicit conversion is not stable. No explanation is given for why the extractor must be stable; I suspect it is because Scala does not want to treat the extractor as an expression because that could quickly become ambiguous:
... match {
foo(bar)(baz)
}
Now which is the constructor and which are the pattern variables?
Luckily you can do this and it works just fine (though, as you commented, introduces other problems):
object Int {
def unapply(v: String) = try Some(v.toInt)
catch { case _: NumberFormatException => None }
}
val Int(i) = "5"
since the type Int
and the object Int
are in different namespaces.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With