Introduction
It’s been some time since last I wrote, and the reason behind this is because I got caught up in learning more about other languages, particularly Lua, JavaScript and Python, and in this adventure I encountered a repeating topic that I thought is also present in .NET yet not often enough discussed, and that is Closures and what can be achieved with them.
What are Closures?
So first, let’s have a look at what wikipedia has to say about closures in general and then, we will try to understand the more practical aspects of it.
In programming languages, closures (also lexical closures or function closures) are techniques for implementing lexically scoped name binding in languages with first-class functions. Operationally, a closure is a record storing a function together with an environment: a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created. A closure—unlike a plain function—allows the function to access those captured variables through the closure’s copies of their values or references, even when the function is invoked outside their scope.
Well, that was surely a mouth full, then again I’m neither an expert on the topic neither an academic, so let’s try to look at how it’s usually applied.
Even though they are called techniques officially, most of the time, especially in .NET, closures “just kinda happen”, and the most prevalent example is in the use of LINQ, here’s one example:
int factor = 2;
Enumerable.Range(0, 5).Select(number => number * factor++).ToList();
Console.WriteLine(factor);
Let’s break down the previous example and see what’s going on:
- The function that was passed into the
Select
method is creating a closure because the factor
variable is passed into the function from outside but not as a parameter but through the use of the variable scope. That means that the function passed into select
can actually manipulate other variables outside its scope. - We called the
ToList
method because the Select
method is a deferred (we will discuss this in a future post) method call, without that method call at the end factor
would have still had the value 2
in it because the Select
method has not been run yet. - The value of the
factor
variable after the Select
method has run will be 7
, the reason for that is because Select
runs 5 times, incrementing the variable for as many times. - The closure captures all of its enclosing scope inside it, that means if other variables were defined outside the scope of the closure, the closure would have access to those as well.
The reason this works is that .NET (C# in this case) is a programming language that supports, as the definition said, first-class functions. This means we can pass functions as parameters to functions, return a function from another function, and even save them in variables. Those are called delegates in .NET and it’s the mechanism that promotes methods and anonymous functions to first class citizens.
Ok, So What About It?
Well, now that we saw an example of it, let’s understand how closures are being used in other languages, as well as what we can do to take advantage or be cautious of them.
How Do Other Languages Use It?
Python and Lua
Python and Lua use closures to create iterators, remember that an iterator is just a function that returns something different every time it is called (in a very abstract way of saying it), like in the case of a collection, it returns the next element, or the next computation for a number sequence or the next property of an item in the case of reflection (again a topic for another time), in .NET, those are called enumerator, of which we talked about in previous posts.
So to achieve this functionality, we depend on state, that means we either have an object with an internal state that changes every time it is called, or…as Python and Lua do it, we can leverage closures to have the same kind of functionality.
JavaScript
Well, since JavaScript does not have classes natively, in JavaScript, we can create something like a class by creating a function which returns a JavaScript object and for its properties, we can assign them to locally declared functions. That creates a closure based on the function that returns the object and also allows us to have something akin to private
fields and methods only found within the scope of the function. In short, we’re creating something as close to a class
concept as we can, given the limitations of JavaScript
The Downside of Closures
Like we mentioned before in the example breakdown, a closure has access to everything around its environment, so the biggest downside is that you can modify a value inside of a closure and it stays modified for all other lines of code in the enclosing scope as opposed to passing a value type (let’s say an int
) to a method.
And for the Fun Advantages
We can use closures to some neat stuff like factory methods and caches that will use an enclosing scope as a hidden state of a function. Here’s an example of a closure used as an iterator:
Func GetIterator(){
int number = 0;
Func next = new Func(() => {
return number++;
});
return next;
}
var iterator = GetIterator();
Console.WriteLine(iterator());
Console.WriteLine(iterator());
Console.WriteLine(iterator());
Let’s break this one down as well and see what is happening:
- On line 1, we create a function called
GetIterator
that itself returns another function which returns an integer (I know, boggles the mind). - On line 2, we declared a local variable called
number
that will serve as our state. - On line 4, we create another function and save it into a variable called
next
that returns an integer and increments the local variable called number
. - On line 8, we return the previously declared function.
- On line 11, we save the returned function from the
GetIterator
function into a variable called iterator
. - From lines 12 to 14, we write to the console the returned value when we call the previously declared variable
iterator
.
Note that when this piece of code runs, we will see that the numbers on that show up on the screen will be 0
, 1
and 2
because at each call to the function, the internal state variable, number
in this case, changes.
In this example, we saw how to create a closure and return it using delegates (Func
). In the next example, we will be creating a caching factory method using a new feature of C# 7 and that feature is called local functions.
Func GetCachedDateTime()
{
DateTime currentDateTime = default(DateTime);
return GetOrCreateCurrentDateTimeInstance;
DateTime GetOrCreateCurrentDateTimeInstance()
{
if (currentDateTime == default(DateTime))
{
currentDateTime = DateTime.Now;
}
return currentDateTime;
}
}
var currentTime = GetCachedDateTime();
Console.WriteLine(currentTime());
Console.WriteLine(currentTime());
Console.WriteLine(currentTime());
This example is almost the same as the previous one in that it has an internal state, and returns a function, but in this case, we don’t need to save the function into a variable before returning it because C# 7 allows us to declare local functions inside other functions, that also means that we can keep the logic of the parent function in line and cleaner and define the inner functions anywhere we want inside the scope of the parent function, including as we see above, after the return
statement.
Also, because we’re caching the DateTime
value from outside the GetOrCreateCurrentDateTimeInstance
function, every time we call it, we will get the same value, and for me, this seems like a nice new shiny tool in any developer’s toolbox, but let’s really see it in action by solving an authorization problem and hopefully, you will see the benefits of closures.
Func<object, string> CanDrinkBeerNotifier(Func<object, bool> selector)
{
return GetMessage;
string GetMessage(object instance)
{
if (selector(instance))
{
return "Yes, you are old enough to drink beer";
}
else
{
return "No, you're not old enough for a beer, go home before I call your parents";
}
}
}
var notifier = CanDrinkBeerNotifier(nameOrAge =>
{
if (nameOrAge is string name && !name.Equals("Bart"))
{
return true;
}
else if (nameOrAge is int age && age > 20)
{
return true;
}
return false;
});
Console.WriteLine(notifier("Homer"));
Console.WriteLine(notifier("Bart"));
Console.WriteLine(notifier(45));
Console.WriteLine(notifier(16));
We can see that in this example, we are building a notifier that can receive a custom selector function, in essence, this is a function factory, we can pass on any logic we want to, in this case, determining if someone is old enough to drink by age, or by name if we know them.
Conclusion
Like any tool, it should be used when appropriate, there are cases where this will come in handy, though this can as easily be accomplished with classes and inheritance, or a hindrance, use your better judgment to determine which case requires which tool.
Thank you and see you next time.
CodeProject