The K-Combinator in Practice
3 min read
Photograph by Sepand Bakhtiari, CC BY-SA 4.0, via Wikimedia Commons
Combinators are functional patterns that glue (AKA compose) together functions. These patterns promote single responsibility and decoupling.
The K-Combinator is more commonly known as the Constant Combinator. It is formally known as the Kestrel Combinator. The K-Combinator can be defined in C# as:
Func<dynamic, T> Constant<T>(T x) => _ => x;
It's a function that always returns the initial value when called with any input. In a sense, it is a mapping of the set of all values to x. Simple!
One common pattern I've seen this used is in the implementation of the
Tee is a way of causing side effects in a pipeline of functions. This can aide in code readability by keeping your control flow in top to bottom direction, while explicitly communicating that a side effect is happening.
Func<T, T> Tee<T>(Action sideEffect) => input => Constant<T>(input)(sideEffect.Invoke(input)); // sometime later... var ItemForSale = ... var ExecutePayment = Tee((total) => ProcessTransaction(total)); var Total = ExecutePayment(CalculateTotal(ItemForSale));
In this case, the
Tee would process the transaction, while passing the input as the result. Using this in combination with the T-combinator ( See Part I in this series ) and you can get something like this:
var Total = ItemForSale .Pipe(CalculateTotal) .Pipe(ExecutePayment) // renaming Pipe to Then var Total = ItemForSale. Then(CalculateTotal). Then(ExecutePayment)
In the end, what it buys you is:
- decoupling of functions
- code that reads from top to bottom
- lazy evaluation of side effects