As an OO developer, maybe I have difficulty seeing its value. What added value do they give? Do they fit in an OO world?
问题:
回答1:
Closures don't give you any extra power.
Anything you can achieve with them you can achieve without them.
But they are very usable for making code more clear and readable. And as we all know clean readable short code is a code that is easier to debug and contains fewer bugs.
Let me give you short Java example of possible usage:
button.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
System.out.println("Pressed");
}
});
Would be replaced (if Java had closures) with:
button.addActionListener( { System.out.println("Pressed"); } );
回答2:
You can see it as a generalization of a class.
Your class holds some state. It has some member variables that its methods can use.
A closure is simply a more convenient way to give a function access to local state.
Rather than having to create a class which knows about the local variable you want the function to use, you can simply define the function on the spot, and it can implicitly access every variable that is currently visible.
When you define a member method in a traditional OOP language, its closure is "all the members visible in this class".
Languages with "proper" closure support simply generalize this, so a function's closure is "all the variables visible here". If "here" is a class, then you have a traditional class method.
If "here" is inside another function, then you have what functional programmers think of as a closure. Your function can now access anything that was visible in the parent function.
So it's just a generalization, removing the silly restriction that "functions can only be defined inside classes", but keeping the idea that "functions can see whatever variables are visible at the point where they're declared".
回答3:
For me, the biggest benefit of closures is when you're writing code that starts a task, leaves the task to run, and specifies what should happen when the task is done. Generally the code that runs at the end of the task needs access to the data that's available at the beginning, and closures make this easy.
For example, a common use in JavaScript is to start an HTTP request. Whoever's starting it probably wants to control what happens when the response arrives. So you'll do something like this:
function sendRequest() {
var requestID = "123";
Ext.Ajax.request({
url:'/myUrl'
success: function(response) {
alert("Request " + requestID + " returned");
}
});
}
Because of JavaScript's closures, the "requestID" variable is captured inside of the success function. This shows how you can write the request and response functions in the same place, and share variables between them. Without closures, you'd need to pass in requestID as an argument, or create an object containing requestID and the function.
回答4:
IMHO it comes down to being able to capture blocks of code and their context to be referenced at some point later on and executed when/if/as required.
They may not seem to be a big deal, and closures definitely aren't something you need to get things done on a daily basis - but they can make code code much simpler and cleaner to write/manage.
[edit - code sample based on the comment above]
Java:
List<Integer> numbers = ...;
List<Integer> positives = new LinkedList<Integer>();
for (Integer number : integers) {
if (number >= 0) {
positives.add(number);
}
}
Scala (skipping some of the other niceties like type inference and wildcards so we're only comparing the effect of the closure):
val numbers:List[Int] = ...
val positives:List[Int] = numbers.filter(i:Int => i >= 0)
回答5:
It's a pity, people no longer learn Smalltalk in edu; there, closures are used for control structures, callbacks, collection enumeration, exception handling and more. For a nice little example, here is a worker queue action handler thread (in Smalltalk):
|actionQueue|
actionQueue := SharedQueue new.
[
[
|a|
a := actionQueue next.
a value.
] loop
] fork.
actionQueue add: [ Stdout show: 1000 factorial ].
and, for those who cannot read Smalltalk, the same in JavaScript syntax:
var actionQueue;
actionQueue = new SharedQueue;
function () {
for (;;) {
var a;
a = actionQueue.next();
a();
};
}.fork();
actionQueue.add( function () { Stdout.show( 1000.factorial()); });
(well, as you see: syntax helps in reading the code)
Edit: notice how actionQueue is referenced from inside the blocks, which works even for the forked thread-block. Thats what makes closures so easy to use.
回答6:
Here are some interesting articles:
- Crossing borders: Closures - by Bruce Tate, mentions some advantages of closures
- A Definition of Closures - by Neal Gafter (one of the people who invented one of the closures proposals for Java)
回答7:
Closures fit pretty well into an OO world.
As an example, consider C# 3.0: It has closures and many other functional aspects, but is still a very object-oriented language.
In my experience, the functional aspects of C# tend to stay within the implementation of class members, and not so much as part of the public API my objects end up exposing.
As such, the use of closures tend to be implementation details in otherwise object-oriented code.
I use them all the time, as this code snippet from one of our unit tests (against Moq) shows:
var typeName = configuration.GetType().AssemblyQualifiedName;
var activationServiceMock = new Mock<ActivationService>();
activationServiceMock.Setup(s => s.CreateInstance<SerializableConfigurationSection>(typeName)).Returns(configuration).Verifiable();
It would have been pretty hard to specify the input value (typeName) as part of the Mock expectation if it hadn't been for C#'s closure feature.
回答8:
As to the concluding query: "Do (closures) fit in an OO world?"
In many languages, closures, along with first-class functions, provide the very foundation for devising OO programs: Javascript, Lua and Perl come to mind.
In another more "traditional" OO language with which I'm familiar, Java, there are 2 primary differences:
- Functions are not first class constructs
- In Java the implementation details of OO (whether closures are used internally) is not exposed directly to the developer as it is in Javascript, Lua and Perl
Therefore in a "traditional OO" language such as Java, the addition of closures is largely just so much syntactic sugar.
回答9:
Maybe in the world of compiled programming the benefits of closures are less noticeable. In JavaScript closures are incredibly powerful. This is for two reasons:
1) JavaScript is an interpreted language, so instruction efficiency and namespace conservation are incredibly important for faster and more responsive execution from large programs or programs that evaluate large amounts of input.
2) JavaScript is a lambda language. This means in JavaScript functions are first class objects that define scope and scope from a parent function is accessible to child objects. This is important since a variable can be declared in a parent function, used in a child function, and retain value even after the child function returns. That means a variable can be reused by a function many times with a value already defined by the last iteration of that function.
As a result closures are incredibly powerful and provide superior efficiency in JavaScript.
回答10:
looking at above examples, I can add my bit.
I appreciate the style, state-hiding part and chaining of expression but this is not enough
A lot can be said about simple and explicit code
- I feel that Scala, Perl and some other Scheme type languages are designed to make kind of a shorthand for the particular way of thinking by the language designer or make them "more productive"
I would take the simplicity of python and early syntax of java etc as better way of simplicity and explicitness
It might be quite fast to write cryptic chained closures in a tight space. however everything can be done by simple objects and algorithms
closures are needed but not that often to warrant them to be first class citizen .