Skip to content

Thinking Functionally: Function composition

Paul Louth edited this page May 16, 2017 · 11 revisions

We've mentioned function composition a number of times in passing now, but what does it actually mean? It can seem quite intimidating at first, but it is actually quite simple.

Say that you have a function f that maps from type T1 to type T2, and say that you also have a function g that maps from type T2 to type T3. Then you can connect the output of f to the input of g, creating a new function that maps from type T1 to type T3.

Functions Composition

Here's an example:

static float f(int x)  => x * 3.0f;  // f is int->float
static bool g(float x) => x > 4.0f;  // g is float->bool

We can create a new function h that takes the output of f and uses it as the input for g.

static bool h(int x)
{
   var y = f(x);
   return g(y);
}

A much more compact way is this:

static bool h(int x) => g(f(x));    // h is int->bool

//test
var x = h(1);   // x == false
var y = h(2);   // y == true

So far, so straightforward. What is interesting is that we can define a new function called compose that, given functions f and g, combines them in this way without even knowing their signatures.

static Func<A, C> compose<A, B, C>(Func<A, B> a, Func<B, C> b) =>
    v => b(a(v));

compose is part of LanguageExt.Prelude and can be used to compose up to 7 functions (Note also that this generic composition operation is only possible because every function has one input and one output. This approach would not be possible in a non-functional language.)

Clone this wiki locally