Writing Business Applications Using A Functional Approach

Advantages of using F#

You may know that business applications are usually written using an Object-oriented language, like Java, C# and Ruby. But what about functional programming? Today, several great functional languages provide rock-solid performance, like Haskell if you want pure functional or Scala and Clojure on the JVM, and, of course, F# on .NET.

Let’s focus and elaborate on the F# side of things. Discover a bit of history, some differences with C#, and the-advantages of using it for writing business applications.

History

When most people hear “.NET” they immediately think C#. F# was first introduced in 2005 and was created by Don Syme and his team at Microsoft Research. It is a general-purpose, strongly typed, multi-paradigm programming language that encompasses functional, imperative, and object-oriented programming paradigms.

As a member of the ML language family, it started as a core implementation of the programming language OCaml for .NET Framework, so it’s suited for a functional-first programming style. Even though the language was started at Microsoft and they still support and help develop it on all their products, in 2013 the F# Software Foundation was formed to help foster the development and community around the language.

F# uses an open development and engineering process, has been open-sourced and licensed under the MIT License, with the community being able to contribute to its development.

Getting started with F# as a C# developer

Let’s start by showing and explaining some of the main differences between C# and F#.  Below, we’ll run through some code samples and see how these concepts may change the way you think about writing software and why they are so important.

C# and Object-Oriented Programming F# and Functional Programming
Variables Immutable values
Statements Expressions
Objects with Methods Types and functions

Immutability is one of the core concepts of functional programming. Immutability can give you a lot of power if you learn to think about solving problems using it. In C#, string is the only data type that is immutable, but in F# every value is immutable by default. For example:

// F#
let bar = 5
bar = bar - 1

// C#
var bar = 5;
bar = bar - 1;

In these two simple examples the value of bar might be obvious in C#, the variable bar is assigned the value and afterward, bar is reassigned bar minus 1, so now bar is 4. However, in the F# example, the value 5 is bound to the name bar forever; it can’t be changed without marking bar as mutable and using the <- operator. What the second line does is check for equality between bar and bar minus 1 and then returns a Boolean, in this case false.

So, why is immutability important? It makes code predictable, easier to work with, and forces you to use a “transformational” approach. Immutable data has no side effects, so that makes it easier to reason about code.

Concurrency is simpler because you don’t have to worry about locks or when passing data around. Since you need to think about transforming data rather than mutating it, code tends to be more modular and scalable.

Expressions and statements may seem quite similar, but they do have a key difference and that is that expressions always produce a value.

// a simple function that returns the square of a number
let square x = x * x

let greaterThanZero number =
if number > 0 then
"Positive"
else
"Negative"

As you can see in those two examples, functions don’t have a return keyword, but both functions return something. If else is an expression so it produces a value, in this case, it’s a string, and the last produced value by an expression is the returned value of the function.

Each invocation of those functions is an expression that takes a value and then produces a string or a number. It may seem alien compared to C#, but you’ll find that you get used to it pretty quickly and that it feels more natural to write.

You may be wondering if every function returns a value, how do you write a function that doesn’t return anything as you do with void in C#? The answer is that those functions return the unit type, something that is almost like void.

let greeting name =
printfn name // returns a unit type

Since we covered using a transformational approach with immutable data, let’s introduce you to a very useful operator– the |> also called the pipe operator and very similar to Unix pipes, where you take something from the left side and pass it as input to something on the right.

let numbers = [1; 2; 3; -1; -5; 6; 7]
let square x = x * x
let even x = x % 2 = 0

let squareEvenList elements =
elements
|> List.filter even
|> List.map square

printfn "%A" (squareEvenList numbers) // [4; 36]

The list data type has some LINQ-like functions like filter, map, sum, groupBy and so on that accept a function as a parameter and they can be linked creating a pipeline using the pipe operator. Arrays and Sequences also have these functions, the difference being that sequence expressions are lazily evaluated while list and array expressions are evaluated eagerly.

As you can see, using the |> operator you can easily break down a problem into small functions and then compose them to create the desired solution.

F# Types are the one thing that C# that doesn’t have. Because F# is a .NET language, it has all the primitive types: float, int, string, etc., it has tuples and .NET objects, but it has two types that are not found in C#: records and discriminated unions.

// discriminate unions
type PaymentType =
| CreditCard of decimal
| DebitCard of decimal
| ACH of string

