From: "zverok (Victor Shepelev)" Date: 2022-08-29T08:05:59+00:00 Subject: [ruby-core:109753] [Ruby master Feature#18980] Re-reconsider numbered parameters: `it` as a default block parameter Issue #18980 has been updated by zverok (Victor Shepelev). My 5c: I came to (almost) peace with `_1`, and we use it extensively in the codebase, and find it quite convenient. Choosing the designation for it is a hard choice, and after years of consideration, I believe that the current solution, if looking somewhat "weird", is a very well-balanced choice, and I still didn' see any that is better. From my PoV, the design space can be described this way: 1. It should be something following the rules of scoping by its name (e.g. `_1` is a valid name of local variable, implying locality). This rules out `@1`, which implies "some special _instance_ variable" 2. It should look _special_. Even if you don't know its meaning (just learning Ruby, or haven't upgraded your knowledge of the language for a long time), it should immediately imply "it is not just a regular name like all other names". This rules out `it`. Even besides "somebody could've used this name already" (and somebody could indeed, besides RSpec, I saw codebases which used this abbreviation to mean "iterator", "item", or "i(ndex of) t(ime point)"), it just doesn't give a strong feeling of "this is a local name, but also a special name". 3. As far as I understand, we are currently quite reluctant towards introducing "Perlisms" like "this character means something new in that context." For operators (verbs) we seek to recombine existing ones (like `in` and `=>` for pattern matching), and for variables/methods, we seek to stay with existing naming schemes. 4. (Have mixed feelings about this one) It probably should allow a sequence of similar names (like `_1`/`_2` do) TBH, I can't think of much better naming scheme which would satisfy _at least_ 1-3. About 4: like @k0kubun, I find myself not very comfortable with `_1` meaning different things depending on the presence of `_2`. As a consequence, I use `_1` extensively in obvious cases (when it is the _only_ one), but while iterating on hashes, I frequently prefer to name the block params explicitly. That actually might be a greater obstacle for adoption (at least for some) than particular naming, which also highlighted by @k0kubun in the "Why I don't use _1" section. In our current codebase (quite large), that switched to Ruby 2.7 a year ago, there are ~200 entries of `_1`, and 6 (six) usages of `_2` (and no usages of `_3`, `_4` etc.). And while `_1` is used extensively and really helps to write shorter and DRYer blocks, our usages of `_2` are mostly "clever-isms" like this: ```ruby users.sort { _2.last_seen_at <=> _1.last_seen_at } # the "clever" way of doing just... users.sort_by(&:last_seen_at).reverse # ...because we still don't have `reverse_sort_by` ``` So, the thinking-out-of-the-box solution might be to preserve `_1`, but deprecate the rest :) ---------------------------------------- Feature #18980: Re-reconsider numbered parameters: `it` as a default block parameter https://bugs.ruby-lang.org/issues/18980#change-98990 * Author: k0kubun (Takashi Kokubun) * Status: Open * Priority: Normal ---------------------------------------- ## Problem Numbered parameters (`_1`, `_2`, ...) look like unused local variables and I don't feel motivated to use them, even though I need this feature very often and always come up with `_1`. ```rb [1, 2, 3].each { puts _1 } ``` I have barely used it in the last 2~3 years because it looks like a compromised syntax. I even hesitate to use it on IRB. ### Why I don't use `_1` I'm not clever enough to remember the order of parameters. Therefore, when a block has multiple parameters, I'd always want to name those parameters because which is `_1` or `_2` is not immediately obvious. Thus I would use this feature only when a block takes a single argument, which is actually pretty common. If I use `_1`, it feels like there might be a second argument, and you might waste time to think about `_2`, even if `_2` doesn't exist, which is a cognitive overhead. If you use `it`, it kinda implies there's only a single argument, so you don't need to spend time remembering whether `_2` exists or not. It is important for me that there's no number in `it`. ## Proposal Hoping to introduce `it` as an alternative to `_1` later, experiment with warning `#it` method calls without any arguments or blocks. If nobody sees serious problems after some warning period, we'll implement `it` as follows: ### Specification ```rb [1, 2, 3].each { puts it } ``` `it`s behavior should be as close to `_1` as possible. `it` should treat array arguments in the same way as `_1`. `it` doesn't work in a block when an ordinary parameter is defined. `it` is implemented as a special case of `getlocal` insn, not a method. `it` without an argument is considered `_1` or a normal local variable if defined. `it` is considered a method call only when it has any positional/keyword/block arguments. ## Past discussions * [Feature #4475] default variable name for parameter: Proposed `it`, and merged as `@1`. * 2019/03/13: [DevelopersMeeting20190311Japan](https://docs.google.com/document/d/e/2PACX-1vTUCmj7aUdnMAdunG0AZo0AdWK-9jvfXcB7DWYmzGtmPc0IuIPGn7eLARoR5tBd6XUUB08W-hH74k-T/pub) * 2019/04/17: [DevelopersMeeting20190417Japan](https://docs.google.com/document/d/1hw6Xca8arG6b0V63zvWnNEtxIjEjEVzS10KXGhzZpI8/pub) * 2019/04/20: [Ruby Committers vs the World](https://youtu.be/5eAXAUTtNYU?t=3118) * [Feature #15723] Reconsider numbered parameters: Renamed `@1` to `_1`. * 2019/08/29: [DevelopersMeeting20190829Japan](https://docs.google.com/document/d/1XypDO1crRV9uNg1_ajxkljVdN8Vdyl5hnz462bDQw34/edit?usp=sharing) * [Feature #15897] `it` as a default block parameter: Proposed `it`, and got closed because `_1` was merged. ### Compatibility `it` has not necessarily been rejected by Matz; he just said [it's difficult to keep compatibility](https://bugs.ruby-lang.org/issues/4475#note-6) and [`it` or `this` _could_ break existing code](https://bugs.ruby-lang.org/issues/15723#note-2). It feels like everybody thinks `it` is the most beautiful option but is not sure if `it` breaks compatibility. But, in reality, does `it`? The following cases have been discussed: * `it` method, most famously in RSpec: You almost always pass a positional and/or block argument to RSpec's `it`, so the conflict is avoided with my proposal. You virtually never use a completely naked `it` ([comment](https://bugs.ruby-lang.org/issues/15897#note-29)). * `it` local variable: With the specification in my proposal, the existing code can continue to work if we consider `it` as a local variable when defined. With the specification in my proposal, existing code seems to break if and only if you call a method `#it` without an argument. But it seems pretty rare (reminder: a block given to an RSpec test case is also an argument). It almost feels like people are too afraid of compatibility problems that barely exist or have not really thought about options to address them. Also, you could always experiment with just showing warnings, which doesn't break any compatibility. Even if it takes 2~3 years of a warning period, I'd be happy to use that in 3 years. ### Confusion We should separately discuss incompatible cases and "works but confusing" cases. Potential confusion points: * RSpec's `it "tests something" do ... end` vs `it` inside the `do ... end` * `it` could be a local variable or `_1`, depending on the situation My two cents: You'd rarely need to write `it` directly under RSpec's `it` block, and you would just name a block argument for that case. In a nested block under a test case, I don't think you'd feel `it` is RSpec's. When you use a local variable `it = 1`, you'd use the local variable in a very small scope or few lines because otherwise, it'd be very hard to figure out what the local variable has anyway. So you'd likely see the assignment `it = 1` near the use of the local variable and you could easily notice `it` is not `_1`. If not, such code would be confusing and fragile even without this feature. The same applies when `it` is a method/block argument. I believe it wouldn't be as confusing as some people think, and you can always choose to not use `it` in places where `it` is confusing. -- https://bugs.ruby-lang.org/ Unsubscribe: