9

I'd like an extractor to implicitly convert its parameters, but it doesn't seem to work. Consider this very simple case:

case class MyString(s: String) {}

implicit def string2mystring(x: String): MyString = new MyString(x)
implicit def mystring2string(x: MyString) = x.s

object Apply {
    def unapply(s: MyString): Option[String] = Some(s)
}

But I'm not able to use it as I would expect:

val Apply(z) = "a"  // error: scrutinee is incompatible with pattern type

Can anyone explain why it fails to convert the parameter from String to MyString? I would expect it to call string2mystring("a") on the fly. Clearly I could work around the issue by saying val Apply(y) = MyString("a"), but it doesn't seem like I should have to do that.

Note: This question is similar to this one, but 1) that one doesn't really have a good answer for why this is happening, 2) the example is more complex than it needs to be.

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

1 Answers1

15

Implicit conversions are not applied when pattern matching. That's not a bug or a problem with your code, it's simply a design decision of the creators of Scala.

To fix it, you should write another extractor that accepts a String — which in turn can call your implicit conversion.

Alternatively, you can try with a view bound, which seems to work as well, and will also work if you later define other implicit conversions to MyString:

object Apply {
  def unapply[S <% MyString](s: S): Option[String] = Some(s.s)
}
Jean-Philippe Pellet
  • 59,296
  • 21
  • 173
  • 234
  • 1
    Thanks. That's a bit disappointing. Do you know what the motivation is for that decision? – dhg Jul 18 '11 at 17:19
  • Yep, adding `def unapply(p: String): Option[String] = Some(p)` to `Apply` does the trick. So I'll go with that. Thanks. – dhg Jul 18 '11 at 17:22
  • @dhg I edited the answer — a view bound seems to work as well. – Jean-Philippe Pellet Jul 18 '11 at 18:36
  • Awesome! The view bound is a great solution. Not only does it not require writing additional methods for every possible type, but it solves another problem I had: If `unapply` takes a tuple, you can't use the overloading trick due to type erasure. But this solves it cleanly and results in compile-time errors for wrong arg types: `def unapply[S <% MyString](p: (S, Int)): Option[String] = Some(p._1.s + p._2)`. – dhg Jul 18 '11 at 19:13