8

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?

Community
  • 1
  • 1
dhg
  • 52,383
  • 8
  • 123
  • 144

1 Answers1

8

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.

Owen
  • 38,836
  • 14
  • 95
  • 125
  • Ha! Your proposed solution is what I am using currently. I'm not entirely happy with it because it requires having two different namespaces, meaning that I can't have `Int.MaxValue` and `val Int(i) = "5"` in the same place. – dhg Mar 15 '12 at 20:35
  • 1
    @dhg True. And now that I think more about it, I think the reasoning in my answer isn't quite right. The real reason is that pattern expressions can't be like regular expressions because Scala has to identify what things are constructors and what things are variables... – Owen Mar 15 '12 at 20:45
  • 1
    You lost me on that last part. – dhg Mar 15 '12 at 20:48
  • thanks for the answer. this seems so common I was actually expecting a library function.. – Alexander Oh Aug 29 '17 at 19:34