-
Notifications
You must be signed in to change notification settings - Fork 15
PartialFunctionApplications
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:
-
multiply
is defined as a function that takes two parameters and returns the result of multiplying them together. - an invocation of
multiply
is partially specified (partially applied) by supplying a parameter2
. This results in a new function that is assigned todouble
, which is a new function that requires just one parameter. -
num1
is supplied todouble
, which fully applies its parameters and so it's invoked, which in turn invokesmultiply
, yielding20
, which is assigned tonum2
.
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
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);
Action
/Func
conversionsCycle
methods- Converting between
Action
andFunc
- Extension methods for existing types that use
Option<T>
- Indexed enumerations
IEnumerable<T>
cons- Option-based parsers
- Partial function applications
- Pattern matching
- Pipe Operators
- Typed lambdas
Any
Either<TLeft,TRight>
None
Option<T>
Success<T>
Union<T1,T2>
Union<T1,T2,T3>
Union<T1,T2,T3,T4>
Unit
ValueOrError