ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:104213] [Ruby master Feature#17942] Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars
@ 2021-06-09  4:55 tyler
  2021-06-09  5:25 ` [ruby-core:104214] [Ruby master Feature#17942] Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructor mame
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: tyler @ 2021-06-09  4:55 UTC (permalink / raw)
  To: ruby-core

Issue #17942 has been reported by TylerRick (Tyler Rick).

----------------------------------------
Feature #17942: Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars
https://bugs.ruby-lang.org/issues/17942

* Author: TylerRick (Tyler Rick)
* Status: Open
* Priority: Normal
----------------------------------------
This proposal builds on the proposed `initialize(@a, @b)` instance var assignment shortcut syntax described in #15192.

1. It allows you to add an *optional* `public`/`protected`/`private` modifier before any instance var parameter. Doing so automatically defines *accessor methods* (with the given access modifier; equivalent to `attr_accessor` inside of a  `public`/`protected`/`private` block) for the instance var it precedes.
2. If the visibility modifier is omitted, then it defaults to automatically _no_ getter/setter methods for that instance var (it _only_ does an assignment of that already-private instance var).

## Parameter properties in TypeScript language

This is inspired by TypeScript's `constructor(public a, private b)` syntax, which allows you to write this ([REPL](https://www.typescriptlang.org/play?#code/MYGwhgzhAEBiD29oG8BQ0PWPAdhALgE4Cuw+8hAFAA7EBGIAlsNGAFw7EC2dApoQBpotBs2h0O3PoOGFGANzD5eWST34BKFOkwBfVPqA)):
```js
class Foo {
    constructor(public a:number, public b:number, private c:number) {
    }
}
```
instead of this:
```js
class Foo {
    constructor(a, b, c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }
}
```

(The `public`/`private` access modifiers actually disappear in the transpiled JavaScript code because it's only the TypeScript compiler that enforces those access modifiers, and it does so at *compile* time rather than at run time.)

Further reading:
- https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties
- https://basarat.gitbook.io/typescript/future-javascript/classes#define-using-constructor
- https://kendaleiv.com/typescript-constructor-assignment-public-and-private-keywords/


## Differences from TypeScript

I propose adding a similar feature to Ruby, but with following differences from TypeScript:

1. Use **`@a`** instead of bare `a`. This makes it *much* clearer that you are assigning directly to instance variables instead of to locals.
    - Rationale: The `@` is actually _part_ of the instance variable name, and is inseparable from it. (This is also consistent with how the `#` is part of the name itself in JavaScript's [(Private instance fields)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields#private_instance_fields).)
    - (`public a` would be a syntax error because there's no such thing as access modifiers for locals. Okay, I guess there's no such thing as access modifiers for instance vars either, which is why...)

1. Make the syntax for ***assigning*** to instance vars (`@a`) (the proposal in #15192) and defining ***accessor methods*** for those instance vars (`public`/`private`) separate/distinct.
    - In other words, rather than make the `public`/`private` keywords a *required* part of the syntax like it is for TypeScript [parameter properties](https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties), you could omit the modifier and it would still do the instance var _assignment*.
    - The `public`/`private` access modifiers be an additional (*optional*) shortcut when you want to add an ***accessor method*** in *addition* to doing an ***assignment*** .
    - Unlike Java and TypeScript where you _can_ add access modifiers to instance variables, in Ruby, `public`/`private` _can't_ be applied to instance variables (direct access is only possible from within the instance). So if we're going to allow a `public`/`private` modifier here at all, They _must_ refer to methods, specifically accessor methods for those instance variables.

1. Keep it **private** by default (which of course `@a` by itself implies—it _is_ private unless you add a public accessor).
    - (Rather than make it `public` by default like it is in TypeScript.)
    - Keeping instance variables completely private is probably what people will want most of the time, and we should optimize the ergonomics for the most common case.
    - Private is a safer default, and should be assumed unless you explicitly ask for a public accessor to be added.
    - I bet TypeScript made the `public` the default mostly to be consistent with JavaScript (which TypeScript compiles to): JavaScript (along with other languages like Java) allows direct access (no getter/setter neede) to instance properties/variables from objects outside the instance. JavaScript doesn't even _have_ a way to make instance variables private (but hopefully will soon with this [proposal](https://github.com/tc39/proposal-private-methods) to add `#a` syntax for private properties).

So this:

```ruby
class Thing
  def initialize(public @a, public @b, @c)
  end
end
```

would be equivalent to this:

```ruby
class Thing
  attr_accessor :a, :b

  def initialize(a, b, c)
    @a = a
    @b = b
    @c = c
  end
```

## How is `initialize(private @a)` different from `initialize(@a)`?

Even though `@a` by itself is already private...
1. This defines a private accessor for that instance var, which lets you write `self.a =` instead of `@a =` (if you want).
2. Having a concise way to do that is helpful, for example if you want to make it a matter of practice/policy to only set an instance variable by going through its *setter method*. (See [discussion here](https://stackoverflow.com/questions/25571642/ruby-private-and-public-accessors).)


Why not just use `initialize(private @a)` to be consistent with TypeScript spec?
- TypeScript's `public`/`private` is not standard JavaScript. In fact, if the [private methods/fields proposal](https://github.com/tc39/proposal-private-methods) had existed when TypeScript added [parameter properties](https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties), I'd like to think that they might have actually *made use* of the new `#b` syntax and gone with a terser syntax like `constructor(public a, #b)` instead of ``constructor(public a, private b)`.


## Upsides of this proposal

1. Removes even more boilerplate (all those `attr_accessor` lines), much of the time

## Downsides of this proposal

1. Only provides a way to define both getter and setter at once. Doesn't provide a way to _just_ define a getter and not a setter, for example.
    - Doesn't seem like a big deal, however. You can just not use this feature and define the getter with `attr_reader :a` instead. Or define private getter/setter with `private @a`




-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [ruby-core:104214] [Ruby master Feature#17942] Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructor
  2021-06-09  4:55 [ruby-core:104213] [Ruby master Feature#17942] Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars tyler
@ 2021-06-09  5:25 ` mame
  2021-06-09  5:32 ` [ruby-core:104215] " merch-redmine
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: mame @ 2021-06-09  5:25 UTC (permalink / raw)
  To: ruby-core

Issue #17942 has been updated by mame (Yusuke Endoh).


FYI: This is (partially) a duplicate of #5825, #8563, #12023, #12578, #12820, and #15192.

----------------------------------------
Feature #17942: Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructor
https://bugs.ruby-lang.org/issues/17942#change-92398

* Author: TylerRick (Tyler Rick)
* Status: Open
* Priority: Normal
----------------------------------------
This proposal builds on the proposed `initialize(@a, @b)` instance var assignment shortcut syntax described in #15192.

1. It allows you to add an *optional* `public`/`protected`/`private` modifier before any instance var parameter. Doing so automatically defines *accessor methods* (with the given access modifier; equivalent to `attr_accessor` inside of a  `public`/`protected`/`private` block) for the instance var it precedes.
2. If the visibility modifier is omitted, then it defaults to automatically _no_ getter/setter methods for that instance var (it _only_ does an assignment of that already-private instance var).

## Parameter properties in TypeScript language

This is inspired by TypeScript's `constructor(public a, private b)` syntax, which allows you to write this ([REPL](https://www.typescriptlang.org/play?#code/MYGwhgzhAEBiD29oG8BQ0PWPAdhALgE4Cuw+8hAFAA7EBGIAlsNGAFw7EC2dApoQBpotBs2h0O3PoOGFGANzD5eWST34BKFOkwBfVPqA)):
```js
class Foo {
    constructor(public a:number, public b:number, private c:number) {
    }
}
```
instead of this:
```js
class Foo {
    constructor(a, b, c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }
}
```

(The `public`/`private` access modifiers actually disappear in the transpiled JavaScript code because it's only the TypeScript compiler that enforces those access modifiers, and it does so at *compile* time rather than at run time.)

Further reading:
- https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties
- https://basarat.gitbook.io/typescript/future-javascript/classes#define-using-constructor
- https://kendaleiv.com/typescript-constructor-assignment-public-and-private-keywords/


## Differences from TypeScript

I propose adding a similar feature to Ruby, but with following differences from TypeScript:

1. Use **`@a`** instead of bare `a`. This makes it *much* clearer that you are assigning directly to instance variables instead of to locals.
    - Rationale: The `@` is actually _part_ of the instance variable name, and is inseparable from it. (This is also consistent with how the `#` is part of the name itself in JavaScript's [(Private instance fields)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields#private_instance_fields).)
    - (`public a` would be a syntax error because there's no such thing as access modifiers for locals. Okay, I guess there's no such thing as access modifiers for instance vars either, which is why...)

1. Make the syntax for ***assigning*** to instance vars (`@a`) (the proposal in #15192) and defining ***accessor methods*** for those instance vars (`public`/`private`) separate/distinct.
    - In other words, rather than make the `public`/`private` keywords a *required* part of the syntax like it is for TypeScript [parameter properties](https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties), you could omit the modifier and it would still do the instance var _assignment*.
    - The `public`/`private` access modifiers be an additional (*optional*) shortcut when you want to add an ***accessor method*** in *addition* to doing an ***assignment*** .
    - Unlike Java and TypeScript where you _can_ add access modifiers to instance variables, in Ruby, `public`/`private` _can't_ be applied to instance variables (direct access is only possible from within the instance). So if we're going to allow a `public`/`private` modifier here at all, They _must_ refer to methods, specifically accessor methods for those instance variables.

1. Keep it **private** by default (which of course `@a` by itself implies—it _is_ private unless you add a public accessor).
    - (Rather than make it `public` by default like it is in TypeScript.)
    - Keeping instance variables completely private is probably what people will want most of the time, and we should optimize the ergonomics for the most common case.
    - Private is a safer default, and should be assumed unless you explicitly ask for a public accessor to be added.
    - I bet TypeScript made the `public` the default mostly to be consistent with JavaScript (which TypeScript compiles to): JavaScript (along with other languages like Java) allows direct access (no getter/setter neede) to instance properties/variables from objects outside the instance. JavaScript doesn't even _have_ a way to make instance variables private (but hopefully will soon with this [proposal](https://github.com/tc39/proposal-private-methods) to add `#a` syntax for private properties).

So this:

```ruby
class Thing
  def initialize(public @a, public @b, @c)
  end
end
```

would be equivalent to this:

```ruby
class Thing
  attr_accessor :a, :b

  def initialize(a, b, c)
    @a = a
    @b = b
    @c = c
  end
```

## How is `initialize(private @a)` different from `initialize(@a)`?

Even though `@a` by itself is already private...
1. This defines a private accessor for that instance var, which lets you write `self.a =` instead of `@a =` (if you want).
2. Having a concise way to do that is helpful, for example if you want to make it a matter of practice/policy to only set an instance variable by going through its *setter method*. (See [discussion here](https://stackoverflow.com/questions/25571642/ruby-private-and-public-accessors).)


Why not just use `initialize(private @a)` to be consistent with TypeScript spec?
- TypeScript's `public`/`private` is not standard JavaScript. In fact, if the [private methods/fields proposal](https://github.com/tc39/proposal-private-methods) had existed when TypeScript added [parameter properties](https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties), I'd like to think that they might have actually *made use* of the new `#b` syntax and gone with a terser syntax like `constructor(public a, #b)` instead of ``constructor(public a, private b)`.


## Upsides of this proposal

1. Removes even more boilerplate (all those `attr_accessor` lines), much of the time

## Downsides of this proposal

1. Only provides a way to define both getter and setter at once. Doesn't provide a way to _just_ define a getter and not a setter, for example.
    - Doesn't seem like a big deal, however. You can just not use this feature and define the getter with `attr_reader :a` instead. Or define private getter/setter with `private @a` and then override with `attr_reader :a` to add a public getter (while keeping the private setter).




-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [ruby-core:104215] [Ruby master Feature#17942] Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructor
  2021-06-09  4:55 [ruby-core:104213] [Ruby master Feature#17942] Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars tyler
  2021-06-09  5:25 ` [ruby-core:104214] [Ruby master Feature#17942] Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructor mame
@ 2021-06-09  5:32 ` merch-redmine
  2021-12-13  0:43 ` [ruby-core:106627] " LevLukomskyi (Lev Lukomskyi)
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: merch-redmine @ 2021-06-09  5:32 UTC (permalink / raw)
  To: ruby-core

Issue #17942 has been updated by jeremyevans0 (Jeremy Evans).


You should probably read @matz's response to a previous request for instance variable parameters: https://bugs.ruby-lang.org/issues/8563#note-3 (which he confirmed had not changed as of 2017: https://bugs.ruby-lang.org/issues/8563#note-18). Your proposal is more complex, but doesn't address the complaint @matz has regarding using instance variables as parameters.  A new issue with your proposal is it would turn `public`/`private` into keywords, when they are currently just methods.

If you want really concise class definitions, use `Struct`.  It doesn't get much more concise than:

```ruby
Thing = Struct.new(:a, :b, :c)
```

----------------------------------------
Feature #17942: Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructor
https://bugs.ruby-lang.org/issues/17942#change-92399

* Author: TylerRick (Tyler Rick)
* Status: Open
* Priority: Normal
----------------------------------------
This proposal builds on the proposed `initialize(@a, @b)` instance var assignment shortcut syntax described in #15192.

1. It allows you to add an *optional* `public`/`protected`/`private` modifier before any instance var parameter. Doing so automatically defines *accessor methods* (with the given access modifier; equivalent to `attr_accessor` inside of a  `public`/`protected`/`private` block) for the instance var it precedes.
2. If the visibility modifier is omitted, then it defaults to automatically _no_ getter/setter methods for that instance var (it _only_ does an assignment of that already-private instance var).

## Parameter properties in TypeScript language

This is inspired by TypeScript's `constructor(public a, private b)` syntax, which allows you to write this ([REPL](https://www.typescriptlang.org/play?#code/MYGwhgzhAEBiD29oG8BQ0PWPAdhALgE4Cuw+8hAFAA7EBGIAlsNGAFw7EC2dApoQBpotBs2h0O3PoOGFGANzD5eWST34BKFOkwBfVPqA)):
```js
class Foo {
    constructor(public a:number, public b:number, private c:number) {
    }
}
```
instead of this:
```js
class Foo {
    constructor(a, b, c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }
}
```

(The `public`/`private` access modifiers actually disappear in the transpiled JavaScript code because it's only the TypeScript compiler that enforces those access modifiers, and it does so at *compile* time rather than at run time.)

Further reading:
- https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties
- https://basarat.gitbook.io/typescript/future-javascript/classes#define-using-constructor
- https://kendaleiv.com/typescript-constructor-assignment-public-and-private-keywords/


## Differences from TypeScript

I propose adding a similar feature to Ruby, but with following differences from TypeScript:

1. Use **`@a`** instead of bare `a`. This makes it *much* clearer that you are assigning directly to instance variables instead of to locals.
    - Rationale: The `@` is actually _part_ of the instance variable name, and is inseparable from it. (This is also consistent with how the `#` is part of the name itself in JavaScript's [(Private instance fields)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields#private_instance_fields).)
    - (`public a` would be a syntax error because there's no such thing as access modifiers for locals. Okay, I guess there's no such thing as access modifiers for instance vars either, which is why...)

1. Make the syntax for ***assigning*** to instance vars (`@a`) (the proposal in #15192) and defining ***accessor methods*** for those instance vars (`public`/`private`) separate/distinct.
    - In other words, rather than make the `public`/`private` keywords a *required* part of the syntax like it is for TypeScript [parameter properties](https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties), you could omit the modifier and it would still do the instance var _assignment*.
    - The `public`/`private` access modifiers be an additional (*optional*) shortcut when you want to add an ***accessor method*** in *addition* to doing an ***assignment*** .
    - Unlike Java and TypeScript where you _can_ add access modifiers to instance variables, in Ruby, `public`/`private` _can't_ be applied to instance variables (direct access is only possible from within the instance). So if we're going to allow a `public`/`private` modifier here at all, They _must_ refer to methods, specifically accessor methods for those instance variables.

1. Keep it **private** by default (which of course `@a` by itself implies—it _is_ private unless you add a public accessor).
    - (Rather than make it `public` by default like it is in TypeScript.)
    - Keeping instance variables completely private is probably what people will want most of the time, and we should optimize the ergonomics for the most common case.
    - Private is a safer default, and should be assumed unless you explicitly ask for a public accessor to be added.
    - I bet TypeScript made the `public` the default mostly to be consistent with JavaScript (which TypeScript compiles to): JavaScript (along with other languages like Java) allows direct access (no getter/setter neede) to instance properties/variables from objects outside the instance. JavaScript doesn't even _have_ a way to make instance variables private (but hopefully will soon with this [proposal](https://github.com/tc39/proposal-private-methods) to add `#a` syntax for private properties).

So this:

```ruby
class Thing
  def initialize(public @a, public @b, @c)
  end
end
```

would be equivalent to this:

```ruby
class Thing
  attr_accessor :a, :b

  def initialize(a, b, c)
    @a = a
    @b = b
    @c = c
  end
```

## How is `initialize(private @a)` different from `initialize(@a)`?

Even though `@a` by itself is already private...
1. This defines a private accessor for that instance var, which lets you write `self.a =` instead of `@a =` (if you want).
2. Having a concise way to do that is helpful, for example if you want to make it a matter of practice/policy to only set an instance variable by going through its *setter method*. (See [discussion here](https://stackoverflow.com/questions/25571642/ruby-private-and-public-accessors).)


Why not just use `initialize(private @a)` to be consistent with TypeScript spec?
- TypeScript's `public`/`private` is not standard JavaScript. In fact, if the [private methods/fields proposal](https://github.com/tc39/proposal-private-methods) had existed when TypeScript added [parameter properties](https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties), I'd like to think that they might have actually *made use* of the new `#b` syntax and gone with a terser syntax like `constructor(public a, #b)` instead of ``constructor(public a, private b)`.


## Upsides of this proposal

1. Removes even more boilerplate (all those `attr_accessor` lines), much of the time

## Downsides of this proposal

1. Only provides a way to define both getter and setter at once. Doesn't provide a way to _just_ define a getter and not a setter, for example.
    - Doesn't seem like a big deal, however. You can just not use this feature and define the getter with `attr_reader :a` instead. Or define private getter/setter with `private @a` and then override with `attr_reader :a` to add a public getter (while keeping the private setter).




-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [ruby-core:106627] [Ruby master Feature#17942] Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructor
  2021-06-09  4:55 [ruby-core:104213] [Ruby master Feature#17942] Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars tyler
  2021-06-09  5:25 ` [ruby-core:104214] [Ruby master Feature#17942] Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructor mame
  2021-06-09  5:32 ` [ruby-core:104215] " merch-redmine
@ 2021-12-13  0:43 ` LevLukomskyi (Lev Lukomskyi)
  2022-12-04  3:54 ` [ruby-core:111196] " nobu (Nobuyoshi Nakada)
  2022-12-04  6:44 ` [ruby-core:111197] " sawa (Tsuyoshi Sawada)
  4 siblings, 0 replies; 6+ messages in thread
From: LevLukomskyi (Lev Lukomskyi) @ 2021-12-13  0:43 UTC (permalink / raw)
  To: ruby-core

Issue #17942 has been updated by LevLukomskyi (Lev Lukomskyi).


> If you want really concise class definitions, use `Struct`.  It doesn't get much more concise than:
> 
> ```ruby
> Thing = Struct.new(:a, :b, :c)
> ```

This construction is bad because:

1. It **looks awkward**, and not consistent with the rest of the classes declarations in the project, also it leads to **Rubocop offence** because of constant case. You can do `class Thing < Struct.new(...)` which looks more consistent but it creates a redundant level of inheritance
2. It doesn't allow to **mix positional and keyword args**, eg. `Struct.new(:a, :b, :c, d: nil)` won't work
3. It doesn't allow to **adjust initialization of one of the arguments**, eg. I want `@b = b.to_i` and the rest initialized as usual.

The lack of this feature makes the language NOT FUN because you are forced to create a lot of **duplication** each time you create a basic class, eg.

```ruby
class PollItem::ToggleVote
  attr_reader :poll_item, :user, :voted, :ip_address

  def initialize(poll_item, user, voted, ip_address:)
    @poll_item = poll_item
    @user = user
    @voted = voted
    @ip_address = ip_address
  end

  def perform; end
end
```

Here we see `poll_item`, `user`, `voted`, `ip_address` names are **duplicated 3 times** each – which means more work when you decide to add/remove/rename the argument, more code – more possibilities for an error. Duplication is NOT FUN – It's not a surprise why there are so many people advocating this feature.

This could theoretically be rewritten to something like this:

```ruby
class PollItem::ToggleVote
  attr_reader :poll_item, :user, :voted, :ip_address

  def initialize(@poll_item, @user, @voted, @ip_address:)
  end

  def perform; end
end
```

Much cleaner! And then to something like this:

```ruby
class PollItem::ToggleVote
  def initialize(@poll_item, @user, @voted, @ip_address:)
    init_readers
  end

  def perform; end
end
```

`init_readers` would create attr_readers for all instance variables in initializer. `public`/`private` keywords as described in the issue are not good as they litter args and they will be repeated a lot, eg. `def initialize(public @poll_item, public @user, public @voted, public @ip_address:)`

If I decide to customize one of argument, I could do this:

```ruby
class PollItem::ToggleVote
  def initialize(@poll_item, @user, @voted, ip_address:)
    @ip_address = IpAddress.parse(ip_address)
    init_readers
  end
end
```

This would be fantastic! This would reduce duplication. This would be Concise – I love ruby especially because of this feat.

I saw **Matz was against this feature**, the main point was:

> def initialize(@foo, @bar)
> end
> does not express intention of instance variable initialization

As for me – **it does express** – there is a word `initialize` and then goes `@foo`, so it means "Please initialize @foo with whatever is passed here".

----------------------------------------
Feature #17942: Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructor
https://bugs.ruby-lang.org/issues/17942#change-95297

* Author: TylerRick (Tyler Rick)
* Status: Open
* Priority: Normal
----------------------------------------
This proposal builds on the proposed `initialize(@a, @b)` instance var assignment shortcut syntax described in #15192.

1. It allows you to add an *optional* `public`/`protected`/`private` modifier before any instance var parameter. Doing so automatically defines *accessor methods* (with the given access modifier; equivalent to `attr_accessor` inside of a  `public`/`protected`/`private` block) for the instance var it precedes.
2. If the visibility modifier is omitted, then it defaults to automatically _no_ getter/setter methods for that instance var (it _only_ does an assignment of that already-private instance var).

## Parameter properties in TypeScript language

This is inspired by TypeScript's `constructor(public a, private b)` syntax, which allows you to write this ([REPL](https://www.typescriptlang.org/play?#code/MYGwhgzhAEBiD29oG8BQ0PWPAdhALgE4Cuw+8hAFAA7EBGIAlsNGAFw7EC2dApoQBpotBs2h0O3PoOGFGANzD5eWST34BKFOkwBfVPqA)):
```js
class Foo {
    constructor(public a:number, public b:number, private c:number) {
    }
}
```
instead of this:
```js
class Foo {
    constructor(a, b, c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }
}
```

(The `public`/`private` access modifiers actually disappear in the transpiled JavaScript code because it's only the TypeScript compiler that enforces those access modifiers, and it does so at *compile* time rather than at run time.)

Further reading:
- https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties
- https://basarat.gitbook.io/typescript/future-javascript/classes#define-using-constructor
- https://kendaleiv.com/typescript-constructor-assignment-public-and-private-keywords/


## Differences from TypeScript

I propose adding a similar feature to Ruby, but with following differences from TypeScript:

1. Use **`@a`** instead of bare `a`. This makes it *much* clearer that you are assigning directly to instance variables instead of to locals.
    - Rationale: The `@` is actually _part_ of the instance variable name, and is inseparable from it. (This is also consistent with how the `#` is part of the name itself in JavaScript's [(Private instance fields)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields#private_instance_fields).)
    - (`public a` would be a syntax error because there's no such thing as access modifiers for locals. Okay, I guess there's no such thing as access modifiers for instance vars either, which is why...)

1. Make the syntax for ***assigning*** to instance vars (`@a`) (the proposal in #15192) and defining ***accessor methods*** for those instance vars (`public`/`private`) separate/distinct.
    - In other words, rather than make the `public`/`private` keywords a *required* part of the syntax like it is for TypeScript [parameter properties](https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties), you could omit the modifier and it would still do the instance var _assignment*.
    - The `public`/`private` access modifiers be an additional (*optional*) shortcut when you want to add an ***accessor method*** in *addition* to doing an ***assignment*** .
    - Unlike Java and TypeScript where you _can_ add access modifiers to instance variables, in Ruby, `public`/`private` _can't_ be applied to instance variables (direct access is only possible from within the instance). So if we're going to allow a `public`/`private` modifier here at all, They _must_ refer to methods, specifically accessor methods for those instance variables.

1. Keep it **private** by default (which of course `@a` by itself implies—it _is_ private unless you add a public accessor).
    - (Rather than make it `public` by default like it is in TypeScript.)
    - Keeping instance variables completely private is probably what people will want most of the time, and we should optimize the ergonomics for the most common case.
    - Private is a safer default, and should be assumed unless you explicitly ask for a public accessor to be added.
    - I bet TypeScript made the `public` the default mostly to be consistent with JavaScript (which TypeScript compiles to): JavaScript (along with other languages like Java) allows direct access (no getter/setter neede) to instance properties/variables from objects outside the instance. JavaScript doesn't even _have_ a way to make instance variables private (but hopefully will soon with this [proposal](https://github.com/tc39/proposal-private-methods) to add `#a` syntax for private properties).

So this:

```ruby
class Thing
  def initialize(public @a, public @b, @c)
  end
end
```

would be equivalent to this:

```ruby
class Thing
  attr_accessor :a, :b

  def initialize(a, b, c)
    @a = a
    @b = b
    @c = c
  end
```

## How is `initialize(private @a)` different from `initialize(@a)`?

Even though `@a` by itself is already private...
1. This defines a private accessor for that instance var, which lets you write `self.a =` instead of `@a =` (if you want).
2. Having a concise way to do that is helpful, for example if you want to make it a matter of practice/policy to only set an instance variable by going through its *setter method*. (See [discussion here](https://stackoverflow.com/questions/25571642/ruby-private-and-public-accessors).)


Why not just use `initialize(private @a)` to be consistent with TypeScript spec?
- TypeScript's `public`/`private` is not standard JavaScript. In fact, if the [private methods/fields proposal](https://github.com/tc39/proposal-private-methods) had existed when TypeScript added [parameter properties](https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties), I'd like to think that they might have actually *made use* of the new `#b` syntax and gone with a terser syntax like `constructor(public a, #b)` instead of ``constructor(public a, private b)`.


## Upsides of this proposal

1. Removes even more boilerplate (all those `attr_accessor` lines), much of the time

## Downsides of this proposal

1. Only provides a way to define both getter and setter at once. Doesn't provide a way to _just_ define a getter and not a setter, for example.
    - Doesn't seem like a big deal, however. You can just not use this feature and define the getter with `attr_reader :a` instead. Or define private getter/setter with `private @a` and then override with `attr_reader :a` to add a public getter (while keeping the private setter).




-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [ruby-core:111196] [Ruby master Feature#17942] Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructor
  2021-06-09  4:55 [ruby-core:104213] [Ruby master Feature#17942] Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars tyler
                   ` (2 preceding siblings ...)
  2021-12-13  0:43 ` [ruby-core:106627] " LevLukomskyi (Lev Lukomskyi)
@ 2022-12-04  3:54 ` nobu (Nobuyoshi Nakada)
  2022-12-04  6:44 ` [ruby-core:111197] " sawa (Tsuyoshi Sawada)
  4 siblings, 0 replies; 6+ messages in thread
From: nobu (Nobuyoshi Nakada) @ 2022-12-04  3:54 UTC (permalink / raw)
  To: ruby-core

Issue #17942 has been updated by nobu (Nobuyoshi Nakada).


This means you want only `initialize` method to be parsed specially?
And when bypassing this method, e.g., `Marshal.load`, no accessor will be defined?

----------------------------------------
Feature #17942: Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructor
https://bugs.ruby-lang.org/issues/17942#change-100479

* Author: TylerRick (Tyler Rick)
* Status: Open
* Priority: Normal
----------------------------------------
This proposal builds on the proposed `initialize(@a, @b)` instance var assignment shortcut syntax described in #15192.

1. It allows you to add an *optional* `public`/`protected`/`private` modifier before any instance var parameter. Doing so automatically defines *accessor methods* (with the given access modifier; equivalent to `attr_accessor` inside of a  `public`/`protected`/`private` block) for the instance var it precedes.
2. If the visibility modifier is omitted, then it defaults to automatically _no_ getter/setter methods for that instance var (it _only_ does an assignment of that already-private instance var).

## Parameter properties in TypeScript language

This is inspired by TypeScript's `constructor(public a, private b)` syntax, which allows you to write this ([REPL](https://www.typescriptlang.org/play?#code/MYGwhgzhAEBiD29oG8BQ0PWPAdhALgE4Cuw+8hAFAA7EBGIAlsNGAFw7EC2dApoQBpotBs2h0O3PoOGFGANzD5eWST34BKFOkwBfVPqA)):
```js
class Foo {
    constructor(public a:number, public b:number, private c:number) {
    }
}
```
instead of this:
```js
class Foo {
    constructor(a, b, c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }
}
```

(The `public`/`private` access modifiers actually disappear in the transpiled JavaScript code because it's only the TypeScript compiler that enforces those access modifiers, and it does so at *compile* time rather than at run time.)

Further reading:
- https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties
- https://basarat.gitbook.io/typescript/future-javascript/classes#define-using-constructor
- https://kendaleiv.com/typescript-constructor-assignment-public-and-private-keywords/


## Differences from TypeScript

I propose adding a similar feature to Ruby, but with following differences from TypeScript:

1. Use **`@a`** instead of bare `a`. This makes it *much* clearer that you are assigning directly to instance variables instead of to locals.
    - Rationale: The `@` is actually _part_ of the instance variable name, and is inseparable from it. (This is also consistent with how the `#` is part of the name itself in JavaScript's [(Private instance fields)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields#private_instance_fields).)
    - (`public a` would be a syntax error because there's no such thing as access modifiers for locals. Okay, I guess there's no such thing as access modifiers for instance vars either, which is why...)

1. Make the syntax for ***assigning*** to instance vars (`@a`) (the proposal in #15192) and defining ***accessor methods*** for those instance vars (`public`/`private`) separate/distinct.
    - In other words, rather than make the `public`/`private` keywords a *required* part of the syntax like it is for TypeScript [parameter properties](https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties), you could omit the modifier and it would still do the instance var _assignment*.
    - The `public`/`private` access modifiers be an additional (*optional*) shortcut when you want to add an ***accessor method*** in *addition* to doing an ***assignment*** .
    - Unlike Java and TypeScript where you _can_ add access modifiers to instance variables, in Ruby, `public`/`private` _can't_ be applied to instance variables (direct access is only possible from within the instance). So if we're going to allow a `public`/`private` modifier here at all, They _must_ refer to methods, specifically accessor methods for those instance variables.

1. Keep it **private** by default (which of course `@a` by itself implies—it _is_ private unless you add a public accessor).
    - (Rather than make it `public` by default like it is in TypeScript.)
    - Keeping instance variables completely private is probably what people will want most of the time, and we should optimize the ergonomics for the most common case.
    - Private is a safer default, and should be assumed unless you explicitly ask for a public accessor to be added.
    - I bet TypeScript made the `public` the default mostly to be consistent with JavaScript (which TypeScript compiles to): JavaScript (along with other languages like Java) allows direct access (no getter/setter neede) to instance properties/variables from objects outside the instance. JavaScript doesn't even _have_ a way to make instance variables private (but hopefully will soon with this [proposal](https://github.com/tc39/proposal-private-methods) to add `#a` syntax for private properties).

So this:

```ruby
class Thing
  def initialize(public @a, public @b, @c)
  end
end
```

would be equivalent to this:

```ruby
class Thing
  attr_accessor :a, :b

  def initialize(a, b, c)
    @a = a
    @b = b
    @c = c
  end
```

## How is `initialize(private @a)` different from `initialize(@a)`?

Even though `@a` by itself is already private...
1. This defines a private accessor for that instance var, which lets you write `self.a =` instead of `@a =` (if you want).
2. Having a concise way to do that is helpful, for example if you want to make it a matter of practice/policy to only set an instance variable by going through its *setter method*. (See [discussion here](https://stackoverflow.com/questions/25571642/ruby-private-and-public-accessors).)


Why not just use `initialize(private @a)` to be consistent with TypeScript spec?
- TypeScript's `public`/`private` is not standard JavaScript. In fact, if the [private methods/fields proposal](https://github.com/tc39/proposal-private-methods) had existed when TypeScript added [parameter properties](https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties), I'd like to think that they might have actually *made use* of the new `#b` syntax and gone with a terser syntax like `constructor(public a, #b)` instead of ``constructor(public a, private b)`.


## Upsides of this proposal

1. Removes even more boilerplate (all those `attr_accessor` lines), much of the time

## Downsides of this proposal

1. Only provides a way to define both getter and setter at once. Doesn't provide a way to _just_ define a getter and not a setter, for example.
    - Doesn't seem like a big deal, however. You can just not use this feature and define the getter with `attr_reader :a` instead. Or define private getter/setter with `private @a` and then override with `attr_reader :a` to add a public getter (while keeping the private setter).




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [ruby-core:111197] [Ruby master Feature#17942] Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructor
  2021-06-09  4:55 [ruby-core:104213] [Ruby master Feature#17942] Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars tyler
                   ` (3 preceding siblings ...)
  2022-12-04  3:54 ` [ruby-core:111196] " nobu (Nobuyoshi Nakada)
@ 2022-12-04  6:44 ` sawa (Tsuyoshi Sawada)
  4 siblings, 0 replies; 6+ messages in thread
From: sawa (Tsuyoshi Sawada) @ 2022-12-04  6:44 UTC (permalink / raw)
  To: ruby-core

Issue #17942 has been updated by sawa (Tsuyoshi Sawada).


LevLukomskyi (Lev Lukomskyi) wrote in #note-6:

> [Y]ou are forced to create a lot of duplication [...]
>
>     class PollItem::ToggleVote
>       attr_reader :poll_item, :user, :voted, :ip_address
>
>       def initialize(poll_item, user, voted, ip_address:)
>         @poll_item = poll_item
>         @user = user
>         @voted = voted
>         @ip_address = ip_address
>       end
> Here we see `poll_item`, `user`, `voted`, `ip_address` names are duplicated 4 times

Not necessarily. You can do with 2 times (counting the use with `attr_reader`):

      def initialize(*args)
        @poll_item, @user, @voted, @ip_address = args
      end

> I saw **Matz was against this feature**, the main point was:
> 
> >     def initialize(@foo, @bar)
>  >     end
> > does not express intention of instance variable initialization
> 
> But – **it does express** – there is a word `initialize` and then goes `@foo`, it means "Please initialize @foo with whatever is passed here".

No, it doesn't. It means "please initialize **the newly created instance** with whatever is passed here as the value of `@foo`." In general, Ruby code `foo.bar(baz)` translates to English as "do bar to foo using baz", not "foo does bar to baz."

----------------------------------------
Feature #17942: Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructor
https://bugs.ruby-lang.org/issues/17942#change-100480

* Author: TylerRick (Tyler Rick)
* Status: Open
* Priority: Normal
----------------------------------------
This proposal builds on the proposed `initialize(@a, @b)` instance var assignment shortcut syntax described in #15192.

1. It allows you to add an *optional* `public`/`protected`/`private` modifier before any instance var parameter. Doing so automatically defines *accessor methods* (with the given access modifier; equivalent to `attr_accessor` inside of a  `public`/`protected`/`private` block) for the instance var it precedes.
2. If the visibility modifier is omitted, then it defaults to automatically _no_ getter/setter methods for that instance var (it _only_ does an assignment of that already-private instance var).

## Parameter properties in TypeScript language

This is inspired by TypeScript's `constructor(public a, private b)` syntax, which allows you to write this ([REPL](https://www.typescriptlang.org/play?#code/MYGwhgzhAEBiD29oG8BQ0PWPAdhALgE4Cuw+8hAFAA7EBGIAlsNGAFw7EC2dApoQBpotBs2h0O3PoOGFGANzD5eWST34BKFOkwBfVPqA)):
```js
class Foo {
    constructor(public a:number, public b:number, private c:number) {
    }
}
```
instead of this:
```js
class Foo {
    constructor(a, b, c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }
}
```

(The `public`/`private` access modifiers actually disappear in the transpiled JavaScript code because it's only the TypeScript compiler that enforces those access modifiers, and it does so at *compile* time rather than at run time.)

Further reading:
- https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties
- https://basarat.gitbook.io/typescript/future-javascript/classes#define-using-constructor
- https://kendaleiv.com/typescript-constructor-assignment-public-and-private-keywords/


## Differences from TypeScript

I propose adding a similar feature to Ruby, but with following differences from TypeScript:

1. Use **`@a`** instead of bare `a`. This makes it *much* clearer that you are assigning directly to instance variables instead of to locals.
    - Rationale: The `@` is actually _part_ of the instance variable name, and is inseparable from it. (This is also consistent with how the `#` is part of the name itself in JavaScript's [(Private instance fields)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields#private_instance_fields).)
    - (`public a` would be a syntax error because there's no such thing as access modifiers for locals. Okay, I guess there's no such thing as access modifiers for instance vars either, which is why...)

1. Make the syntax for ***assigning*** to instance vars (`@a`) (the proposal in #15192) and defining ***accessor methods*** for those instance vars (`public`/`private`) separate/distinct.
    - In other words, rather than make the `public`/`private` keywords a *required* part of the syntax like it is for TypeScript [parameter properties](https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties), you could omit the modifier and it would still do the instance var _assignment*.
    - The `public`/`private` access modifiers be an additional (*optional*) shortcut when you want to add an ***accessor method*** in *addition* to doing an ***assignment*** .
    - Unlike Java and TypeScript where you _can_ add access modifiers to instance variables, in Ruby, `public`/`private` _can't_ be applied to instance variables (direct access is only possible from within the instance). So if we're going to allow a `public`/`private` modifier here at all, They _must_ refer to methods, specifically accessor methods for those instance variables.

1. Keep it **private** by default (which of course `@a` by itself implies—it _is_ private unless you add a public accessor).
    - (Rather than make it `public` by default like it is in TypeScript.)
    - Keeping instance variables completely private is probably what people will want most of the time, and we should optimize the ergonomics for the most common case.
    - Private is a safer default, and should be assumed unless you explicitly ask for a public accessor to be added.
    - I bet TypeScript made the `public` the default mostly to be consistent with JavaScript (which TypeScript compiles to): JavaScript (along with other languages like Java) allows direct access (no getter/setter neede) to instance properties/variables from objects outside the instance. JavaScript doesn't even _have_ a way to make instance variables private (but hopefully will soon with this [proposal](https://github.com/tc39/proposal-private-methods) to add `#a` syntax for private properties).

So this:

```ruby
class Thing
  def initialize(public @a, public @b, @c)
  end
end
```

would be equivalent to this:

```ruby
class Thing
  attr_accessor :a, :b

  def initialize(a, b, c)
    @a = a
    @b = b
    @c = c
  end
```

## How is `initialize(private @a)` different from `initialize(@a)`?

Even though `@a` by itself is already private...
1. This defines a private accessor for that instance var, which lets you write `self.a =` instead of `@a =` (if you want).
2. Having a concise way to do that is helpful, for example if you want to make it a matter of practice/policy to only set an instance variable by going through its *setter method*. (See [discussion here](https://stackoverflow.com/questions/25571642/ruby-private-and-public-accessors).)


Why not just use `initialize(private @a)` to be consistent with TypeScript spec?
- TypeScript's `public`/`private` is not standard JavaScript. In fact, if the [private methods/fields proposal](https://github.com/tc39/proposal-private-methods) had existed when TypeScript added [parameter properties](https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties), I'd like to think that they might have actually *made use* of the new `#b` syntax and gone with a terser syntax like `constructor(public a, #b)` instead of ``constructor(public a, private b)`.


## Upsides of this proposal

1. Removes even more boilerplate (all those `attr_accessor` lines), much of the time

## Downsides of this proposal

1. Only provides a way to define both getter and setter at once. Doesn't provide a way to _just_ define a getter and not a setter, for example.
    - Doesn't seem like a big deal, however. You can just not use this feature and define the getter with `attr_reader :a` instead. Or define private getter/setter with `private @a` and then override with `attr_reader :a` to add a public getter (while keeping the private setter).




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2022-12-04  6:44 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-09  4:55 [ruby-core:104213] [Ruby master Feature#17942] Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars tyler
2021-06-09  5:25 ` [ruby-core:104214] [Ruby master Feature#17942] Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructor mame
2021-06-09  5:32 ` [ruby-core:104215] " merch-redmine
2021-12-13  0:43 ` [ruby-core:106627] " LevLukomskyi (Lev Lukomskyi)
2022-12-04  3:54 ` [ruby-core:111196] " nobu (Nobuyoshi Nakada)
2022-12-04  6:44 ` [ruby-core:111197] " sawa (Tsuyoshi Sawada)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).