Skip to content
This repository has been archived by the owner on Feb 19, 2018. It is now read-only.

CS2 Discussion: Features: Getters and Setters #17

Closed
carlsmith opened this issue Jul 30, 2016 · 61 comments
Closed

CS2 Discussion: Features: Getters and Setters #17

carlsmith opened this issue Jul 30, 2016 · 61 comments

Comments

@carlsmith
Copy link

carlsmith commented Jul 30, 2016

ES6 getters and setters don’t have there own discussion yet, and they came up in the discussion of decorators, specifically the need the ensure that any syntax that CoffeeScript adopts for defining getters and setters must allow decorators to be applied to the function literals.

This is a general discussion, but to carry over what was brought up already: From the example @carlmathisen used when he raised the issue, we can currently do this (note that readonly is a decorator):

class Person
  name: readonly ->
    return "#{@first} #{last}"

That works well, but the getter and setter syntax needs to allow decorators to be applied in a similar way (or an alternative must be provided).

EDIT by @GeoffreyBooth Consensus from the below thread:

The get and set shorthand syntax is too infrequently used, and a discouraged practice, for CoffeeScript to support directly. Getters and setters can be created via the Object.defineProperty method, so they technically already are supported in CoffeeScript; supporting the shorthand syntax as well just makes them more convenient to use, but Douglas Crockford argues that we should rarely if ever be using them.

So the task for CS2 is to have the compiler throw an error when it appears that a get or set shorthand syntax keyword is being used. Things like the following:

class A
  get b: ->

c =
  get d: ->

e = ->
  get f: ->

And set for all of the same. The above all compiles, which isn’t a good thing. These should throw compiler errors, without prohibiting people from using variables or functions named get or set elsewhere in the code. Basically when a call to a function named get or set is given an argument that is an object with one property, and that property’s value is a function, we throw an error. In other words, this:

  get({
    b: function() {}
  });

If anyone needs to call a function named get and pass an object, well, they just need to define the object ahead of time and assign it to a variable, and then call get like get(obj). That’s a reasonable workaround for what should be a tiny edge case. What we shouldn’t do, even though we could, is make get and set keywords. They’re not keywords in JavaScript, and they strike me as quite plausible names for functions, so I don’t want to cause a breaking change for people who have such variable names when we can solve this problem with more precision.

@carlmathisen
Copy link

carlmathisen commented Jul 31, 2016

Not really a solution, but more of an FYI if anyone were thinking in the same direction I were initially:

If we were to just add functions to a prototype object of an ES6 class when transpiling, it won't work well since super only works when using ES6 syntax sugar:

This works, but we don't have decorators out of the box like CoffeeScript has today:

class Person {
  speak() {
    return "hey"
  }
}

class John extends Person {
   speak() {
    return super.speak() +  " dude!"
  }
}

The following code would allow us to wrap the prototype function with decorators, but super fails.

John.prototype.speak = () => { 
  return super.speak() + " dude!"
  }
}

Uncaught SyntaxError: 'super' keyword unexpected here

Here's the code in Babel REPL

In other words, we probably need get/set.

@dadleyy
Copy link

dadleyy commented Jul 31, 2016

the example given,

class Person
  name: readonly ->
    return "#{@first} #{last}"

is flawed in the sense that the context of this (@first) is not the same context as that readonly has available.

@carlmathisen
Copy link

carlmathisen commented Jul 31, 2016

Extremely good point, @dadleyy. Fat arrow binding doesn't work either, since the scope becomes the prototype declaration of Person instead if this.

@objectkit
Copy link

objectkit commented Aug 1, 2016

I hope this is on topic. Explicit support for getters and setters in the new CS would be good. However, if there were something like this...

class DataObject
    # meta propName/setterName collison
    set id (@id) ->

the first thing to come to attention is the naming of the property 'id'. In the example above, there is a setter named id, and a property called id. This is obviously not going to work at runtime in ES, as unfortunately, setter and getter names cannot have the same name as any other property. An easy solution would be:

class DataObject
    # using a property named _id
    set id (@_id) ->

but in an ideal world, it would be great if that was taken care of automatically in transpilation e.g. with a getter/setter | propName collision being rectified in the output code, perhaps using the underscore convention? E.g. in the first example, the output code would have a setter named id, but would store the value on a property named _id.

Another thing which would be nice (but difficult) would be implicit getters. I know. I said I hated implicit returns, and now I'm asking for discussion about implicit setters and getters! But hear me out...

