Recently I've had to become familiar with what functional programming is and why exists. At times I think I can fairly accurately describe my introductions to it as nothing short of confusing, often akin to stumbling around in the dark often asking myself why should I use it?
Anyway, these are the first kinds of questions you often have when you're told to use something or that something is to be done a certain way.
So while exploring the ins and outs of what it means to do a thing in a more 'functional way', I decided it investigate exactly what the heck that means. The phrase "being more functional" in style or doing it "the functional way" started to annoy me the more I heard it - A bit like moustaches.
To give you some perspective on my background, I've been developing software all my professional life and never once needed to think about functional programming. In fact, I'd initially wondered if it was just the cool thing to do now. A big part of functional programming I'm told is about using functions(it's in the name!) specifically using them as values as input to other functions and also having functions returned as output from other functions. Er, ok, so what that hasn't told me, is anything about why how I should use them to be productive!
Hopefully, I can start to paint a picture by collecting all these functional ideas throughout this post.
Another idea I've heard was an idea to transform a list into a list of something else. You do this a lot in programming so this seems like a good idea... how is this a functional idea?
Here, for example, you specify the list and a function that does some transforming on the list and you end up with another list - which is the result of that function transforming every element on the original list:
// Passing values into a function :
transformed_list = list.Select(transformerFn); // passes each item in list ie. transformerFn(each_item)
The Select() function operates on the list (which is input to Select() itself because Select() is an extension method which binds to the list and then the Select() function internally uses the provided transformer function to execute on each item in the list, and then producing a transformation of that list. This idea, is quite important as programming in general is all about calling functions that return results and in most cases those results are the transformations that those functions performed. This concept comes into play quite a lot when using LanguageExt which is a C# library that helps you be more 'functional'.
Ok, I passed in a function to another function - I get it, big woop - that's 'functional' programming? In a word no, that's just an idea of many that form up the ideas of functional programming. It turns out there are so many more.
But why use this idea of passing a function into other functions business, what is the benefit? Hopefully that becomes clearer moving forward.
The transformation function will run on the list but Select() organises a new copy of the list to be created with the results of the transformation in it. This is another function idea: the idea that you don't want to modify or have a way of modifying something you're acting on, you just act on it and never change it. The results of you acting on it can be a new list (which has obviously been modified to become that new list) but the original list is not changed - it is read-only. The function Select() is designed to operate with but not change the list. It produces a new list as a result.
The other idea is that, if that you can't change the object you are operating on, neither can anyone else, so now, no one can mess with it if you start using/reading it. If you do want to 'modify' it, you need to generate another new object or copy it make the changes in those objects.
No one will be affected by your changes because no one else knows about your new object yet...unless you pass it into a new function(which hopefully will be designed like Select() and only read or copy your data).
One of many goals and ideas of functional programming styles like this tries to avoid mutation of state. That's what I was told.
Look at this for instance, which is a way to avoid state mutation - its not obvious that the functions are not manipulating the original data but they are designed not to - this is what you'd see: (so can be considered the functional styled approach, this that respect):
Person person = new Person("Stuart", "Mathews")
.Rename("PETER")
.Rename("Chris");
This code creates a new person object and then renames it twice - you don't see how this is done this because you don't know how these functions are written - but that Rename() function is set up in a special way and will copy the original Person it is operating on(modifying a new object/copy) and this new object is renamed and then the next rename works on another copy etc. It's not important how it did it for now, but observe that it can be done and it's not obvious that this is what this function is doing. (how it actually does it is here: Mostly copies of itself
The original person object is never modified although it conceptually modified because we rename() it twice.
This is avoiding changing the state change of any input into your function. This might seem deceptive as you can't tell that it's making a copy of the input data but it is and its a good thing (not mutating input's state) for reasons hopefully I can elaborate on soon.
So when someone says "When a program is written in a functional style from the outset" - it's at least about implementing and devising a strategy to avoid state mutation using functions like I've shown you so far but there are other things that make up functional programming style.
Functions like Select() above that don't change the state of their input and solely use their input for its functionality and thus will always return the same output given the same input. These types of functions are called pure functions. In the next post, Pure functions I talk about the benefits of pure functions in more detail but briefly:
You want pure functions because they avoid state mutation which is one of the things we want in a functional programming style.
Generally speaking, it's always helpful in any programming to aim for the following:
• Modularity (dividing software into reusable components)
• Separation of concerns (each component should only do one thing)
• Layering (high-level components can depend on low-level components, but not vice versa)
• Loose coupling (changes to a component shouldn’t affect components that depend on it)
And pure functions do these automatically as well as avoiding state change and producing reliable results:
- Pure functions return values computed exclusively from arguments that are immutable (can't change)
- So you have to ensure these parameters can't change somehow (for example use immutable parameter types)
- These functions only depend on provided argument input and that input should never change (arguments are immutable)
- Pure functions do not throw exceptions.
- A pure function is easily reasoned about, meaning for any specific input, a specific output is given - if you start throwing exceptions, that output may not be given. We don't like that. It must always be given.
- Pure functions cannot do any IO. This is indeterminate resources which could go wrong and influence an otherwise already predefined outcome (for a particular argument input there is a known result for the function) maybe because the hard drive failed or network linked failed - all these things must not affect the known outcome that the function is supposed to deliver.
- Because its computation only depends on the input parameter and nothing else. (Note: such functions can thus be made static)
Ok so far we've had 4 ideas that constitute functional programming:
- Passing of functions to other functions. (Higher order function aka HOF)
- Pass around functions that operate on data, to other functions that ensure that the resultant operations don't change the original data but produce new data. (like Select())
- Avoid state change by utilising functions that require immutable objects - it makes your functions pure (and all that this entails).
Generally speaking, C# is not inherently a functional programming language so it doesn't make these things easy to do straight out the blocks you need to do them yourself! However, there are things that C# has in the language that helps you to design with immutability in mind.
One example is using get-only properties and read-only fields among other things. You can also use a library like LanguageExt to helps deliver these ideas.
C#’s greatest shortcoming: everything is mutable by default, and the programmer has to put in a substantial amount of effort to achieve immutability
Some common operations on lists/ sequences that are used a lot in functional programming are:
- Mapping: passing each item in the sequence as each argument to the function - resulting in a new sequence as its result (Select)
- Filtering: yields a new sequence that meets the predicate condition (Where)
- Sorting: yields a new sequence according to the key (OrderBy and OrderBydescending)
In all these examples you provide a function as input and it operates on the list somehow as explained above but doesn't modify the list.
Now a little word on Higher-order functions(HOFs).
These often look like "sub-contractors" because they input workers or "contractors" (which are functions) and later on ask/expect those functions to work/run. This is much like a building subcontractor gives work to another contractor(function/person) and tells them when to start working.
HOFs help separation of concerns - you pass in the subcontractor(function) which is defined somewhere else separately but they can be experts on what they do, meaning that HOF doesn't need to perform that work or know how to, they just run the provided function. You've separated a piece of work that the HOF would otherwise have to do by passing that piece of work as a function to the HOF.
Another great thing about HOFs is that they can receive the function, then choose when to run a function, so it can do optional execution of the provided function(contractor) or pass it to some other HOF you might actually ask it to execute and perform that work. This is called lazy evaluation. You evaluate the function.work when you've determined it to be necessary.
This is quite useful when the functions are expensive and can conditionally not be run by way of a choice the HOF makes internally. Passed in functions(contractors) to HOfs are often called callbacks or continuations. In these instances, HOFs apply/run the provided function internally, either immediately, as in the case of Select() or later at a later stage if conditions are met.
Some other useful things to remember when passing around functions as parameters to other HOFs is how to declare a function argument is a function. You'll need to define what are valid kinds of functions that are permitted to be passed to your function.
Here are a few ways to define a function as a parameter.
- Func<T>, Func<T,R>, Func<T,T R> - functions that take parameters T, and return R
- Action, Action<T>, Action<T,T> - operations that don't return anything they just perform 'actions'
I'll end today's post with this quote which is quite revealing and helpful I think.
Function programming focuses on functions and data transformations rather than objects.
But this doesn't mean you don't use objects or object orientated programming, you can and do. Here is a demonstration of an object that is designed to be used in a functional way - merely being designed to avoid state change: Mostly copies of itself
There is a lot more to what it means to be using a functional approach besides passing functions around, using pure functions and coding to avoid state change and hopefully, I'll find more in the coming few days. In the meantime, I think this is a good start to understanding what is functional means.
In the next post, Pure functions I talk about the benefits of pure functions in more detail.