Search code examples
scalafunctional-programmingpartial-functions

Factoring out common cases in pattern matching with partial function


I routinely use partial functions to factor out common clauses in exception handling. For example:

val commonHandler: PartialFunction[Throwable, String] = {
  case ex: InvalidClassException => "ice"
}

val tried = try {
  throw new Exception("boo")
}
catch commonHandler orElse {
  case _:Throwable => "water"
}

println(tried) // water

Naturally, I expected that the match keyword also expects a partial function and that I should be able to do something like this:

  val commonMatcher: PartialFunction[Option[_], Boolean] = {
    case Some(_) => true
    case None => false
  }

  Some(8) match commonMatcher // Compilation error

What am I doing wrong?


Solution

  • The match is a keyword, not a method, and its syntax does not accept a partial function on its right side (see below). There however exists a pipe method (since 2.13), which, same as map or foreach, accepts a partial function. You can therefore write:

    import scala.util.chaining._
    
    Some(8).pipe(commonMatcher)
    

    There was some discussion regarding this (see Pre SIP: Demote match keyword to a method) and there was a PR which made possible to use the match a bit more like a method (Change match syntax #7610), with a dot, but still the syntax is the match keyword needs to be followed by case clauses, see https://docs.scala-lang.org/scala3/reference/syntax.html:

    InfixExpr         ::=  ... other variants ommitted here ...
                        |  InfixExpr MatchClause
    
    SimpleExpr        ::=  ... other variants ommitted here ...
                        |  SimpleExpr ‘.’ MatchClause
    
    MatchClause       ::=  ‘match’ <<< CaseClauses >>>
    
    CaseClauses       ::=  CaseClause { CaseClause }
    CaseClause        ::=  ‘case’ Pattern [Guard] ‘=>’ Block
    

    Compare this with catch syntax:

    Expr1             ::=  ... other variants ommitted here ...
                        |  ‘try’ Expr Catches [‘finally’ Expr]
    
    Catches           ::=  ‘catch’ (Expr | ExprCaseClause)