Ruby 2.0初のプレビュー版がリリース! 注目機能は!?
2012年11月2日、Ruby 2.0.0-preview1のリリースがアナウンスされました。Ruby 2.0はRuby生誕20周年となる2013年の2月24日にリリースが予定されています。現在の安定版であるバージョン1.9系の次のメジャーバージョンアップとなります。ちなみに、1.9の正式版が初めてリリースされたのは2007年12月25日でした。
Ruby 2.0のリリースマネージャ、遠藤侑介さんがメーリングリストに流したアナウンスによれば、2.0.0の主な新機能は以下の通り。
- Refinements
- キーワード引数
- Enumerator#lazy
- Module#prepend
- Hash への変換メソッド #to_h
- %i: シンボルの配列のリテラル
- 正規表現エンジンを Onigmo に変更
- DTrace サポート
それぞれの機能について特に説明がなかったので、西村が分かる範囲で少し各機能について触れておきます。間違っていたらご指摘頂けると嬉しいです。
Refinementsは既存クラスの挙動を変更したり、拡張するときの影響範囲を名前空間で限定する機能。Rubyはオープンクラスなので定義済みのクラスは、いつでも再オープンしてメソッドを追加・削除することができます。言語のコアライブラリの機能でも変更できて、Ruby on Railsのユーリティティライブラリ、ActiveSupportは割とやりたい放題というか、良い感じの拡張が入っています。例えば、ハッシュのmergeを逆方向にするとか、ディープにマージするというようなことはRuby言語の標準には入っていなくて、ActiveSupportに含まれます。こうした拡張機能は、Rubyコミュニティでゆるやかに共有されていて、小さなライブラリでもActiveSupportの一部を取ってきていたり、類似の拡張をすることがあります(多くの場合はlib/core_extとかlib/extといった辺りに入っていて、たいていはarray.rbとかhash.rbといったように標準クラスの挙動に手を入れています)。そうやって拡張したとき問題となるのが、名前の衝突です。名前が衝突するだけならまだしも、似たような挙動だったら嫌ですね。Refinementsは、オープンクラスの柔軟性と強力さを維持したまま、いわゆる“モンキーパッチ”の影響範囲を限定するために採用された機構です。
キーワード引数は文字通り、メソッドの個々の引数に内容を示すキーワードを付与できる機能です。GitHubにミラーされているRubyのソースコードリポジトリのここにテストコードがあります。ちょっと抜き出すと:
def f1(str: "foo", num: 424242)
[str, num]
end
f1 # => "foo", 424242
f1(str: "bar") # => "bar", 424242
f1(str: "bar", num: 111111) # => "bar", 111111
という感じです。これまでRuby界隈ではハッシュのリテラルを引数の最後に渡すことで似たようなことはできていました。Rubyではメソッドの引数の最後では、ハッシュリテラルのブレースを省略でき、なおかつRuby 1.9からはハッシュはJSONぽく書けるので、以下のような記法が可能で、割と標準的な書き方でした。
def f1(value, options = {})
if options.has_key?(:foo)
foo = options[:foo]
end
# do something
end
f1(123, foo: "bar", bar: 111111)
呼び出し側はいいですが、これだと呼ばれる側はoptionsのデフォルト値とマージしたり、個別に変数を取り出したりと面倒でした。キーワード引数が広まれば、こうした手間がなくなり、ずいぶん使いやすくなりそうです。
Enumerator#lazyは遅延評価のための新しいAPIです。nagachikaさんのスライドを見ると分かりますが、mapやselect、zipなどコレクション全体を評価するようなメソッドの前に、(0..5).lazy.map などとして lazy を挟むことで遅延評価が可能になります。無限の要素を扱ったり、停止条件がいつ来るか個別にループしてみないと分からない(事前に調べるのが面倒)ようなケースでスッキリと書け、処理に必要なメモリ量も少なくて済みます。Rubyは1.8から1.9へのバージョンアップで、ずいぶん関数型の特徴を取り入れましたが、遅延評価が入ったことで、またそうした性質を強めているように感じられます。
Module#prependは、既存クラスの挙動を変えるときに使えるようです。Railsの世界では長らく method_alias_chain を使って既存メソッドを呼ぶ前段階でゴニョゴニョっとやってからオリジナルのメソッドを呼ぶというようなことが行われていました。既存のライブラリに手を入れずに、ちょっとだけ動作を変更するということができます。Module#prependは、これをスッキリした形でRuby言語としてできるようにするもので、クラス(モジュール)の継承ツリーにモジュールを入れ込む仕組みです。Railsのソースコードの黒魔術感が減りそうな気がします。Module#include(ミックスイン)と似ていますが、この2つは継承ツリーの中でモジュールを差し込む場所が違っていますし、ミックスインは、どちらかと言えば多数のクラスに共通する挙動を引っ張り出してまとめておく仕組みとして使われています。
シンボルの配列がリテラルに書けるようになりました(パッチ)。以下のような感じです。%w(apple banana) のような表記のシンボル版ですね。
%i[foo bar] # => [:foo, :bar]
ということで、Ruby 2.0の新機能のうち、いくつか紹介してみました。
【追記】アナウンスには含まれていませんが、Ruby 2.0ではVMの改善やFlonum採用によってパフォーマンスアップも図られているそうです(@unakさん、情報ありがとうございます!)。Flonumというのは、ここに議論がありますが、浮動小数点の埋め込み即値です。Rubyでは、これまでにも一定範囲に収まる整数(Fixnum)ではオブジェクトを生成せずに、ポインタにフラグを付けて値として扱う実装になっていましたが、64ビット環境では整数に加えて浮動小数点でも同じことを実現するようです。並の演算ならガッツリ速くなりそうですね!