views:

362

answers:

8

The more I delve into javascript, the more I think about the consequences of certain design decisions and encouraged practices. In this case, I am observing anonymous functions, a feature which is not only JavaScript-provided, but I see strongly used.

I think we can all agree on the following facts:

  • the human mind does not deal with more than 7 plus minus two entities (Miller's law)
  • deep indentation is considered bad programming practice, and generally points out at design issues if you indent more than three or four levels. This extends to nested entities, and it's well presented in the python Zen entry "Flat is better than nested."
  • the idea of having a function name is both for reference, and for easy documentation of the task it performs. We know, or can expect, what a function called removeListEntry() does. Self-documenting, clear code is important for debugging and readability.

While anonymous functions appears to be a very nice feature, its use leads to deeply nested code design. The code is quick to write, but difficult to read. Instead of being forced to invent a named context for a functionality, and flatten your hierarchy of callable objects, it encourages a "go deep one level", pushing your brain stack and quickly overflowing the 7 +/- 2 rule. A similar concept is expressed in Alan Cooper's "About Face", quoting loosely "people don't understand hierarchies". As programmers we do understand hierarchies, but our biology still limits our grasping of deep nesting.

I'd like to hear you on this point. Should anonymous functions be considered harmful, an apparent shiny syntactic sugar which we find later on to be salt, or even rat poison ?

CW as there's no correct answer.

+2  A: 

I think closures have enormous benefits which should not be overlooked. For example, Apple leverages "blocks"(closures for C) with GCD to provide really easy multithreading - you don't need to setup context structs, and can just reference variables by name since they're in scope.

I think a bigger problem with Javascript is that it doesn't have block scope(blocks in this case referring to code in braces, like an if statement). This can lead to enormous complications, forcing programmers to use unnecessary closures to get around this Javascript design limitation.

Mike
+6  A: 

Anonymous functions are more useful functionally than they are harmful legibly. I think that if you format your code well enough, you shouldn't have a problem. I don't have a problem with it, and I'm sure I can't handle 7 elements, let alone 7 + 2 :)

Jason
+3  A: 

Actually, hierarchies help to overcome 7+/-2 rule the same way as OOP does. When you're writing or reading a class, you read its content and nothing of outside code so you are dealing with relatively small portion of entities. When you're looking at class hierarchies, you don't look inside them, meaning then again you are dealing with small number of entities.

The same if true for nested functions. By dividing your code into multiple levels of hierarchy, you keep each level small enough for human brain to comprehend.

Closures (or anonymous functions) just help to break your code into slightly different way than OOP does but they doesn't really create any hierarchies. They are here to help you to execute your code in context of other block of code. In C++ or Java you have to create a class for that, in JavaScript function is enough. Granted, standalone class is easier to understand as it is just easier for human to look at it as at standalone block. Function seems to be much smaller in size and brain sometimes think it can comprehend it AND code around it at the same time which is usually a bad idea. But you can train your brain not to do that :)

So no, I don't think anonymous functions are at all harmful, you just have to learn to deal with them, as you learnt to deal with classes.

vava
Yes, but pushing this point of view, any program would be just a deeply nested call to anonymous functions, which is what, I think, functional languages do, hence the )))))))))))))) at the end of every LISP program. de-nesting is supposed to improve readability, reducing indenting, keeping the number of nested parentheses down to a manageable minimum, and in general having self-describing code. If you nest it, you are basically doing the computer's job: performing a stack of calls, and in the process you are not labeling (hence describing) anything.
Stefano Borini
LISP code just as readable and self describing as any OOP code. You just have to read it differently, it is a special skill you have to spent time to get. Say, German language is as much readable as English for a person who know them both. The same situation here.
vava
Except that you have no idea about the action you perform on an object until the end of the phrase, something that is even more evident in japanese, where the verb goes _always_ at the end. I personally believe that SVO languages are superior than SOV languages, and I carry my opinion to programming languages.
Stefano Borini
)))... at the end of Lisp code have little to do with function calls specifically, as parentheses are used for much more than function calls.
Pavel Minaev
@Pavel: wrongly stated. I was referring to nesting, not to function calls. However, my poor knowledge of LISP does not allow me to express opinions on how it works.
Stefano Borini
A: 

Who ever came up with the idea of requiring functions to be bound to identifiers did every programmer a disservice. If you've never done functional programming and you're not familiar with and comfortable with functions being first-class values, you're not a real programmer.

In fact, to counter your own argument, I would go so far as to consider functions bound to (global) names to be harmful! Check Crockford's article about private and public members and learn more.

