Simplifying F# lambda expressions with partial function application

21 Jun 2012

Here's another useful trick from Ivan Towlson‘s A Practical Developer’s Introduction to F#.

Suppose you have a function divisiblyBy and a list of numbers. Then you can apply the filter function to get a list of even numbers:

// val divisibleBy : int -> int -> bool
let divisibleBy factor value = value % factor = 0
let evenNums = List.filter (fun x -> divisibleBy 2 x) [1..10]
  

This lambda syntax is a bit noisy, but can oftentimes be transformed into a shorthand lambda syntax. When the argument passed into the lambda — in this case x — is the last argument to the function within the lambda body, a bit of compiler trickery combined with the use of partial function application can help simplify the expression. Combining partial function application with function composition even means we don't have to explicitly create a lot of smaller functions, such as one that negates another function:

let evenNums = List.filter (divisibleBy 2) [1..10]       // partial function application
let oddNums = List.filter (not << divisibleBy 2) [1..10] // and function composition
  

Of course, the concept of partial function application isn't tied to lambda expressions:

let divisibleByTwo = divisibleBy 2           // val divisibleByTwo : (int -> bool)
let notDivisibleByTwo = not << divisibleBy 2 // val notDivisibleByTwo : (int -> bool)
let a = divisibleByTwo 4                     // val a : bool = true
let b = notDivisibleByTwo 4                  // val b : bool = false
  

The partial application of divisibleBy results in a new function of one less argument, since we provided a value for the factor argument. As you can tell by their signatures, divisibleByTwo and notDivisibleByTwo are functions that take one argument, the value, and return a bool. The compiler knows that divisibleByTwo and notDivisibleByTwo take one and only argument for them to fully evaluate and that filter takes as argument a function that goes from an argument of type 'T — in this case int — to bool.

val List.filter : ('T -> bool) -> 'T list -> 'T list
  

Now all types align and we’ve simplified the code a little.

PS: Partial function application is also possible in C# if you define functions using one of the Func<T> or Action<T> delegates.