1
votes

Can I match an entire Scala Object by matching on a field of that object ?

Imagine, I have a trait Command having a field def name: String. I have several objects of this trait. Ls, Cd, Mkdir, Echo etc. Each of these objects have a specific string bound to name.

e.g. Mkdir.name = "mkdir", Cd.name = "cd".

Now, I receive a string from external system call it: input. I want to match this string to get one of these objects. Only that I don't want multiple case clauses, one for each object. This is because, I'm running validations for different subset(s) of these commands.

My code looks like this:

input match {
case o @ (Mkdir.name | Cd.name | Rm.name) => // Some common code to run and return the matched Command (not string)
...
}

The problem is that, within each case, I need to know which Command's name was matched by the input. I can't know this with this code since here I'm only matching a string to a union of strings thereby here losing the context of what object (Command) that matched string was a part of.

So, ultimately my question is that can I match an object by matching on one of it's field?

2
Why matching is used? "find" is more suitable here: Seq(Mkdir,Cd).find(_.name==input)pasha701
What about having a method getCommandByName on this method you write all the matches one by one. And then you use this method on all other parts.Luis Miguel Mejía Suárez
@pasha701 I can use find and most probably, I will. But this question still stands.VaidAbhishek
There is no such way for partial pattern in Scala 2.x, but extractorcchantep

2 Answers

1
votes

Custom extractors are your friend for this sort of use case.

object IsCommand {
  def unapply(input: String) : Option[Command] = input.toLowerCase match {
    case Ls.name => Some(Ls)
    case Mkdir.name => Some(Mkdir)
    case Rm.name => Some(Rm)
    ...
    case _ => None
  }
}

You can now do pattern matching as follows:

"ls" match {
  case IsCommand(command) => ... // command will be the object Ls
}

I'm not sure if there's a way to shorten the code in the unapply method without use of macros or the like, but in any case you only have to write it once.

0
votes

As alternative to solution prosed by Astrid, you could create CommandExtractor, which would serve as parametrized extractor and then create instances you'd use to pattern match:

class CommandExtractor(commands: Command*) {
  def unapply(name: String): Option[Command] = commands.find(_.name == name)
}

val MkdirOrEcho = CommandExtractor(Mkdir, Echo)
val RmOrCdOrPwd = CommandExtractor(Rm, Cd, Pwd)

input match {
   case MkdirOrEcho(c) => ??? // matching Mkdir or echo
   case RmOrCdOrPwd(c) => ??? //matching rm, cd or pwd
}

The downside of this is you're losing information about which command you're receiving but since you already need to match multiple commands in one branch, then you probably don't need that information.