Functional thread 6: Functions

[)roi(]

Executive Member
Joined
Apr 15, 2005
Messages
6,282
Reaction score
405
Location
Screen Shot 2018-10-08 at 16.25.06.png
The goal for this thread is a beginner friendly introduction to a basic building block we have in our toolkit, namely Functions.

Note:
All the code in this thread is going to be C#, however the concepts should easily translate to most of the mainstream languages; DM me if you need help converting this.

The areas we'll touch on in this thread will be:
  • Functions versus Methods.
  • Lambda Expressions (Functions)
  • Delegates
  • Type aliases
  • Nested Functions
  • Function Purity
  • Composition
  • Currying / Partial Application
  • Higher Order Functions
  • Pointfree
...and if time permits as a bonus I'll discuss the topic of Exception Handling and how this managed in Functional Programming.

Links to the previous functional threads:
 
Last edited:
Functions vs Methods
The question What is a function? can on the surface appear to have a very obvious answer:
a function is an encapsulation of a unit of code to make it easy to execute multiple times.
Huh? ... but that sounds just like a method?

Functions
So while that summary is acceptable, it's still missing the essence of what a function is; as it applies to both mathematics and functional programming (FP).

In mathematics, a function:
  • always defines its input as a set of arguments
  • always returns an output.
More so its internal computation is based solely on its inputs, devoid of any influence from global state, for example:
C#:
public static string FullName(string name, string surname) {
  return name + " " + surname;
}
The internal computation in the above static function is only computed from the given inputs: name and surname, hence this style of computation is predictable.
Note:
Static methods although residing inside a class are standalone computation units that are neither bound to or affected by the internal state of an object.
This detachment from state; object or otherwise is what makes Functions so different from Methods.


Methods
Methods in contrast are explictly tied to a class instance (object), and can derive their inputs both from their named input arguments as well as global object state. More so they do not need to explictedly return a value because much like their input, their output can also directly affect global object state, for example:
C#:
public class Employee {
  public string Name { get; set; }
  public string Surname { get; set; }

  public Employee(string name, String surname) {
    Name = name;
    Surname = surname;
  }

  public string Fullname() {
    return Name + " " + Surname;
  }

  public void Titlecase() {
    var culture = Thread.CurrentThread.CurrentCulture;
    var textInfo = culture.TextInfo;
    Name = textInfo.ToTitleCase(Name);
    Surname = textInfo.ToTitleCase(Surname);
  }
}
In the first example Fullname is a method that returns a string but does not have any defined inputs; looking at the computation we can of course see that it relies upon both the Name and Surname fields. It is this ability to access and modify global object state that separates a method from a function.

More so the method unlike the function is wholly dependant on the instantiation of class that encapsulates it. i.e. we cannot execute the Fullname method call, until we have instantiated an instance of Employee
C#:
var employee = new Employee("Jack", "Sprat");
var fullname = employee.Fullname(); // "Jack Sprat"
The second example is the Titlecase method which again has no defined inputs, but also has no defined output re void. On top of this it's computation also directly obtains information from global state, outside what its object can provide e.g. CurrentCulture and then uses this to directly mutate the values of the Name and Surname instance fields.

This is the essence of impurity, and is the reason why methods are generally so difficult to test, and why we need mock objects and Dependency Injection.

Function vs Method Summary
It's important to note that functions, static or otherwise similar to method counterparts do not explicitly prevent you from accessing global state like CurrentCulture or pseudo global state e.g. a Singleton object.
So in order to correctly derive function purity is not just a matter of making everything a static function; we actually have to consider how our statement calls derive their results.
...and remember not everything can be pure
; because for example: things that involve IO (input/output):
  • Mouse and touch screen input
  • Reading / writing to disk
  • Rendering the UI to the screen
... will never be pure, and that's ok. We simply choose to focus our attention on the other computations, which can be pure and by definition predictable.

The single most important reasons to chase predictability is ease of testing and the ability to deliver more reliable software; this is achieved solely because of the predictability of computations that are based solely only their define inputs, devoid of the effects of state.
 
Last edited:
Lambda Expressions (Functions)
What Microsoft has termed to be Lambda Expressions is just a term to present the missing features of functions, or more specifically those features that enable C# functions to be treated as first class citizens by the compiler.

First Class Functions
Functions are considered first class when a language supports:
  • passing functions as arguments to other functions
  • returning them as the values from other functions
  • assigning functions to variables
Without first class functions, the majority of functional programming's algebras ("patterns") would not be possible in C#.


Traditional C# function vs. Delegates
Static function
In C# terms, “static” means “relating to the type itself, rather than an instance of the type”. We access a static member using the type name of the class that encapsulates its declaration, as opposed to the instance reference of the class.

Here's a simple example of the original C# syntax to declare a static function that adds two integer values and returns the result.

C#:
int Add(int x, int y) {
  return x + y;
}
Static methods are generally faster to invoke on the call stack than instance methods, and very easy to test and reason about when they are specifically designed for function purity.

Hindley-Milner Typing
If you are new to Functional Programming then you either know nothing about [Hindley-Miller typing](https://en.wikipedia.org/wiki/Hindley–Milner_type_system) or are still unaccustomed to type signatures in general. Types are the meta language that enables succint and effective communication, and for the most part the common standard to record this is Hindley-Milner.

In Haskell which uses Hindley-Milner, the previous Add function would be declared as follows:

Add :: (int, int) -> int

Where int is the type of the arguments and the result (the final int); basically the Add function written in this form reads as a function that takes two int's and return an int. Being purely a type signature it does not prescribe the internal computation; so this type signature can equally apply to Multiply, Subtract, Divide, or essentially any binary operation that requires two integers and returns an integer.

Note: we will further expand on our knowledge of Hindley-Miller typing as we progress with this thread.

Delegates
Delegates are essentially the C# way to specify the type signature of a function in a similar way to Hindley-Miller; it is a pure function declaration, that does not place any restriction on the actual implementation beyond the stipulation of the argument and result types.

If we look at our previous static Addfunction:

C#:
int Add(int x, int y) {
  return x + y;
}
The equivalent delegate typing for this would be:

C#:
delegate int BinaryOp(int x, int y);
Note:
The choice of the name BinaryOp is an indication that this delegate is not limited to only addition as either the name AddOp or AddDelegate would falsely imply.

Implementation of a Delegate type
To complete the declaration of the Add function by providing the implementation, we have a number of way to do this owing to the flexibility of the syntax as it developed with the each .Net release, for example:

C#:
BinaryOp Add = delegate(int x, int y) => { return x + y; };
... and similarly any one of these alternatives is also valid syntax.

C#:
BinaryOp Add = delegate(x, y) { return x + y; };
BinaryOp Add = (int x, int y) => x + y;
BinaryOp Add = (x, y) => x + y;
the final one's syntax is what today is more commonly referred to as a lambda.


Alternative and more flexible syntax for functions
C# also allows us to define the Add function in a format that on the surface appears to be yet another syntactic alternative; one that appears to not require the use of a delegate type.

C#:
Func<int, int, int> Add = (x, y) => a + b;
...that conclusion would however not be correct, because Func is a delegate type that is defined as part of the .Net standard library, for example:

C#:
public delegate TResult Func<in T1,in T2,out TResult>(T1 arg1, T2 arg2);
... a generic delegate type with three variables (T1, T2, TResult) that is explicitly used to provide a more consistent and type flexible way to assign function signatures. The .Net standard library provides for the specification of functions from an arity of 1 to 16, and similarly provides a number set of generic Action delegate types for functions that return a void, and a generic Predicate delegate for single arity bool returning functions.
Note:
  • For the most FP tends to steer clear of Action delegate functions, primarily because they don't return a result.
  • The Predicate delegate is also rarely used because that type signature can easily be accommodated by the Func delegate type, for example: `Func<int, bool> GreaterThan....`
 
Last edited:
...so this type signature can equally apply to Multiply, Subtract, Divide, or essentially any binary operation that requires two integers and returns an integer...

Not to detract from this excellent thread, but Divide shouldn't belong there... a returned int is not guaranteed. Also, zero is included by definition, leading to potential problems.

Any case, its been a week, when is the next post? :D
 
Not to detract from this excellent thread, but Divide shouldn't belong there... a returned int is not guaranteed. Also, zero is included by definition, leading to potential problems.
Glad you like the thread.

As for the that comment; it was related to the HM binary type signature:
Add :: (int, int) -> int

If you consider that this is only a specification of the function's type i.e. defining its input and output; then there is no restriction placed on the specifics of the internal computation; which means that this type signature is match for any binary type operation involving integers, including division, for example:
C#:
Func<int, int, int> Divide = (a, b) => a / b;
var result1 = Divide(2, 3); // 0
var result2 = Divide(5, 2); // 2
var result3 = Divide(0, 2); // 0
var result4 = Divide(2, 0); // DivideByZeroException
The 4th example dividing by 0 is what I think you were referring to.

Consider that managing this DivideByZeroException is part of the internal computation and the adding an assert or exception handling doesn't change its type signature, for example this implementation still has the same type signature:
C#:
Func<int, int, int> Divide = (a, b) => {
  try {
    return a / b;
  } catch (Exception e) {
    return -1;
  }
};

var result1 = Divide(2, 3); // 0
var result2 = Divide(5, 2); // 2
var result3 = Divide(0, 2); // 0
var result4 = Divide(2, 0); // -1 (Exception management included as part of implementation)


Some Extra Information
Plus if we generalise this binary type signature by adding generic placeholder variables like a, for example:
BinaryOp :: (a, a) -> a
...we end up with a binary operator type signature that covers a far greater spectrum of types, because a is a generic placeholder variable for any type. Which means that this generic type BinaryOp is also relevant for Add, Multiply, Subtract and Divide.

Further more if we extend this idea by applying a second generic placeholder, for example:
ReducerOp :: (a, b) -> a
... we up with a generic binary operation type that not only supports the basic arithmetic operators (+, -, *, /), but also more complex patterns like the State / Action pattern that is used e.g. by Redux and also Linq's Aggregate static method, ...

Any case, its been a week, when is the next post? :D
Sorry, it's been a busy week, and I'm now out of the country for the next 2 weeks; however I was planning to set aside some time over the coming weekend to write and publish the next post, so look out for this on the weekend.

The areas that should be covered include:
  • More advanced Func delegate usage
  • More complex HM type signatures.
  • Currying / Partial Application
  • Tuples
  • Higher Order Functions like Linq's Aggregate, Select, ...
I may decide to postpone the higher functions to a separate post, because it's quite an extensive topic to cover; however I'll probably at least touch on Linq's Aggregate because that is essentially a continuation of the generic binary operation I referred to above as the ReducerOp type.
 
Last edited:
Fair enough. In fact, blame my brain for only caring about the implementation :p

Looking forward to the next one
 
The Flexibility of the Lambda Form
As we touched on in the previous post; C#'s function form which flies under the banner of lambda hinges on the use of the generic delegates to provide us with:
  • a far more fine grained way to specify our function's type signatures.
  • a way to stage our function's input using partial application and currying.


Single Argument Dilemma
In mathematics, or more specifically calculus; functions only have a single input argument, and always return a value, and this strict specification underpins the techniques and algebras that are exercised in functional programming.

function.png

...but how can we do anything useful if functions are restricted to a single input argument?

To answer this, let's go back to the definition for our binary Add function:
C#:
static int Add(int a, int b) {
  return a + b;
}
... obviously this has two input arguments a and b; so how does calculus accomplish this simple computation with only 1 argument?

If we recall the Add function's type signature is translated to Hindley-Milner as follows:
C#:
Add :: (int, int) -> int
To understand how the above Hindley-Milner type signature can equate with a single argument requires a bit of a side track to Tuples

Side track: Tuples
In mathematics, a tuple is a fixed-size collection of values, where each value can have a different type. This distinguishes them from a list, which can have any length, but whose elements must all have the same type.

Prior to C# 7, we used the following syntax to define a tuple with two integer elements:
C#:
Tuple<int, int> tuple = Tuple.Create(1, 2);
...and as of C# 7, we can achieve the same with its new shorter Tuple syntax that aligns well with Hindley-Milner typing:
C#:
var tuple = (1, 2);
...and if we mouse over our tuple variable in Visual Studio we see its type has been defined as:
C#:
(int, int) tuple
... i.e. a match to our Hindley-Milner signature for Add; meaning we could think of our two input arguments as a single tuple argument with two integer elements.
Note:
Tuples are represented using the same syntax in Haskell i.e. elements wrapped in rounded brackets: (int, int), and this is an alternative way to specify multiple arguments in functional programming.

So why did we touch on Tuples?
... because whilst we would typically define our Add function in C# with the following Hindley-Milner type signature ...
C#:
Add :: (int, int) -> int
... this is not the standard way to define this in Calculus or Haskell; it's far more typical for the Add function's type signature in Hindley-Milner to look like this:
C#:
Add :: int -> int -> int

huh?
... that looks a bit strange doesn't it; nevermind we're going to step through:
  • what that type signature implies
  • what we must do to convert our delegate signatures to match this format
  • ...more importantly why would we need this?

Currying: Overview
what that type signature implies?

To make sense of the following type signature:
C#:
Add :: int -> int -> int
... we first need to become familiar with the concept of currying.

Wikipedia
In mathematics and computer science, currying is a technique of translating a function with multiple arguments into a sequence of interlinked function calls, each with a single argument. Currying is useful in both practical and theoretical settings. In functional programming languages, and many others, it provides a way of automatically managing how arguments are passed to functions and exceptions. In theoretical computer science, it provides a way to study functions with multiple arguments in simpler theoretical models which provide only one argument.

By currying a multiple parameter function we can essentially exploit mathematical techniques in our code, techniques that for the most part hinge off the mathematical concept of a function.

To make sense of currying in C#, let's first walk through the process converting our Add function:
C#:
int Add(int a, int b) {
  return a + b;
}
The equivalent lambda form of our Addfunction, using the generic Func delegate types is:
C#:
Func<int, int, int> Add = (a, b) => a + b;
However this lambda function, like the original function definition still has the following Hindley-Milner type signature:
C#:
Add :: (int, int) -> int

Currying: Conversion
what we must do to convert our delegate signatures to match this format?


Ok so let's now curry this function; essentially turning it into a sequence of interlinked function calls. Let's keep in mind the objective that each function call only has single argument; our original has two, so we need separate that as follows:
  • outer function takes a single int and then returns a function Func<int, int>
  • Func<int, int> is a function that again takes a single int, and then finally returns our output int.
C#:
Func<int, Func<int, int>> Add = (a) => (b) => a + b;
The right hand side of our curried function format needs to match up to what is happening on the left hand side (the delegate type), basically we have separated our input arguments a and b with the lambda operator => and similarly leading onto our implemenation => a + b; .

Note:

If we just focus in on the right side of our function, and substitute in the types for the variables:
C#:
(a) => (b) => a + b;

// substituting type for variables
(int) => (int) -> int + int;

// which can be simplified to:
int => int => int
We can hopefully more clearly see the relationship between the C# curried format versus that of the Hindley-Milner type signature:
C#:
Add :: int -> int -> int

So how do we execute a curried function?
We call our curried Add function no different to any other function call; a single integer value of 2.
C#:
var Add2 = Add(2);
The result of Add(2) happens to be the next function call in the curried sequence. Essentially Add2 at this point is the same as the following lambda function definition:
C#:
Func<int, int> Add2 = (b) => 2 + b;
...except that variable a has been replaced with 2 ... now let's pass in the second argument:
C#:
var result = Add2(3); // which then computes 2 + 3 ==> 5

Computing a curried function in a single step
We can also perform this computation in a single step as follows:
C#:
var result = Add(2)(3); // 5
The double rounded brackets are btw a good indicator that we are dealing with a curried function.

Alternative way to specify our curried Add function:
An alternative way to represent our curried Add function is to use a combination of the lambda style mixed with the traditional method of stipulating a function's argument:
C#:
Func<int, int> Add(int a) {
  return (b) => a + b;
}
This format should hopefully help to clear up some ambiguity of how the curried form works; the curried Add essentially expects one integer argument a and then returns a lambda function Func<int, int>, a function which requires a single argument b and returns an int result representing a + b.
 
Last edited:
A simpler and more reusable way to curry
The DRY principle (i.e. do not repeat yourself) is a well practice principle in function programming, and tedious tasks like having to rewrite all our functions in a curried format would simply not be acceptable or practical. So we need to have a way to curry a function without having to change the way in which we specify it.

Curry helper methods to the rescue
The process of currying a method is essentially a repetitive task that can be simplified with a few helper static methods. To curry for example an existing binary integer function we need a perform the following conversion; expressed as Hindley-Milner type signature:
C#:
curry :: ((int, int) -> int) -> (int -> int -> int)
Here's the same concept expressed as a generic function in C#:
C#:
public static class ƒ {
  public static Func<A, Func<B, C>> curry<A, B, C>(this Func<A, B, C> func) {
    return a => b => func(a, b);
  }
}
The curry function is defined also using extension method syntax i.e. this keyword, which means we could use it both as a standard static function call, and as an extension method call, for example:
C#:
// Traditional Add function
static int Add(int a, int b) {
  return a + b;
}

var curried_Add = ƒ.curry<int, int, int>(Add); // using static function call

// Lambda add function
Func<int, int, int> add = (a, b) => a + b;

var curried_add = add.curry();  // using extension method syntax

In the above examples; you should notice that in the second example we could curry a lambda function quite simply, by using the .curry() extension method syntax, and the C# compiler was also able to infer the type signature for us.

In the first example, using the traditional static function syntax; we had to use the curry helper as a static function call, and we also had to provide the types because unfortunately the C# compiler today still can't automatically relate the syntax of a traditional function with its lambda equivalent; and for similar reasons we also could not use curry as an extension method in this context.

In both cases the type for the curried_Add and curried_add variables is Func<int, Func<int, int>>:

Note:
We can also create curry helper functions for the other arities that we would typically encounter in our codebase, for example:
C#:
// function ƒ synbol
// macOS: alt-f
// Windows: ctrl-alt f
public static class ƒ {
  #region Currying
  public static Func<A, Func<B, C>> curry<A, B, C>(this Func<A, B, C> func) {
    return a => b => func(a, b);
  }
  public static Func<A, Func<B, Func<C, D>>> curry<A, B, C, D>(this Func<A, B, C, D> func) {
    return a => b => c => func(a, b, c);
  }
  public static Func<A, Func<B, Func<C, Func<D, E>>>> curry<A, B, C, D, E>(this Func<A, B, C, D, E> func) {
    return a => b => c => d => func(a, b, c, d);
  }

  // ... greater arities
  #endregion
}

Currying: Why does it matter?
...more importantly why would we need this?

In mathematics and functional programming, one of the key precepts for functions, is the ability to create a new function that is the composition of other functions, for example:
C#:
var result = Math.Cos(Math.Sin(1.2)); // 0,596198168589484
result in the above example is a computation that is the combined result of the Math.Sin(1.2) passed into Math.Cos as it's input parameter. This is essentially function composition.

Now if we consider that the average function only a returns a single value; we should be able to acknowledge that composition is far easier to accomplish with functions when the input arity always is match to the output. i.e. one parameter in, and one result out.

For example, it is impossible to compose these two functions together:
C#:
int Add(int a, int b) {
  return a + b;
}

int Multiply(int a, int b) {
  return a * b;
}
Because both expect two input parameters, and only return a single output value, and like mismatched puzzle pieces; these are simply not composable.

So, why is composition so important?
Ok so before we explore how Currying can be used to overcome miss matched function arities; let's first reason about why composition is such an important concept, and why it's something we'd want to take advantage of in our code.
In the same way we tend to prefer a finished product as opposed to a bag of electronic bits that were used during its construction...

Composition is simply a way to build something that is more valuable than just the sum of its parts:
  • it's a simple conceptual affordance that not only allows us to more easily embrace software engineering's DRY principle. i.e. we avoid unnecessary duplication
  • ... but we also end up with a compositional wrapper that can help simplify the use of more complex, under the wrapper, computations.

Composition
We've touched on function composition a few of times in passing now, but what does it actually mean?

Wikipedia
In computer science, function composition (not to be confused with object composition) is an act or mechanism to combine simple functions to build more complicated ones. Like the usual composition of functions in mathematics, the result of each function is passed as the argument of the next, and the result of the last one is the result of the whole.

The most typical type of function composition is with two unary functions:
C#:
compose :: (a -> b) -> (b -> c) -> (a -> c)
To make function composition more easier to use, let's define a helper static extension method for this.
C#:
public static class ƒ {
  public static Func<A, C> compose<A, B, C>(this Func<A, B> lhs, Func<B, C> rhs) {
    return a => rhs(lhs(a));
  }
}
Ok so let's have a look at the following two binary functions, how can we compose them when their input arities are incompatible with their output?
C#:
Func<int, int, int> add = (a, b) => a + b;
Func<int, int, int> multiply = (a, b) => a * b;
Composing these two functions will require us to fix at least one of the arguments of add:
C#:
var add_multiply = add.curry()(2).compose(multiply.curry()); // 1st Add argument is 2
var result = add_multiply(4)(6); // 36

// which is the equivalent of :
var result2 = multiply(add(2, 4), 6); // 36
We achieve the fixing of only one of the arguments of add by first currying it, then passing in our fixed argument value of 2, which leaves us with a unary function. We then compose this unary function with the curried version of multiply; because multiply is a binary function; expecting two arguments, but we only have one, so by currying multiply; we are able to compose our unary function with the curried version of multiply.

It's a bit of syntactic brain teaser, but it should hopefully start to give an idea of how currying allows us to manipulate the normal computation flow. More so, currying a function will allow us to exploit a number of techniques that derive from mathematics.

Note:
The concept of currying and composition is likely to be still a bit muddled at this point, but this should hopefully clear up later on in this thread

Partial Application
Partial application and currying are distinctly different techniques that happen to share some similarities with the type signature and implementation. The idea of partial application is that if you fix the first n arguments of the function, you get back a function that only requires the remaining arguments; the partial applied arguments are essentially encapsulated in the "new" function in a style similar to dependency injection.

Similar to composition and currying, we can define a set of helper static methods to make use of partial function application easier, for example:
C#:
public static class ƒ {
  public static Func<B, C> partial<A, B, C>(this Func<A, B, C> func, A a) {
    return b => func(a, b);
  }
}
Here's an example of using it to fix the 1st argument of the add function:
C#:
var partialAdd = add.partial(2);
var result1 = partialAdd(3); // 5
var result2 = partialAdd(4); // 6
partialAdd is a unary function formed by fixing the 1st argument of add to the value of 2.

Note:
We can also create partial helper functions for the other arities that we would typically encounter in our code base, for example:

C#:
public static class ƒ {
  #region Partial Application
  public static Func<B, C> partial<A, B, C>(this Func<A, B, C> func, A a) {
    return b => func(a, b);
  }
  public static Func<B, C, D> partial<A, B, C, D>(this Func<A, B, C, D> func, A a) {
    return (b, c) => func(a, b, c);
  }
  public static Func<C, D> partial<A, B, C, D>(this Func<A, B, C, D> func, A a, B b) {
    return c => func(a, b, c);
  }

  // ... greater arities
  #endregion
}

...and that's it for this article. I've specifically avoided high order functions, because this post is already quite lengthy.

Next Post
The next post will focus on higher order functions, including the recreation of a few of C#'s Linq extension methods, ...with a twist.
 
Last edited:
Side Note:
Keep in mind that many of the concepts conveyed in this thread are already exploited in the C# functional library like Language-Ext; which means you don't have to recreate many of helper functions yourself, and you can tap into by adding the nuget package for Language-Ext Core, for example:
  • currying
  • composition
  • partial application

The reason why I am presenting the topic in detail is to help improve your overall understanding of these concepts; and also because some of these concepts won't necessarily be part of Language-Ext Core, in which case you'd have to exploit these on your own in a codebase.
 
@[)roi(] thanks for these threads, I haven't been able to get around to 5 (and now 6) due to time constraints, but they're nicely informative and nicely explained.
 
I had a bit more time spare last night and this evening; so here's a bonus post for this week. It's of course a continuation from the last post; hope you like it.

Binary functions
In mathematics, a binary function is a function that takes two input arguments, as opposed to a binary operation where both the input and output arguments are of the same type. Many mathematical and functional can be generalised as binary functions, a few of which we'll cover in post.

Some binary function examples
Here's some examples of binary functions, that also are binary operations.

C#:
public static class Binary {
  public static int Add(int a, int e) {
    return a + e;
  }

  public static int Multiply(int a, int e) {
    return a * e;
  }

  public static int Subtract(int a, int e) {
    return a - e;
  }

  public static int Divide(int a, int e) {
    return a / e;
  }
}

Use Examples
C#:
var r1 = Binary.Add(4, 2); // 6
var r2 = binary.Multiply(4, 2); // 8
var r2 = binary.Subtract(4, 2); // 2
var r2 = binary.Divide(4, 2); // 2
The above four binary functions also happen to share the same Hindley-Milner type signature:

C#:
       Add :: (int, int) -> int
 Multiply :: (int, int) -> int
Subtract :: (int, int) -> int
    Divide :: (int, int) -> int
...and the only real difference aside from the function name is the binary operator +, *, -, / used in its computation.

Code repetition
This type of code whilst neither a lot or bad; still has incorporated repetition, something which we can be abstracted away, and this is an area where higher order functions can assist.
 
Higher-order Functions
A function is considered to be a higher order if it does at least one of the following:
  • takes one or more functions as arguments.
  • returns a function as its result.
A higher order function essentially creates a far more flexible unit of computation; one whose internal operation is not rigidly set at time the function is created. To make sense of this concept, we'll start by defining a single Higher Order function that can duplicate the above arithmetic computations and more.

Binary Higher Order
Let's define a binary higher order function that allows us to inject our desired computation as an argument; as you can hopefully see our function has three arguments:
  • two integer values; for simplicity sake, named a and e
  • a third lambda delegate type argument f that will hold our desired a binary computation.
C#:
public static class Binary {
  public static int Op(int a, int e, Func<int, int, int> f) {
    return f(a, e);
  }
}

Use examples
Because of the fact that the f argument can be replaced by any binary computation, we can recreate the previous usage examples as follows:
C#:
var r1 = Binary.Op(4, 2, (a, e) => a + e); // 6
var r2 = Binary.Op(4, 2, (a, e) => a * e); // 8
var r3 = Binary.Op(4, 2, (a, e) => a / e); // 2
In the above example, also note that we had to repeat providing our input values 3 times, but with a simple combination of partial function application principles we can avoid this, for example:.
C#:
public static class Binary {
  public static Func<Func<int, int, int>, int> Op(this int a, int e) {
    return f => f(a, e);
  }
}
This essentially curries the f part of our function, which mean we can separately apply the input values, and then compute the results for each computations, for example:
C#:
var OpWithInput = Binary.Op(4, 2);

// ...and then compute 3 binary operations
OpWithInput((a, e) => a + e); // 6
OpWithInput((a, e) => a * e); // 8
OpWithInput((a, e) => a / e); // 2
However this approach is the opposite of what is typically done in Functional Programming; it far more to set the computation and curry the input i.e. the other way around. Before we demostrate that for sake of expediency I'm also going to convert our Higher Function to generics; so that we are no longer limited to computations with only Integers, for example:
C#:
public static class Binary {
  public static Func<A, A, A> Op<A>(this Func<A, A, A> f) {
    return (a, e) => f(a, e);
  }
}
As you can hopefully see we have a function name Op that used a generic placeholder A to specify the type used for a particular binary computation; secondly the binary function is at the forefront, meaning we can fix the computation, and the a later stage provide the curried input arguments.

Function examples:
Ok now let's recreate our original 4 binary functions using this new generic Binary.Op higher order function, and also a new double typed function to calculate the area of a rectangle.
C#:
var add = Binary.Op<int>((a, e) => a + e);
var multiply = Binary.Op<int>((a, e) => a * e);
var subtract = Binary.Op<int>((a, e) => a - e);
var divide = Binary.Op<int>((a, e) => a / e);
var rectDiagonal = Binary.Op<double>((l, w) => Math.Sqrt(w * w + l * l));


Usage examples:
...and finally let's apply some values to each of these binary abstractions:
C#:
var result_add = add(4, 2); // 6
var result_multiply = multiply(4, 2); // 8
var result_subtract = subtract(4, 2); // 2
var result_divide = divide(4, 2); // 2
var result_diagonal = rectDiagonal(4d, 2d); // 2,82842712474619
I hope you can agree at this stage that not only does this approach result in less code, but also substantially increases the versatility of a function.


Summary
As we seen, a binary function is a computation involving two input arguments, returning a single result. Internally the computation combines two inputs values through some type of operation. You may have also noticed that I have tried to always use the following two variables to represent these two inputs....
C#:
(a, e) => // internal binary computation over `a` & `e`
... and there's a reason that; 1st off let's expand a bit more on the meaning these two variable placeholders:
C#:
/*
  a: accumulator
  e: element
*/
The accumulator can be thought as the current tally for our computation, and the element is the next value for processing; essentially the accumulator is accumulating the result of our binary computation whilst there more elements to be processedl; at the end of the processing; the accumulator is essentially our result.

Obviously in our simple Binary Operation examples we only ever had two input elements: 4 and 2. Which means that in this scenario, it's all going to appear a bit pointless, ... because C# provides basic operators for most of this anyway.

However you should not so easily dismiss this general concept, because this binary function concept is an important building block in Functional Programming, and one that we specifically use to construct many far more complex algebras, for example:
  • Functor
  • Applicative Functor
  • Monad.
Functional Algebras

Canvas 1.png

This binary operation with an accumulator and element is more typically referred as a Fold or an it's algebra name Foldable, and like most things in FP it has a foundation in mathematics, more specifically: Category Theory, where it's classified as a Catamorphism.

Whew, .... yes it's all a bit of a mouth full; so for now let's just call it a Fold.
 
Last edited:
Fold
Wikipedia
In functional programming, fold (also termed reduce, accumulate, aggregate, compress, or inject) refers to a family of higher-order functions that analyze a recursive data structure and through use of a given combining operation, recombine the results by recursively processing its constituent parts, building up a return value.

A Fold has a binary function type signature that look similar to this:
C#:
BinaryOp :: (a, a) -> a
However the above type signature, limits the flexibility of our Fold to a computation over a single type i.e. an endomorphism; but if we allow one of the input variables to have a different generic type, we can substantially increase the flexibility of our binary function, by now permitting transformation between our input and output types.
C#:
Fold :: (a, b) -> a
But before we dive straight into the implementation of a Fold; let's first look at some code examples involving firstly the sum of a List of elements, followed by the product of those same elements.

Calculate the sum of the elements
C#:
var input = new List<int> { 1, 2, 3, 4, 5 };
var sum = 0; // intial value
foreach (int element in input) {
  sum += element;
}
var result = sum; // 15

Calculate the product of the elements
C#:
var input = new List<int> { 1, 2, 3, 4, 5 };
var product = 1; // intial value
foreach (int element in input) {
  sum *= element;
}
var result = product; // 120
The above two code examples, aside from variable name differences, again have quite a bit of code duplication; essentially the only real differences between the two is the binary operator i.e. +, *, and the initial starting value 0 or 1.

This initial value has a specific name in mathematics; it's called the identity element, and as you should have seen from the above code; the value is different depending on the operation and the type of element.
In mathematics, an identity element or neutral element is a special type of element of a set with respect to a binary operation on that set, which leaves other elements unchanged when combined with them.
So as you can imagine if we were to encapsulate the above two processes in a higher order function, we would also need to allow for the provision of an identity element argument.

To summarise our function would need the following:
  • An input set of elements, for example, a list of integer values.
  • An identity element that is both neutral for the element type and the computed operation.
  • A binary Fold function, for example +, *, ..

Fold Higher Order Function
Ok let's define a Fold higher order function for a List of any type:
C#:
public static class ListExtensions {
  public static U Fold<T, U>(this List<T> input, U identity, Func<U, T, U> f) {
    var accumulator = identity;
    foreach (T element in input) {
      accumulator = f(accumulator, element);
    }
    return accumulator;
  }
}
First off the choice of generic variable T and U bears no significance aside from the fact that they just indicate that our Fold might return different type to that of it's input. Now if you compare this code, you should see many similarities with the code for the sum and product examples; except for of course the function's generic signature and the three arguments: input, identity and the binary fold function.

Fold usage examples:
Ok so let's now recreate the same outcome as before, by using our Fold higher order function to accomplish this.

Sum
C#:
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var result = numbers.Fold(0, (a, e) => a + e); // 15

Product
C#:
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var result = numbers.Fold(1, (a, e) => a * e); // 120

Convert to String and Concatenate
C#:
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var result = numbers.Fold("", (a, e) => a + e.ToString()); // "12345"
So hopefully you can appreciate that this little algebra has not only done away with our need for a loop to compute binary aggregations over a list of elements, but also more clearly presented the purpose for the binary computation; which btw is more formally referred to as Declarative Style of Programming.
In computer science, declarative programming is a programming paradigm—a style of building the structure and elements of computer programs—that expresses the logic of a computation without describing its control flow.
... but Fold can do so much more; in the next post we'll see example of how this simple algebra is used to construct some of the more powerful concepts in Functional Programming, for example:
  • Flatten
  • Filter
  • Functor
  • Monad
  • Applicative Functor
 
Last edited:
Anyway that's it for this week... the next post may be a bit delayed, as I have a busy remaining week and weeks ahead. If you have any questions or need help translating the code to your language of choice; then just send me a PM.
 
Review of Fold
Before we look at flattening, let's first revisit the Fold higher order function and turn it into a version that works for all types that implement the IEnumerable interface.
C#:
public static B Fold<A, B>(this IEnumerable<A> input, B identity, Func<B, A, B> f) {
  var accumulator = identity;
  foreach (T element in input) {
    accumulator = f(accumulator, element);
  }
  return accumulator;
}
So what changed?

We simply changed this extension method type...
C#:
this List<A> input
...to this...
Code:
this IEnumerable<A> input
This very simple type change allows our Fold higher order function to now operate over the same scope of collection types that are supported by Linq , for example:
  • Array
  • Dictionary
  • Queue
  • Stack
  • ...
...or more simply all the container types that conform to the IEnumerable interface.
Flatten
Flattening is a simple but vital operation when dealing with container types, inevitably we'll have some computations that'll end up nesting containers; a container wrapped in a container ... for example:
C#:
var xss = new List<List<int>> {
  new List<int> { 1, 2 },
  new List<int> { 3, 4 },
  new List<int> { 5, 6 }
};
This is an example of List embedded in another List; ok but what if we wanted that to be flattened to a single list, for example:
C#:
var r = new List<int> { 1, 2, 3, 4, 5, 6 };
An imperative way to flatten this is as follows:
C#:
public static List<int> Flatten(List<List<int>> xss) {
  var result = new List<int>();
  xss.ForEach(xs => result.AddRange(xs));
  return result;
}

var r = Flatten(xss); // { 1, 2, 3, 4, 5, 6 }
Not too complicated and certainly not too long; functionally the same can be achieved with Fold, for example:
C#:
public static IEnumerable<A> Flatten<A>(this IEnumerable<IEnumerable<A>> ts) {
  return ts.Fold(new List<A>(), (a, e) => { a.AddRange(e); return a; });
}

var r = xss.Flatten(); // equivalent of flatten.
Essentially our identity value is a new List<A>(), and our Fold algebra already has the ability to iterate over the elements in our outer List; we then in the same way as our imperative example add the collection of the inner elements to our accumulator a, which started off as new List<A>(), our identity value.

Easy peasy, again no need to write a new loop when we have an existing algebra like Fold that can perform this type of operation.
 
Last edited:
Map
Wikipedia
In many programming languages, map is the name of a higher-order function that applies a given function to each element of a functor, e.g. a list, returning a list of results in the same order. It is often called apply-to-all when considered in functional form.

The concept of a map is not limited to lists: it works for sequential containers, tree-like containers, or even abstract containers such as futures and promises.

Another way to think of map is as a mapping between two sets, for example:
Map.png
Or alternatively we can think of it as a type of switch function in C# that maps between the input value and the output value, for example:
C#:
public static int increment(int x) {
  switch (x) {
    case 0: return 1;
    case 1: return 2;
    case 2: return 3;
    case 3: return 4;
      ...
    case int.MaxValue - 1: return int.MaxValue;
  }
}
In functional programming the Functor's map computation is more associated with a computation over the elements of a container type, for example:

Incrementing the elements of a container by 1
C#:
var xs = new List<int> { 1, 2, 3, 4, 5, 6 };

public static List<int> Increment(List<int> xs) {
  var result = new List<int>();
  xs.ForEach(x => { result.Add(x + 1); });
  return result;
}

var r = Increment(xs); // { 2, 3, 4, 5, 6, 7 }
This again is not a very complicated process, and certainly wouldn't raise any red flags when searching for code to refactor, but that's the point, a lot of the code we write is made up of many small repetitions that we wouldn't immediately flag as such.

The above computation can again be achieved with Fold, for example:
C#:
public static IEnumerable<B> Map<A, B>(this IEnumerable<A> xs, Func<A, B> fn) {
  return xs.Fold(new List<B>(), (a, e) => { a.Add(fn(e)); return a; });
}

var r = xs.Map(x => x + 1);  // equivalent of increment
Similar to Flatten our identity value is a new List<A>(), and our Fold algebra, as stated previously, already has the ability to iterate over the elements in our outer List; we then, in the similar way to our imperative example, apply a unary computation Func<A, B> f to each of our inner elements and add this to our accumulator a, which as we mentioned started off as new List<A>(), our identity value.

This function, we've called Map is a formal algebra in mathematics, called a Functor, and has the following Hindley Milner type signature.
C#:
Map :: Functor f -> (a -> b) -> f a -> f b
This is a new form of HM signature; so let's start off first by understanding what this means.

Functor f informs us that the generic variable f conforms to a Functor; in C# you can think of this as a container type like List that conforms to the interface IEnumerable. This conformance however is a bit broader than that typically covered by an C# interface in that it not only stipulates the methods that a conforming type must support, but also requires conformance to the rules (or axioms) of the Functor algebra.

The remainder of the HM type signature describes the Map function; a unary computation (a -> b) from generic type a to b; where we are required to provide an f a as input; which you can think of in C# as a List<A> i.e. a container conforming to functor with elements of type a; for example:
C#:
var xs = new List<int> { 1, 2, 3, 4, 5 };
Here we have a container type List; containing a type int; in generic terms this would be our a. Next we can see that our HM type signature returns a f b; a functor of type b; a different generic type to a; which means that we can transform the type of the element stored in our container, for example:
C#:
var xs = new List<int> { 1, 2, 3, 4, 5 };
var r = xs.Map(x => x.ToString()); // { "1", "2, "3", "4", "5" }
The Map extension method will work with List because it conforms to the IEnumerable interface, so inside our Map higher order function we pass in a lambda x => x.ToString() to transform our int elements to a string; in this case our generic a is an int and our generic b is a string.
Note:
The generic types a and b can however be the same type, for example a computation that increments our int elements would still return an int; the fact that we have two generic variables for the input and output elements does not mean we have to transform the type of our elements; it can remain the same type.

Functor's Axioms
As we said; Functor conformance in the HM type signature is not quite exactly the same as conformance to a C# interface. The key difference is that the conformance to an algebra like many things in mathematics also requires conformance to a set of rules, or axioms, that are there to ensure that our particular implementation of the Map is in conformance with the expected behaviour for a Covariant Functor, namely:

Identity
xs.Map(x => x) is equivalent to xs
C#:
var xs = new List<int> { 1, 2, 3, 4, 5 };
var r = xs.Map(x => x);  // { 1, 2, 3, 4, 5 }
Essentially xs and r must be equal; mapping with identity i.e.lambda: x => x should be neutral.

Composition
xs.Map(x => f(g(x))) is equivalent to xs.Map(g).Map(f)
C#:
int Increment(int x) => x + 1; //  substitute for `g`
int Square(int x) => x * x;  // substitute for `f`

var xs = new List<int> { 1, 2, 3, 4, 5, 6 };
var r1 = xs.Map(x => Square(Increment(x))); // { 4, 9, 16, 25, 36, 49 }
var r2 = xs.Map(x => Increment(x)).Map(x => Square(x)); // { 4, 9, 16, 25, 36, 49 }
Essentially r1 and r2 must be equal; mapping over the composition of two computations must be the same as mapping over the 1st computation followed by the 2nd computation.

In C# where the type system is not as powerful as Haskell we could define these rules as unit tests to ensure that our particular implementation of Map has this conformance.

Next Post
Subtle Type Signature Variations
The abstract simplicity that underpins our function programming algebras (patterns) is largely what makes them so powerful, more so there are some very subtle variations between a functor, applicative and monad. To start to appreciate the subtle variations between these algebras; it's best to start off with a comparison of their Hindley Milner type signatures.
C#:
       Map :: functor     f ->   (a -> b) -> f a -> f b              // least flexible
     Apply :: applicative f -> f (a -> b) -> f a -> f b              // in between
   FlatMap :: monad       m -> (a -> m b) -> m a -> m b              // most flexible
In the next post we'll start to compare a few more of the primary algebras that are used by functional programming, to not only understand what their signatures mean, but also how these subtle type signature variations in comparison with functor have made them more flexible units of computation.
 
Last edited:
In the last post we tackled the Functor's ability to map a function over elements in a container type like List, while preserving not only the container, but also ensuring the mathematical purity of the computation.

...but isn't this Linq...?
Those of you familiar with Linq were probably wondering...
...why do I need map when Linq's Select does exactly the same thing?
...and that would be a great observation... yes the Map I presented in the previous post and Linq's Select are identical, and we can see this by comparing their extension method signatures, for example:
C#:
// Map signature
public static IEnumerable<B> Map<A, B>(this IEnumerable<A> xs, Func<A, B> fn) { ... }

// versus Linq's Select signature
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult> selector)
Microsoft used much longer variable names for the generics; so to make the visual comparison easier; let's replace these long generic variable names with something shorter, for example:
C#:
// Map signature
IEnumerable<B> Map<A, B>(this IEnumerable<A> xs, Func<A, B> fn) { ... }

