What is the difference between a lambda expression, a closure and a delegate?

Asked

Viewed 2,228 times

38

From what I’ve been reading the three concepts are quite similar, but I was confused as to the clear and exact definition of them.

As far as I know, a lambda expression for being understood as a inline function, or a procedure that passes for another procedure, as in the pseudo-code below:

func(lambda (Type arg1, Type2 arg2) {
    doSomethingWithArgs()
}, anotherArg);

However the concept I have of Closure is exactly the same from above, with two differences:

  • For some reason, it seems important to highlight that a Closure references a variable outside its scope.
  • Closures can be assigned to variables and manipulated as if they were objects.

However, I recently learned Groovy, which introduced me to a slightly different concept of Closure:

A Groovy Closure is like a "code block" or a method Pointer. It is a Piece of code that is defined and then executed at a later point. It has some special properties like implicit variables, support for currying and support for free variables (which we’ll see later on). We’ll ignore the nitty gritty Details for now (see the formal Definition if you want those) and look at some simple examples. (Source)

In simplest words: A Closure is a Groovy object (incidentally: It is a Java object) that behaves like a block of code and a first class citizen, which can be passed as a parameter to methods, assigned to variables and have its method signature reset.

Coincidentally, C# presents exactly the same concept, which he calls Delegate. However, the description of this Feature highlights that a delegate can be assigned to another existing method that conforms to his signature.

A delegate is a type that Safely encapsulates a method, similar to a Function Pointer in C and C++. Unlike C Function pointers, delegates are Object-oriented, type safe, and Secure. The type of a delegate is defined by the name of the delegate. The following example declares a delegate named Del that can encapsulate a method that takes a string as an argument and Returns void. Source

What are the differences between a lambda expression, a closure and a delegate?

2 answers

29


I will not go into detail about the functioning since the doubt is more related to terminology.

Introducing

Each language can have its own definition. In general terms there is a more accepted definition of what each one is. There is some controversy but there are reliable formal sources about some terms that express the referred concepts.

Someone who comes from another language might find these terms wrong. In general it is interesting to use the terms that the community you are inserted uses to facilitate communication. But it is good to understand all the terms and how the resource works. If you meet someone with more academic bias she will express it in the most correct way, worrying about using the most correct term in each situation. But most people will worry more about just communicating the idea.

Terms

You can also find the terms Function Pointer, functor, Anonymous Function, first class Function, Function Object, high order Function, nested Function (this I find a little misleading), callback (which is also possible without being exactly a lambda), inline Function (I find this term even more ambiguous) and others to name the mechanism or concept. Not forgetting the translations of these terms that can also be used, it is common to speak in "anonymous function". Some of these terms deal with slightly different things. They may be defining some specific detail.

Delegate

We can say that the delegate is the mechanism used to implement the concept of Amble. In C# for example, the term lambda is used to indicate a simpler syntax, plus a delegate. Delegates are usually objects in object-oriented languages. Typically created from a class/structure with the necessary infrastructure to treat these delegates according to language rules. Example.

Code Block

One lambda is a function defined as a block of code to be executed in the future, so in some languages the term block code.

Lambda

Probably a more commonly used terms to express the concept. Strictly a lambda would be to closure that has no ability to capture variables but is not always interpreted this way. Note that I used this term preferably in every answer.

They differ from commonly known named functions that are "fixed".

Closure

The term closure is usually used when the lambda capture, enclosure, a state, possibly a variable, of the scope external to its body but within the scope in which the lambda defined. These variables can be assigned multiple names: non local, free variables or upvalues. If you finish executing a function where a lambda was defined and the return of this function is precisely the lambda which has closed a local variable, this variable will be highlighted and will go along with the lambda can be accessed at another time when it is invoked for execution.

A simple example (made in Javascript that is more universal):

function funcao() { 
    var x = 1;
    return function (n) { 
        return x + n; //sempre vai retornar o argumento usado na chamada desta lambda + 1
    } 
}
var func = funcao();
func(2); //retorna 3

