There is a lot of mystery around how languageExt actually works and most of this I think is down to the fact that many of us start from a non-functional background, I think they call this an imperative style of programming or an object orientated style or....whatever.

Functional programming is declarative and less imperative and you can get a geneal idea about this here: Being a bit more functional

LanguageExt is used to deliver some of the benefits in inherently functional language to C#.

What are these benefits you ask? 

  1. Removal of boilerplate code (I'll show you how)
  2. Removal of some classes of errors that are prone to occur without functional ideas
  3. Remove state from code (I'll show you how)
  4. Make code easily parallelizable
  5. Make code produce no side-effects
  6. Same output with the same input
  7. Purely mapping domain → range
  8. Lazy loading ok for evaluating
  9. Can cache result of an input for an already evaluated input value because the function needs not bo run again and we'll always know the result for that input without having to send that input into the function again(if we know the input and the resulting output for that input)
  10. Order of which pure functions is not needed - any order yields the same results.

Let's talk about some concepts.

In maths, you have functions and these are not exactly like c# functions:

y is Add1(x) = x + 1

Exactly 1 input (x) for exactly one output (y) - is a mapping. A mathematical function is a mapping.

You can't have multiple inputs for mathematical functions as you can in c#- it's quite constrained. 

In Mathematical functions, y and x are both immutable and can't change.

if you were to create a c# function that mimicked a maths function, that's a seemingly restrictive function in C# -it would basically mean every single one of your functions could only take on the parameter and that parameter can't change over time and the result of that function can't change over time. Some of these ideas are very useful, however, specifically this requirement or inability for values to change or mutate over time.

A pure function any function(including a maths function) that operates solely on its input ie its parameters (but doesn't change it) and sole produces a result based on its input. Obviously, in a math function, you have exactly one input and exactly one output. In C#, if you wanted to create a pure function(such that the input and output don't/can't change you can do that). That input and that output once defined can't be changed(input can't change when passed in and output can't change once produced by function). Math functions are pure functions, c# function need to be made pure...and I'll show you that later.

The term used is immutability of the input and the output. Immutable can be used/read and operated on but they cannot be written to or modified. 

So if you wrote a c# function that did this, you have a pure function too. 

What pure functions give you are:

  • Same output with the same input - that is, each and every time you pass in input the same output for that specific input is produced.
    • This means its easy to reason about these kinds of functions. More on this later.
    • They dont depend on anything other than the input to produce the function therefore they will never give an alterative result, given the same input
  • No side effects, that is nothing about the input or output changes.
    • This obviously means nothing can't mess up the results later down the road or change them so that others relying on the outputs are affected because they've been changed.
  • Parallelizable, that is running fn(1)=2 or fn(2)=3, in any order or on any thread doesn't affect the results of these functions.
    • That's because these functions are independent of each other, and you can split up the various input into these functions across threads, and each thread will just act on its provided input and produce the respective input and at the end you can take the results of both thread's computation of functions and you have the whole set of results for the whole set of input.
  • Once you know the input and the output for a function, you can cache the input and the output values.
    • if you ever, later on, pass in the same input value, you can use the output value you cached or that input you stored as that function will never produce anything other than the output it produced the first time for the input. This is useful in programming.
  • Lazy loading for evaluating, you can run fn(1)=2 now or fn(1)=2 later, it doesn't matter - it will always result in the same result, ie 2 for input of 1.
    • It doesn't matter at which time(pure functions aren't dependant on time or state or anything that might change those) so you can run them now, or you can pass the function around as a parameter(Higher order functions) and have them executed at a later time and it won't make a difference to the output - the same input produces the same output every time all the time.

You can already see the power of using a pure function in programming, right? 

So some useful benefits of these pure functions if you can get 'em are that you can pass fn(x)=y around as parameters to function, you can put them in lists and you can, as mentioned earlier, evaluate them when you want, now, tomorrow or in five minutes. Generally passing around functions is cool and to this, you need to have a language that supports doing. This feature of a programming language of allowing functions to have other functions as parameters and is called higher-order function) this such as javascript, c# and c/c++(not functional languages), haskell, closure(functional programming languages), to mention a few have this ability to pass functions around is not restricted to passing around pure function(the only functions we've talked about up until now), but any c# functions can be passed around in c# but those that are not pure, don't have the benefits listed above. 

Restricting a C# function to not change its parameters(like ref params or seemingly classes passed in ie. reference types) or in general not being able to have more than one parameter might seem pretty restrictive to get all the benefits mentioned above... Also, the scenario that the function can't produce side effects means you can't call code that could cause side effects:

  1. Code calling IO like querying databases or reading from files or the internet
  2. Code that throws exceptions - as this would mean that for a particular input to a function, you might not get the same result for the input every time. 

That's a pretty dire prospect for a C# application right?

So we understand the benefits of using pure function and using immutable input and output types. But were are aware of the cost of doing this. Let's try and get the best of both worlds. So pure functions don't mutate their input when they use it and they prevent the output they produce from changing. If you don't use pure functions, you can try and maintain this idea of not changing the original input or output using a technique described here: Being a bit more functional but we're digressing a bit because we're talking about writing pure functions which by definition ensure immutability.

Mathematical functions are pure functions by definition and they are representable as Func<IN, OUT> delegates, but as you can see this defines any function that takes in one IN parameter and one OUT result. In math, the input is defined as the domain and the output is the range, in as much as you're producing a range for each item in the domain. Such that this is how you'd represent it in a C# function: Func<domain, range>

So how can you have multiple domain or INs? Theoretically, you can't in pure functions that are modelled like math functions - have one parameter.

Remember mathematical functions only take in one input and produce one output, and we want to maintain this idea for our C# functions but use multiple parameters - sounds impossible, doesn't it?

Side note:

  1. All of this functional programming stuff only really makes if you work with pure functions (no external dependency and input/output to function is immutable)
  2. Exceptions are not liked because they break the mathematical function model (whereby you need to produce an output which is always corresponding to an input…never does this change…exceptions cause this to change )

Generally, this is enough to get you started thinking about why functional programming and pure functions are useful.

You can actually pass multiple parameters to a pure function(woah, but didn't you just say ...) and still keeps the concept of only one input to a function, without any compromises! If your mind is blown by that statement, stick around and I'll show you how. This is done using partial functions. 

This is achieved in two ways,

  1. Pass a single tuple(with multiple items in it) - see we use only one tuple param.
  2. Pass a function as the parameter (this is called currying and I'll explain it later)

Ok keep those tow points in mind (even if they don't make sense)

To thus represent mathematical functions like Func<domain, range> you need a way to be able to pass in multiple parameters as in C# but still maintain this mathematic mapping consistency. Currying allows this:

Func<int, Func<int, Unit» f = x = y ⇒ Print(“{y+x}”)

What this C# declaration is saying is that we have a function f which takes in one int parameter x (as per our requirement) and returns a function as output (as per our requirements). The output is a function which is a function that takes in an int parameter y and adds it to x. So x is available to the 2nd param, ie the function (Func<int, Unit> which only explicitly takes in a int y. The body of the function uses it in the expression y+x and so can access x(and y) and use it sothe function get x for free alongside its already declared y input. Effectively x is baked into Func<int, unit> is returned as output, and when its run, it has x baked into it.

So this is what currying is, it captures the outer input and uses in the output provided. And it allows for passing more than 1 parameter to functions that explicitly take only one parameter.  Currying is a sideline topic for me at the moment and it's not crucial to use it but if you do, you get a pure function with all its benefits.

Perhaps what I'll do is later put an example here but as this is a theoretical discussion of curring and partial function(which uses currying), we'll happily gloss over this for now - perhaps until later.

 You can use functional ideas and notation mentioned above in C# even though C# is not a functional programming language. You can use the C# language.Ext library to simulate many of these ideas and get their benefits. It provides ways to use currying and partial functions and provides ways to prevent state change of input and outputs to functions. It also allows the removing of some boilerplate code such an eliminating for-each and if statements conditionals which are a source of errors.

Read the next part about using Language.Ext  in Functional programming Part 2: Using Language Ext to get an overview of what it offers to C# with respects to functional programming.


Comments powered by CComment

Twitter