8

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?

Joshua MN
  • 1,486
  • 2
  • 13
  • 26

1 Answers1

5

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.

Akos Krivachy
  • 4,915
  • 21
  • 28
  • 1
    Thanks. The solutions are somewhat helpful, but what about when I have to match really different things? i.e. token can be a `Token[Value]` (== `Keyword`) or a `Token[Int]` ( == `IntegerLiteral` ) ? – Joshua MN Nov 12 '13 at 08:00
  • 1
    @AkosKrivachy What about defining a `match` method for the `Keyword` class that will do the implicit conversion to a `Keywords.Value`? I tried [something similar](http://pastebin.com/9CkszZGf) and I failed. I'm uncertain about how to pass a `case` block to such a method. The syntax for the `match` method should be the same as for a regular `match` and solve the problem. – Trylks Feb 26 '15 at 13:35
  • 2
    @Trylks Well writing your own `match` does not really solve the problem, but creates different code. The implicit doesn't need to be invoked since you have access to the underlying `result` value. I fixed your code to work: http://pastebin.com/EBPx0sGC but you have to explicitly use `a \`match\` { ... }` when calling it so it's not a super-nice solution. Also try commenting out the implicit and my bit of example code will continue to work, so this is somewhat a different problem. – Akos Krivachy Feb 26 '15 at 14:56
  • @AkosKrivachy I see. I thought the backticks would not be needed when invoking the method, only on definition, so yes, different code semantically AND syntactically :/ (You are right, the implicit is there for any regular use, but it doesn't work for `match` nor is needed for ` `match` ` ). The good thing: now I know what is a `PartialFunction` :) Thanks a lot. – Trylks Feb 26 '15 at 15:41