Joshua MN
Joshua MN

Reputation: 1516

How to make implicit conversion work during pattern matching

I have an enum for keywords and operators (and some other too), e.g. (all are similar):

object Keywords extends Enumeration {
    val AND, ARRAY, BEGIN, ...= Value

    case class Keyword(keyword: Value) extends Token[Value] {
        def this(keyword: String) = this(Keywords.fromString(keyword))
        def value = keyword
    }

    implicit def valueToKeyword(keyword: Value) = new Keyword(keyword)
}

this implicit conversion allows me to pass enum values where Tokens are expected e.g.

def testFunction[T](t: Token[T]) = ...
testFunction(Keywords.ARRAY) // gets converted
testFunction(Operators.PLUS) // gets converted too

it also seems that the same implicit conversion is not applied during matching i.e.

val token = new Keyword("ARRAY")
token match {
    case Keywords.ARRAY => ... // not selected but SHOULD be
    case Operators.PLUS => ... // completely different Enum
    ...
}

Why? How to overcome this?

Upvotes: 8

Views: 1898

Answers (1)

Akos Krivachy
Akos Krivachy

Reputation: 4966

This doesn't work because:

token match {
  case Keywords.ARRAY => println("Array")
  case _ => println("Something else")
}

is essentially a PartialFunction with the following type signature: PartialFunction[Keywords.Value, Unit]. Which means an implicit won't be applied, because it's either isDefinedAt or it isn't for that input.

If it isn't defined than case _ => ... will catch everything in my example code. If it's not defined at all and nothing will match it then you will get a MatchError thrown.

In your case token of type Token[Value] isn't defined in the Partial Function that the match will compile to, because only types of Keywords.Value are defined.

Three solutions

If you really want implicits then you could explicitly ask for an implicit (yes, that sentence is funny :))

implicitly[Keywords.Value](token) match {
  case Keywords.ARRAY => println("Array")
  case _ => println("Something else")
}

Or you can explicitly state the type of token, to invoke the implicit magic:

val token: Keywords.Value = new Keyword("ARRAY")
token match {
  case Keywords.ARRAY => println("Array")
  case _ => println("Something else")
}

Or the simplest solution if you can live without implicits:

token.value match {
  case Keywords.ARRAY => println("Array")
  case _ => println("Something else")
}

I know it's not the answer you're looking for, but I hope that you understood what match {...} really means and what Partial Functions are.

Upvotes: 5

Related Questions