Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 956
Patrick Brinich-Langlois Avatar asked Sep 26 '22 10:09

Patrick Brinich-Langlois


2 Answers

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!

like image 150
Aleksei Matiushkin Avatar answered Sep 28 '22 04:09

Aleksei Matiushkin


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.

like image 24
sawa Avatar answered Sep 28 '22 05:09

sawa