Ruby2.0のRefinementsで遊んでみた


こんにちは。寺岡です。

前回の予告通り、今回はRuby2.0のRefinementsについて書きたいと思います。
Ruby2.0.0で実験的機能として追加されるRefinementsは、対象のクラスのメソッドを部分的に変更、追加するための機能です。

ruby2.0.0-rc2のインストール

まずは実行環境の準備です。
rvmインストール済の環境にRuby 2.0.0最後のRC版であるruby-2.0.0-rc2をインストールします。

Refinementsを使ってみる

Refinementsの書式は以下となります

簡単な例として、Stringクラスにhogerizeメソッドを追加してみます。
refineメソッドを呼び出してRefinementsを定義するとwarningが出ますが、気にせずに実行してください。

using HogerizeStringを実行することで、Stringクラスにhogerizeメソッドを追加することができました。

RefinementsでInteger#/を書き換えてみる

今度は既存のクラスのメソッドを書き換えてみましょう。

rubyでは割り算のオペランドがどちらも整数の場合戻り値は整数になります。
小数の結果を得たいときは、どちらかのオペランドをto_fで浮動小数点数に変換してから呼び出します。

いちいちto_fするのは面倒なので、Refinementsを使って書き換えてみましょう。

……上手くいきませんでした。
Rubyの整数型は、抽象クラスIntegerを継承したFixnumとBignumに分かれており、
Refinementsで定義されたメソッドの優先順位はサブクラスよりも低いため、Integerに対してrefineで定義しても、Fixnumの/メソッドが呼び出されてしまうためです。

気を取り直してFixnumとBignumをrefineするように修正して実行してみます。

今度は狙い通りに動きましたね!

Refinementsのスコープについて

Refinementsを利用するためのusingメソッドは、トップレベルスコープでのみ実行できます。
メソッドの中や、クラス、モジュールの宣言中は利用できません。
また、usingの有効範囲は「同一ファイル内のusing以降のコード」となっています。

Refinementsのスコープ周りはなかなか複雑なので、詳しくは本家のwikiを御覧ください。
https://bugs.ruby-lang.org/projects/ruby-trunk/wiki/RefinementsSpec#Scope-of-refinements

まとめ

今回はRefinementsをちょっとだけ試してみました。
将来的には現在の静的なファイル単位のスコープではなく、もっと動的な機能へ進化してくれるだろうと期待しています。
現在はexperimental(実験的機能)として実装されているため本番のコードでは利用すべきではありません。
しかし、特定の範囲に選択的にmixinを実現する非常に先進的な機能なので、将来のためにRefinementsの賢い使い道を研究しておくと良いかもしれません。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です