Module#module_functionって必要なのかしら

ユーティリティ関数をモジュールに定義しておいて、名前空間を分けてグローバルに利用するというアプローチが良くある。Rubyでは、moduleを使ってメソッドをグローバル名前空間と切り分ける。例えばピュアRubyのWebサーバ「Webrick」には、webrick/httputils.rbというのがあって、

module WEBrick
  module HTMLUtils

    ##
    # Escapes &, ", > and < in +string+

    def escape(string)
      str = string ? string.dup : ""
      str.gsub!(/&/n, '&amp;')
      str.gsub!(/\"/n, '&quot;')
      str.gsub!(/>/n, '&gt;')
      str.gsub!(/</n, '&lt;')
      str
    end
    module_function :escape

  end
end

と書かれている。クライアント側では、

      @body << <<-_end_of_html_
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<HTML>
  <HEAD><TITLE>#{HTMLUtils::escape(@reason_phrase)}</TITLE></HEAD>
  <BODY>
    <H1>#{HTMLUtils::escape(@reason_phrase)}</H1>
    #{HTMLUtils::escape(ex.message)}
    <HR>

というように使っている。これはいいんだけど、これと似たことをやる場合に、ぼくは、

module MyUtils
  def self.encrypt(str, options)
      lower  = ("a".."z").to_a.rotate(options[:key]).join
      upper  = lower.upcase
      str.tr('a-z', lower).tr('A-Z', upper)
    end
  end
end

puts MyUtils.encrypt("Hellow World!", key:3)

とやるのが正解かと思っていた。両者の違いは何だ? なぜModule#module_functionを使うんだろうか?

module MyUtils
  def self.meth
    puts "meth"
  end
end

module MyUtils2
  def meth
    puts "meth"
  end

  module_function :meth
end

MyUtils::meth => "meth"
MyUtils2::meth => "meth"

というように両者は、ほぼ同じに見える。class.cを見ると、

void
rb_define_module_function(VALUE module, const char *name, VALUE (*func)(ANYARGS), int argc)
{
    rb_define_private_method(module, name, func, argc);
    rb_define_singleton_method(module, name, func, argc);
}

とある。つまり両者の違いは、Module#module_functionを使うと、一旦インスタンスメソッドとして定義したものを、プライベートにしつつ特異クラスにコピーを定義するというところ。違いがハッキリでるのは以下のような例。

module MyUtils
  def self.meth
    puts "meth"
  end
end

module MyUtils2
  def meth
    puts "meth"
  end

  module_function :meth

  def meth
    puts "meh?"
  end
end

MyUtils::meth # => "meth"
MyUtils2::meth # => "meth"

class MyClass
  include MyUtils2
end

MyClass.new.meth  # => "meh?"
MyUtils2::meth    # => "meth"

しかし、分からんな。module_functionを使う場合でも、インスタンスメソッドなんて使わないだろうし、プライベートになるならほとんど使えない。いちいち「def self.meth」とselfを付ける記法が嫌だとしても、

module MyUtils
  class << self
    def meth
      puts "meth"
    end

    def meh
      puts "meh?"
    end
  end
end

MyUtils::meth # => "meth"
MyUtils::meh  # => "meh?"

とすればいいこと。うーん、好みの問題ってことかしら。ともあれ、モヤモヤしていたModule#module_functionのことが分かってスッキリした。