24
def foo(_, _='override')
  _
end

p foo("bye bye")
p foo("hello", "world")

Output:

"override"
"hello"

I could understand if the result was:

"override"
"world"

or even:

"bye bye"
"hello"

But the result I'm getting causes me confusion.

Gerry
  • 10,584
  • 4
  • 41
  • 49

3 Answers3

4

Default arguments are evaluated earlier in time than regular arguments if an argument is passed for it, otherwise they are evaluated last. Almost certain but unsure how to prove it.

Meaning in this example:

at time 0 call p foo("hello", "world")

at time 1 _ = 'override'

at time 2 _ = "world"

at time 3 _ = "hello"

at time 4 variables are printed and you see "hello"

EDIT here is some evidence:

def foo(_, _='override',_)
  _
end

p foo("bye bye","goodbye")
p foo("hello", "world", "three")

prints

"override"
"three"
sawa
  • 165,429
  • 45
  • 277
  • 381
Mike S
  • 11,329
  • 6
  • 41
  • 76
  • 1
    Since you have "otherwise", the "and only if" is redundant. – sawa Mar 24 '16 at 18:03
  • 1
    Good point, the "otherwise" was sort of an afterthought but I'll fix it. EDIT oops you already fixed it. – Mike S Mar 24 '16 at 18:07
  • In your ordering timeline, you have the assignment to be default, arg2, arg1. However in your second example what is the proposed order? default, arg2, arg1, arg3? (For the first three I'm using the order from your timeline). – Gerry Mar 24 '16 at 18:08
  • In my second example, my proposed order for the first print statement is arg1, arg3, default. And for the second print statement yes it would be default, arg2, arg1, arg3. – Mike S Mar 24 '16 at 18:11
  • I don't get the logic behind your ordering of arguments and it also doesn't hold true in this example: def foo(_, _, _='override') _ end; foo('one', 'two', 'three') #output = 'one' – Gerry Mar 30 '16 at 23:36
1

Ican't find better explanation than this one

ruby magic underscore

The reason for this is found in Ruby’s parser, in shadowing_lvar_gen. All the normal checks for duplication are skipped iff the variable name consists of exactly one underscore.

Abdoo Dev
  • 1,216
  • 11
  • 16
  • 2
    +1 for some interesting info I didn't realise about destructuring assignment. However the article doesn't address my issue so it seems you are telling me to "read the source"? :) – Gerry Mar 24 '16 at 16:24
  • This behaviour got expanded in 2.0.0 from only allowing an underscore to allowing all variables starting with an underscore to have duplicate names. Also, the correct link to the (1.9.3) source [is this](https://github.com/ruby/ruby/blob/ruby_1_9_3/parse.y#L8474). – Martin Tournoij Mar 24 '16 at 16:28
  • 2
    This doesn't answer the question at all. – sawa Mar 24 '16 at 16:34
0

One possible explanation is that name _ stands for "unused variable". You're not supposed to even reference it, let alone expect any values of it. As it is a special name, it gets special treatment from the parser (such as suppressing "duplicate parameter" errors). I imagine that no one cared to make this code produce a logical result, because, again, these are unused variables.

If you rename it to anything else (say, a), you'll get an error, because now this method signature doesn't make sense.

Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
  • 1
    That doesn't answer the question. – sawa Mar 24 '16 at 15:48
  • @sawa: how about now? – Sergio Tulentsev Mar 24 '16 at 15:55
  • 2
    I think pretty much everybody agrees that `_` should not be referred to in a production code. But there should be some logic behind it that returns a particular value. I think the OP is asking about that. Your answer is not wrong, but is still not answering the question. – sawa Mar 24 '16 at 15:56
  • @sawa: yeah, I think it's right in the middle of the UB land. Would you like to inspect instruction sequences for this? :) – Sergio Tulentsev Mar 24 '16 at 15:57
  • @sawa & Sergio Correct, I was wondering about the logic behind result. I wasn't about to put it into production. :) – Gerry Mar 24 '16 at 16:03