This is (hopefully) the first post of a series re-visiting the classic patterns by the Gang of Four in the canonical book “Design Patterns: Elements of Reusable Object-Oriented Software” in terms of functional programming. For this first post I am posting my own implementation of Visitor Pattern.
The internet is full of webs with really good explanations, so for these posts I won’t try to do it better than everybody else and will simply re-direct to any material that I have found useful. In this case, there is a a very good description in Refactoring Guru https://refactoring.guru/design-patterns/visitor and the example that I am using here comes from http://www.newthinktank.com/2012/11/visitor-design-pattern-tutorial/ .
Now, the code!
An element (represented here with a case class) is visitable if it provides an accept method, which takes a visitor and executes the visit method. The implementation of the visit method -and in fact, even the fact that it exists- is completely out of the scope of the elements to be visited.
Note that the visited element is passing itself as a parameter to the visitor element. This is done to keep the element.accept(visitor) syntax from the original Java example, altough it is kind of a mix between Object Oriented and Functional programming.
A visitor element needs to provide the visit method. Here I have implemented it as an object in order to be a singleton: as it it stateless, I don’t want to create multiple copies of the visitor. Also, this approach allows to have multiple visitors, and just choose the one we want in execution time. If only one is desired, a way more elegant approach would be to provide the accept method with a typeclass.
The visit function is a high order function. When called, it returns a function that receives a Visitable and returns a Double. This is equivalent to do something like visit(v: Visitable): Double but I like the idea of creating a different function for every kind of Visitable.
Altogether, I really like how functional programming fits this design pattern
The implementation is straightforward. As the visitables all inherit from a sealed trait, we can do a pattern matching on their type. The only thing to note here is line 8, where we are implicitly declaring the match function.
This is the wiring. Again, note that taxVisitor is a singleton object, so that we could have several ones here and decide which to use in execution time.