jashkenas / jashkenas/coffeescript

Revisiting getters and setters

Open
#5,394 26 comments 4 reactions 0 assignees View on GitHub
Dominant language
CoffeeScript
Stars
16.6k
Forks
2k
PR merge metrics
No merged PRs in 30d

Description

This is a feature request. I think it's time to revisit getters and setters, originally discussed in #4915, and explicitly unsupported on https://coffeescript.org/#unsupported-get-set

### Input Code

```coffee
class Foo
get random: -> Math.random()
get name: -> Db.query 'name'
set name: (newName) -> Db.update 'name', newName
```

### Expected Behavior

```js
class Foo {
get random() {
return Math.random();
}
get name() {
return Db.query('name');
}
set name(newName) {
Db.update('name', newName);
}
}
```

### Current Behavior

`error: 'get' cannot be used as a keyword, or as a function call without parentheses`

### Motivation

I have two arguments for why getters and setters should be added to CoffeeScript.

One argument is **increased recommended usage**. Previously @GeoffreyBooth [argued](https://github.com/jashkenas/coffeescript/issues/4915#issuecomment-366580577) that getters/setters seemed likely useful in reactive JavaScript libraries, but "my worry is hypothetical, so maybe it’s not worth worrying about until there’s a concrete example". Now a concrete example is [SolidJS](https://github.com/solidjs/solid), an increasingly popular reactive library for building user interfaces, which uses getters/setters extensively as a way to detect reactivity. For example, components get a `props` argument, and use of `props.name` in e.g. `

Hello {props.name}

` automatically reacts to changes to the `name` property. In the case of `props`, that's all done by SolidJS automatically, but [the SolidJS documentation](https://www.solidjs.com/docs/latest/api) encourages using getters extensively (try searching the page for `get ` including the space). Specifically, in order to build your own reactive objects that work just like the built-in ones, you build an object with getters and/or setters. In SolidJS, use of reactive data needs to be wrapped in a closure for automatic reactivity to work. For single states, this is done via a "getter" function (a function that takes no arguments), which is fine in CS; but for complex state with multiple attributes, it's done by building an object with multiple getters.

(Incidentally, other than this issue, [writing SolidJS apps in CoffeeScript works well](https://github.com/edemaine/solid-meteor-demo). In fact, early versions of SolidJS were written in CoffeeScript! But before the move to getters/setters.)

The second argument is **better TypeScript support** (for #5307). The [recommended workaround for getters and setters in CoffeeScript](https://coffeescript.org/#unsupported-get-set) is to use `Object.defineProperty`. But [TypeScript doesn't understand the effect of `Object.defineProperty`](https://www.typescriptlang.org/play?#code/G4QwTgBGCmDGAuIB2BzANtCBeCBvAsAFAQQDuAlgCbwAWAXBAIwBMADKwDRElgjzkB7BowBsEAPQQAnEQC+AbiJEA8gCMAVnHgA6StABm5JNAAKYAQAdoYeAE8AFDATJ00DhADkNaORQ14Hu4ExBAo0PAM+gCuSAiCSPYAlHjcJFDhUWBIELTkAM7aFNQ0Ejk0+dq8-AKKIbJcIXnhkTFxAgmgaMnBaenwmdm5BUW02BCdEABUZRVVgrUksnKJtUSw7XkCGNpoAiiOWi7b3r7+K0RAA). It's not even possible to [provide an explicit type](https://www.typescriptlang.org/play?#code/G4QwTgBGCmDGAuIB2BzANtCAuCBvAsAFAQQDuAlgCbwAWOSArgLYBG0YANESWCPOQHt6zNp24QU0eBBrRyKGvAAUASmGt2XYhADOUmXIXLGGsCoDcRAL4QAvHnEVqdCAEYATAAZPWnn0E4rgBsEAD0EACc1paERADyLABWcPAAdJTQAGbkSNAACmACAA7s8ACeSjAIyOjQHBAA5LLyig31BNqS8DiZDEgIgkiqDto8UgxgSBC05DqpTrRh0zSzqbz8AjEkVr66Uj19AwJDoGgqIyRj8BNTM3MLNHYQpxAAVMur64JbEFbWFkQiLBjjoBBhUmgBChKikauDmkYAYQgA) because a single assignment to the variable must provide *all the properties at once*, and `Object.defineProperty` can only do one at a time and only after the object has been initialized.

### Context

Conveniently, CS2 already made `get foo: ->` syntax invalid, and similarly for `set`. (Actually, [`get foo: ->` alone is valid](https://coffeescript.org/#try:get%20foo%3A%20-%3E), but it becomes invalid if you [add a blank line before it](https://coffeescript.org/#try:%0Aget%20foo%3A%20-%3E). This is presumably a bug.) It was reserved for this very purpose: `get`/`set` became useful enough to add (and to prevent confusion if someone guessed at the correct notation). So, we can use the obvious syntax and all CS2 code remains compatible.

A `get`/`set` prefix would also go well with `private`, `protected`, and `public` annotation prefixes, which are [TypeScript features](https://www.typescriptlang.org/docs/handbook/2/classes.html#public). (Note that `private`, `protected`, and `public` are reserved words in CoffeeScript, so they could be added while being backward incompatible. It's a separate discussion whether they are worth adding.)

### Alternatives

If the ambiguity with calling functions `get` or `set` is considered too confusing, we could consider a new syntax that is not ambiguous. For example, inspired by ideas in #5367, we could add a `:` prefix:

```coffee
class Foo
:get random: -> Math.random()
:get name: -> Db.query 'name'
:set name: (newName) -> Db.update 'name', newName
```

I personally find this uglier, so would prefer straight `get prop:`/`set prop:` syntax.

Contributor guide

Open the contributing guide

Research direction

Start by reviewing the prior discussion in #4915 and the unsupported get/set documentation, then compare the proposed `get prop:`/`set prop:` syntax with the alternative in this issue. Done means the syntax choice and compatibility implications are settled, with the provided CoffeeScript examples producing the shown JavaScript getter and setter output.

Written by the indexing model from the issue text.

Assessment

Tech stack
coffeescript
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.