Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling 'puts' on an expression changing the way it's interpreted?

Tags:

ruby

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?

like image 672
cgag Avatar asked Jan 15 '23 19:01

cgag


2 Answers

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.

like image 116
Max Avatar answered Jan 27 '23 17:01

Max


{ ... } 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)
like image 23
DigitalRoss Avatar answered Jan 27 '23 17:01

DigitalRoss