Akatsuki Hackers Lab | 株式会社アカツキ(Akatsuki Inc.)

Akatsuki Hackers Labは株式会社アカツキが運営しています。

Rubyへの累計コミット数18,000以上。アカツキ所属のパッチモンスター中田さんに機能の開発秘話を聞いた

直感的な文法や生産性の高さから、世界中の人々に愛されるオブジェクト指向スクリプト言語Ruby。この言語には継続的に新しい機能や文法が追加されており、利便性が向上し続けています。コミッターの方々による日々の努力が、Rubyの改善を支えているのです。

コミッターのなかでも、とりわけRubyに大きな貢献をしてきたのがアカツキでフルタイムRubyコミッターを務める中田伸悦さん。(アカツキのCSRの取組みについてを記事下部参照)
github.com

中田さんはRubyへのコミット数が全コミッターのなかで最多であり、通称“パッチモンスターと”呼ばれています。

今回のインタビューでは、中田さんがRubyへのコントリビューションを始めたきっかけや、印象に残る機能改修について解説してもらいました。「Rubyのことをもっと詳しく知りたい」「オープンソースソフトウェア(以下、OSS)へのコントリビューションを続ける秘訣を知りたい」と思う方には必見の内容です!

※ 本インタビューはアカツキ所属ではない中薗昴様にインタビューいただきました。
※ インタビューはリモートにて実施しています。

気負わないからこそ、長く活動を続けられた

──中田さんはRubyへのコミット数が非常に多いことで有名です。GitHub上にあるRubyのcontributorsページを見ると、なんと累計コミット数は18,000を超えているとか。
Contributors to ruby/ruby · GitHubより引用。

コミット数が多いのは、私の活動のスタイルも大きく影響しています。

Rubyコミッターのなかには、特定の領域のみを専門的に取り組まれている方もいます。たとえば笹田耕一さんならば、過去にはRubyVM周りの評価速度の高速化やGarbage Collectionの改善を、現在はRactorと呼ばれる並列処理の改修を行ってきました。
そういった方々は、複雑度の高い機能の設計や実装を中長期にわたって行いますから、必然的にコミットの頻度は少なくなります。一方、私は領域を限定することなく細かな機能追加・バグ改修などにも積極的に取り組んできましたから、コミット数は自然と増えていきました。

──そもそも、中田さんがRubyを使い始めたきっかけは何だったのでしょうか?

昔の話になりますが、1990年代後半頃、私はLinuxファイルシステムの多言語化に取り組んでおり、カーネルモジュールに組み込むためのコードを書いていました。もともとはスクリプト言語としてPerlを用いていましたが、どうもPerl5が自分の肌に合わなかったんです。代わりにRubyを試してみたところ、非常に便利だと感じてその後もよく使うようになりました。

Rubyを使いこんでいくと、言語のバグが原因で意図した挙動にならないケースがあったんです。修正パッチを書いてまつもと(ゆきひろ)さんにメーリングリストで送るようになったのが、コントリビューションのきっかけでした。

──プログラミング言語やライブラリ・フレームワークのコミッターのなかには、途中でモチベーションを失い活動をやめてしまう方もいます。なぜ、中田さんは当時から現在に至るまで精力的に活動できているのでしょうか?

いくつか理由があります。Rubyという言語がとても便利で興味を持続できたから。解きがいのあるバグや実装しがいのある機能が継続的に登場してきたから。それから、無理をして(コミッターとしての活動を)続けようと思わなかったから、ですかね。

人によっては「OSS活動を毎日続けなければ」と考えて気負う方もいるかもしれません。しかし私の場合はそう考えてしまうと、かえって負担になり続けられなくなってしまうんです。GitHub上にある私のコミット履歴を見てもらうとわかりますが、時々コミットが途切れている期間もあるんですよ。その期間はやる気がなくなっていて、休んでいます(笑)。気力が出てくるまで何もしないです。気負わないスタイルだったからこそ、活動を続けることができたんじゃないでしょうか。

【携わった機能①】Numbered parameter

──今回のインタビューでは、これまで中田さんが設計や実装に携わった、印象に残る機能を教えていただきたいです。

まずは、私が実装を担当したNumbered parameterですかね。機能について議論が行われたRedmineチケットはこちらになります。