// versus Linq's Select signature
// TResult = B
// TSource = A
IEnumerable<B> Select<A, B>(this IEnumerable<A> source, Func<A, B> selector)
As you can clearly see these two have exactly the same type signature.


So why did we not just focus on using Linq?
That's a good question, and the key reason is a because of something that we touched on briefly in an earlier post; that the positioning of the input source as the 1st argument and the function as the 2nd argument is not the typical, preferred o most flexible form for higher order functions.
i.e. it's typically far more powerful to position the function 1st and the curry the input source.
C#:
Map :: Functor f -> (a -> b) -> f a -> f b
This was briefly demonstrated using with these binary higher order operations:

C#:
public static class Binary {
  public static Func<A, A, A> Op<A>(this Func<A, A, A> f) {
    return (a, e) => f(a, e);
  }
}

var add = Binary.Op<int>((a, e) => a + e);
var multiply = Binary.Op<int>((a, e) => a * e);
var subtract = Binary.Op<int>((a, e) => a - e);
var divide = Binary.Op<int>((a, e) => a / e);
var rectDiagonal = Binary.Op<double>((l, w) => Math.Sqrt(w * w + l * l));

var result_add = add(4, 2); // 6
var result_multiply = multiply(4, 2); // 8
var result_subtract = subtract(4, 2); // 2
var result_divide = divide(4, 2); // 2
var result_diagonal = rectDiagonal(4d, 2d); // 2,82842712474619