Some people use the term closure even when this catch does not occur. And I think that is not right. I understand that the lambda do not need to capture the variable in order to use the term closure, otherwise it gets very ephemeral. I believe the fact of lambda Having the ability to make this capture, even if you don’t do it in a specific instance, is what matters. And almost always (in almost all languages) a lambda has this ability, so it is frequent to use only a single term for any situation.

The term closure is usually used to indicate lexical closure. There is also the term syntatic closure closer to a macro found in languages such as Lisp.

An example of C#:

public static Func<int,int> Func() {
    var x = 1;
    Func<int, int> inc = p => {
                                x++;
                                return p + x;
                            };
    return inc;
}

It would be compiled for something like this:

private class Closure { 
    public int x;
    public int AnonymousFunction(int p) {
        this.x++;
        return p + this.x;
    }
}
public static Func<int,int> Func() {
    Closure c = new Closure();
    c.x = 1;
    Func<int, int> inc = c.AnonymousFunction;
    return inc;
}

I put in the Github for future reference.

Miscellanea and conclusion

Note that whatever name you’re using, the concept of lambda presupposes that it is treated as a value in some way, so that it can be stored in a variable or passed as an argument of a function to be executed later Lazy Evaluation, for example. I don’t know Amble that cannot be attributed to another object. And I think that if this is not done, it completely loses the sense of its use. It is usually expected that a lambda can be returned as a result of a function. But not all languages allow this and I find it a great limitation. This occurs mainly in languages that do not have a Garbage Collector. Without it it is very difficult to implement closures in languages.

The ability to reset the signature depends more on the language having dynamic or static typing.

Use whatever term is or the form that is implemented, I find it a fundamental resource in any language. I learned its use very early and can greatly simplify many codes with it.

See the wikipedia article (always remembering that in English always gives more details and is usually more correct).

Very academic definition if you have patience. Understanding all this is important in certain software but understanding the general idea and basic concepts of this resource is enough to make your stock control, your real estate site, your app shopping list.

Some answers related to the subject:

  • Excellent answer. In summary: In practice (and even in theory), it’s all the same thing. But each language implements and explains this in a way. Groovy and C# seem to implement exactly the same way: the [enter your preferred name here] are objects, first class citizens, and can capture references to functions that conform to your signature. But other languages like Java (from version 8) implement this as functional interfaces.

  • @Sid is almost the same thing, it is important to note that there are differences in the use of some terms, but rarely are these different ones relevant to such small ones in most cases. Often, but not all, the explanation is different but the practical result is the same. Java has a difficulty, they did not implement this feature at first, then it became more difficult to do the best without breaking compatibility. And this is not the first time this has happened. Actually there is another problem but I will avoid raising controversy :P

  • Yes, indeed, but I think the way Java implemented it breaks the branch, doesn’t it? By the way, I think the Java people could have been a little more cheeky: "import" the Groovy closures and define a literal for this object as a block of code that is a lambda expression. When compiling, the literal could simply expand into the anonymous class.

  • Certainly. I don’t know Groovy more than superficially but I remember having good ideas about him. Several languages that run on JVM did better than Java itself.

8

Delegate is equivalent to a function/method pointer. That is, it is a way for you to turn a method call into an object.

Func<string, int> fn = int.Parse;
MetodoX( fn );

When calling the method Metodox this may use delegate as a normal function.

Lambda commonly refers to anonymous methods, i.e., a method that is declared within another method. In C# and Javascript lambda is most associated with the expression format:

Func<int,int> fn = (x,y) => x + y;

The only way to call an anonymous method (which can be declared by a lambda expression without any harm), is through the Delegate with compatible subscription.

Closure occurs when a variable of scope above is "captured" by an anonymous method:

int z = 0;
Func<int,int> fn = (x,y) => (x + y) * z;

In the above example the variable z will be available in the anonymous method defined in fn. The anonymous method can change this variable so that this change will be reflected in the parent context.

int z = 0;
Func<int,int> fn = (x,y) => { z = 9; return (x + y) };
fn(1,2);
Console.WriteLine(z);

In the example above the value 9 will be printed, because the variable z was changed within the anonymous method in fn.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.