🙈

MatzにrejectされたES6風HashリテラルをこっそりRubyで使う

2020/10/12に公開

ES6風Hashリテラルとは

ここで言う「ES6風Hashリテラル」というのは、ES6(ES2015)でobject property shorthandとして導入された省略記法です。

a = 42
b = "abc"
c = {a, b}
pp c #=> {:a=>42, :b=>"abc"}

最近のJavaScriptを使っている人なら見慣れた記法ですね。Rubyでもこういう風に書けると便利そう、と思う人はもちろんいるわけで、この記法を取り入れるべく過去にもbugs.ruby-lang.orgで提案がありました。が、残念ながら今のところrejectされたままです。

似た記法として、 c = {a:, b:} というのも提案されましたが、こちらもrejectされています。

ところが、最近のRubyでは、こういうリテラルをそのまま動かす(!)技が存在しています。もちろん、Rubyの実行ファイルにパッチを当てたりしないで、普通のCRuby処理系を使って実現します。

以下のような2つのファイルを用意します。

  • lib/es6_hash.rb
def es6_like_hash(a, b)
  c = {a, b}
  c
end
  • sample.rb
require "ruby-next/language/runtime"
require "ruby-next/language/proposed"

require_relative "lib/es6_hash"

pp es6_like_hash(42, "abc")

実行してみます。

$ ruby sample.rb
{:a=>42, :b=>"abc"}

ちゃんと動きます。すごい。

その秘密は、もちろん require "ruby-next/..." という行にあります。これはRubyNextというライブラリを読み込むものです。

RubyNextとは

RubyNextは、Evil MartiansのVladimir Dementyevが開発しているライブラリで、一言で言うとRubyのトランスパイラです。

RubyNextはgemでインストールできます。

$ gem install ruby-next

もちろんGemfileに書き、bundle installして使うこともできます。

RubyNextはRubyのファイルをトランスパイルし、結果を別のファイルとして保存することもできるのですが、実行時にもrequireを(require_relativeも)すげ替えて、ライブラリを書き換えつつ実行することができます。
新しいリテラルを導入する(つまりスクリプトをparseしてバイトコードにコンパイルする前にトランスパイルする)ことができるのはそのためです。言い換えると、この技はライブラリにしないと使えません(上の例だと、sample.rbに直接このリテラルを書くことはできません)。

また、requireのすげ替えはあらゆるディレクトリに対して行われるわけではありません。標準ではlibappspecなど、決まった名前のディレクトリに対してのみ適用されます。それ以外のディレクトリでも使えるようにするには、RubyNext::Language.watch_dirsを設定します(これが分からなくて、同階層に置いてもぜんぜん動かなくてしばらくハマりました……)。

Edge features と Proposed features

さて、Rubyでもトランスパイルができるようになったとして、何をどうトランスパイルするかですが、RubyNextにはいくつかの機能があります。

  • 新しいバージョン用のRubyスクリプトを動かす(例: Ruby 2.6でRuby 2.7の新機能を使う)
  • まだリリースされていないバージョン用のRubyスクリプトを動かす
  • まだコミットされていない(提案のみの)機能を使ったRubyスクリプトを動かす

RubyNextのドキュメントでは、2番目のものがEdge features、3番目のものがProposed featuresと呼ばれています。ここで我々が使いたいものはProposed featuresですね。

Proposed featuresを使えるようにするためのコードが、先ほどのsample.rbにあったrequire "ruby-next/language/proposed"になります。

なお、現時点(RubyNext 0.10.0)でProposed featuresとして利用できるものは以下の2つです。

  • Method reference operator (.:) (#13581)
  • Shorthand Hash notation (data = {x, y}) (#15236)

今回はそのうちの後者を使ってみた、というわけです。もちろん前者のメソッド参照演算子も使えます。

まとめ

RubyNextがこのようなProposed featuresを導入しているのは、新しい機能を正式に導入する前に、多くの人によって簡単に試せる機会を提供するためだそうです。これはもちろんJavaScriptのエコシステムを参考にしています。

そこで提案したいのですが、Ruby言語の開発でもこれと同じようなアプローチを採用してみてはどうでしょう?「マージか取り消しか」の二者択一ではなく、「トランスパイラのユーザーから広くフィードバックを集め、それを元に機能を受け入れる」というプロセスがあってもよいのではないでしょうか。

Ruby Nextは、Rubyを未来に向けて前進させる、そんなトランスパイラを目指しています。
(Ruby NextトランスパイラでRubyの新機能を使おう(翻訳)より)

Rubyのより良い進化を促すためにも、興味のある方はRubyNextを試してみましょう。

参考

Discussion