CTOのひらいさだあきです。
RubyKaigi 2017が、2017/09/18から2017/09/20まで広島国際会議場で開催されています。グッドパッチのエンジニアも数名で広島に来ています。

これはRubyの父Matzによる二日目の基調講演のレポートです。また、去年の基調講演レポートはこちらです。

The Many Faces of Module

Types、Concurrency、Performanceに続いて、今年はModuleについての基調講演でした。
RubyのModuleにはさまざまな機能や使い方があり、それぞれが紹介されました。

  1. mixin
  2. Namespace
  3. Singleton
  4. Set of method (メソッドの集合)
  5. Unit of method combination (メソッド結合の単位)
  6. Refinement
  7. Structural signature(Proposal)

Moduleの歴史

はじめに、Moduleのこれまでの歴史が説明されました。
Simula (1968)という世界で一番最初のオブジェクト指向言語について紹介されました。
この言語にはObject、Class、Inheritance、差分プログラミングなどオブジェクト指向の概念がすでに含まれていました。この時点での継承は単一継承で、その後多重継承という概念が発明されました。

まつもとさんが調べた中で、最初の多重継承はFlavorsというLisp方言に実装されていました。
単一継承の場合クラス構造はツリー構造になりますが、多重継承はネットワーク構造となり複雑になります。この問題を解決するためにC3 linearization algorithmが発明されました。単純な構造の場合は問題ありませんが、複雑な継承の場合、このアルゴリズムの結果を人間が想像することは難しく、そのためLispの人たちはFlavorsを考えつきました。mixinに該当する機能は当時Flavorsと呼ばれていました。

mixin

Rubyは多重継承の仕組みをmoduleという名前で採用しました。moduleには以下のような特徴があります。

  • moduleはClassというclassのスーパークラス
  • moduleはインスタンスを生成できない
  • moduleは他のクラスを継承できない
  • ほかのmoduleから機能を受け継ぐことはできても、classを受け継げない
  • moduleはincludeを使って、機能を差し込むことができる
  • moduleはmixinの単位として作られた

Rubyの歴史をできる限りさかのぼってみると、最初のバージョンからmoduleとmixinは存在していました。またmoduleはほかの機能にも使われるようになり、たとえばnamespaceとして使われるようになりました。

Namespace

moduleは名前空間のように、classやmoduleをまとめるために使われるようになりました。Net::HTTPなどはその使い方をしています。
moduleがRubyにとりこまれてから、このようないれものとしてのmoduleの使い方がされるようになるまであまり時間はかからなかったそうです。

Singleton

またmoduleはSingletonとしても使われるようになりました。
FileUtilsなどはSingletonとして使われています。FileUtilsには関連する特異メソッドがまとめられています。インスタンスを作ったり、includeされることを期待しているわけではありません。FileUtilという場所を示すことを目的としています。

Set of method (メソッドの集合)

メソッドとしての集まり、機能の集まりとしてのmoduleもあります。
ひとつの例としてMathモジュールはメソッドの集まりを表現しています。
Math.sinとして利用できますし、include Mathとすると、sinなどの数学関数を直接呼び出せませます。
またmodule_functionという仕組みで、インスタンスメソッドを特異メソッドにコピーして呼び出すこともできます。

Unit of method combination (メソッド結合の単位)

Ruby 2.0のタイミングで、moduleに新しい役割が追加されました。メソッド結合(メソッドコンビネーション)の単位としての機能です。

alias_method_chain というテクニックがあります。あるメソッドに別名をつけて退避し、新しいメソッドを定義してその別名を呼ぶことで、既存のメソッドを置き換えることができます。これはRailsなどで多用されていました。

これは便利ですが欠点もあります。うっかり2回chainすると、メソッドが書き換えられて壊れてしまいます。そして、より安全にchainを作るために提案されたのがModule#prependです。

Module#prependは静的なので失敗することが少なく安全です。その代わりに動的性は少ないので、prependしたものをあとから外すことはできません。
Rubyではクラス構造などは容易に変更できるべきではないと考えているため、スーパークラスを途中で変更したり、一度includeしたmoduleを外すといった機能は意図的に提供していないそうです。

Module#prependを使うと、CLOS(Common Lisp Object System)のメソッドコンビネーションの機能や、アスペクト指向プログラミングの考え方と同様に既存のメソッドを包み込んでフックすることができます。

Refinement

さらに、同じくRuby 2.0からRefinementの単位という機能も与えられました。Rubyはオープンクラスという機能があり、これを利用して既存のクラスを書き換えることができます。
これは一般的にMonkey Patchingと呼ばれています。なぜMonkeyなのか、なぜその名前で呼ばれるのか調べていたら Guerrilla → Gorilla → Monkey という遷移をたどっていたそうです。

Monkey Patching の典型例はActiveSupportです。
Monkey Patching はグローバルに影響があるので、ひとりだけがMonkey Patchingを提供して、他の人はそれを利用するといった使い方がよいと思います。RailsだとActiveSupportがそれを引き受けています。

これらはRubyのよいところではありますが、特定の範囲内にとどめたいと思うのが世の常です。スコープを区切ったモンキーパッチができるようになれば、とても強力な仕組みになります。

RubyのrefinementはLexicalな変更になります。moduleの中にrefinementの機能を入れることで、Rの中だけでStringクラスを置き換えるという使い方ができるようになっています。

refinementの利用場面としては主に2つあります。
ひとつはRSpecのように既存クラスにたくさんのメソッドを追加する局面になります。追加するメソッドをrefinementに詰め込んで、必要な場所でだけ追加するといった形です。

ただRSpecはまだrefinementを利用していません。大量にusingを書かないといけないことが問題で、これを解決するいいアイディアがまだないそうです。

もうひとつの利用局面としては既存のメソッドを置き換えるということがあります。ただし、メソッドを呼び出した先の先で同じメソッドを呼び出されても、refinementの対象になりません。
refinementは既存メソッドを置き換える機能としては限定的ですが、予想外のところで変わることはなく、ある種のトレードオフになっています。

Structural signature

まだ提案されているだけですが、Structural signatureの考え方があります。
Structual type(構造型)をmoduleを使ってチェックしようという提案がありました。

Strというmoduleを定義して、to_sとto_strというメソッドがあることをconformでチェックしようという使い方です。conformは仮の名前です。メソッドを持っていない場合、例外が発生します。

is_a?を使うとダックタイピングを阻害するため、推奨されていません。現在はrespond_to?を使っている人は多いと思います。これは同じようなチェックをまとめてでてきるようにするという提案です。将来、静的な型が導入されるに当たって、構造型のチェックを行うことは断言しているので、このようなアイディアが復活する可能性も考えられます。

セッションのまとめ

moduleはmixinの単位として誕生しましたが、さまざまな目的で利用されるようになりました。このようにRubyではさまざまことを、他の言語を参考にしながらデザインしてきました。

Rubyを書き始めて20年以上が経ちました。Rubyのリードデザイナーであることは変わりませんが、手を動かして直すというよりRubyはこうあるべきというゴールを示して、コミュニティの人たちが直すということが増えてきました。Rubyはいまでは「私の言語」ではなく「我々の言語」という感じです。

まとめ

Rubyのmoduleについて、その思想や経緯を含めて、こんなにもまとまって話を聞いたのははじめてでした。moduleの最後に紹介されていたStructural signatureは、型とともにとても楽しみな内容でした。今後のRubyへの期待が高まります。
RubyKaigiはまだ続くので、他のセッションも楽しみです!

最後に、グッドパッチではエンジニアの採用を行なっています。ご興味のある方はぜひお気軽にオフィスに遊びに来てください。お待ちしています!