Read this article if you want to demystify Functional Programming and understand why and how to start using FP paradigms in C#.
In the title I have written FP and C# but the question you probably wonder is :
What the hell should we want to use FP paradigms in an OO language like C# ?
Here are some arguments to answer this question (from my POV) :
- Write less code and more meaningful one : improved code clarity
- Avoid side effects by using immutability and pure functions : Easier concurrent programming
- Easier testing : it’s really easy to test functions
- Easier debugging
- Funnier to write
OK but why don’t we use a FP language instead of C# ?
In companies it’s really hard to change the habits. We don’t all work in the Sillicon Valley and in my reality the latest versions in the production environments are : Java 8, .NET Framework 4, NodeWhat ?, …
In my world organizations are not yet ready to invest in pure FP languages like F#, Kotlin, Clojure or whatever. They are stuck in legacy code…
To go further on this topic I invite you to watch this video on Functional Core, Imperative Shell : https://discventionstech.wordpress.com/2017/06/30/functional-core-and-imperative-shell/
It’s a pattern that could be really useful to every developers on earth I think.
Functional core (declarative) composed of pure functions (in / out) and easy to test without any mocks.
Imperative shell or reactive container the service logic that you can test with integration tests
Now I have proved the interest of FP in OO language let’s go for a quick lesson.
Let’s demystify Functional Programming (FP)
“In computer science, functional programming is a programming paradigm — a style of building the structure and elements of computer programs — that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data.” — wikipedia
Basically FP is all about functions and immutability :
- Pure functions
- Lambda functions (anonymous)
- High order functions
- Currying & partial application
If you have already looked at it, you have probably read other words like functors, monads and monoïds that can be very scary but in reality concepts are not.
Let’s start by explaining 3 concepts :
We never want to mutate an object in FP we want to avoid side effects so we prefer creating a new one.
If we have mutable objects it could be possible for another function to mutate the object we were working on concurrently.
With immutability it ensures a function remains pure, it makes concurrency much more easy to handle and makes our dev life easier.
A pure function don’t refer to any global state. The same inputs will always get the same output.
Combined with immutable data types mean you can be sure the same inputs will give the same outputs.
Higher order function
A function that does at least one of the following:
- Takes one or more functions as arguments
- Returns a function as its result
Before demonstrating code I highly recommend you to read this great article about FP concepts (I won’t do better for sure) :
Now you know the basics behind FP we can start deep diving into Language-ext.
What is Language-ext ?
According to the creator of the lib Paul Louth : It’s a library that uses and abuses the features of C# to provide a functional-programming ‘base class library’ that, if you squint, can look like extensions to the language itself.
His desire here is to make programming in C# much more reliable and to make the engineer’s inertia flow in the direction of declarative and functional code rather than imperative.
It’s basically “one lib to rule them all” : https://github.com/louthy/language-ext
Language-ext vs LinQ
Yes FP paradigms in C# exist since the introduction of LinQ. Here is a mapping between some language-ext functionalities and their equivalence in LinQ :
Easy immutable record types
Implementing immutable data structures is a nightmare in OO languages (Equals, GetHashCode, IEquatable<A>, IComparer<A>, implementing operators: ==, !=, <, <=, >, >=).
That’s why there is a Record class in language-ext :
No more out parameters
Yes in C# we have out parameters on TryParse for example now it’s finished :
Many functional languages disallow null values, as null-references can introduce hard to find bugs. Option is a type safe alternative to null values. (it also exists in F#).
Avoid nulls by using an Option
An Option<T> can be in one of two states :
some => the presence of a value
none => lack of a value
Match : match down to primitive type
Map: We can match down to a primitive type,or can stay in the elevated types and do logic using map.
- Lambda inside map won’t be invoked if Option is in None state
- Option is a replacement for if statements ie if obj == null
- Working in elevated context to do logic
Lists are functors
We can map them :
What happens when you apply a function to another function? When you use map on a function, you’re just doing function composition :
Monads apply a function that returns a wrapped value to a wrapped value. Monads have a function “bind” to do this.
Suppose half is a function that only works on even numbers.
What if we feed it a wrapped value? This is where bind comes in!
Exception handling made easier the use of try. No more needs of ugly try/catch that pollutes the flow of your code you can describe your pipeline in a really readable way.
Here if something fails, you have the exception in the failure match part. The better part is that Try has a brother called TryAsync that is demonstrated in the real life example.
Memoization is some kind of caching, if you memoize a function, it will be only executed once for a specific input.
Partial application allows you to create new function from an existing one by setting some arguments.
Either represents a value of two types, it is either a left or a right by convention left is the failure case, and right the success case.
Fold vs Reduce (Aggregate in LinQ)
- Fold takes an explicit initial value for the accumulator. The accumulator and result type can differ as the accumulator is provided separately.
- Reduce uses the first element of the input list as the initial accumulator value (just like the Aggregate function). The accumulator and therefore result type must match the list element type.
Real life example
This example demonstrates the kind of usage you could have of language-ext in application services : chaining operations / pipelining, improve error handling, no more redundant checks, …
Start using language-ext in your projects is a really good idea. This lib can make your life much more easier.
You can see it at your first step to go into the FP world, before one day be able to use a language like Clojure or F#.
All the source code is available in my github repository : https://github.com/ythirion/fp-in-csharp-sandbox. I have written this article based on slides I have created for FP workshops : https://github.com/ythirion/fp-in-csharp-sandbox/blob/master/fp-in-cs.pdf
If you want to go further :
- FP in pictures : http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html#just-what-is-a-functor,-really?
- Gitter on language-ext : https://gitter.im/louthy/language-ext
- Doc and examples : https://github.com/louthy/language-ext/issues
- What’s new in C# 8 : https://medium.com/swlh/how-c-8-helps-software-quality-cfa81a18907f
- “functional core, imperative shell” video : https://discventionstech.wordpress.com/2017/06/30/functional-core-and-imperative-shell/
- Domain modeling made functional book from Scott Wlaschin : https://www.amazon.com/Domain-Modeling-Made-Functional-Domain-Driven/dp/1680502549/ref=sr_1_1?crid=FXE4W7BCGETO&keywords=domain+modeling+f%23&qid=1576686914&sprefix=design+thin%2Caps%2C330&sr=8-1