-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Docs: Better explanations of comprehensions #2039
Comments
Here is a more symmetrical terminology for classifying the four forms:
#1 and #2 can produce the same JS code, but they give syntax flexibility on emphasizing either the loops or the body. #3 and #4 can produce the same JS code, but they give syntax flexibility on emphasizing either the loops or the resulting elements. #1/#2 differ from #3/#4 in the code they produce. Code from #3/#4 produces an array, which can either be assigned to a variable, inlined into an outer expression, or implicitly returned to the caller. Code from #1/#2 is more terse and generally slightly more performant, because it doesn't produce a new array. |
fn = -> a(); b() # "function literal" fn = -> # any name other than "function literal"
a()
b() Pretty crazy to call those two semantically equivalent constructs different things, right? That's how I feel about differentiating "comprehension" and "array expression" in the first post. Same thing with
You've been using this word a lot, but I think you mean to say "precedents". |
I don't know why it's crazy to come up with different names for two syntactical forms. Sure, both of your examples are functions literals, but the first one uses the single-line function literal form, while the second one uses the block function literal form. What possible benefit do we get from vague terminology? There are precedents all over the place for using names to distinguish alternative syntaxes of the same semantic constructs. You have prefix if vs. postfix if. You have infix arithmetic expressions vs. RPN. You have whitespace-based blocking vs. brace-based blocking. |
Because they're indistinguishable to the compiler. They just look different to us humans. So they're not actually different.
It's not vague when it describes a set of things that are indistinguishable from one another. Go ahead and call the syntaxes whatever you like -- single-line, multi-line, whatever -- it doesn't change the fact that they all cause indistinguishable AST nodes of type
Correct, and those names are all defining syntactic properties. Here, we are trying to categorise/distinguish the set of all AST nodes generated with any syntax containing |
But shouldn't documentation be written for us humans? |
@michaelficarra Who is "we"? You may be trying to distinguish sets of AST nodes, but I'm trying to distinguish syntactical forms, which is more important to most CoffeeScript users. The context of this discussion is the documentation here: It's a syntax document:
|
@geraldalewis Documentation written for humans? Now that's just crazy talk. :) |
@michaelficarra I am reminded that there are only three truly difficult things in computer science: coming up with good names for things and avoiding off-by-one errors. Gotta run. Look forward to more discussion later in the day. |
Here is an example cut at the docs that starts with the imperative forms first. CoffeeScript supports several looping constructs, with two interesting variations. First, you can use natural-language variations of constructs that emphasize the "body" of the loop. Second, loops can be turned into array expressions (aka "comprehensions"). Let's begin with traditional imperative loops. Imperative LoopsThe for-in loop operates on arrays.
The for-of loop allows you to iterate over the keys and values of an object:
The "while", "loop", and "until" statements allow low-level looping:
Postfix LoopsImperative loops can be written in postfix form to emphasize the actions:
You can use the "when" construct to avoid actions on certain loop values:
Array ExpressionsNow we get to a powerful and fundamental feature of CoffeeScript--loops can be turned into array expressions. Let's start with a simple example:
The above code maps values to a new array called cubes, where each element in cubes is the cube of the corresponding element from values. All loops can be turned into expressions using the model above. Also, if a loop is the final statement executed in a function, then the loop is also implicitly turned into an array expression.
ComprehensionsWhen you create an array from another array, you sometimes want to emphasize the mapping function, placing it first in the expression. This syntactical construct is called a list comprehension.
All looping constructs can be turned into comprehensions.
Folks coming from Python and other languages should understand the following behaviors of CoffeeScript for nested comprehensions:
Closure WrappersWhen using a JavaScript loop to generate functions, it's common to insert a closure wrapper in order to ensure that loop variables are closed over, and all the generated functions don't just share the final values. CoffeeScript provides the do keyword, which immediately invokes a passed function, forwarding any arguments.
|
@geraldalewis @jashkenas @TrevorBurnham @michaelficarra See my prior comment on this thread for a proposed rewrite of the "Loops and Comprehensions" section of the docs. First, I introduce headings to call out the different forms that can be used:
Also, I try to lead with the most common form of loops, which are the imperative loops. I'm pretty sure this will put newbies on familiar ground right away, and I think that even advanced programmers will still want to learn the imperative form first. By starting with simple imperative loops, I am able to introduce for/of much earlier than the current docs do. |
The current “Loops and Comprehensions” section of the docs is almost exactly @showell’s comment above, minus the sub-headings which I feel make things more intimidating than they need to be. Closing as this has already been done. |
There are a couple open issues on nested comprehensions, where folks ask whether CoffeeScript should be more like Python and Haskell. However those issues get resolved, we can probably start by documenting the current behavior more precisely.
First, without getting into abstract definitions, it's pretty easy to call out the two main differences between CoffeeScript and Python:
Second, it's important to call out the distinction between these lines in coffeescript:
Third, it's important to distinguish these two syntactical constructs (which produce the same code):
The two pieces of code are identical semantically, but they are different syntactically. IMHO we should use a precise definition of "comprehension" that refers specifically to the first syntax. Wikipedia and Python's PEP both provide precedence for calling out comprehensions as a specific syntactical notation for producing lists. Wikipedia emphasizes the analogy to set builder notation, which specifically puts the output function first in the expression.
I propose these definitions:
I predict these definitions will be up for extensive debate. See the discussion between me and @michaelficarra at issue 2030.
Once the definitions get nailed down, I propose that we reorganize the docs to show all four forms separately, even if we can't agree to come up with four different terms for the forms.
Other related issues:
The text was updated successfully, but these errors were encountered: