Search code examples
rubymodulemixins

Ruby 'module_function' vs. including module


In Ruby, I understand that module functions can be made available without mixing in the module by using module_function as shown here. I can see how this is useful so you can use the function without mixing in the module.

module MyModule
  def do_something
    puts "Hello, World!"
  end
  module_function :do_something
end

Why might you want to have the function defined both of these ways?

Why not just have

def MyModule.do_something

OR

def do_something

In what kind of cases would it be useful to have the function available to be mixed in, or to be used as a static method?


Solution

  • Think of Enumerable.

    This is the perfect example of when you need to include it in a module. If your class defines #each, you get a lot of goodness just by including a module (#map, #select, etc.). This is the only case when I use modules as mixins - when the module provides functionality in terms of a few methods, defined in the class you include the module it. I can argue that this should be the only case in general.

    As for defining "static" methods, a better approach would be:

    module MyModule
      def self.do_something
      end
    end
    

    You don't really need to call #module_function. I think it is just weird legacy stuff.

    You can even do this:

    module MyModule
      extend self
    
      def do_something
      end
    end
    

    ...but it won't work well if you also want to include the module somewhere. I suggest avoiding it until you learn the subtleties of the Ruby metaprogramming.

    Finally, if you just do:

    def do_something
    end
    

    ...it will not end up as a global function, but as a private method on Object (there are no functions in Ruby, just methods). There are two downsides. First, you don't have namespacing - if you define another function with the same name, it's the one that gets evaluated later that you get. Second, if you have functionality implemented in terms of #method_missing, having a private method in Object will shadow it. And finally, monkey patching Object is just evil business :)

    EDIT:

    module_function can be used in a way similar to private:

    module Something
      def foo
        puts 'foo'
      end
    
      module_function
    
      def bar
        puts 'bar'
      end
    end
    

    That way, you can call Something.bar, but not not Something.foo. If you define any other methods after this call to module_function, they would also be available without mixing in.

    I don't like it for two reasons, though. First, modules that are both mixed in and have "static" methods sound a bit dodgy. There might be valid cases, but it won't be that often. As I said, I prefer either to use a module as a namespace or mix it in, but not both.

    Second, in this example, bar would also be available to classes/modules that mix in Something. I'm not sure when this is desirable, since either the method uses self and it has to be mixed in, or doesn't and then it does not need to be mixed in.

    I think using module_function without passing the name of the method is used quite more often than with. Same goes for private and protected.