// records
type Customer =
{ Name: string
  Age: int
  Payment: PaymentType }

Discriminated unions are a type that can be one of many named cases, possibly each with different values and types. Records, on the other hand, are a named, ordered grouping of values with structural equality included. Records fields differ from classes in that they are automatically exposed as properties and they are used in the creation and copying of records.

Functional F# for business applications

After seeing some of the differences between the two languages, why would you write an application in F#?

Firstly, you could write a complete OOP application in F# with absolutely no problems, but its power lies in its type system that is a great tool for domain modeling if you are working with DDD (Domain Driven Design). Also, it’s a functional-first approach that should result in cleaner and easier to maintain code.

As we’ve learned, most applications are written using an object-oriented design, and it usually starts well respecting good design practices and SOLID principles, but something happens and these applications become “heavy” and hard to maintain. Mark Seemann, programming, software development and architecture writer and speaker, has a nice metaphor for this phenomenon. It’s like your app is a ball on top of a hill and your job is to keep the ball in the top, but that balancing act is hard and the older the application, the harder it is to maintain that balance.

By comparison, a functional architecture is more like a pit of success. The “ball” is not on a hill anymore, it’s in a valley and this seems to happen naturally.

So how does this happen? Well, the answer is pure functions. What are pure functions? Pure functions are functions that don’t have any side effects, for a given input it will always have the same output. But how do you do anything useful without side effects (no input-output)? Well, it’s all about separation. Move all the IO code at the edge of your program and try to keep your business logic in pure functions.

Advantages of pure functions:

  • Easier to test
  • Easier to combine
  • Easier to parallelize
  • Easier to reason about
  • Can be lazy

Ok, pure functions are great. But what about patterns? 


Image source:
https://www.slideshare.net/ScottWlaschin/domain-modeling-made-functional-kandddinsky-2019

Joking aside, there are some core principles when doing FP– functions, types, and composition. Functions are pure and impure (I/O operations), and types will help you define the input and output for your functions. Defining your own types will give you the power of the compiler and that already eliminates a class of bugs at runtime. 

Composition is a simple concept, take two functions where the input of one has the same type as the output of the other and you can compose them. Pretty simple right? If you are familiar with Unix systems then you might be thinking of pipping and the principle is the same.

let add a b = a + b
let times c d = c * d
let add2times3 = add 2 >> times 3

add2times3 2 //answer 12

In this example, you can see the >> composition operator used to combine simple functions in a third more complex function. In this example, however, partial application was used. Partial application is a feature of FP where you take a function with multiple parameters and you can create a new function from it with a default value applied to one or more of its parameters. But why would you do this? What are the advantages? 

The answer is simple. Just like in Unix where you have small apps that do one thing well and you pipe them to create powerful workflows, you do the same here. You create small easy functions that do one thing well and then you compose them to create more powerful functions and then you can compose those to create even more powerful functions. It’s just like playing with Legos, you take simple pieces and then build complex structures out of them.

Pure functions, types, composition. All those things sound great, but how do they translate to functional architecture, and what does it look like?

Doesn’t this look like the Onion architecture or the layered or hexagonal architecture? This is also called the ports and adapters architecture, and the idea behind the ports and adapters architecture is that ports make up the boundaries of an application. A port is something that interacts with the outside world: user interfaces, message queues, databases, files, command-line prompts, etc. While the ports constitute the interface to the rest of the world, adapters translate between the ports and the application model.

Implementing this architecture in an object-oriented programming language is hard (keeping the ball on top of the hill), but in using FP this pattern emerges naturally, as a nice side effect.

To be honest, the latest version of C# got pattern matching and record types and it seems that the trend is to implement more and more functional features into the language. That’s great, but the problem is that the language is getting bloated and developers don’t usually take advantage of these features. Plus it’s really hard to add FP to a language that was not designed for this from the beginning. This is why F# is a more natural fit for this architecture and also has the OOP parts if you’ll ever need them.

If you can, then go full F# on your project. If not (project constraints, no people with FP expertise), then a hybrid approach would be the best course of action, where you write the business stuff in a pure manner, creating an F# library and the impure/IO parts in C#. 

Functional programming may look hard at first, maybe even alien, but once you “get it” you’ll wonder, “Why didn’t I do this before? It’s so simple and elegant!” So give it a shot, write a small functional project and you just might get hooked.

Background Image