Jim Puls
functions as first-class entities, and the fact that they can be bound to identifiers are separated concepts. In any case, binding a given, descriptive name to a function is useful for documentation. If I give you an anonymous function, you have no way of understanding its usage unless you peek the inside and "execute" its logic. If I tell you that the function is associated to the identifier readFile, you get its behavior just by reading at the associated identifier. This is not possible with anonymous functions, and the advantage of not having to give a name hurts in readability.
Stefano Borini
@Stefano: I don't think it's accurate to say that "functions as first-class entities, and the fact that they can be bound to identifiers are separate concepts". To be able to treat functions as first-class entities, you must be able to avoid giving them a name -- correct? Otherwise they can't be passed to or returned from functions as nameless values like `42`, `6.9` or `"a string"`. And functions as first-class entities is a powerful, useful construct, would you agree?
j_random_hacker
You don't need to give a name to a function like `function(x, y) { return x+y; }` - it's about as useful as writing `++i; // increments i`.
Pavel Minaev
@j_random: yes, but there's a difference. While a number, string, float entity mainly expresses *value*, a function expresses *behavior*, which is the core of its existence. Since we program combining behavior with data, dealing with deeply nested unnamed behavior is, in my opinion, harmful, save the most trivial cases (as Pavel reports).
Stefano Borini
@Stefano: I agree with most of what you said, except that I think it's the deeply-nestedness, rather than the anonymousness, which is at fault. As you say, anonymousness is only appropriate for small functions (e.g. sort comparators or higher-order functions that produce small "modifications" of functions).
j_random_hacker
+12  A: 

As I see it, the problem you're facing is not anonymous functions, rather an unwillingness to factor out functionality into useful and reusable units. Which is interesting, because it's easier to reuse functionality in languages with first-class functions (and, necessarily, anonymous functions) than in languages without.

If you see a lot of deeply nested anonymous functions in your code, I would suggest that there may be a lot of common functionality that can be factored out into named higher-order functions (i.e. functions that take or return ("build") other functions). Even "simple" transformations of existing functions should be given names if they are used often. This is just the DRY principle.

j_random_hacker
This is it exactly. There's nothing inherently wrong with using anonymous functions in situations where they make sense, but when they do anything non-trivial, it's better to put the real functionality in a named function (or several, even).
mcv
A: 

I also think anonymous functions (in latest languages often referred as closures) have great benefits and make code often more readable and shorter. I sometimes am getting really nuts when I have to work with Java (where closures aren't first class language features).

If indentation and too many encapsulated function-variables are the problem then you should refactor the code to have it more modular and readable.

Regarding java-script I think that function-variables look quite ugly and make code cluttered (the encapsulated function(...){} string makes java-script code often less readable). As an example I much prefer the closure syntax of groovy ('{}' and '->' chars).

manuel aldana
+2  A: 

Amusingly, JavaScript will let you name "anonymous" functions:

function f(x) {
   return function add(y) {
        return x+y;
   };
}
Mark Bessey
Once you understand that those are **function expressions**, not just "anonymous functions", it becomes less amusing ;)
kangax
@Mark Bessey: Not sure exactly what you're saying. Most languages that enable you to produce values of function type (== anonymous functions) will enable you to associate those values with variable names. This is just the same as assigning the "anonymous" value `4.2` to the named variable `x` in the C statement `x = 4.2;`.
j_random_hacker
I guess the point I was trying to make is that function expressions have an optional name property, which is ironic for an "anonymous" function. For example: var s = function square(x) {return x*x;}which defines a function named square and binds it to the varable s. There's rarely a good reason to not name fnctions n Javascript. The function name shows up in stack traces, for example, which can help with debugging.
Mark Bessey
@Mark Bessey There are actually perfectly valid reasons to **avoid named function expressions**. I wrote an article on this subject some time ago — http://yura.thinkweb2.com/named-function-expressions/ In particular, see "JScript bugs" section.
kangax
Kangax, that's interesting. Fortunately, I'm mostly working in an environment where JScript bugs are irrelevant to me, but it's good to know that those problems exist.
Mark Bessey
@Mark Bessey: Thanks for explaining, I see what you mean now. Yes, that's a funny little wrinkle, interesting that it does have some uses (and cause some problems) though!
j_random_hacker
A: 

If a function is not understandable without a name, the name is probably too long. Use comments to explain cryptic code, don't rely on names.

kennebec
comments don't execute. code does.
Stefano Borini