Skip to content

PartialFunctionApplications

David Arno edited this page Feb 17, 2020 · 5 revisions

Partial Function Applications

Succinc<T> partial application guide


Introduction to partial applications

Normally with imperative languages like C#, when specifying a method invocation, all the parameters must be specified (applied) and the method is executed.

In the functional world though, things aren't as restrictive as this. it's possible to supply just some of the parameters of a function and have them be partially applied. This can be demonstrated with an example in F#:

let multiply x y = x * y
let double = multiply 2
let num1 = 10
let num2 = double num1

Walking through what's happening:

  1. multiply is defined as a function that takes two parameters and returns the result of multiplying them together.
  2. an invocation of multiply is partially specified (partially applied) by supplying a parameter 2. This results in a new function that is assigned to double, which is a new function that requires just one parameter.
  3. num1 is supplied to double, which fully applies its parameters and so it's invoked, which in turn invokes multiply, yielding 20, which is assigned to num2.

Were we to try something similar with C#,

var multiply = (x, y) => x * y;
var double = multiply(2);
var num1 = 10;
var num2 = double(num1);

we'd get a series of compiler errors, the most serious of which is that we are trying to invoke multiply with just one parameter. The aim of Succinc<T>'s partial application features is to achieve a way of expressing the above code, in C#, such that it compiles and works as expected, and has as little syntactic "noise" as practicable. This gives us the following C# code, which is as near as we can get to the F# example:

var multiply = Lambda<double>((x, y) => x * y);
var doubleNum = multiply.Apply(2);
var num1 = 10;
var num2 = doubleNum(num1);

The above example uses Succinc<T>'s Lambda functionality to simplify the use of partial applications. This same feature can be used to simplify partially applying methods too.

Consider a method implementation of the multiply function above:

public static double Multiply(double x, double y) => x * y;

When we now reference Multiply without parentheses, the compiler treats it as a method group. Extension methods, such as Apply cannot be invoked via method groups. However, we can use Lambda to force Multiply to become typed and thus able to be partially applied:

var doubleNum = Lambda<double>(Multiply).Apply(2);
var num1 = 10;
var num2 = doubleNum(num1); // num2 == 20

Going beyond functional programming: TailApply

In the standard functional programming world, it is taken for granted that partial function application involves supplying the first n parameters of a method to get back a partially applied function. But Succinc<T> offers another variation on this.

With the .NET Framework there exist numerous methods who's last parameter is an optional boolean parameter. That true/false at the end of the method call gives no real clue as to what's happening. These flag parameters are generally considered bad practice, and each such method ought to really exist as a pair of methods with names that explain their difference.

By way of an example, consider the Directory.Delete() method. Call Directory.Delete(someDir) or Directory.Delete(SomeDir, false) and it will delete SomeDir if empty. Change it to Directory.Delete(SomeDir, true) and it will recursively delete all the contents of the directory and then the directory itself. Being a core feature of the framework, you probably knew this, but only know it though through reading the docs; not through looking at the method name and parameter.

So Succinc<T> offers a solution to this. By tail-wise partially applying such methods, new - meaningful - names can be assigned to them:

var delete = Action<string, bool>(Directory.Delete);
var deleteEmptyDir = delete.TailApply(false);
var recursivelyDeleteAll = delete.TailApply(true);
Clone this wiki locally