scalapattern-matchingpartialfunctionpartial-functions

Pattern-matching: split code with PartialFunction?


I'm struggling to achieve a "simple" behaviour: divide pattern matching code in two separate functions.

I'm simplifying the model for clearness purpose:

abstract class Animal
case object Dog extends Animal
case object Cat extends Animal
case object Bird extends Animal
case object Bat extends Animal
case object Dolphin extends Animal

I want to pattern match on these on different functions (because the actual code is pretty long), but there are other arguments so PartialFunctions are giving me the sick...
In a perfect world I could write :

type PF = PartialFunction[(Animal, Int, String), String] 
private def processFlying(a: Animal, n: Int, loc: String): PF = {
  a match {
    case Bird => n + " birds found in " + loc
    case Bat => n + " bats found in " + underground(loc)
  }
}
private def processMarine(a: Animal, n: Int, loc: String): PF = {
  a match {
    case Dolphin => n + " dolphins found in " + submarine(loc)
  }
}
private def processPet(a: Animal, n: Int, loc: String): PF = {
  a match {
    case Dog => n + " dogs found in " + loc
    case Cat => n + " cats found in " + loc
  }
}
def processAnimal(a: Animal, number: Int, location: String) = {
  val processAll = processFlying orElse processMarine orElse processPet
  processAll(a, n, location)
}

However that does not work. Mostly because I need several argument in my functions. "Why U no use tuples?" would you say? Well I tried and the compiler won't stop complaining the expected type is different than actual type, and different to my alias :(

Any help, tips, or alternative ides will be useful!
Cheers


EDIT: I followed Cyrille's answer bu I also need to perform some work before the match, like this:

val processFlying: PF = {
  // doSomething, like declare a sub-function f
  {
      case (b: Bird, n, loc) => f(b)
      case (b: Bat, n, loc) => f(b)
  }
}

Solution

  • Your problem is that you're mixing method definition and function definition.

    def processFlying(a: Animal, n: Int, loc: String): PF
    

    is the signature of a method (of your surrounding object), that takes three arguments and return a PF, ie a PartialFunction[(Animal, Int, String), String].

    So, assuming this signature is what you want, you will only be able to get a PartialFunction if you already have an Animal, an Int and a String...

    What you more probably want is to define a PF value (without parameters), so more something like

    val processFlying: PF = {
      case (Bird, n, loc) => ...
      case (Bat, n, loc) => ...
    }
    

    EDIT

    To answer your second request (although it is probably overkill, since defining your helper as a private def will do the job), you can alway put the PF block in a closure:

    val processFlying = {
      def f = ...
      val res = {
        case (Bird, n, loc) => f(...)
        case (Bat, n, loc) => f(...)
      }
      res
    }
    

    However, you have to assign a value to the PartialFunction defining block, otherwise the parser will have trouble knowing what to do with it. That's only because PartialFunction definition with cases block and closures share the {} syntax.