Skip to content

Thinking Functionally: Function values

Paul Louth edited this page May 17, 2018 · 3 revisions

Let’s look at the simple function again:

int Add1(int x) => x + 1;

What does the x mean here? It means:

  • Accept some value from the input domain.
  • Use the name x to represent that value so that we can refer to it later.

This process of using a name to represent a value is called "binding". The name x is "bound" to the input value.

So if we evaluate the function with the input 5 say, what is happening is that everywhere we see x in the original definition, we replace it with 5, sort of like search and replace in a word processor.

    Add1(5)

    // replace "x" with "5"
    // Add1(5) == 5 + 1 == 6
    // result is 6

It is important to understand that this is not assignment. x is not a “slot” or variable that is assigned to the value and can be assigned to another value later on. It is a onetime association of the name x with the value. The value is one of the predefined integers, and cannot change. And so, once bound, x cannot change either; once associated with a value, always associated with a value.

This concept is a critical part of thinking functionally: there are no “variables”, only values.

Function values

If you think about this a bit more, you will see that the name Add1 itself is just a binding to “the function that adds one to its input”. The function itself is independent of the name it is bound to.

When you type int Add1(int x) => x + 1; you are telling the C# compiler “every time you see the name Add1, replace it with the function that adds 1 to its input”. Add1 is called a function value.

To see that the function is independent of its name, try:

    Func<int, int> plus1 = Add1;
    int r1 = Add1(5);
    int r2 = plus1(5);

You can see that Add1 and plus1 are two names that refer ("bound to") to the same function.

You can always identify a function value because its signature has the standard form Func<domain, range>.

Simple values

Imagine an operation that always returned the integer 5 and didn’t have any input.

Functions const

This would be a “constant” operation.

How would we write this in C#? We want to tell the C# compiler “every time you see the name C, replace it with 5”. Here’s how:

    const int C = 5;

There is no mapping this time, just a single int. What’s new is an equals sign with the actual value printed after it. The C# compiler knows that this binding has a known value which it will always return, namely the value 5.

In other words, we’ve just defined a constant, or in C# terms, a simple value. To bring this into parity with the function definition above it could be represented thus:

    static int C => 5;

That has the same behaviour as the const int when used, but has the signature Func<int>. There is no mapping from domain to range as before: Func<domain, range>.

Simple values vs. function values

It is important to understand that in functional programming, unlike idiomatic C#, there is very little difference between simple values and function values. They are both values (of Func) which can be bound to names and then passed around. And in fact, one of the key aspects of thinking functionally is exactly that: functions are values that can be passed around as inputs to other functions, as we will soon see.

Note that there is a subtle difference between a simple value and a function value. A function always has a domain and range and must be “applied” to an argument to get a result. A simple value does not need to be evaluated after being bound. Using the example above, if we wanted to define a “constant function” that returns five we would have to use:

    Func<Unit, int> C = _ => 5;

    // or
    static int C(Unit _) => 5;

The signature for these functions is:

    Func<Unit, int>

More on unit, function syntax and anonymous functions later.

"Values" vs. "Objects"

In a functional programming language like F#, most things are called "values". In an object-oriented language like C#, most things are called "objects". So what is the difference between a "value" and an "object"?

A value, as we have seen above, is just a member of a domain. The domain of ints, the domain of strings, the domain of functions that map ints to strings, and so on. In principle, values are immutable. And values do not have any behaviour attached them.

An object, in a standard definition, is an encapsulation of a data structure with its associated behaviour (methods). In general, objects are expected to have state (that is, be mutable), and all operations that change the internal state must be provided by the object itself (via "dot" notation).

In F#, even the primitive values have some object-like behavior. For example, you can dot into a string to get its length:

    "abc".Length

But, in general, we want to avoid using "object" for standard values when discussing the functional approach in C#, reserving it to refer to instances of true classes, or other values that expose member methods.

Naming Values

One of the areas that's likely to get seasoned C# heads worked up is my choice of naming style. The intent is to try and make something that 'feels' like a functional language rather than follows the 'rule book' on naming conventions (mostly set out by the BCL).

There is however a naming guide that will stand you in good stead whilst reading through this documentation:

  • Type names are PascalCase in the normal way
  • The types all have constructor functions rather than public constructors that you instantiate with new. They will always be PascalCase:
    Option<int> x = Some(123);
    Option<int> y = None;
    List<int> items = List(1,2,3,4,5);
    Map<int, string> dict = Map((1, "Hello"), (2, "World"));
  • Any (non-type constructor) static function that can be used on its own by using static LanguageExt.Prelude are camelCase.
    var x = map(opt, v => v * 2);
  • Any extension methods, or anything 'fluent' are PascalCase in the normal way
    var x = opt.Map(v => v * 2);

Even if you don't agree with this non-idiomatic approach, all of the camelCase static functions have fluent variants, so actually you never have to see the 'non-standard' stuff.

If you're not using C# 6 yet, then you can still use this library. Anywhere in the docs below where you see a camelCase function it can be accessed by prefixing with Prelude.

NEXT: How types work with functions