<機能概要>
Numbered parameterはブロックパラメータの宣言を省略できる機能のこと。
Ruby 2.7で導入された。通常の場合、ブロックで引数を受け取るには以下のように仮引数を定義する必要がある。

[1, 2, 3].map { |num| ‘数値は’ + num.to_s } # => ["数値は1", "数値は2", "数値は3"]

だが、Numbered parameterを使うことで、仮引数を定義しなくても「_1」「_2」などの変数名を用いて暗黙的に引数を参照できる。上記のコードは以下のように修正可能。

[1, 2, 3].map { ‘数値は’ + _1.to_s } # => ["数値は1", "数値は2", "数値は3"]
──Redmineチケットが発行されたのは約10年前。かなり昔から議論されてきた機能なのですね。

ブロックパラメータの宣言を省略したいという要望は、昔からずっとあるんですよ。このRedmineチケットで最初に提案されていたのは「Account.all.each { |a| my_account << a.money }」のような記法を「Account.all.each { my_account << it.money }」のように変えたい、つまり、暗黙的な引数を「it」として参照したいという要望でした。

しかし、この案は「it」が予約語のように見えてしまうとか、RSpecの「it」と紛らわしいとか、複数のパラメータを扱いたいときに困るなどの課題が生じました。そこで「@1」や「@2」などの変数名で引数を参照できるようにしてはどうだろう、という方向性で話が進みました。

──確かにRedmineチケット内では、2019年3月にまつもとさんが「I accept @1 idea. nobu (Nobuyoshi Nakada) will implement it. 」と書かれています。しかし、現在採用されているのは「_1」「_2」などの記法ですから、この後さらに文法が変更されたのですね。
https://bugs.ruby-lang.org/issues/4475より引用。

はい。別のチケットが起票されて、適切な記法についてさらに議論が行われました。「@1」や「@2」などの記法ではインスタンス変数っぽく見えてしまう*という意見が挙がったんです。二転三転した後、最終的に「_1」「_2」のような記法に決定しました。 *1

──なるほど。別チケット内で、まつもとさんが「After discussion and consideration, we picked _1, _2... as numbered parameters.」と最終決定されていますね。中田さんがこの機能を実装するにあたり、工夫・苦労された点はありますか?
https://bugs.ruby-lang.org/issues/15723の画面キャプチャ

まだ「@1」「@2」という記法だった頃に「ブロックパラメータに複数の要素が渡された場合にどのような挙動にすべきか」について議論したことが印象に残っています。初期の実装では(仮に複数のブロックパラメータが渡されたとしても)「@1」は必ず最初の要素だけにアクセスする仕様にしていました。

しかし、それでは旧来のようにブロックパラメータを宣言する場合の挙動と、Numbered parameterの挙動が変わってしまい、良くないという意見が挙がったんです。最終的には現在の仕様に落ち着きました。

──1つの機能が取り込まれるまでに、コミッター間でさまざまな議論が行われているのですね。

そうですね。この事例のように、議論されていく過程で記法や仕様が変わっていくこともありますよ。Rubyが好きな方々は、Redmine上で行われている議論の途中経過などを追っていただくと、きっと楽しめると思います。

【携わった機能②】右代入

──他にはどのような機能に携わりましたか?

Ruby 3.0でexperimentalな機能として導入された右代入ですかね。この機能についてやりとりしたRedmineチケットはこちらです。

<機能概要>
以下のコードのように「=>」という演算子を用いることで、式の右側に記述された変数への代入が可能。

{ x: 1, y: 2, z: 3 } => test
p test #=> {:x=>1, :y=>2, :z=>3}

この機能はパターンマッチの一部として実現されているため、右側には任意のパターンが書ける。一方でインスタンス変数などに右代入できないという制約がある(インスタンス変数はパターンとして書けないため)。例えば以下のような構文はシンタックスエラーになる。

{ x: 1, y: 2, z: 3 } => @test
──右代入が考案された経緯について解説していただけますか?

Rubyにおける変数の特徴として、代入が宣言を兼ねていることが挙げられます。この特徴があることで、変数代入の右辺には変数と同じ名前のメソッドを書けないとか、if修飾子の中で宣言・代入したものは、修飾子の左側では使えないなどの制限が出てくるんですよ。

さらに、この仕様になっているために、長いメソッドチェーンがある場合などに視線の動きが右方向に移動した後、(変数名を確認するために)目線が先頭に戻らなければならない。読み書きする際に負担になるという意見も挙げられていました。

解決策として右方向への代入機能がほしいという議論は昔からされていたんです。おそらく話題としては10年くらい前からありましたね。

──ずいぶん長い間、議論が行われてきたのですね。どのような理由で、右代入の導入までに時間がかかっていたのですか?

いくつか理由がありますが、そのひとつとして(右代入を表現するための)記号がなかなか決まらなかったことが挙げられます。どのような記法にすれば、直感的にわかりやすく、かつ既存の文法とも衝突しないかについて議論がくり返されてきました。

右代入のアイデアの原案になったのは、RubyKaigi 2019の「ruby committers vs the world」というコーナーに出てきた、パイプライン演算子を用いてその計算結果を変数に代入する案ですね。この案については、こちらのチケットで私がサンプルを示しています。

中田さんの示していた案

1.. |> take 10 |> map {|e| e*2} |> (x)
p x #=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

この例からわかるように、初期の案では変数名の前後に丸括弧が入っていたんですよ。パイプライン演算子では「|>」で連結した先に通常はメソッド名が来るので、丸括弧で囲ってやれば、既存の文法と衝突することなく右代入を実現できると考えました。

──この当時はまだ、現在のように「=>」を用いる形ではなかったのですね。

=>」を使う形では、(RubyではHashクラスの宣言にも「=>」が用いられるため)既存の文法と衝突すると思われていたんです。しかし、議論が進むうちに「=>」でも衝突のない形で実装できることがわかり、この案に決定しました。

現在の右代入は、パターンマッチングの機能の一部として実現されています。パターンマッチングでは、inのあとに変数名を1つだけ書くと、その変数に全ての値がキャプチャされるんですよね。その機能を拡張するような形で右代入が導入されました。

【携わった機能③】endless method definition

──他に中田さんが携わった機能はありますか?

Ruby 3.0でexperimentalな機能として導入されたendless method definitionですかね。実はこの機能は、もともとRubyコミッターのひとりである遠藤さんがエイプリルフールに提案したジョークでした。Redmineチケットはこちらです。

<機能概要>
endを記述せずに済む、メソッド定義の新文法。

def sum(a, b) = a + b
p sum(3, 5) #=> 8

上記のコードは、以下のコードと同じ意味になる。

def sum(a, b)
  a + b
end
──ジョークですか!?

endlessと名のつく機能を“えんど”うさんが提案した、という時点ですでにジョークっぽい感じですが(笑)。これは、「Rubyの文法はendを多用するので、Rubyが終わりそうで縁起が悪い。だからendのない文法を考えました」という趣旨のエイプリルフールネタでした。

チケット画面のキャプチャ。
遠藤さんのキレの良いジョークが光っている。

最初に提案されたのは、defの後にコロンがついた以下のような文法です。

def: value(args) = expression

多くの人が冗談半分に受け取っていましたが、まつもとさんはこのアイデアに乗り気で「コロンがない文法ならば入れてもいいんじゃないか?」と言い出しました。「本当なの!?」と思いましたけどね(笑)。そして機能の導入が決まり、実装担当として私が割り当てられました。

苦戦するかと思いましたが、メソッド定義のルールを設定している部分を修正すれば、意外にもシンプルに実現できることがわかったんです。過去のメソッド定義では、メソッドの引数を表す括弧の後に「=」が来るパターンはあり得なかったんですね。だからこそ、既存機能と衝突することなくendless method definitionを入れ込めました。

──先ほどのRedmineチケットには「中田さんが一晩で書いてくれました」という趣旨のコメントが書かれています。シンプルに実現できたとはいえ、すごい実装スピードですね。

確かに一晩でやりましたね。エイプリルフールのネタでしたから、みんなの注目が集まっているうちにパパッとやってしまいたいじゃないですか(笑)。

どうすれば中田さんのようなRubyコミッターになれますか?

──ここからは、読者であるエンジニアの方々に向けて「これからRubyへのコントリビューションを始めたい人は何から手をつけるといいか」を教えてください。

入門としてとっつきやすいのは、ドキュメントを書いていただくことですね。まだまだ整備しきれていないドキュメントがあるので、手をつけていただけるとずいぶん助かります。

それから最近では、Rubyのコア機能の一部をC言語ではなくRubyで書き直す取り組みが行われています。そのプロジェクトに携わるのも入口としてはおすすめですね。また、自分の手元にRubyのソースコードを落としてきて、組み込みクラスの機能を改善・追加してみることも、言語を理解するうえでは有益だと思います。

──そうして改善・追加した機能をRedmine上で提案しても良いでしょうか?

もちろん良いと思いますが、今回のインタビューでもご紹介したように、機能の採否を決めるための議論は長期にわたることも多いです。コード修正よりも、むしろ議論で承認をもらう難易度の方が高いかもしれません。

そのほかに、いまRedmineで報告されているバグを直すこともプログラムを理解する助けになるでしょうが、最近挙がっているバグはRubyの仕様に詳しくなければ修正に手間取るものが多いです。あまり入門としては適切でないかもしれませんね。

──Redmineのチケット起票時に大切にすべきことはありますか?

どのようなユースケースで使われる機能なのか、またはバグならばどんなときに再現するのか、サンプルコードを書いていただくことですね。例があることで、他のコミッターたちがチケットの意図を理解しやすくなります。

──非常に参考になりました! 最後に、中田さんが今後取り組む予定の、Rubyの改善について教えてください。

温めているアイデアはいくつもあるのでピックアップしてお話しすると、まずはディレクトリ構造の整理をしていきたいです。Rubyにはディレクトリ構造のトップレベルにファイルが多すぎるという課題があるため、整理したいという意見が以前からよく挙がっていました。なかなか手間がかかるため何度か手戻りをしている状態ですが、そろそろ修正を完了させたいですね。

それからTimeクラスの拡張をしたいです。Timeクラスに存在している「Time.parse」メソッドは「Date.parse」メソッドの機能を借りて実現していますが、この機能はユーザーの直感に反した変換処理をしてしまうケースがあるんですよ。例えば「Time.parse('123')」という使い方をすると「その年の5月3日」に変換されます。「その年の1月1日から数えて123日目」という解釈がされてしまうためです。非常にわかりにくいですよね。

また、Dateクラスはいま主担当のメンテナーがいません。メンテナーがいない機能に依存するのはリスクが高いため、DateクラスとTimeクラスとの依存関係を切り離したいという思いもあります。

これらの課題を解決するために、「Time.new」という新しいメソッドを作成しようと考えています。先日、「Time.new」の設計・実装を管理するために下ごしらえのRedmineチケットを発行しましたから、これから頑張って進めていきたいです。

──中田さんの今後の活動が楽しみです。今回はどうもありがとうございました!
筆者プロフィール

中薗 昴(monoq合同会社)
週の半分はシステムエンジニア、もう半分はライター・編集者として働くパラレルワーカー。システムエンジニアとして培った知見を強みに、専門性の高いIT系インタビューを執筆可能。現在は複数企業のオウンドメディアのコンテンツ制作業務を支援している。

<アカツキのCSR活動について>

アカツキではCSR活動*2の一貫としてオープンソースソフトウェア(以下OSS)への貢献を推進しています。
アカツキで開発/運用しているコンテンツは多大なるOSSの力を借りて成り立っております。
そのお返しとしてOSSの貢献を推進しており、またその成果はいずれ自分たちへのプラスとなって返ってくると信じているからです。
業務で得た知見をOSSへフィードバックすることはもちろん、OSS活動をされる方のバックアップも積極的に推進しており、CRubyのコミット数No.1である中田伸悦さんも、フルタイムRubyコミッター(正社員)としてアカツキに所属して頂いています。
他にも多くのRubyやRoRなどのOSSコミッターが所属しており*3、団体や取り組みのスポンサー*4も実施しています。

*1:Rubyのインスタンス変数は「@name」のように、変数冒頭に「@」をつける記法が用いられる。

*2:Corporate Social Responsibility:CSR 企業の社会的責任の意。企業が倫理的観点から事業活動を通じて、自主的に社会に貢献する責任のことである(Wikipediaより

*3:Ruby&Ruby on Railsコミッタである松田明氏や、Ruby&Linuxカーネルルコミッタである小崎資広氏、serverspec開発者 宮下剛輔氏などに所属していただいています。

*4:Ruby Association, The Linux Foundation/CNCF, RubyKaigi, Erlang & Elixir Fest, builderscon, etc...