Skip to content
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

[CS2] An explanation of why we don’t currently support certain features #4469

Merged
merged 10 commits into from Apr 4, 2017
Merged
22 changes: 22 additions & 0 deletions documentation/sections/contributing.md
@@ -0,0 +1,22 @@
## Contributing
Contributions are welcome, feel free to fork a copy and submit a pull request.

If there is a core part of ECMAScript you think we are missing, head on over to [CoffeeScript issues on GitHub](http://github.com/jashkenas/coffeescript/issues), and dig around in the open and closed issues.

For more resources on adding to CoffeeScript, please see [the Wiki](https://github.com/jashkenas/coffeescript/wiki/%5BHowto%5D-Hacking-on-the-CoffeeScript-Compiler) or [How The Parser Works](https://github.com/jashkenas/coffeescript/wiki/%5BHowTo%5D-How-parsing-works) if you are interested in learning how the parser works.

There are several things you can do to increase your odds of having the pull request accepted:

* Create tests! Any pull request should probably include basic tests to verify you didn't break anything, or future changes won't break your code.
* Follow the CoffeeScript style guide
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a style guide? Maybe this should just say that they should follow the style of the rest of the codebase.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can rip this line out. I was thinking that we probably should have a style guide in place.

* Ensure any ECMAScript addition is mature (at Stage 4), with no further potential syntax changes.
* Make sure that you are keeping to the core ideas of CoffeeScript
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is vague, we could just leave it out.

* Adding only the features that help the community rather than a specific use case.

Of course, it's entirely possible that you have a great addition, but it doesn't fit directly with the CoffeeScript code base.

This might be the time to...

## Roll Your Own

You are of course, welcome to fork the language and add in the features you want. There are quite a few really interesting projects that have done so, and more are welcome.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section should include a link to the wiki page where Jeremy has compiled a list of CoffeeScript’s derivatives.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

11 changes: 11 additions & 0 deletions documentation/sections/get_set.md
@@ -0,0 +1,11 @@
## get and set
Get and set are intentionally not implemented as keywords in CoffeeScript.

This is by design. While using get/set is still possible, CoffeeScript considers them anti-patterns.

In ECMAScript these convenience function decorators were introduced for very specific uses with the browser DOM.

If you are still convinced you need to use `get`/`set`, here are some workarounds:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the documentation should avoid sounding judgmental. This section should probably be written more like:

get and set, as keywords preceding functions or class methods, are intentionally not implemented in CoffeeScript. This is to avoid grammatical ambiguity, since in CoffeeScript such a construct looks identical to a function call (e.g. get(function foo() {})) and because there is an alternate syntax that is slightly more verbose but just as effective:

(insert the best Object.defineProperty example here)


* Use a [Proxy object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). Also see an [example of using Proxy](https://nemisj.com/why-getterssetters-is-a-bad-idea-in-javascript/).
* Add them direcly [the long way round](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty).
103 changes: 103 additions & 0 deletions documentation/sections/let_const.md
@@ -0,0 +1,103 @@
## let and const

`let` and `const` are intentionally left out of CoffeeScript at this time.

These keywords don't fit in with the core tennents of CoffeeScript, and are easily mitigated by careful naming and thoughtful design. The benefit is a lot less typing on your part.

Neither keyword adds sufficient value, especially when we compare them against a few of the core tennents:

* Don't have to think about variable declaration
* Be terse
* Reduce complexity
* Keep your code clean
* Reduce code quality issues
* Increase readability
* Bring out the best from ECMAScript

Ah! You might think, that is all very well and good, but I make a lot of mistakes in my code, and these keywords protect me from myself and others!

Well, indeed these keywords may help you. However CoffeeScript has already dealt with some of these issues.

Let's explore a little.

### The `let` Keyword

Quite a lot of time was spent trying to find reasons to generate `let` in our latest branch, and what rules we would need to check.

After quite a bit of testing it turned out that `let` wasn't really much of an advantage over var. CoffeeScript was originally written to help deal with scope leakage and already implements _all variables as var_ and in the correct scope. CoffeeScript doesn't have the issue of accidental global variables.

```coffeescript

a = 5
b = () ->
c = 6

```

And our resulting ECMAScript:


```javascript

// Generated by CoffeeScript 2.0.0-alpha1
(function() { // We generate the entire file within a closure by default, to avoid variabe leakage.
var a, b; // a, b are visible in all remaining scopes as a result of closures.
// however they are NOT visible globally, only within this file / module.
a = 5;

b = function() {
var c; // c is not seen outside of this scope.
return c = 6;
};

}).call(this);

```

Aha! Right there in that function, you could accidentally override `a`!

Well this is true. But consider [this conversation](https://github.com/jashkenas/coffeescript/issues/238#issuecomment-153502) in the issues:

> It's certainly possible to accidentally blow away an outer variable within an inner scope, but (in Ruby and Python), this turns out not to be a very common problem. If you don't nest functions too deeply, don't introduce a lot of globals, and give things proper names, it never arises.

And here is another useful comment by jashkenas

> And if they do clash, shadowing the variable is the wrong answer. It completely prevents you from making use of the original value for the remainder of the current scope. Shadowing doesn't fit well in languages with closures-by-default ... if you've closed over that variable, then you should always be able to refer to it.

If you take the time to name things well, you actually have to go out of your way to clobber another variable. The trade off for making you take more time naming your variables is one less keyword to have to type over and over.

Hrmmm. Good point. But what about `const`?!? I REALLY want to make everything constant!

### The `const` Keyword

`const` is another typical keyword that doesn't really add significant value. Primarily `const` is used to avoid accidental reassignments. In practice this is just like `let` above, make sure you are consistant with scope, and name your variables well.

`const` also causes a dilemma for CoffeeScript, because suddenly we can get block level shadowing, but it's only const! Well then why not include `let`! Well hopefully you see why we haven't included `let` by now.
So how the heck do I make things constant?

```coffeescript

a = 0
b = 1
` const c = a + b `

c++ # Error - c is a const.

```


In reality the const keyword refers to the variable itself, _not always the value_ of that object.

```javascript

const x = 5;
const y = {};
x++; // error. Assignment to const
y[x] = x; // {'5': 4} Wait what!??
y = x; // error. Assignment to const

```

In other words, the statement const x++ doesn't let you change the variable as you would expect. However it WILL let you change the contents of y, because you aren't changing what y is pointing at. It is still the same object!

You actually have to 'freeze' an object to make it a constant. Use [Object.freeze(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section is way too long. I would try to reduce it to at most three paragraphs, and link to other sources to add more detail. Also try to write it in a dry tone like the rest of the documentation. Something like:

When CoffeeScript was designed, var was intentionally omitted. This was to spare developers the mental housekeeping of needing to worry about variable declaration (var foo) as opposed to variable assignment (foo = 1). The CoffeeScript compiler automatically takes care of declaration for you, by generating var statements at the top of every function scope. This makes it impossible to accidentally declare a global variable.

let and const add a useful ability to JavaScript in that you can use them to declare variables within a block scope, for example within an if statement body or a for loop body, whereas var always declares variables in the scope of an entire function. When CoffeeScript 2 was designed, there was much discussion of whether this functionality was useful enough to outweigh the simplicity offered by never needing to consider variable declaration in CoffeeScript. In the end, it was decided that the simplicity was valued more. Ultimately there is nothing provided by let or const that you cannot achieve with var and careful naming of variables, avoiding reusing variable names within the same function scope.

6 changes: 6 additions & 0 deletions documentation/sections/unsupported.md
@@ -0,0 +1,6 @@
## Unsupported ES5+ Functionality

There are a few intentional ommisions in the language.

* `let` / `const` keywords
* `get` / `set` convenience keywords
43 changes: 43 additions & 0 deletions documentation/sections/why_coffeescript.md
@@ -0,0 +1,43 @@
## Why CoffeeScript?
There are a few things you should know about CoffeeScript.

CoffeeScript is about:

* Reducing complexity
* Keeping your code clean
* Being terse
* Removing as many 'Bad Parts' as possible
* Reducing code quality issues
* Increasing readability
* Being stable
* Bringing out the best from ECMAScript

CoffeeScript is NOT about:

* Having the latest XX/YY feature
* Implementing every aspect of ECMAScript, just because it's there.
* Trying to appease developers from other languages

There are many style choices within the language, and they have been carefully thought out. Taking time out to understand how CoffeeScript works, and how to work well with it will help you write better, easier to read, safer and more reliable code.

## Why not just use ECMAScript 7?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation always refers to ES5 or ES2015 or ES2015+, never “ES6” or “ES7”.



ECMAScript has adopted much of the syntax and ideas of CoffeeScript. Unfortunately it still exposes you to many of the 'Bad Parts' of the language. On top of that the ECMASCript syntax is cumulative, allowing a much larger spectrum of potential errors.

ECMAScript 6/7 are standards, and you should probably know how to work with it. You might assume that working with ECMAScript 6 is more direct or cleaner. But using those newer features still requires a transpiler, for instance Babel. If you consider most front end targetted code still needs a build tool like Grunt, then you will find adding an extra step for CoffeeScript very trivial.

You will find:

* CoffeeScript has adopted the vast majority of stable ECMAScript features.
* CoffeeScript protects you by avoiding a wide spectrum of errors.
* You have consistent variable behavior without having to think about context and usage of every variable.
* You reduce cognitive load of the syntax and can get down to solving problems.
* Most parentheses are optional, making functional programming styles much cleaner.
* CoffeeScript forces indenting, which makes code much more readable, and eliminates many nesting errors.
* Most Parentheses go away and no more semicolan errors.
* CoffeeScript has an existential operator `?`, which makes checking for missing elements a breeze.
* CoffeeScript is fully interoperable with ECMAScript code.
* Cleaner syntax for yields, async, and unbound functions `() ->`

You can learn CoffeeScript in a few hours, and you will be amazed at how much better your code will be.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like marketing copy. I would maybe rewrite this section to focus on specifically what CoffeeScript offers that ES2015+ doesn’t. There are still many benefits that CoffeeScript has over ES2015+, most importantly (to me) significant whitespace/forced indentation, optional braces and parentheses, and the existential operator. When I introduce people to CoffeeScript and try to convince them of its value, I often use this example, with its JavaScript equivalent side-by-side:

if foo?
  foo()

This illustrates all three of my points:

  • The existential operator does a proper check for truthiness, better than the if (foo) that most JavaScript developers would write. (Next I show them if foo?.bar?.baz?.)
  • The significant whitespace/forced indentation and optional braces means that if someone edits this code down the line and puts a second line of code under the if, we don’t need to worry about remembering to add braces.
  • Fewer braces and parentheses means less punctuation surrounding your code, which means there’s less to read and therefore less to be distracted by—and therefore bugs should be easier to spot.

15 changes: 14 additions & 1 deletion documentation/v2/body.html
Expand Up @@ -16,6 +16,7 @@
<section id="overview">
<%= htmlFor('introduction') %>
<%= htmlFor('overview') %>
<%= htmlFor('why_coffeescript') %>
</section>
<section id="coffeescript-2">
<%= htmlFor('coffeescript_2') %>
Expand Down Expand Up @@ -98,6 +99,15 @@
<%= htmlFor('embedded') %>
</section>
</section>
<section id="unsupported">
<%= htmlFor('unsupported') %>
<section id="let_const">
<%= htmlFor('let_const') %>
</section>
<section id="get_set">
<%= htmlFor('get_set') %>
</section>
</section>
<section id="breaking-changes">
<%= htmlFor('breaking_changes') %>
</section>
Expand All @@ -106,7 +116,7 @@
</section>
<section id="source-maps">
<%= htmlFor('source_maps') %>
</section>
</section>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mind please giving me write access to this branch? There are lots of little things like this trailing whitespace, and misspellings, that are easier to just fix than point out in comments.

<section id="cake">
<%= htmlFor('cake') %>
</section>
Expand All @@ -130,6 +140,9 @@
<section id="annotated-source">
<%= htmlFor('annotated_source') %>
</section>
<section id="contributing">
<%= htmlFor('contributing') %>
</section>
</section>
<section id="changelog">
<%= htmlFor('changelog') %>
Expand Down
9 changes: 9 additions & 0 deletions documentation/v2/sidebar.html
Expand Up @@ -3,6 +3,9 @@
<li class="nav-item">
<a href="#top" class="nav-link" data-action="sidebar-nav">Overview</a>
</li>
<li class="nav-item">
<a href="#top" class="nav-link" data-action="sidebar-nav">Why CoffeeScript</a>
</li>
<li class="nav-item">
<a href="#coffeescript-2" class="nav-link" data-action="sidebar-nav">CoffeeScript 2</a>
</li>
Expand Down Expand Up @@ -81,6 +84,9 @@
<li class="nav-item">
<a href="#embedded" class="nav-link" data-action="sidebar-nav">Embedded JavaScript</a>
</li>
<li class="nav-item">
<a href="#unsupported" class="nav-link" data-action="sidebar-nav">Unsupported Features</a>
</li>
</ul>
</li>
<li class="nav-item">
Expand Down Expand Up @@ -119,6 +125,9 @@
<li class="nav-item">
<a href="#annotated-source" class="nav-link" data-action="sidebar-nav">Annotated Source</a>
</li>
<li class="nav-item">
<a href="#contributing" class="nav-link" data-action="sidebar-nav">Contributing</a>
</li>
</ul>
</li>
<li class="nav-item">
Expand Down