Implementing web-services in a pure functional way is not a trivial task, because it is common to a query database (or a different type of mutable data store) on all the stages of the server request processing. This complicates separation of the pure functional core and imperative shell of the program, which is the primary principle of the functional software architecture. This article focuses on resolving this problem by using free monads, which makes it possible to implement a new domain-specific language that is defined by purely functional data structures, to describe imperative operations. Each operation is later interpreted into actual data mutation by an interpreter. This allows defining the algorithms as a combination of atomic DSL operations, which may have side-effects, in a purely functional way, while only the interpretation logic of those operations remains imperative.In contrast to a standard approach, the amount of the imperative code in free monad based algorithms scales depending on the number of atomic DSL operations instead of the size of the whole program. This can bring a big difference, especially if atomic operations are frequently re-used. As part of the research, an HTTP-based message exchange service was implemented in its own free monad based domain-specific language. As a basis, functional programming language Scala was used. The article shows the implementation of the server endpoint that sends the message to a specific conversation. The endpoint implementation consists of many atomic database queries and contains multiple branches of the control flow.First, each atomic database query or group of related queries is described as a pure functional record where dynamic query parameters are defined as values of the record fields. Then, using a free monad constructor, these operators are upgraded to monads and get the combination logic from the already existing monadic instances. Usually either one is used as a basis, since it provides a convenient way to handle exceptions. Next, atomic monadic operations are combined into algorithms. Most functional languages have syntactic sugar to write expressions, which combine multiple monads. In case of Scala, it is a for-expression. To interpret a DSL-based expression, an interpreter should be declared and used. The interpreter is a function with a defined behaviour for atomic DSL operations which are defined earlier. This behaviour may not be purely functional and may have side effects.The conducted research confirms that the usage of free monad based functional domain-specific programming languages is suitable in context of implementing HTTP-services, and it may significantly decrease the amount of the imperative code.
Read full abstract