If a single setter was implemented, wouldn't it make sense in the majority of cases to also have an implicit getter? Such that even though we would only see the typed code from the first example, the output code would look like this:

class DataObject {
    set id (value) {
        this._id = value;
    }
    // this implicit getter was added automatically by the compiler
    get id () {
        return this._id;
    }

Data leakage is the risk of course, as is the case with implicit returns, but just throwing it out there.

Anyhow, back on point. Support for getter/setter syntax in CS6 would be very good. Especially in the context of decorators...

class DataObject
    @final @type String
    set id (@_id) ->

In the example above, decorators could be used to ensure that only values of type String are provided, and that the setter can only be used once. Subsequent attempts would throw an error.

But to really stretch the boat out there, what if property definition was changed slightly? A property is declared as it is now, but, at discretion of the developer, setters or getters could be added only if needed. I suppose I am couching on using decorators as type enforcements on setters here...

class DataObject

    # standard definition of a prototype property as today
    id: null

    # an optional override of the setter for this property
    # set/get | prop name collision would be taken care of by the compiler with rewrites    
    @type String
    set id (@id) ->

Thoughts?

@dadleyy
Copy link

dadleyy commented Aug 1, 2016

great stuff @objectkit.

Another thing which would be nice (but difficult) would be implicit getters. I know. I said I hated implicit returns, and now I'm asking for discussion about implicit setters and getters! But hear me out...

This idea and the example given kinda remind me of objective-c @property syntax which I think was a successful feature in that language. I think it is worthwhile for us to continue exploring.

but in an ideal world, it would be great if that was taken care of automatically in transpilation e.g. with a getter/setter | propName collision being rectified in the output code

very interesting! I'm not sure if we need to address this since you're going to have the same problem in es6 - not being able to have a getter/setter with the same name as another property (specifically the one that it is intended to work with). I'd say that it is up to the developer to design around this; what are they trying to do? Is there value of having a getter with the same name as a property or is that just a side-effect of wanting to have logic in a setter but needing to support access to the underlying data?

I think there is tremendous appeal to have logic in the setting of values; I'm pretty sure this is how vuejs handles data binding and dom updates, but I also get scared that people might end up mis-using this feature - creating complex setter where they should be defining functions on the prototype; I'm not sure we would want to see things like this:

class User {
  set name(val) {
    let {errors} = this;
    this.$name = val;
    function success() {
      errors["name"] = false;
    }
    function fail() {
      errors["name"] = true;
    }
    this.save().then(success).catch(fail);
  }
  get name() { return this.$name; }
}

On the other hand, I feel like if some kind of mutation needs to happen to the value of a property it's better to come up with a new property and add the getter there. For example, if the developer is trying to:

class TokenID {
  get id() {
    return `${this.token}_${this.id}`;
  }
}

they really should probably be doing:

class TokenID {
  get token_id() {
    return `${this.token}_${this.id}`;
  }
}

@dadleyy
Copy link

dadleyy commented Aug 1, 2016

We're also going to want to figure out how to handle defining getters and setters outside the initial definition of classes like the can do with functions now. For example we're currently allowed to:

class User
  constructor: ->

User::anotherFn = ->

what would this look like for getters/setters? If we decide to go with the get/set as keywords:

class User
  get full_name: ->

I think we might be hurting ourselves in the sense that I'm sure we'd want to avoid having to support something like:

User::get full_name = ->

Generally speaking I'm more in favor of a language's use of keywords than operators (isnt/is vs ===/!==) in situations like this but If we were to use a new function operator (e.g ~>) we would be able to have more flexibility in where we can define getters and setters:

class User
  full_name: ~>           # getter
  full_name: (new_val) ~> # setter

User::full_name = ~>           # getter
User::full_name = (new_val) ~> # setter

@carlmathisen
Copy link

Just remember that by adding functions on the prototype object and not in the class definition, super isn't available anymore. Thus removing an important feature of ES6 classes.

@dadleyy
Copy link

dadleyy commented Aug 1, 2016

@carlmathisen good call - I didn't know that. I wonder why super is restricted that way; isn't it possible to accomplish accessing a super class's prototype somehow anyways? Is there an ask that the compiler always "lifts" prototype definitions into the main definition of classes then? We'd still write:

class FriendListManager
  constructor: (@user) ->

FriendListManager::addFriend = (other) ->

but the compiler would raise addFriend into the compiled class definition:

class FriendListManager {
  /* ... */
  addFriend(other) {
  }
};

I think this raises complications regarding the compiler's ability to check the presence of the initial definition and what do to if it is missing. Anyways I think we'd want to do some sort of "lifting" for getters/setters right?

side note - the mdn website has an issue with their example on the super page; their Square class defines getters and setters for the area property but attempt to assign it a value in the setter:

class Square extends Polygon {
  constructor(length) {
  }

