rubyprocrefinements

Why doesn't to_proc work inside Ruby refinements?


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?


Solution

  • 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!