Project

General

Profile

Bug #15708

Implicit numbered argument decomposes an array

Added by sawa (Tsuyoshi Sawada) 9 months ago. Updated 5 months ago.

Status:
Rejected
Priority:
Normal
Target version:
-

Description

In the following, @1 refers to the entire item iterated:

a = [1, 2, 3]
a.map{|x| x} # => [1, 2, 3]
a.map{@1} # => [1, 2, 3]

whereas in the following, @1 refers to the first item achieved by decomposing the item iterated, behaving the same as x given by |(x)| rather than by |x|:

a = [[1], [2], [3]]
a.map{|x| x} # => [[1], [2], [3]]
a.map{|(x)| x} # => [1, 2, 3]
a.map{@1} # => [1, 2, 3]

Is this intended?


Files

single-implicit-arg-no-destructure.diff (471 Bytes) single-implicit-arg-no-destructure.diff jeremyevans0 (Jeremy Evans), 04/11/2019 09:34 PM

Related issues

Related to Ruby master - Misc #15723: Reconsider numbered parametersFeedbackActions
Related to Ruby master - Bug #16178: Numbered parameters: _1 should be the same as |x| and _0 should not existClosedActions

History

#1

Updated by mame (Yusuke Endoh) 9 months ago

Yes, it is actually intended. @1 is equivalent to |at1, at2, at3, ...|'s at1.

a = [1, 2, 3]
a.map{|x,| x} # => [1, 2, 3]
a.map{@1} # => [1, 2, 3]

a = [[1], [2], [3]]
a.map{|x,| x} # => [1, 2, 3]
a.map{@1} # => [1, 2, 3]

Honestly, I'm not a fan of the behavior, though.

#2

Updated by shevegen (Robert A. Heiler) 9 months ago

Honestly, I'm not a fan of the behavior, though.

I think it comes down a lot to personal preferences. For example I actually
like being able to use @NUMBER_HERE :) - although in "production" code,
I may use the oldschool variant simply because, although it is longer,
it is more explicit; but I think it is fine to have the possibility to use
@NUMBER if one wants to. My primary reason for liking this, aside from once
having suggested something vaguely similar :P, is that it reminds me of $1
$2 etc...