  get area() {
    return this.height * this.width;
  }

  set area(value) {
    this.area = value;  // bad: infinite call stack
  } 
}

@rattrayalex
Copy link
Contributor

I think ~> is interesting, but a tricky bit of syntax to have to remember for a relatively infrequently used feature.

I think the User::get full_name = -> concern likely does not apply; in this case, one would probably use Object.defineProperty in any case. And I don't think it's a style of programming that should probably be encouraged...

Are there likely technical barriers to using get and set keywords in a way that mimics ES6?

@GeoffreyBooth
Copy link
Collaborator

What’s our consensus here? Are getters and setters a feature we want CoffeeScript to support?

If so, can someone write an example showing the proposed syntax? Ideally the syntax would be backwards-compatible or as close to backwards-compatible as possible.

@dadleyy
Copy link

dadleyy commented Sep 6, 2016

I think we should take the same stance on this issue that we are on the module syntax and classes in the next compiler - get the syntax as close to es6 as possible and then it's the job of the next transpiler (babel) to worry about older runtimes.

Therefore, my suggestion is that we support:

# inside class definition
class DietManager

  constructor: -> # ...

  get total_calories: ->
    total = 0
    total += calories for {calories} in @meals
    total

  set field: (new_value) ->

# inside object literal
current_user =
  get full_name: -> "#{@last_name}, #{@first_name}"

and then compile that down to it's es6 form:

class DietManager {
  constructor() { }
  get total_calories() { /* ... */ }
  set field(new_value) { /* ... */ }
};

var current_user = {
  get full_name() { /* ... */ }
};

I will say that I think this is a must-have. There are good reasons to avoid the over-use of these features but I say leave it up to the end user to know the right time and place for them - same is true for classes/generators/etc...

@rattrayalex
Copy link
Contributor

Personally I think it makes sense to build get and set if they're build-able. They're good features, and don't have downsides. But if it's too hard to build, I believe it's possible to get the benefits with Object.defineProperty.

@dadleyy
Copy link

dadleyy commented Sep 8, 2016

I believe it's possible to get the benefits with Object.defineProperty

Most definitely. Currently, if I want getters on a class I usually do the following sugar inside my modules (with an example not split up into modules for brevity):

attr = (target, name, descriptor) -> Object.defineProperty target, name, descriptor
attr.access = (target, property, get) -> attr target, property, {get}

class Revisions

  constructor: (@original = {}) ->
    @revision_history = []
    attr.access this, "latest", @apply

Revisions::apply = ->
  {revision_history, original} = this
  result = Object.assign {}, original
  # ... use items in store to apply to result ...
  result

class UserManager extends Revisions

