Skip to content

Revisiting getters and setters #5394

Open
@edemaine

Description

@edemaine

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

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

Expected Behavior

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 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, 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. <h1>Hello {props.name}</h1> automatically reacts to changes to the name property. In the case of props, that's all done by SolidJS automatically, but the SolidJS documentation 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. 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 is to use Object.defineProperty. But TypeScript doesn't understand the effect of Object.defineProperty. It's not even possible to provide an explicit type 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, but it becomes invalid if you add a blank line before it. 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. (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:

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions