It seems that to_proc
doesn't work on methods defined in refinements:
module ArrayExtensions
refine Array do
def sum
reduce(0, :+)
end
end
end
using ArrayExtensions
puts [[1, 2, 3]].map { |array| array.sum } # => 6
puts [[1, 2, 3]].map(&:sum) # => array.rb:13:in `map': undefined method `sum' for [1, 2, 3]:Array (NoMethodError)
puts [1, 2, 3].method(:sum).to_proc.call # => array.rb:14:in `method': undefined method `sum' for class `Array' (NameError)
Is this the intended behavior? Is there a workaround?
NB The answer below is correct for legacy rubies. In Ruby 2.5+ map(&:sum)
works as expected.
Scoping of refinements is restricted to current context. Since refinements are intended to be not global, as an opposite to monkey patches, any try to call the refined method from outside is prevented. In the code below:
puts [[1, 2, 3]].map { |array| array.sum } # => 6
the scope is fine, we are inside the same scope where this refinement was defined. But here:
puts [[1, 2, 3]].map(&:sum)
the scope is transferred to the context of Symbol
class (!). As stated in the documentation:
When control is transferred outside the scope the refinement is deactivated.
The analogy here would be a private method. Though, while it is exactly as stated in docs, I am unsure whether this behaviour is intended. I believe, an interpreter should take care about such cases. But this question is better to address Matz :)
P.S. Good question!
Of course it is intended. The scope for a refinement is either the module block or the file where using
was called. In your (&:sum)
, you are not (explicitly) calling the method sum
; you only have a symbol :sum
there. Calling is done somewhere in the Ruby C-implementation when Symbol#to_proc
is used. That environment is where your refinement is not effective.
A workaround is to explicitly call the method within that file.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With