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

前端 未结 4 1522
长情又很酷
长情又很酷 2021-02-13 13:29

Presenting the Idiom

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

module          


        
4条回答
  •  误落风尘
    2021-02-13 14:00

    Your parse of the idiom is pretty spot on, but I'll go through it and try to clear up any questions you mentioned.

    1. Foo.constants

    As you mentioned, this returns an array of module constant names as symbols.

    2. Array#map

    You obviously know what this does, but I want to include it for completeness. Map takes a block and calls that block with each element as an argument. It returns an Array of the results of these block calls.

    3. Object#method

    Also as you mentioned, this does a method lookup. This is important because a method without parentheses in Ruby is a method call of that method without any arguments.

    4. &

    This operator is for converting things to blocks. We need this because blocks are not first-class objects in Ruby. Because of this second-class status, we have no way to create blocks which stand alone, but we can convert Procs into blocks (but only when we are passing them to a function)! The & operator is our way of doing this conversion. Whenever we want to pass a Proc object as if it were a block, we can prepend it with the & operator and pass it as the last argument to our function. But & can actually convert more than just Proc objects, it can convert anything that has a to_proc method!

    In our case, we have a Method object, which does have a to_proc method. The difference between a Proc object and a Method object lies in their context. A Method object is bound to a class instance and has access to the variables which belong to that class. A Proc is bound to the context in which it is created; that is, it has access to the scope in which it was created. Method#to_proc bundles up the context of the method so that the resulting Proc has access to the same variables. You can find more about the & operator here.

    5. grep(Class)

    The way Enumerable#grep works is that it runs argument === x for all x in the enumerable. The ordering of the arguments to === is very important in this case, since it's calling Class.=== rather than Foo::Bar.===. We can see the difference between these two by running:

        irb(main):043:0> Class === Foo::Bar
        => true
        irb(main):044:0> Foo::Bar === Class
        => false
    

    Module#=== (Class inherits its === method from Method) returns True when the argument is an instance of Module or one of its descendants (like Class!), which will filter out constants which are not of type Module or Class. You can find the documentation for Module#=== here.

提交回复
热议问题