F# and first-class functions, part 2: point-free style

In the previous instalment, we saw that F# provided composition operators to allow you to concisely build up anonymous functions without the overhead of lambda syntax. Occasionally, however, a function will be so amazingly useful that you actually want to give it a name so you can use it from more than one place.

A soothing lie about F# functions

F#, among its other daring innovations, does indeed allow you to define named functions:

let isNotError v = not (isError v)

Notice that F# uses the let keyword to declare and define functions. That’s the same keyword it uses to declare and assign variables*:

let penguin = "ppog"

The reason for this is simple: a function is conceptually equivalent to a variable which just happens to be of a function type rather than a data type. (The equivalence is conceptual, not physical — there is an implementation difference. But for the time being it’s helpful to think of everything as variables, just some variables of function types and some of data types.)

* Note: F# variables don’t actually vary — they’re immutable values. But I’m going to keep calling them variables anyway.

Introducing point-free style

So our isNotError example is conceptually creating a variable named isNotError, of type Variant -> bool, and assigning the function body to that variable. Effectively, the declaration is equivalent to:

let isNotError = fun v -> not (isError v)  // fun is F#'s keyword for lambdas
 
// The C# equivalent would be:
// Func<Variant, bool> isNotError = v => !(IsError(v));

But as we discussed before, in this case the lambda is unnecessary verbosity — we should be able to just use a composition operator on the functions directly:

let isNotError = not << isError

And indeed we can! We can define the isNotError function purely in terms of other functions, and their combination using higher-order functions (in this case the << function).

This idiom is called point-free style, and is common in functional languages like F# and Haskell which make heavy use of higher-order functions.

Digression: point-free style and language interop

At the implementation level, point-free and conventional declarations do work differently. Consider the following:

let ine1 x = (not << isError) x
let ine2 = (not << isError)

To F# code, these result in the same thing: an “is not error” function of type Variant -> bool. But at the assembly level they’re quite different. ine1 is emitted as a CLR function, with a signature (in C# terms) of public static bool ine1(Variant x). ine2 is emitted as a CLR property, of type FSharpFunc<Variant, bool>. So there is an implementation distinction between variables and functions, and point-free style puts you on the ‘variable’ side of the fence.

Going further with point-free style

The example we’ve been looking at so far is pretty trivial, though. Let’s consider something a bit more realistic. Let’s suppose that I want a function in my variant library which removes all “missing” values from a list of variants. The F# List module contains a filter function, which is pretty much identical to the LINQ Where operator, so we can easily use that:

let present list = List.filter (not << isMissing) list

Here the list argument seems to be doing as much useful work as v was doing in our first example, i.e. sod all. Let’s see what happens when we get rid of it:

let present = List.filter (not << isMissing)

It works! So we’ve been able to create a new function, present, by applying List.filter to another function. In this case the filter parameter was an anonymous function created by the composition operator, but it could just as easily be a named function. Here’s a function to extract numeric values from a list of variants, using the List.map function which is like the LINQ Select operator:

let toNumeric = List.map coerceToNumeric

In fact, the function could even be a parameter. Suppose I want a function to get me everything in a list that doesn’t match a particular predicate?

let except f = List.filter (not << f)  // except: ('a -> bool) -> ('a list -> 'a list)

The except function takes a predicate, f, and returns a function. That function, when applied to a list, returns the members of the list which do not satisfy the predicate. Here’s how we might apply it:

let presentValues = (except isMissing) someList
// or
let presentValues = someList |> except isMissing

Or, of course, instead of applying it to a list, we can save the function we get back as a new named function:

let present = except isMissing  // present: List<Variant> -> bool
 
// Applying the present function:
let presentValues = present someList

Surely some mistake?

If you’re reading closely, these last examples seem a bit fishy. The List.map function, for example, takes two arguments: a transform, and a list to apply it to. There’s no overload that takes just a transform and returns a function that you can later apply to a list. It’s as though you tried to write in C#:

Func<IEnumerable<Variant>, IEnumerable<double>> present =
  Enumerable.Select(CoerceToNumeric);  // Won't compile

That doesn’t work because Select has no overload that takes only a Func. Similarly for our except function:

public static Func<IEnumerable<T>, IEnumerable<T>> Except<T>(Func<T, bool> f)
{
  return Enumerable.Where(Compose(Not, f));  // Won't compile
}

Obviously, this isn’t actually a limitation — you quickly get around it using lambda notation. I’m just putting these in to illustrate that the ability to apply List.map and List.filter to functions doesn’t happen ‘out of the box’ — it’s a F# special.

(I also put them in to illustrate another way F# makes it much easier to work with higher-order functions: not having to write out all those generic type parameters! Even for a simple function like except, the C# declaration is already a lot ‘noisier’ than the F# declaration. That’s because F# infers the argument and return types automatically, as if you could just write the C# var keyword on arguments and methods. It even automatically makes functions generic where possible, as in the case of the except function.)

So what’s going on in F#? How do List.filter and List.map magically manage to return functions when they’re given a function, instead of complaining that they have the wrong number of arguments? The answer is that F# supports a technique called partial function application, and we’ll look at this in more detail in the next instalment.

Tagged as F#

5 Responses to “F# and first-class functions, part 2: point-free style”

  • [...] F# and first-class functions, part 3: partial function application Tagged as F#We ended the previous instalment with a bit of a mystery. To recap, we were using the List.filter and List.map functions — [...]

  • Re: “variables”, to me it’s more useful to think of
    let penguin = "ppog"
    as defining a function named penguin that returns “ppog”.

  • [...] style. In fact, this is such an important idiom that it has its own name: point-free style. In the next instalment, we’ll look at how point-free style encourages and facilitates abstraction. Add your [...]

  • Re your note “F# variables don’t actually vary”: Variables do NOT have that name because they vary during the execution of a program or over time. They have had this name for a long time before (stateful) programming was invented. Here is the relevant text from Wikipedia [my emphasis]:

    “What it means for a variable to vary

    Varying, in the context of mathematical variables, DOES NOT mean change in the COURSE OF TIME, but rather dependence on the context in which the variable is used”.

    Regards
    Harald M.

  • [...] F# and first-class functions, parts one, two, three and [...]

  • Leave a Reply

Archives

Join our mailer

You should join our newsletter! Sent monthly:

Back to Top