4 ms·
Is it just me or was this article entirely about lambdas and higher order functions? There wasn't a single example that captured a variable outside of the scope
by pmr_ 15y ago
Is it just me or was this article entirely about lambdas and higher
order functions? There wasn't a single example that captured a
variable outside of the scope of the function and passed it
around.
Lambdas by them self are incredibly useful but a closure has a lot
more benefits. Your API can return functions that manipulate values
through references without exposing exposing the value and giving a
lot of flexibility.
Personally, I consider higher-order functions and lambdas convincing
enough to have them added to a language, but it takes more than that
to convince a broad majority that does not have much exposure to
functional programming. While showing how cool you can sum things up
with Haskell is certainly nice, the degree of flexibility and
usability that is added to APIs is much more convincing.
Edit: Had to write this in a hurry and now came back to make it more
substantial.
- deleted 15y ago[deleted]
- jberryman 15y agoYes, the author seems to be referring to higher-order functions as "closures". > Listing 1. The simplest possible closure > 3.times {puts "Inside the times method."} that's not the simplest possible anything. it's magical syntactic sugar gone wild.
- evincarofautumn 15y agoNot really “magical syntactic sugar gone wild”, just “everything is an object”. In Ruby, integers can respond to messages just as well as anything else, and the built-in “times” message takes a lambda (“closure” in the article). There is some small irony in that “times” is just a very thin wrapper for a “for” loop.
- itissid 15y agoYou are right I think comparing merits of closures on just a comparison with for loops and lambdas alone is not comprehensive enough. Closures are called "A poor mans way of doing OO". And its in no way less powerful than any OO language I have seen. Javascript is a living example of this. I use currying so much in my production code base, to state one of the advantages of closures. The fact is that even if java can do this with inner classes and not natively means you have to write much more code. And more code for doing the same thing is only useful when it gives you more power. Even with MORE CODE to do closure simulation JAVA gives you only "INCOMPLETE" closures(final/class level variable access only in the inner scope). And for currying one says: "return new Function<K, V>(){ public void apply(...){...}}". Composing functions is also harder. So its not just for loop there is more to it.
- drostie 15y agoThis is a valid criticism, and if you wanted to really make the case you'd say I think, "think about every time you have ever written an anonymous class. That anonymous class was probably an ugly closure." So Java programmers basically already know that if you want to list your MP3s you might have to write: String[] mp3s = new File("/home/drostie/Music").list(new FilenameFilter() { public boolean accept(File dir, String name) { return (name.length > 4) && ( name.substring(name.length - 4).toLowerCase() === ".mp3" ); } }); In Node.js this becomes: var mp3s = fs.readdirSync("/home/drostie/Music").filter(function (name) { return name.slice(-4).toLowerCase() === ".mp3"; }); But as you said, this doesn't use the environment. The question is, when do you really need the environment? One answer is that it's usually important when that environment is changing and you're modifying that environment; then you have to worry, "Am I modifying the right value? Do I have the right value?" And that's a much more difficult case. MIT's Abelson-Sussman lectures are very clear when talking with Scheme about assignment. "Why do you care about assignment? Because sometimes you want new Date(), or Math.random(), or any number of other constructs which don't return the same thing when you call them twice in a row." There are some simple use cases for this, like tracking how much work you're doing. In client-side JavaScript you might write something like this: var el_text_area = document.getElementById("wordbox"), el_charcount = document.getElementById("charcount"), clock; el_text_area.onblur = function () { // stop updating when the user isn't typing. if (clock !== undefined) { clearInterval(clock); } }; el_text_area.onfocus = function () { el_text_area.onblur(); //clear the clock if it somehow already exists. clock = setInterval(function () { el_charcount.value = el_text_area.value.length; }, 50); }; All of those external variables are being dynamically modified. The last case where I really used closures was as a debugging tool; I wrote a function which would take as input a function and 'wrap' it up for tracing its execution. Here it is in its full gory detail: function log_call(fn) { var count = 0; return function () { var out, name = fn.name + "#" + (count += 1); function log(verb, value) { console.log(name + " " + verb + " ", value); } log("<--", [].slice.call(arguments, 0)); try { out = fn.apply(this, arguments); log("-->", out); return out; } catch (e) { log("--X", e); throw e; } }; } I use it by writing code like this: function fibs(n) { return n <= 1 ? n : fibs(n - 2) + fibs(n - 1); } // fibs seems slow, let's see what's going on here fibs = log_call(fibs); //later in the code fibs(22); The above code is in fact horribly inefficient, and gets to fibs#57313 before it starts to construct the Fibonacci numbers. The debug trace shows all of this, if you run it. We can solve that by now defining a caching decorator: function cached_onearg(fn) { var cache = {}; return function (x) { var out; if (cache.hasOwnProperty(x)) { return cache[x]; } else { return (cache[x] = fn.call(this, x)); } }; } fibs = log_call(cached_onearg(function (n) { return n <= 1 ? n : fibs(n - 2) + fibs(n - 1); )); The logger now shows a much more reasonable 43 calls.