None of these will win in a beauty contest, but e. g. $1 $2 is very simple
to type and also simple to remember - we just point to the matching capture
group. (I actually also "manually" count within the code comment for regexes,
e. g. I have lines like:

 when /(.)(.)(.)/ # $3

or something like that, just so that I can quickly see which capture group
I was interested in. Other personal preferences may include things such as
endless range :D - or other changes. Anyway I digress.

I think the primary part of sawa's comment is to clarify on the behaviour,
and ideally note it down as the correct behaviour too. Ruby users may
otherwise be confused (and defined behaviour is also easier to test
against).

Mame said that the behaviour is as expected; this is probably the correct
answer. I would suggest to also include one of sawa's example in the
official documentation - this may help avoid confusion for other ruby
folks, when ruby 2.7 is released come xmas.

Updated by hsbt (Hiroshi SHIBATA) 9 months ago

Sorry, your inconvenience experience. We have an issue of bugs.ruby-lang.org. I fixed it on this morning(JST).
I removed duplicated issues and copy from them.

nobu (Nobuyoshi Nakada) said:

Intended.

It equals

a.map{|x,| x} # => [1, 2, 3]

Updated by Eregon (Benoit Daloze) 8 months ago

As I said in #15723,

I believe the |x,| behavior for @1 can only be considered a bug.

It prevents array_of_arrays.each { p @1 } to work correctly.
Why would we want to prevent that and make this pattern not general, dangerous, inconsistent and unusable for nested arrays?
This doesn't make any sense to me.

How can this be intended?
It ignores elements and make one of the simplest use of @1 wrong.

array_of_arrays = [[1,2], [3,4]]
array_of_arrays.each { p @1 }
# => 1
# => 3

The same happens for every block with @1 which passed value happens to be an Array.

This kind of behavior is what I learned in programming languages classes as a design flaw,
because it cannot handle properly elements independent of their representation.

Updated by Eregon (Benoit Daloze) 8 months ago

FWIW, I would bet >99% of Rubyists would agree this is a bug: https://twitter.com/eregontp/status/1115318993299083265

Updated by dgutov (Dmitry Gutov) 8 months ago

This is what happens when one syntactic sugar(*) collides with another.

(*) a.map { |x,| x } being a shorthand for a.map { |(x)| x } , and sometimes not, depending on the runtime values.

Neither of these are good, IMO (one for consistency and strong typing, and a lot of people have already expressed their feelings about the other).

It's too late to get rid of the first one, I think. But we can still reverse the decision on the new one.

Updated by Eregon (Benoit Daloze) 8 months ago

For some reason, a reply on the tracker seems to have been lost, or removed.
I think it is highly relevant, so I'll quote it here:

sholden (Scott Holden) wrote:

This is definitely not the behavior I would expect. In everything that I've seen, developers are describing the feature such that

a.map{|x| x} == a.map{ @1 }

This would be a very surprising behavior for people to stumble upon.

Updated by Eregon (Benoit Daloze) 8 months ago

FWIW, the replies on my tweet above is some good sign that very few Rubyists expect this behavior and it breaks the basics assumptions of how the feature can be used.

Updated by jeremyevans0 (Jeremy Evans) 8 months ago

Attached is a patch that will turn off destructuring if the only implicit block variable is @1:

# equivalent to proc{|x| x}
proc{@1}.call([1,2])
# => [1, 2]

# equivalent to proc{|_,x| x}
proc{@2}.call([1,2])
# => 2

# equivalent to proc{|x,y| y; x}
proc{@2; @1}.call([1,2])
# => 1

I think this results in semantics that most people would expect, even if they don't like the implicit block argument syntax.

Updated by nobu (Nobuyoshi Nakada) 8 months ago

jeremyevans0 (Jeremy Evans) wrote:

Attached is a patch that will turn off destructuring if the only implicit block variable is @1:

-   args->nd_ainfo->rest_arg = excessed_comma;
+   if (max_numparam > 1) {
+       args->nd_ainfo->rest_arg = excessed_comma;
+   }

It can be done by just removing the line, regardless max_numparam.

#11

Updated by nobu (Nobuyoshi Nakada) 8 months ago

  • Related to Misc #15723: Reconsider numbered parameters added

Updated by sawa (Tsuyoshi Sawada) 6 months ago

説明をありがとうこざいます。バクでないことは分かったので、閉じて下さい。

#13

Updated by hsbt (Hiroshi SHIBATA) 6 months ago

  • Status changed from Open to Rejected

Updated by Eregon (Benoit Daloze) 5 months ago

  • Assignee set to matz (Yukihiro Matsumoto)
  • Status changed from Rejected to Assigned

IMHO, this is very much a bug, and the single reason I heard for it seems largely outweigh by being non-intuitive and breaking code.
nobu (Nobuyoshi Nakada) BTW, that reason has not been written here yet and should be, please write it down.
IMHO using @N will always be surprising for debugging since it changes arity.

So I reopen this and assign to matz.

I believe we all agree that if we accept only a single unnamed parameter, that it should be the same as { |x| }.
I don't think that rule should change with multiple unnamed parameters.

TBH, I don't think this behavior is rational language design (taking the semantics of a almost-never-used syntax (because it's dangerous) for a new syntactic sugar), but it's just my opinion.

Updated by sawa (Tsuyoshi Sawada) 5 months ago

これは意図されたものなので、バグではありません。勘違いによりアサインされてしまったようなので、閉じてください。

この挙動に対する不満を述べているコメントがあるようですが、それなら明らかにそれは仕様として別のイシューとして書かれるべきであり、バグに対してまつもとさんにアサインされて、判断を仰ごうとしているのがそもそも自己矛盾だと思います。

#16

Updated by nobu (Nobuyoshi Nakada) 5 months ago

  • Status changed from Assigned to Rejected

Updated by mame (Yusuke Endoh) 5 months ago

Eregon (Benoit Daloze) I think sawa says "This behavior was intentional so not a bug. Please file a new ticket if you request a feature change."

sawa (Tsuyoshi Sawada) I know you can write English better than I. Please do not use Japanese in an English ticket.

Updated by Eregon (Benoit Daloze) 5 months ago

I think we don't need a new ticket.
This ticket description explains the problem and I argue it is bug, even if the current behavior was intended by nobu.
Let's let matz judge.

Also, if the behavior is kept we should have a clear reason written on this ticket.
I see no reason on the ticket besides "intended" which doesn't explain the rationale, so I think the discussion shouldn't be closed.
OTOH I see usages which are broken by this behavior (e.g., array_of_arrays and map), and yet no answer to that.

#19

Updated by Eregon (Benoit Daloze) 2 months ago

  • Related to Bug #16178: Numbered parameters: _1 should be the same as |x| and _0 should not exist added

Also available in: Atom PDF