4

I just ran into an issue where the value returned by a function is different depending on whether or not I call puts on that result. I'm wondering if this is to be expected, or some sort of parser error.

This only occurs if the block passed to the function uses the do...end syntax, not the inline {...} syntax. Here's an example:

arr = ["a", "b", "c"]
puts i = arr.index { |x| == "b" }
#=> 1

as expected, but this does not work as I'd expect:

arr = ["a", "b", "c"]
puts i = arr.index do |x|
  x == "b"
end
#=> #<Enumerator:0xSomeId>

Though it works fine if I do:

arr = ["a", "b", "c"]
i = arr.index do |x|
  x == "b"
end
puts i
#=> 1

It looks like it's interpreted as if it were passed no block at all (returning the enumerator is the expected behavior of calling arr.index with block). Is this normal? Is this behavior explained/documented anywhere?

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
cgag
  • 127
  • 3
  • 7
  • 2
    It's due to `{ ... }` having a higher precedence than `do ... end`, see: http://stackoverflow.com/questions/2122380/using-do-block-vs-brackets – pjumble Aug 20 '12 at 01:05
  • 2
    Use `arr.index("b")` then you don't have to worry about it :) – Mark Thomas Aug 20 '12 at 01:29

2 Answers2

7

do...end blocks associate with the leftmost method while {...} blocks associate with the rightmost, due to precedence. In your second example, the block is being associated with puts, which does nothing with it.

It seems like weird behavior in this case, but it's this feature of do...end blocks that give a lot of Ruby DSL's their clean, readable syntax.

Max
  • 21,123
  • 5
  • 49
  • 71
  • Usually this isn't an issue because each line should be performing a specific operation. Grouping too many things together not only confuses other developers, but may also confuse the parser. – tadman Aug 20 '12 at 02:21
2

{ ... } binds more tightly than do ... end, so your do ... end block ends up attached to IO#puts (which ignores it) and not Array#index.

If you put then parens into the puts() call you can see:

puts(i = arr.index do |x| x == 'b' end)
DigitalRoss
  • 143,651
  • 25
  • 248
  • 329