Skip to content

Commit

Permalink
Split apart the “Breaking Changes” and “Unsupported Features” section…
Browse files Browse the repository at this point in the history
…s into separate sidebar items and files
  • Loading branch information
GeoffreyBooth committed May 2, 2017
1 parent 9491f71 commit a7b0267
Show file tree
Hide file tree
Showing 15 changed files with 345 additions and 276 deletions.
226 changes: 136 additions & 90 deletions docs/v2/index.html

Large diffs are not rendered by default.

141 changes: 0 additions & 141 deletions documentation/sections/breaking_changes.md
Original file line number Diff line number Diff line change
@@ -1,144 +1,3 @@
## Breaking Changes From CoffeeScript 1.x to 2

CoffeeScript 2 aims to output as much idiomatic ES2015+ syntax as possible with as few breaking changes from CoffeeScript 1.x as possible. Some breaking changes, unfortunately, were unavoidable.

<section id="breaking-changes-default-values">

### Default values for function parameters and destructured elements

Per the [ES2015 spec regarding function default parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters) and [destructuring default values](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Default_values), default values are only applied when a value is missing or `undefined`. In CoffeeScript 1.x, the default value would be applied in those cases but also if the value was `null`.

```
codeFor('breaking_change_function_parameter_default_values', 'f(null)')
```

```
codeFor('breaking_change_destructuring_default_values', 'a')
```

</section>
<section id="breaking-changes-bound-generator-functions">

### Bound generator functions

