Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the "#map(&proc)" idiom work when introspecting module classes?

Presenting the Idiom

I found an interesting but unexplained alternative to an accepted answer. The code clearly works in the REPL. For example:

module Foo
  class Bar
    def baz
    end
  end
end
Foo.constants.map(&Foo.method(:const_get)).grep(Class)
=> [Foo::Bar]

However, I don't fully understand the idiom in use here. In particular, I don't understand the use of &Foo, which seems to be some sort of closure, or how this specific invocation of #grep operates on the result.

Parsing the Idiom

So far, I've been able to parse bits and pieces of this, but I'm not really seeing how it all fits together. Here's what I think I understand about the sample code.

  1. Foo.constants returns an array of module constants as symbols.

  2. method(:const_get) uses Object#method to perform a method lookup and return a closure.

  3. Foo.method(:const_get).call :Bar is a closure that returns a qualified path to a constant within the class.

  4. &Foo seems to be some sort of special lambda. The docs say:

    The & argument preserves the tricks if a Proc object is given by & argument.

    I'm not sure I fully understand what that means in this specific context, either. Why a Proc? What "tricks," and why are they necessary here?

  5. grep(Class) is operating on the value of the #map method, but its features are not obvious.

    • Why is this #map construct returning a greppable Array instead of an Enumerator?

      Foo.constants.map(&Foo.method(:const_get)).class
      => Array
      
    • How does grepping for a class named Class actually work, and why is that particular construction necessary here?

      [Foo::Bar].grep Class
      => [Foo::Bar]
      

The Question, Restated

I'd really like to understand this idiom in its entirety. Can anyone fill in the gaps here, and explain how the pieces all fit together?

like image 457
Todd A. Jacobs Avatar asked Jul 16 '12 13:07

Todd A. Jacobs


1 Answers

&Foo.method(:const_get) is the method const_get of the Foo object. Here's another example:

m = 1.method(:+)
#=> #<Method: Fixnum#+>
m.call(1)
#=> 2
(1..3).map(&m)
#=> [2, 3, 4]

So in the end this is just a pointfree way of saying Foo.constants.map { |c| Foo.const_get(c) }. grep uses === to select elements, so it would only get constants that refer to classes, not other values. This can be verified by adding another constant to Foo, e.g. Baz = 1, which will not get grepped.

If you have further questions please add them as comments and I'll try to clarify them.

like image 111
Michael Kohl Avatar answered Oct 19 '22 19:10

Michael Kohl