Chain of responsibility in Scala using partial functions

Manuel Rodríguez
3 min readJun 21, 2019

--

image source: https://refactoring.guru/design-patterns/chain-of-responsibility

Chain of Responsibility is a well know design pattern. It was first introduced in the canonical book Design Patterns: Elements of Reusable Object-Oriented Software by the Gang of Four. There are plenty of descriptions around, with the one in wikipedia being quite good, so if you are not familiar please start by reading it here before continuing with this article.

Doing a small research on how to do a fully functional implementation in Scala, I stumbled upon this article. Although it does present a good approach, in my opinion their proposal has a couple of mistakes and things to be improved. The rest of this article will present my own approach, explaining all concepts from the very beginning so they are clear to non-experts (including myself).

A partial function of type PartialFunction[A, B] is a unary function (a function that receives one input parameter) where the domain does not necessarily include all values of type A. PartialFunction[A, B] type extends the type (A) => B. This means that the result of a function returning PFRule will be a function that receives an Int and returns a String, although it won’t be defined for all integers

3.

{ case n: Int …} means “create an anonymous match function that receives an Int, calls it n, and matches against the guard”. It is partial, because it is not defined for every n. If n is not an Int, or n is an Int that does not satisfy f(n), this will not return any value.

Our numberRule function receives an Int=>Boolean as the argument. This is a function that receives an integer and returns a boolean. This function is used in the guard to check if this rule should be applied for that integer.

It is straightforward to see how the combination of both elements creates a rule for our Chain of Responsibility. If the rule can be applied for the input values it will return a value, and if it cannot it will pass the responsibility to the next one.

7 to 10.

These are the rules for our system. Note that 7 and 8 do not cover all the possible values for the input (in particular, when n=5), so there needs to be a fallback function defined for every input value. In this case we’ll be returning an error.

11.

orElse simply means “try to apply the function, and if it is not defined then apply the next one”. Here we are defining how we’ll apply the rules for our Chain of Responsibility. The resulting value is a partial function, as there is no way of knowing if all the partial functions cover all the possible input values

14.

This function finally goes from Int to String, with no Partials. applyOrElse tries to apply numberRules to n, and if not possible then the fallback function defaultValue

17 to 22.

Some examples of how this works.

--

--

Manuel Rodríguez

Developer at New Relic. This is where I keep the cool stuff that I learn in my free time