Ok so what does that mean for Map and Linq's Select?
Whilst Linq is certainly a great adaptation of many of the Functional Programming algebras, it has its limits, because it essentially only exposes some FP surfaces, ...but that's not a problem for us now, because by having learnt how to create Map we can simply create a new overload to enable this, for example:

C#:
public static IEnumerable<B> Map<A, B>(this Func<A, B> fn, IEnumerable<A> xs) {
  return xs.Map(fn);
}
...and plus points we've again exploited DRY, by ensuring that this overload calls our previous implementation of Map.

This type signature version of Map is however still not an exact match to our Hindley-Milner signature:
C#:
Map :: Functor f -> (a -> b) -> f a -> f b
Why? ... because whilst the function is the 1st argument, the input argument is not curried, but as we already covered in this post; to curry a function is a relatively easy thing to do with our curry helper functions, for example:
C#:
var curriedMap = ƒ.Curry<Func<int, int>, IEnumerable<int>, IEnumerable<int>>(ƒ.Map);

var IncrementMap = curriedMap(x = x + 1);
var xs = new List<int> { 1, 2, 3, 4, 5 };
var r = IncrementMap(xs); \\ { 2, 3, 4, 5, 6 };
Here we have created a function called IncrementMap that can increment the elements in an IEnumerable container.

Yeah, yeah I know that curried type signature is awful; but unfortunately C# type inference is a bit lacking when it comes to transitioning between the historical function type syntax and the new lambda syntax, for example an ideal way to write a curried version of Map is as follows:
C#:
public static Func<IEnumerable<A>, IEnumerable<B>> Map<A, B>(this Func<A, B> fn) {
  return xs => xs.Map(fn);
}
This will work just fine; however there is a problem that will affect us when we try to add Functor conformance for another container type, for example:
C#:
// Conforming a Maybe type (otherwise known as an Option or Optional type)
//
public static Func<Maybe<A>, Maybe<B>> Map<A, B>(this Func<A, B> fn) {
  return xs => xs.Map(fn);
}


What is not immediately apparent is the conflict this creates.
The problem is to do with the way that C# determines if two extension methods are duplicates; basically C# only uses the method name and arguments to draw this conclusion, for example:
C#:
public static Func<Maybe<A>, Maybe<B>> Map<A, B>(this Func<A, B> fn)
//            ^======================^ |---------------------------|
//                     Ignored          C# only compares this part to
//                                      determine duplicate overloads
//
// meaning this signature is...
//
public static Func<IEnumerable<A>, IEnumerable<B>> Map<A, B>(this Func<A, B> fn)
//            ^==================================^ |---------------------------|
//                           Ignored               considered a duplicate of this
Essentially the return type is unfortunately ignored when C# determines if there are any duplicate method overloads. Which means we have no choice but to leave our extension methods uncurried to avoid duplicate compiler errors. Fortunately in use this shouldn't be a problem, as I'll demonstrate.
C#:
Func<int, int> powerOf2 = (int x) => (int)Math.Pow(2, x);
var bits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
powerOf2.Map(bits).Print(); // ---> {1, 2, 4, 8, 16, 32, 64, 128, 256}
...because our Map is an extension method we can simply dot chain the function call from the lambda powerOf2 and then pass in required argument bits without any need to curry before hand.


