Visitor pattern is dead. Long live to Visitor pattern. (With Kotlin examples) Functional programming gained a big momentum in the IT field; many things come and go, but FP is not one of them. It is much more expressive than OOP I started dig into it a few years ago, during LambaConf in Bologna and the more insight i get, the more i love FP. In June i went to workshop and, the teacher went deep on and on them. I also finally understood what a is 🎉, but this is another story 😊. I used to think about pattern matching as an interesting way to destructure lists. Now i know that it is a founding block of FP software design, and an effective way to address the need of decoupling domain objects data and behaviors. this algebraic data types Pattern matching Monad Let’s see an example! ERP localized by Country Let’s say that we want to write an ERP software, to compete into markets of countries; Italy and Germany. We are going to write many CRUD operations on many domain objects. Nothing challenging here. two different But what about modeling country specific business rules, on different data structures, inside the same functionality flow? For example, both of the countries have articles in their databases and for sure you want to do searches on them. But search rules and fetched data could be totally different. As data structures are different, we can’t just have the domain entity “Article”: we need at least an “ItalianArticle” and a “GermanArticle”. Consider that data stores could differ also by structure. The FP way Let’s see a implementation: write a sum type Then we specialize it as the product types and . Also, we expect that when we search for something, sometimes we found nothing. So, we are going to consider also . Scala Article . ItalianArticle GermanArticle ArticleNotFound sealed trait Article case ( ) class ItaArticle id: , itaData1: , itaData2: Int String Int extends Article case ( ) class DeuArticle id: , deuData1: , deuData2: Int Int Int extends Article case ( ) class ArticleNotFound extends Article Every time that we receive an Article back from somewhere, we are going to pattern match on it. Inside the match, we gain access to its specialized data (article: ) : = { article { ita: => print(ita.itaData1) deu: => print(deu.deuData2) nf: => print( ) } } def doSomething Article Unit match case ItaArticle case DeuArticle case ArticleNotFound "None" Isn’t it a Switch? At a first sight, it could look like a classic procedural “switch”, followed by a down-casting. There is an important difference: the compiler is aware if we are matching every type of Article or not. If, for example, we forget to match on ArticleNotFound (article: ) : = { article { ita: => print(ita.itaData1) deu: => print(deu.deuData2) } } def doSomething Article Unit match case ItaArticle case DeuArticle then by default is going to raise a warning. sbt compiler As the compiler knows that something is wrong, we can make it raise an error instead of a warning. New requirements: Spain At this point, something totally unexpected happens! Business comes to us with a new requirement 😱: we need to distribute also in Spain. Actually we were prepared, so let’s add to our sum type. SpaArticle Article sealed trait Article case ( ) class ItaArticle id: , itaData1: , itaData2: Int String Int extends Article case ( ) class DeuArticle id: , deuData1: , deuData2: Int Int Int extends Article case ( ) class SpaArticle id: , spaData1: , spaData2: Int Float String extends Article case ( ) class ArticleNotFound extends Article Now we need to add Spanish business logic. With a normal switch, it would be a pain to search for every places where we implemented country specific business rules. With pattern matching instead, the compiler tell us where we have to intervene. Is it possible with OOP? Yes of course, with the ! Visitor pattern If you don’t know about , have a look at Visitor pattern here If you don’t know about the have a look at . Gang of four here Visitor pattern has been known for a long time as an When you add a new item type, then you are going also to add a new method to the Visitor interface. In this case, the compiler breaks every concrete visitor, until you implement the new method in each of them. This is one of the reasons to be an anti-pattern. anti-pattern. Actually, for our concerns, this “issue” looks like exactly what we are searching for! Kotlin example Here it follows a Kotlin implementation of our sum types: { } ( itaData1: String, itaData2: ) : Article { { consumer.use( ) } } ( deuData1: String) : Article { { consumer.use( ) } } interface Article fun applyTo (consumer: ) ArticleConsumer class ItaArticle val val Int override fun applyTo (consumer: ) ArticleConsumer this class DeuArticle val override fun applyTo (consumer: ) ArticleConsumer this You can notice that instead of using the “accept” and “Visitor” naming, i prefer to think about “data that are applied to a data consumer”. (I ) ’m searching for a better naming, so if you have suggestions, please leave a comment! Thanks And here we can implement country specific business rules { } : String { x = article.applyTo( : ArticleConsumer { { x = article.itaData1 } { x = article.deuData1 } { x = } }) x } interface ArticleConsumer fun use (article: ) ItaArticle fun use (article: ) DeuArticle fun use (article: ) ArticleNotFound fun useAnArticle (article: ) Article var "" object override fun use (article: ) ItaArticle override fun use (article: ) DeuArticle override fun use (article: ) ArticleNotFound "not found" return The semantic of this example is totally the same as of FP pattern matching. When we are going to add the new country, then the compiler is going to break everything, until we don’t implement the new in every . fun use(article: SpaArticle) ArticleConsumer Conclusions The original purpose of the Visitor pattern was to iterate an operation over collections of heterogeneous objects, that doesn’t share the same interface and data types. In this article I proposed to use it instead as a It is useful when you end up enumerating your domain entities and need to . routing point. set a localized domain context is argued as being an issue. In this use case, it is the key feature of the pattern. Enumerating domain entities in methods I also demonstrated that . this usage is semantically equal to FP pattern matching Please leave your opinion and feedback in comments. Thank you for reading!