Search code examples
scalaswitch-statementpattern-matchingdecompositionpartialfunction

Is it possible to decompose Scala match statements using partial functions?


I have a match statement like this:

    i match {
      case x if x == 0 ⇒
        romanNumeral

      case x if x >= 1000 ⇒
        this.roman(i - 1000, s"${romanNumeral}M")

      case x if x >= 900 ⇒
        this.roman(i - 900, s"${romanNumeral}CM")

      // etc.

Depending on how this is formatted, it could be about 30 lines of redundant code. So I'm wondering whether it is possible to make this code more dry.

So I created a partial function:

  private def toRoman(upperGuard: Int, token: String, romanNumeral: String): String = {
    case value: Int if value >= upperGuard ⇒
      this.roman(upperGuard - 1, s"$romanNumeral$token")
  }

which I then tried to include into the above matcher like:

    i match {
      case x if x == 0 ⇒
        romanNumeral

        toRoman(1000, "M", romanNumeral)
        toRoman(900, "CM", romanNumeral)
        // etc.

... But this won't work because Scala's compiler doesn't recognize these functions are the case statements it is looking for.

Is there any way to make this work?


Solution

  • You could create your cases as partial functions:

    private def toRoman(upperGuard: Int, token: String, romanNumeral: String): PartialFunction[Int, String] = {
        case value if value >= upperGuard =>
          this.roman(upperGuard - 1, s"$romanNumeral$token")
    }
    
    val firstCase: PartialFunction[Int, String] = {
        case x if x == 0 => romanNumeral
    }
    

    And then compose it like this:

    val toRomanPartialFunction = firstCase
      .orElse(toRoman(1000, "M", romanNumeral))
      .orElse(toRoman(900, "CM", romanNumeral))
    

    After that you could use it like regular function:

    toRomanPartialFunction.apply(443) 
    toRomanPartialFunction(443)