Composition is however going to be a bit more of a challenge
To create a function that composes Map and and a function like powerOf2 is still quite long winded in terms of function conversion and type inference; due to the fact that C#'s compiler won't automatically relate an old function declaration to its equivalent lambda form...
most likely because the lambda function form doesn't support generics...
...which basically means we're forced to type adorn the conversion from the old function syntax form to its equivalent lambda form:
C#:
Func<Func<int, int>, IEnumerable<int>, IEnumerable<int>> mapLambda = ƒ.Map;
Then we're able to compose it with powerOf2:
C#:
var powerOf2Map = mapLambda.Partial(powerOf2); // partially apply map with powerOf2

var bits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
powerOf2Map(bits).Print() // ---> {1, 2, 4, 8, 16, 32, 64, 128, 256}


Lambda Lifting Helper Functions
The long type adornments can be some what streamlined by creating a few lambda helper function, for example:
C#:
public static partial class ƒ {
  public static Func<A, B, C> Lambda<A, B, C>(Func<A, B, C> fn) => fn;
}
Which now enables us to compose this in a single line:
C#:
var powerOf2Map = ƒ.Lambda<Func<int, int>, IEnumerable<int>, IEnumerable<int>>(ƒ.Map).Partial(powerOf2); // partially apply map with powerOf2
.
var bits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
powerOf2Map(bits).Print() // ---> {1, 2, 4, 8, 16, 32, 64, 128, 256}
Yes, I know it's ugly, but keep in mind that were pushing the limits of what the C# compiler can support today.
Type Erasure
... is also not going to solve anything here; because turning everything into an object just shifts the goalpost and creates a whole new set of issues.
 
