Most of us have an idea of what functional programming is, and have at least used some of the functional features of our best known programming language such as Lamba or LINQ in .Net.
When I’ve tried to learn more about functional programming, I’ve always felt that something is missing. There is a lot focus on the functions themselves, but less on where to put them in a structure. I’ve asked fellow programmers, but even if they seem very confident in writing functions, they get quite fuzzy when it comes to implementation of a real business problem, and how to structure the code.
Since I’ve used reactjs for many years I have one good example of how to implement a functional code design. But I have troubles applying these principles to the server side, or to see them as a more common approach. So I applied to a workshop at DDDEurope: Lean and functional modelling, with Marcello Duarte. This is what I learned:
You have to start with the programming
To understand the functional design you need to start with the programming. By understanding some of the most fundamental concepts in functional programming it will be easier to grasp the modelling part later. Now, I’ll probably not be the best to explain this after just two days of learning, but I can give you an overview of what you need to dig into. We used Scala for the programming exercises, but I think this is something that you can find in several languages and some of it maybe even in C#.
Higher order function: “A higher order function is a function that takes a function as an argument, or returns a function. Higher order function is in contrast to first order functions, which don’t take a function as an argument or return a function as output”. Higher order functions.
Composition and currying: “A curried function is a function that takes multiple arguments one at a time. Given a function with 3 parameters, the curried version will take one argument and return a function that takes the next argument, which returns a function that takes the third argument. The last function returns the result of applying the function to all of its arguments.”
Curry and function composition
Functors and Categories
Monads: “A monad is a way of composing functions that require context in addition to the return value, such as computation, branching, or I/O. Monads type lift, flatten and map so that the types line up for lifting functions
a => M(b), making them composable. It’s a mapping from some type
a to some type
b along with some computational context, hidden in the implementation details of lift, flatten, and map.”
Monads made simple
Abstractions and compositions: “Software solutions should be decomposable into their component parts, and recomposable into new solutions, without changing the internal component implementation details.”
Abstraction and composition
Thanks to Eric Elliot for the descriptions and examples above!
When trying to write clean code there are things that I sometimes stumble on, e.g. keeping the number of parameters down and writing pure functions without side effects. To use a more functional approach is making it easier to write clean code.
Functional architecture and design is often based on events and event-driven development. Concepts like event sourcing and CQRS can be considered as functional architectures. These architectures can of course be implemented (and is implemented) with any programming language and a wide range of technologies, but functional programming is very well suited for implementing this kind of architecture.
With event-driven development you identify a business flow and let the system reflect that flow. Data might change shape during this flow; for example in a purchasing domain you talk about a price of a product you buy, but in the finance domain the same amount is considered a cost of the same product. It is not just a different status of the product; it is considered a totally different thing.
A common way of identifying the business flow, is to do an event storming. At the event storming business events are identified, and also roles that are involved in firing the events, and the data that is sent. The team can do the event storming by themselves, but if possible, it is good to involve the business. The events, roles and data is written on post-its and put on the wall to illustrate the flow.
During the event storming it is important to avoid too much talk about implementation details (which is hard), and use cases (also hard). Try to focus on how the business work, the flow, and why things are done in a certain way. Look out for the core domain, and try to understand how it contributes to the business. It is important to understand what the business makes money of!
When the flow is defined it is possible to identify the services. One service should reflect one part of the business (domain). The data flows between the services in form of events, and very often the user roles indicate where it is suitable to draw the boundary of the service:
The services can be implemented in many different ways and they have their own databases or data stores. E.g. one is using a SQL database, while the other is using Eventstore and a CQRS approach. Since the services in this example is part of the same deployable unit there are some limitations when it comes to choosing languages and technology. Each service is responsible for its business rules, and don’t know anything about the others. Each service owns its own data.
In functional programming it is crucial to separate responsibilities into different parts of the code. This has spread to the object oriented systems too, so for many of us this comes very natural. The business logic is considered as the core of the system, and if you change technical implementations around it (database, client api’s etc.), it should be possible to leave the business logic untouched.
The flow throughout the system
The business logic implementation is composed of many independent functions which are called in a fluent way. I like to think of a system in the form of streams where the data flow and via projections and usage of different functions the data take different forms depending on what is should be used for. The flow sometimes temporarily stop waiting for input and is then saved in queues or databases. With event sourcing the flow could be reset and replayed at any time.
As a simple example, think about using LINQ or Lambda for filtering and mapping the the data that flows throughout the system. Then you’ve taken a big step. But try to think beyond that; can your own functions be written and applied to the flow in an even better way? Maybe, if we learn how to use the monads, functors etc. that was described earlier in this text.
As an object oriented programmer I have some difficulties to stop thinking in objects with methods and properties. One problem with objects is that the functions often have side-effects, such as reading or modifying a property. Then the outcome of that function might not always be the same, with the same in-data. Side-effects must be avoided to make the functions composable.
The data that shall be modified in a function must be sent in as a variable, be recreated with the changes applied, and then sent back as the result of the function. The data is immutable. For those of us that has worked with reactjs, we’re quite familiar with this.