Bound generator functions, a.k.a. generator arrow functions, [aren’t allowed in ECMAScript](http://stackoverflow.com/questions/27661306/can-i-use-es6s-arrow-function-syntax-with-generators-arrow-notation). You can write `function*` or `=>`, but not both. Therefore, CoffeeScript code like this:

```coffee
f = => yield this
# Throws a compiler error
```

Needs to be rewritten the old-fashioned way:

```
codeFor('breaking_change_bound_generator_function')
```

</section>
<section id="breaking-changes-classes">

### Classes are compiled to ES2015 classes

ES2015 classes and their methods have some restrictions beyond those on regular functions.

Class constructors can’t be invoked without `new`:

```coffee
(class)()
# Throws a TypeError at runtime
```

Derived (extended) class `constructor`s cannot use `this` before calling `super`:

```coffee
class B extends A
constructor: -> this
# Throws a compiler error
```

Class methods can’t be used with `new` (uncommon):

```coffee
class Namespace
@Klass = ->
new Namespace.Klass
# Throws a TypeError at runtime
```

</section>
<section id="breaking-changes-bare-super">

### Bare `super`

Due to a syntax clash with `super` with accessors, bare `super` no longer compiles to a super call forwarding all arguments.

```coffee
class B extends A
foo: -> super
# Throws a compiler error
```

Arguments can be forwarded explicitly using splats:

```
codeFor('breaking_change_super_with_arguments')
```

Or if you know that the parent function doesn’t require arguments, just call `super()`:

```
codeFor('breaking_change_super_without_arguments')
```

</section>
<section id="breaking-changes-super-in-non-class-methods">

### `extends` for function prototypes, and `super` in non-class methods

CoffeeScript 1.x allowed the `extends` keyword to set up prototypal inheritance between functions, and `super` could be used manually prototype-assigned functions:

```coffee
A = ->
B = ->
B extends A
B.prototype.foo = -> super arguments...
# Last two lines each throw compiler errors in CoffeeScript 2
```

Due to the switch to ES2015 `extends` and `super`, using these keywords for prototypal functions are no longer supported. The above case could be refactored to:

```
codeFor('breaking_change_super_in_non-class_methods_refactor_with_apply')
```

or

```
codeFor('breaking_change_super_in_non-class_methods_refactor_with_class')
```

</section>
<section id="breaking-changes-dynamic-class-keys-exclude-executable-class-scope">

### Dynamic class keys exclude executable class scope

Due to the hoisting required to compile to ES2015 classes, dynamic keys in class methods can’t use values from the executable class body unless the methods are assigned in prototype style.

```coffee
class A
name = 'method'
"#{name}": -> # This method will be named 'undefined'
@::[name] = -> # This will work; assigns to `A.prototype.method`
```

</section>

<section id="breaking-changes-literate-coffeescript">

### Literate CoffeeScript parsing now more standardized

In CoffeeScript 2’s parsing of Literate CoffeeScript this has been refactored to now be more careful about not treating indented lists as code blocks; but this means that all code blocks (unless they are to be interpreted as comments) must be separated by at least one blank line from lists.

Code blocks should also now maintain a consistent indentation level—so an indentation of one tab (or whatever you consider to be a tab stop, like 2 spaces or 4 spaces) should be treated as your code’s “left margin,” with all code in the file relative to that column.

Code blocks that you want to be part of the commentary, and not executed, must have at least one line (ideally the first line of the block) completely unindented.
21 changes: 21 additions & 0 deletions documentation/sections/breaking_changes_bare_super.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
### Bare `super`

Due to a syntax clash with `super` with accessors, bare `super` no longer compiles to a super call forwarding all arguments.

```coffee
class B extends A
foo: -> super
# Throws a compiler error
```

Arguments can be forwarded explicitly using splats:

```
codeFor('breaking_change_super_with_arguments')
```

Or if you know that the parent function doesn’t require arguments, just call `super()`:

```
codeFor('breaking_change_super_without_arguments')
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
### Bound generator functions

Bound generator functions, a.k.a. generator arrow functions, [aren’t allowed in ECMAScript](http://stackoverflow.com/questions/27661306/can-i-use-es6s-arrow-function-syntax-with-generators-arrow-notation). You can write `function*` or `=>`, but not both. Therefore, CoffeeScript code like this:

```coffee
f = => yield this
# Throws a compiler error
```

Needs to be rewritten the old-fashioned way:

```
codeFor('breaking_change_bound_generator_function')
```
27 changes: 27 additions & 0 deletions documentation/sections/breaking_changes_classes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
### Classes are compiled to ES2015 classes

ES2015 classes and their methods have some restrictions beyond those on regular functions.

Class constructors can’t be invoked without `new`:

```coffee
(class)()
# Throws a TypeError at runtime
```

Derived (extended) class `constructor`s cannot use `this` before calling `super`:

```coffee
class B extends A
constructor: -> this
# Throws a compiler error
```

Class methods can’t be used with `new` (uncommon):

```coffee
class Namespace
@Klass = ->
new Namespace.Klass
# Throws a TypeError at runtime
```
11 changes: 11 additions & 0 deletions documentation/sections/breaking_changes_default_values.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
### Default values for function parameters and destructured elements

Per the [ES2015 spec regarding function default parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters) and [destructuring default values](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Default_values), default values are only applied when a value is missing or `undefined`. In CoffeeScript 1.x, the default value would be applied in those cases but also if the value was `null`.

```
codeFor('breaking_change_function_parameter_default_values', 'f(null)')
```

```
codeFor('breaking_change_destructuring_default_values', 'a')
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
### Dynamic class keys exclude executable class scope

Due to the hoisting required to compile to ES2015 classes, dynamic keys in class methods can’t use values from the executable class body unless the methods are assigned in prototype style.

```coffee
class A
name = 'method'
"#{name}": -> # This method will be named 'undefined'
@::[name] = -> # This will work; assigns to `A.prototype.method`
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
### Literate CoffeeScript parsing

CoffeeScript 2’s parsing of Literate CoffeeScript has been refactored to now be more careful about not treating indented lists as code blocks; but this means that all code blocks (unless they are to be interpreted as comments) must be separated by at least one blank line from lists.

Code blocks should also now maintain a consistent indentation level—so an indentation of one tab (or whatever you consider to be a tab stop, like 2 spaces or 4 spaces) should be treated as your code’s “left margin,” with all code in the file relative to that column.

Code blocks that you want to be part of the commentary, and not executed, must have at least one line (ideally the first line of the block) completely unindented.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
### `extends` for function prototypes, and `super` in non-class methods

CoffeeScript 1.x allowed the `extends` keyword to set up prototypal inheritance between functions, and `super` could be used manually prototype-assigned functions:

```coffee
A = ->
B = ->
B extends A
B.prototype.foo = -> super arguments...
# Last two lines each throw compiler errors in CoffeeScript 2
```

Due to the switch to ES2015 `extends` and `super`, using these keywords for prototypal functions are no longer supported. The above case could be refactored to:

```
codeFor('breaking_change_super_in_non-class_methods_refactor_with_apply')
```

or

```
codeFor('breaking_change_super_in_non-class_methods_refactor_with_class')
```
40 changes: 0 additions & 40 deletions documentation/sections/unsupported.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,3 @@
## Unsupported ECMAScript Features

There are a few ECMAScript features that CoffeeScript intentionally doesn’t support.

<section id="unsupported-let-const">

### `let` and `const`: Block-Scoped and Reassignment-Protected Variables

When CoffeeScript was designed, `var` was [intentionally omitted](https://github.com/jashkenas/coffeescript/issues/238#issuecomment-153502). 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 more valued. In CoffeeScript there remains only one type of variable.

Keep in mind that `const` only protects you from _reassigning_ a variable; it doesn’t prevent the variable’s value from changing, the way constants usually do in other languages:

```js
const obj = {foo: 'bar'};
obj.foo = 'baz'; // Allowed!
obj = {}; // Throws error
```

</section>
<section id="unsupported-named-functions">

### Named Functions and Function Declarations

Newcomers to CoffeeScript often wonder how to generate the JavaScript `function foo() {}`, as opposed to the `foo = function() {}` that CoffeeScript produces. The first form is a [function declaration](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function), and the second is a [function expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function). As stated above, in CoffeeScript [everything is an expression](#expressions), so naturally we favor the expression form. Supporting only one variant helps avoid confusing bugs that can arise from the [subtle differences between the two forms](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function#Function_declaration_hoisting).

Technically, `foo = function() {}` is creating an anonymous function that gets assigned to a variable named `foo`. Some very early versions of CoffeeScript named this function, e.g. `foo = function foo() {}`, but this was dropped because of compatibility issues with Internet Explorer. For a while this annoyed people, as these functions would be unnamed in stack traces; but modern JavaScript runtimes [infer the names of such anonymous functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name) from the names of the variables to which they’re assigned. Given that this is the case, and given that not all functions in function expressions can be named (for example, the functions in `first.fn = ->; second.fn = ->` can’t both be named `fn`) it’s simplest to just preserve the current behavior.

</section>
<section id="unsupported-get-set">

### `get` and `set` Keyword Shorthand Syntax

`get` and `set`, as keywords preceding functions or class methods, are intentionally unimplemented 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) that is slightly more verbose but just as effective:

```
codeFor('get_set', 'screen.height')
```

</section>
9 changes: 9 additions & 0 deletions documentation/sections/unsupported_get_set.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
### `get` and `set` keyword shorthand syntax

`get` and `set`, as keywords preceding functions or class methods, are intentionally unimplemented 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) that is slightly more verbose but just as effective:

```
codeFor('get_set', 'screen.height')
```
13 changes: 13 additions & 0 deletions documentation/sections/unsupported_let_const.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
### `let` and `const`: block-scoped and reassignment-protected variables

When CoffeeScript was designed, `var` was [intentionally omitted](https://github.com/jashkenas/coffeescript/issues/238#issuecomment-153502). 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 more valued. In CoffeeScript there remains only one type of variable.

Keep in mind that `const` only protects you from _reassigning_ a variable; it doesn’t prevent the variable’s value from changing, the way constants usually do in other languages:

```js
const obj = {foo: 'bar'};
obj.foo = 'baz'; // Allowed!
obj = {}; // Throws error
```
5 changes: 5 additions & 0 deletions documentation/sections/unsupported_named_functions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### Named functions and function declarations

Newcomers to CoffeeScript often wonder how to generate the JavaScript `function foo() {}`, as opposed to the `foo = function() {}` that CoffeeScript produces. The first form is a [function declaration](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function), and the second is a [function expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function). As stated above, in CoffeeScript [everything is an expression](#expressions), so naturally we favor the expression form. Supporting only one variant helps avoid confusing bugs that can arise from the [subtle differences between the two forms](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function#Function_declaration_hoisting).

Technically, `foo = function() {}` is creating an anonymous function that gets assigned to a variable named `foo`. Some very early versions of CoffeeScript named this function, e.g. `foo = function foo() {}`, but this was dropped because of compatibility issues with Internet Explorer. For a while this annoyed people, as these functions would be unnamed in stack traces; but modern JavaScript runtimes [infer the names of such anonymous functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name) from the names of the variables to which they’re assigned. Given that this is the case, and given that not all functions in function expressions can be named (for example, the functions in `first.fn = ->; second.fn = ->` can’t both be named `fn`) it’s simplest to just preserve the current behavior.
36 changes: 33 additions & 3 deletions documentation/v2/body.html
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,41 @@
<%= htmlFor('contributing') %>
</section>
</section>
<section id="breaking-changes">
<%= htmlFor('breaking_changes') %>
</section>
<section id="unsupported">
<%= htmlFor('unsupported') %>
<section id="unsupported-let-const">
<%= htmlFor('unsupported_let_const') %>
</section>
<section id="unsupported-named-functions">
<%= htmlFor('unsupported_named_functions') %>
</section>
<section id="unsupported-get-set">
<%= htmlFor('unsupported_get_set') %>
</section>
</section>
<section id="breaking-changes">
<%= htmlFor('breaking_changes') %>
<section id="breaking-changes-default-values">
<%= htmlFor('breaking_changes_default_values') %>
</section>
<section id="breaking-changes-bound-generator-functions">
<%= htmlFor('breaking_changes_bound_generator_functions') %>
</section>
<section id="breaking-changes-classes">
<%= htmlFor('breaking_changes_classes') %>
</section>
<section id="breaking-changes-bare-super">
<%= htmlFor('breaking_changes_bare_super') %>
</section>
<section id="breaking-changes-super-in-non-class-methods">
<%= htmlFor('breaking_changes_super_in_non_class_methods') %>
</section>
<section id="breaking-changes-dynamic-class-keys-exclude-executable-class-scope">
<%= htmlFor('breaking_changes_dynamic_class_keys_exclude_executable_class_scope') %>
</section>
<section id="breaking-changes-literate-coffeescript">
<%= htmlFor('breaking_changes_literate_coffeescript') %>
</section>
</section>
<section id="changelog">
<%= htmlFor('changelog') %>
Expand Down
Loading

0 comments on commit a7b0267

Please sign in to comment.