Last edited:
How can we Map over functions with multiple arguments
Keep in mind that Map function is built around unary higher order function that only takes 1 argument, which means the only way to pass in multiple values is to have them wrapped in a container type.

From mathematics, we have two solutions:
  • Tuples
  • Currying

Using Tuples for a Map over multiple arguments
C#:
var addition = ƒ.Lambda<(int, int), int> (x => x.Item1 + x.Item2);
var valueTuples = new List<(int, int)> { (4, 1), (3, 2), (2, 3), (1, 4) };
addition.Map(valueTuples).Print(); // ---> {5, 5, 5, 5}
Note:
In C# 7, the syntax for working with Tuples has been greatly simplified from:
  • ValueTuple<int, int> to (int, int), which btw aligns with both mathematics and Haskell.
  • .Item1 accesses the 1st element in the tuple, .Item2 the 2nd element, and so on.


Using Currying for a Map over multiple arguments
C#:
var addition = ƒ.Lambda<Func<int, Func<int, int>>>(a => b => a + b);
var valuesForA = new List<int> { 4, 3, 2, 1 };
var valuesForB = new List<int> { 1, 2, 3, 4 };
var addResult1 = addition.Map(valuesForA);
// ---> { Func<int, int>, Func<int, int>, Func<int, int>, Func<int, int> }

