views:

206

answers:

3

Why is it that the following code runs fine

p (1..1000).inject(0) { |sum, i|
    sum + i
}

But, the following code gives an error

p (1..1000).inject(0) do |sum, i|
    sum + i
end

warning: do not use Fixnums as Symbols
in `inject': 0 is not a symbol (ArgumentError)

Should they not be equivalent?

+3  A: 

This looks like a effect of the difference in binding between do/end and brackets:

brackets, used as you are above, will bind to the last function chained while do/end will bind to the first.

I think thats sort of a odd way to say it, but basically the first instance is passing the block to the function 'inject', while the second is actually trying to pass the block to the first method 'p'.

Pete
"while the second is actually trying to pass the block to the first method (1..1000)." Make that "to the first method `p`" and you're correct.
sepp2k
I corrected it. overlooked that in the code above at first glance!
Pete
+11  A: 

The block written using the curly braces binds to the inject method, which is what your intention is, and it will work fine.

However, the block that is encapsulated in the do/end block, will bind to the p-method. Because of this, the inject call does not have an associated block. In this case, inject will interpret the argument, in this case 0, as a method name to call on every object. Bacuase 0 is not a symbol that can be converted into a method call, this will yield a warning.

wvanbergen
A: 

The problem is with the p at the beginning. If you omit these you'll see that both work fine:

# Works!
[5, 6, 7].inject(0) do |sum, i| # Correctly binds to `inject`.
  sum + i
end

# Works too!
[5, 6, 7].inject(0) { |sum, i|  # Correctly binds to `inject`.
  sum + i
}

But this won't work:

# Kablammo! "p" came first, so it gets first dibs on your do..end block.
# Now inject has no block to bind to!
p [5, 6, 7].inject(0) do |sum, i|   # Binds to `p` -- not what you wanted.
  sum + i
end
John Feminella