  constructor: (user) ->
    super user

# ... methods to update parts of the user, e.g:
UserManager::addFriend = (other_user) ->
  {revision_history} = this
  revision_history.push {type: "friendship", payload: other_user}

UserManager::commit = (callback) ->
  {latest} = this
  # save to API...

# ... elsewhere:

manager = new UserManager {name: "danny"}
{latest: user} = manager

I just think it would be nice to see this kind of thing be recognized as something that - considering writing it in es6 would be more concise - should make it into cs w/o the need for the user to provide that sugar. I think getters are a nice gateway into very cool patters seen in immutablejs and redux.

@connec
Copy link

connec commented Jan 31, 2017

I'm not sure about adding the syntaxes get property and set property to CS. Not only do they look like totally valid method calls, they're actually ambiguous in most cases. Getting this to parse cleanly, without a bunch of hacks in the parser as well as in the AST, would be a challenge I expect.

class A
  get name: -> # get({ name: function () {} })
  set name: -> # set({ name: function () {} })

get name: -> # get({ name: function () {} })
obj = { get name: -> } # Probably a getter
obj =
  get name: -> # Getter, maybe?
obj = get name: -> # obj = get({ name: function () {} }) ?

key: get name: -> # { key: get({ name: function () {} }) }
key: { get name: -> } # Probably a getter

We could say it's only valid in explicit objects, except that would mean they're not valid in classes.

On top of that, I'm not sure that enabling getters and setters - which commit the sins of making expensive operations look cheap, and potentially side-effecting operations look pure - is a good idea.

Consider immutablejs, which explicitly uses defineProperty when creating its Record types. The definition of the Record constructor uses defineProperty, but you don't need to write any getters or setters in order to use them.

I think the Object APIs are sufficient for the few important uses of getters and setters.

@rattrayalex
Copy link
Contributor

I don't really think this would jibe super well with coffeescript, but in case it's the syntax that's a problem, here's how I'm solving it in LightScript:

class A:
  prop() -get> 
    this._prop
  prop(newValue) -set> 
    this._prop = newValue

@triskweline
Copy link

On top of that, I'm not sure that enabling getters and setters - which commit the sins of making expensive operations look cheap

The Uniform access principle has a long tradition in language design.

Getters and setters also allow us to avoid breaking changes when we later decide that a stored property must be computed or delegated.

@GeoffreyBooth
Copy link
Collaborator

I don’t think getters and setters rise to the level of something so abhorred that they should be banned from the language. They’re right there in the MDN example of a class. Ember.js uses them extensively.

I understand people can use defineProperty to create getters and setters today, but that seems like forcing people to use a workaround.

@mrmowgli
Copy link

mrmowgli commented Feb 1, 2017 via email

@jashkenas
Copy link

As requested by @GeoffreyBooth, moving a comment over to this ticket.

As a side note — it would be nice if discussion around language features could happen over on the coffeescript Issues pages, so that folks who are watching the project can get alerted that they're being talked about / decisions being made.

Now to the meat:

If there is some fundamental compatibility reason why we must have getters and setters in CoffeeScript, then that's a little sad, but it's alright.

But up until that point, getters and setters have always been an intentional omission from CoffeeScript (like named functions, manual variable declaration, loose equality ==, and so on). They're one of the bad parts of JavaScript that CoffeeScript as a concept benefits from leaving out.

As you write ES3, you know what's a function call and what's a property access. When you use getters and setters, it's suddenly opaque to you which is which, with the possibilities of serious performance considerations and also side effects in places where you might not expect them.

This uncertainty brings with it a little bit of syntax sugar — to make a method call look like a property access. Why not just let a method call be a method call?

@dadleyy
Copy link

dadleyy commented Mar 13, 2017

Why not just let a method call be a method call?

It is nice to be able to use the "computed value"-ness of getters in browser-side rendering where templates come into play:

<p>{{diet_manager.total_calories}}</p>

although I suppose you could always just calculate the value in code before rendering or have some kind of call helper.

@GeoffreyBooth
Copy link
Collaborator

GeoffreyBooth commented Mar 13, 2017

So when I last commented I was thinking about Ember.js’ computed properties, which I assumed use get and set under the hood. (And maybe they do, I haven’t checked.) I tried to find an example of Ember requiring a developer to use getters and setters and I couldn’t, basically because they created Ember.get and Ember.set as wrappers for the functionality. The closest I could find is this: emberjs/rfcs#108

But I think my general point is still valid. There’s a lot of activity nowadays in frameworks that observe plain JavaScript objects (React, Vue, etc.) and re-render as a result of changes. I think it’s only a matter of time before some framework will require the object being observed to use getters and setters; or if one wants to write a component or plugin for one of these libraries, it will need to use getters and setters. (Imagine writing a plugin for a new type of Ember computed property.) Of course you can always go the “long way around” and use Object.defineProperty, but that feels worse than backticks. Granted, my worry is hypothetical, so maybe it’s not worth worrying about until there’s a concrete example. But seeing how rapidly frameworks like React adopt even non-finalized ES7 features like decorators, I feel like it’s only a matter of time before someone decides that getters and setters are a must-use, especially since they’re part of ES5.

One last “real” case: the JavaScript proxy pattern:

(function() {
    // log all calls to setArray
    var proxied = jQuery.fn.setArray;
    jQuery.fn.setArray = function() {
        console.log( this, arguments );
        return proxied.apply( this, arguments );
    };
})();

Sometimes I want to modify an imported library function, without forking the underlying library. I might use something like the above. If the library in question used a getter or setter instead of a function, I would need to use a getter or setter to override it. Again, I could use Object.defineProperty, but that feels like a hack. (Not that the proxy pattern is a hack 😉 .)

More fundamentally, if it’s not discouraged by MDN—and indeed, it’s right there in their basic class example—and it can already be achieved via Object.defineProperty, then what do we gain by denying people a more-convenient syntax?

@GeoffreyBooth
Copy link
Collaborator

So the PR for documentation is in progress over at jashkenas/coffeescript#4469 (thanks @mrmowgli). I think the other thing that we have consensus that we should do is have the compiler throw an error when it appears that a get or set keyword is being used. Things like the following:

class A
  get b: ->

c =
  get d: ->

e = ->
  get f: ->

And set for all of the same. The above all compiles, which isn’t a good thing. These should throw compiler errors, without prohibiting people from using variables or functions named get or set elsewhere in the code. Basically when a call to a function named get or set is given an argument that is an object with one property, and that property’s value is a function, we throw an error. In other words, this:

  get({
    b: function() {}
  });

If anyone needs to call a function named get and pass an object, well, they just need to define the object ahead of time and assign it to a variable, and then call get like get(obj). That’s a reasonable workaround for what should be a tiny edge case. What we shouldn’t do, even though we could, is make get and set keywords. They’re not keywords in JavaScript, and they strike me as quite plausible names for functions, so I don’t want to cause a breaking change for people who have such variable names when we can solve this problem with more precision.

Is everyone okay with this proposal? @connec, what do you think, and how difficult do you think it would be to implement? I’m thinking that perhaps it might not require a change in grammar, since we’re just throwing an error?

@connec
Copy link

connec commented Mar 24, 2017

Whilst it is probably a pragmatic way of ensuring we don't get bug reports about get and set, it does seem a bit hacky - would documenting a 'gotcha' not be sufficient?

@GeoffreyBooth I reckon those could be caught reasonably easily by adding a check to Call nodes that throws if the variable is an identifier named get or set, and the first argument is an implicit object literal (obj.generated is true). I suppose the error message would be something along the lines of

error: getters and setters are not supported - disambiguate with explicit parentheses or braces.

The case that would be harder to catch with a helpful error would be { get f: -> } - currently that throws error: unexpected ( which might be interpreted as a bug in the compiler when handling get and set. That error looks like an error from the grammar, which I'm not sure how to trap.

@GeoffreyBooth
Copy link
Collaborator

Whilst it is probably a pragmatic way of ensuring we don’t get bug reports about get and set, it does seem a bit hacky - would documenting a ‘gotcha’ not be sufficient?

My other motivation for throwing an error is to avoid breaking changes in the future if it turns out we need to support this syntax for some reason. If we make the get/set shorthand syntax throw an error now, then if we change our minds and decide to support the shorthand syntax someday, it wouldn’t be a breaking change.

@PandaWhisperer
Copy link

Apologies in advance for just barging in here and commenting on a closed issue, but I've been a big fan of CoffeeScript for quite some time, and I was really stoked when I found out recently that not only did ES6 not kill it, but version 2 is inching closer to a release. 😃

However, I'm a bit curious about your stance on getters and setters. Now, I'm really not in a position to discuss the technical merits much, since I haven't coded much in CoffeeScript or ES6 recently. But I would like to point out that they're in ES6, and CoffeeScript aims to be a superset, so that kinda makes me feel that they should be supported. Is it really that complicated? Or is the argument just that Crockford doesn't like them?

Anyhow, whatever the final decision will be, perhaps it would be nice to mention them on the CS2 website.

Also, I'd be happy to volunteer if there's anything I could help out with. After having taken a much-needed break from coding, I feel ready to jump back in the game. And what better way to do that than giving back to the community that's given me so much cool stuff to play with in the past?

Cheers! 🍻

@GeoffreyBooth
Copy link
Collaborator

A mention is in the docs on the 2 branch, that will get published to coffeescript.org with the next beta release: https://rawgit.com/jashkenas/coffeescript/2/docs/v2/index.html#unsupported-get-set

Something to keep in mind is that getters and setters are supported in CoffeeScript, even version 1.x. It’s just the shorthand syntax that isn’t supported. The note in the docs explains how to use the verbose syntax. We could’ve found some way to support the shorthand syntax or something similar to it, but the short answer is that the grammar is ambiguous (is it get foo or get(foo)?) and they’re considered a bad practice anyway, so we shouldn’t be making them easier to use.

Also, CoffeeScript is intentionally not a superset of JavaScript: https://rawgit.com/jashkenas/coffeescript/2/docs/v2/index.html#unsupported. It never has been, ever since the beginning and the lack of var or function declarations. That said, CoffeeScript does try to support all the “good parts” of JavaScript, including good new ES features that reach Stage 4.

Help is most welcome! https://github.com/jashkenas/coffeescript/issues?q=is%3Aopen+is%3Aissue+label%3Apriority

@CliffS
Copy link

CliffS commented May 7, 2017

they’re considered a bad practice anyway, so we shouldn’t be making them easier to use.

I think there is a danger of CS evangelising over this when the battle has effectively already been lost in ES6. I have to say that I agree with @wcjohnson above when he said:

But if I have to type Object.defineProperty(@.prototype, blah, ->blah) and they can just type "get" I'm still that much closer to losing the argument. And I think it is a real argument for a lot of teams out there.

Please reconsider.

@mrmowgli
Copy link

mrmowgli commented May 7, 2017 via email

@joeldrapper
Copy link

joeldrapper commented May 8, 2017

FYI, there’s a really elegant way to use getters / setters in CoffeeScript version 1 and 2 (thanks @carlmathisen) by creating a new method on the Object class. I’ve called it property, but you could call it defineProperty, accessor or anything else.

Object::property = (name, accessors) ->
  Object.defineProperty @::, name, accessors

Example use

class Person
  @property "name",
    set: (value) ->
      names = value.split " "
      @firstName = names[0]
      @lastName = names[names.length - 1]

    get: ->
      "#{@firstName} #{@lastName}"

joel = new Person
joel.name = "Joel Drapper"

console.log joel.firstName
# "Joel"

console.log joel.lastName
# "Drapper"

console.log joel.name
# "Joel Drapper"

If you’d rather not risk extending the Object class, you can create your own BaseObject class with a property class method and extend that instead.

class BaseObject
  @property: (name, accessors) ->
    Object.defineProperty @::, name, accessors

class Person extends BaseObject
  ...

@carlmathisen
Copy link

@joeldrapper This also works in CS1 btw 👍

@CliffS
Copy link

CliffS commented May 8, 2017

@joeldrapper That's great. If the devs aren't going to add getter/setters to the language, at least could we have this trick in the unsupported section of the manual?

@joeldrapper
Copy link

@CliffS Yes, it might be helpful to mention the technique here.

@GeoffreyBooth
Copy link
Collaborator

The docs are in the repo. See the markdown files in documentation/sections, and the examples they reference in documentation/examples, on the 2 branch. Please feel free to submit a PR.

@PandaWhisperer
Copy link

@GeoffreyBooth thanks for clearing that up. When I searched the page I didn't find anything, but I looked again yesterday and now the lack of the shorthand syntax (including the motivation for it) is present.

@joeldrapper I've seen this trick mentioned elsewhere before and I think it's fairly neat, save for the need to extend Object or insert a new base class into your own object hierarchy. I do agree with @mrmowgli that while the Object.defineProperty syntax might work, it's definitely too cumbersome and lengthy to use on a regular basis. But I can see the issue the core devs are having with the proposed syntax, which would potentially definitely break backwards compatibility with CS1.

+1 for @CliffS idea to at least mention the workaround in the main documentation.

@ghost
Copy link

ghost commented Nov 19, 2017

Did anyone mentioned that you can delete get and make lazy loaded property, but you can't delete defineProperty?

@GeoffreyBooth Are you seriously offering to use forks in issues? So that project readme should contain "if you gonna work with this code you need to install fork, cause vanilla coffeescript is incompatible with modern js"

@joeldrapper
Copy link

joeldrapper commented Jan 1, 2018

Call me crazy, but would it be such a bad idea to define getters and setters like Ruby? Methods without arguments are getters and methods ending in = with just one argument are setters.

class Person
  name=: (value) ->
    names = value.split " "
    @firstName = names[0]
    @lastName = names[names.length - 1]

  name: ->
    "#{@firstName} #{@lastName}"

  save: () ->
    ...

You could force a normal method with no arguments like this save: () ->.

@coffeescriptbot coffeescriptbot changed the title Getters and Setters CS2 Discussion: Features: Getters and Setters Feb 19, 2018
@coffeescriptbot
Copy link
Collaborator

coffeescriptbot commented Feb 19, 2018

Issue migrated to jashkenas/coffeescript#4915

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

16 participants