// Array of functions where the `a` values have been partially applied,
// i.e. `addResult1` is a List of unary functions that require the `b` values.
Ok let's now Map the B values.
C#:
var addResult2 = addResult1.Map(valuesForB);

// Error: this won't work -- inference error; the Map algebra doesn't support this
Problem is that we're essentially have a many to many situation:
  • Many unary functions -- partially applied with the A values
  • Many B values
This is not the way Map works, so maybe let's try 2 Map operations?
C#:
var addResult2 = addResult1.Map(f => valuesForB.Map(f));
// ---> {{5, 4, 3, 2}, {6, 5, 4, 3}, {7, 6, 5, 4}, {8, 7, 6, 5}}
Whoa... not only do we have a nested result; IEnumerable<IEnumerable<int>>, we ended up with 16 values instead of 4 we needed.

The result which is the Cross Product of our 2 input lists:
essentially the results from a loop within a loop
The fact that we couldn't continue to bind our computation any further with Map demonstrates the limits of Functor; whilst it's an excellent solution to use for transformation of elements, it is however not able to bind a process where effects are present; as in the case where our integer values transformed into unary functions.


Alternative FP Solution
For interest sake, there is another higher order extension method that is expressly built for this; Zip, which like a physical zipper, zips input two sources together and then can apply a binary operation to the result, for example:
C#:
var valuesForA = new List<int> { 4, 3, 2, 1 };
var valuesForB = new List<int> { 1, 2, 3, 4 };
valuesForA.Zip(valuesForB, (a, b) => addition(a, b)); // ---> {5, 5, 5, 5}
In the above example, Zip create a List of tuples which was then pass into our into addition function which requires a single tuple argument, of two integers.

Next Post
The next post will be introducing an algebra called Monad, which as it happens is designed to work with effects, more specifically it will enable us to mathematically bind computations where effects are involved.

Future posts in this thread should also cover:
Note:
  • Exception handling will draw a comparison between the traditional way of handling exceptions in C# versus FP's approach with sum types.
  • We'll build the 3 types shown above; Maybe, Either, Validation
 
Last edited:
Top
Sign up to the MyBroadband newsletter
X