SICP doesn’t cease to amaze me. Here’s a challenge for you to solve, which I found in the chapter Building Abstractions with Data. I will provide a solution in C#.
The quiz is:
Would you be able to implement a 2-tuple (a pair) of integers, only leveraging functions and lambdas, without using neither variables nor class fields?
I mean, you should store 2 integers in a tuple, without writing anything like the following:
At first, it seems impossible, isn’t it?
A 2-tuple, or pair, is a data structure that holds 2 values. For example, a pair of integers is easily obtained in C# with:
It is equally easy to retrieve the original values with:
or even better, using pattern matching and tuple deconstruction, with:
In this quiz I assume a wider definition for tuple: anything we can provide 2 integers and that is able to give them back when we invoke the functions First
and Second
on it. It should also be possible to sum two tuples.
In other words, the structure to build should pass the following tests:
I’m sure you could easily write a custom implementation of pair of integers using classes, may be storing the 2 integer values in 2 class fields. The challenge is: implement a purely-functional version, with no variable and no class field. Only rely on functions.
Somehow or other, the pair must retain the 2 integer values. In order to store a value you need a variable, a class field or a property, don’t you? Actually, there are other means for storing a value and making it available for a later use. You can use functions. The following example might give you a suggestion. Let’s start from this function:
or, with a lambda:
Its type is:
Since in C# functions are first-class citizens, you might use secretRevealer
as a method’s return value:
Doing so, when you can call GetSecretRevealer()
you get back a function that you can later invoke, which in turn will return a string:
So far, nothing special.
Enter closures. What if we introduce a variable in GetSecretRevealer
’s body?
The moment secretRevealer
is invoked, the variable name
is undefined, because we are outside its scope. Yet, its value is somehow retained, as it is present in the resulting string. This is because the function returned by GetSecretRevealer
is a closure: it has captured the variable name
value.
Does it give you any suggestion?
Ok, let’s try with this. What if we convert the variable name
to a method’s parameter?
or, more concisely, using an expression-bodied member:
It still works. Although there are no variables at all in GetSecretRevealer
, the value passed in input is retained, just like the variable name
in the previous example:
The value of name
has been captured by the closure returned by GetSecretRevealer
. You could leverage this fact to store values, and use closures to build data structures, with no need of variables and fields.
Here we go:
It’s simpler than you think once you wrap your head around the idea of functions that return functions that take functions as parameters.
Let’s start from Build
: Build
’s goal is to create our purely-functional version of tuple of integers, so it must get the value of a
and b
in input. If Build
could speak, it would say:
Invoke me, and pass me the 2 integers you want to store. I will return you back a Magic Closure, that can capture those two integers.
Store that Magic Closure and whenever you want those 2 integers back, invoke it.
This is all I do. If you want to know what Magic Closure does, ask it.
Let’s ask Magic Closure:
I’m created by
Build
when you give it 2 integer values. Since I’m a closure, I can capture their values. Unfortunately, I cannot give you them back to you as a tuple, since we deliberatelly decided to only rely on functions. Here’s a trick we might use: pass me a function, and I will invoke it providing it exactly the 2 integers you need as parameters.
Let’s talk about the functions we should pass the Magic Closure: what should they look like?
One of them can be a function to get the first value. Something like:
If it was a lambda, it would be:
Simingly, the function to get the second value would be:
You should recognise their body in:
where _
is used to signify that that particular value won’t be used;
If the Magic Closure receives first
or second
, all it has to do is to invoke them passing them the 2 integers, and return back the result. The Magic Closure implementation is then:
The type of MagicClosure
is:
that we can explain as:
Build
should get a
and b
and return the Magic Closure:
Now, we are almost done. We can define First
and Second
as extension methods of the Magic Closure.
They invoke the Magic Closure, providing them the lambda to get the first value, and the lambda to get the second one.
Now, think about it: the Magic Closure is our tuple: we create it using a sort of factory method, Build
, and we can invoke First
and Second
on it to get its internal state. In orher words, regardless if it is implemented with functions, it is in fact a data structure.
To demonstrate it, let’s implement a sum of tuples.
To reason about the hard-to-read signature, you can mentally replace Func<Func<int, int, int>, int>
with Tuple
. Doing so, the above would read as:
Straighforward, isn’t it?
Actually, you can simplify the whole syntax using a delegate
to alias Func<Func<int, int, int>, int>
:
And since the Magic Closure
is our tuple, we can really rename it to Tuple
:
Notice how
Build
acts just like a data structure constructor, or a class factory method;Tuple
is a functional replacement of a class;Build
, First
, Second
and Add
are the functional equivalent of class instances.