https://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112021-06-09T04:55:35ZRuby Issue Tracking SystemRuby master - Feature #17942: Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructorhttps://bugs.ruby-lang.org/issues/17942?journal_id=923942021-06-09T04:55:35ZTylerRick (Tyler Rick)tyler@tylerrick.com
<ul><li><strong>Subject</strong> changed from <i>Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars</i> to <i>Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructor</i></li></ul> Ruby master - Feature #17942: Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructorhttps://bugs.ruby-lang.org/issues/17942?journal_id=923952021-06-09T04:56:52ZTylerRick (Tyler Rick)tyler@tylerrick.com
<ul><li><strong>Description</strong> updated (<a title="View differences" href="/journals/92395/diff?detail_id=60124">diff</a>)</li></ul> Ruby master - Feature #17942: Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructorhttps://bugs.ruby-lang.org/issues/17942?journal_id=923972021-06-09T05:22:41Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-2 priority-4 priority-default" href="/issues/5825">Feature #5825</a>: Sweet instance var assignment in the object initializer</i> added</li></ul> Ruby master - Feature #17942: Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructorhttps://bugs.ruby-lang.org/issues/17942?journal_id=923982021-06-09T05:25:54Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul></ul><p>FYI: This is (partially) a duplicate of <a class="issue tracker-2 status-2 priority-4 priority-default" title="Feature: Sweet instance var assignment in the object initializer (Assigned)" href="https://bugs.ruby-lang.org/issues/5825">#5825</a>, <a class="issue tracker-2 status-6 priority-4 priority-default closed" title="Feature: Instance variable arguments (Rejected)" href="https://bugs.ruby-lang.org/issues/8563">#8563</a>, <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: Allow ivars to be used as method arguments (Open)" href="https://bugs.ruby-lang.org/issues/12023">#12023</a>, <a class="issue tracker-2 status-6 priority-4 priority-default closed" title="Feature: Instance Variables Assigned In parameters ( ala Crystal? ) (Rejected)" href="https://bugs.ruby-lang.org/issues/12578">#12578</a>, <a class="issue tracker-2 status-6 priority-4 priority-default closed" title="Feature: Shorter syntax for assigning a method argument to an instance variable (Rejected)" href="https://bugs.ruby-lang.org/issues/12820">#12820</a>, and <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: Introduce a new "shortcut assigning" syntax to convenient setup instance variables (Open)" href="https://bugs.ruby-lang.org/issues/15192">#15192</a>.</p> Ruby master - Feature #17942: Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructorhttps://bugs.ruby-lang.org/issues/17942?journal_id=923992021-06-09T05:32:20Zjeremyevans0 (Jeremy Evans)merch-redmine@jeremyevans.net
<ul></ul><p>You should probably read @matz's response to a previous request for instance variable parameters: <a href="https://bugs.ruby-lang.org/issues/8563#note-3" class="external">https://bugs.ruby-lang.org/issues/8563#note-3</a> (which he confirmed had not changed as of 2017: <a href="https://bugs.ruby-lang.org/issues/8563#note-18" class="external">https://bugs.ruby-lang.org/issues/8563#note-18</a>). Your proposal is more complex, but doesn't address the complaint <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/13">@matz (Yukihiro Matsumoto)</a> has regarding using instance variables as parameters. A new issue with your proposal is it would turn <code>public</code>/<code>private</code> into keywords, when they are currently just methods.</p>
<p>If you want really concise class definitions, use <code>Struct</code>. It doesn't get much more concise than:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Thing</span> <span class="o">=</span> <span class="no">Struct</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">:a</span><span class="p">,</span> <span class="ss">:b</span><span class="p">,</span> <span class="ss">:c</span><span class="p">)</span>
</code></pre> Ruby master - Feature #17942: Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructorhttps://bugs.ruby-lang.org/issues/17942?journal_id=952972021-12-13T00:43:18ZLevLukomskyi (Lev Lukomskyi)
<ul></ul><blockquote>
<p>If you want really concise class definitions, use <code>Struct</code>. It doesn't get much more concise than:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Thing</span> <span class="o">=</span> <span class="no">Struct</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">:a</span><span class="p">,</span> <span class="ss">:b</span><span class="p">,</span> <span class="ss">:c</span><span class="p">)</span>
</code></pre>
</blockquote>
<p>This construction is bad because:</p>
<ol>
<li>It <strong>looks awkward</strong>, and not consistent with the rest of the classes declarations in the project, also it leads to <strong>Rubocop offence</strong> because of constant case. You can do <code>class Thing < Struct.new(...)</code> which looks more consistent but it creates a redundant level of inheritance</li>
<li>It doesn't allow to <strong>mix positional and keyword args</strong>, eg. <code>Struct.new(:a, :b, :c, d: nil)</code> won't work</li>
<li>It doesn't allow to <strong>adjust initialization of one of the arguments</strong>, eg. I want <code>@b = b.to_i</code> and the rest initialized as usual.</li>
</ol>
<p>The lack of this feature makes the language NOT FUN because you are forced to create a lot of <strong>duplication</strong> each time you create a basic class, eg.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">PollItem::ToggleVote</span>
<span class="nb">attr_reader</span> <span class="ss">:poll_item</span><span class="p">,</span> <span class="ss">:user</span><span class="p">,</span> <span class="ss">:voted</span><span class="p">,</span> <span class="ss">:ip_address</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">poll_item</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">voted</span><span class="p">,</span> <span class="n">ip_address</span><span class="p">:)</span>
<span class="vi">@poll_item</span> <span class="o">=</span> <span class="n">poll_item</span>
<span class="vi">@user</span> <span class="o">=</span> <span class="n">user</span>
<span class="vi">@voted</span> <span class="o">=</span> <span class="n">voted</span>
<span class="vi">@ip_address</span> <span class="o">=</span> <span class="n">ip_address</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">perform</span><span class="p">;</span> <span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>Here we see <code>poll_item</code>, <code>user</code>, <code>voted</code>, <code>ip_address</code> names are <strong>duplicated 4 times</strong> 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.</p>
<p>This could theoretically be rewritten to something like this:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">PollItem::ToggleVote</span>
<span class="nb">attr_reader</span> <span class="ss">:poll_item</span><span class="p">,</span> <span class="ss">:user</span><span class="p">,</span> <span class="ss">:voted</span><span class="p">,</span> <span class="ss">:ip_address</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="vi">@poll_item</span><span class="p">,</span> <span class="vi">@user</span><span class="p">,</span> <span class="vi">@voted</span><span class="p">,</span> <span class="vi">@ip_address</span><span class="p">:)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">perform</span><span class="p">;</span> <span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>Much cleaner! And then to something like this:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">PollItem::ToggleVote</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="vi">@poll_item</span><span class="p">,</span> <span class="vi">@user</span><span class="p">,</span> <span class="vi">@voted</span><span class="p">,</span> <span class="vi">@ip_address</span><span class="p">:)</span>
<span class="n">init_readers</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">perform</span><span class="p">;</span> <span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p><code>init_readers</code> would create attr_readers for all instance variables in initializer. <code>public</code>/<code>private</code> keywords as described in the issue are not ideal as they litter args and they will be repeated a lot, eg. <code>def initialize(public @poll_item, public @user, public @voted, public @ip_address:)</code>, though it's better than nothing.</p>
<p>If I decide to customize one of argument, I could do this:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">PollItem::ToggleVote</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="vi">@poll_item</span><span class="p">,</span> <span class="vi">@user</span><span class="p">,</span> <span class="vi">@voted</span><span class="p">,</span> <span class="n">ip_address</span><span class="p">:)</span>
<span class="vi">@ip_address</span> <span class="o">=</span> <span class="no">IpAddress</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">ip_address</span><span class="p">)</span>
<span class="n">init_readers</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>This would be fantastic! This would reduce duplication. This would be Concise – I love ruby especially because of this feat.</p>
<p>I saw <strong>Matz was against this feature</strong>, the main point was:</p>
<blockquote>
<p>def initialize(@foo, @bar)<br>
end<br>
does not express intention of instance variable initialization</p>
</blockquote>
<p>But – <strong>it does express</strong> – there is a word <code>initialize</code> and then goes <code>@foo</code>, it means "Please initialize @foo with whatever is passed here".</p>
<p>It'd be sad to block this very useful feature (I'm writing such classes every day for 10+ years) because of any tiny obstacles.</p> Ruby master - Feature #17942: Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructorhttps://bugs.ruby-lang.org/issues/17942?journal_id=1004792022-12-04T03:54:38Znobu (Nobuyoshi Nakada)nobu@ruby-lang.org
<ul></ul><p>This means you want only <code>initialize</code> method to be parsed specially?<br>
And when bypassing this method, e.g., <code>Marshal.load</code>, no accessor will be defined?</p> Ruby master - Feature #17942: Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructorhttps://bugs.ruby-lang.org/issues/17942?journal_id=1004802022-12-04T06:44:27Zsawa (Tsuyoshi Sawada)
<ul></ul><p>LevLukomskyi (Lev Lukomskyi) wrote in <a href="#note-6">#note-6</a>:</p>
<blockquote>
<p>[Y]ou are forced to create a lot of duplication [...]</p>
<pre><code>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
</code></pre>
<p>Here we see <code>poll_item</code>, <code>user</code>, <code>voted</code>, <code>ip_address</code> names are duplicated 4 times</p>
</blockquote>
<p>Not necessarily. You can do with 2 times (counting the use with <code>attr_reader</code>):</p>
<pre><code> def initialize(*args)
@poll_item, @user, @voted, @ip_address = args
end
</code></pre>
<blockquote>
<p>I saw <strong>Matz was against this feature</strong>, the main point was:</p>
<blockquote>
<pre><code>def initialize(@foo, @bar)
end
</code></pre>
<p>does not express intention of instance variable initialization</p>
</blockquote>
<p>But – <strong>it does express</strong> – there is a word <code>initialize</code> and then goes <code>@foo</code>, it means "Please initialize @foo with whatever is passed here".</p>
</blockquote>
<p>No, it doesn't. It would mean "please initialize <strong>the newly created instance</strong> with whatever is passed here as the value of <code>@foo</code>." In general, Ruby code <code>foo.bar(baz)</code> translates to English as "do bar to foo using baz", not "foo does bar to baz."</p>