The Year of Concurrency

まつもとゆきひろ 氏(以下、まつもと):おはようございます。私は九州の生まれではないんですが、福岡県がRubyのことをたくさん支援してくれているので、だいたい2ヵ月に1回くらいこっちに来ています。なので、だいぶ福岡は近いイメージがあるんですけど。

今日は、Ruby3の話をしようと思ってやってきました。Rubyは「良い」と言われてるんですね。

(会場笑)

まつもと:たぶん。みなさんの「良い」は、Rubyは生産的であると。柔軟性があるとか、そして何より「使っていて楽しい」と言ってRubyのことを愛してくださっている方がたくさんいらっしゃって、だからこそこの会場にこんなたくさんの人たちが、人によってはすごい遠くから……ウルグアイから来て下さる人がいるわけで、本当にありがたいことだと思います。

しかし、一方でRubyが良くないと言われてるところもあるわけです。例えば、定番ではありますが「Rubyは遅い」とか、「Rubyはマルチコアを活用できてない」とか、あるいは「チームやプロジェクトが大きくなるにしたがって、Rubyの欠点が見えてくる」ということを言う人たちも実際にいます。

とは言っても、悪いからダメかというとそうでもなくて「だいたいのことには十分によい」と言ってもいいんじゃないかなぁと思います。

例えばGithubであるとか、Airbnbであるとか、Instacartであるとか、Cookpadであるとか、その他大きなものも含めて、たくさんの会社がRubyを使って下さっています。彼らよりも多くのトラフィックを捌くサイトは、そんなにたくさんはないので、そういう意味でいうとRubyは十分に速いと言ってもいいんじゃないかなぁと思います、だいたいの場合は。

もし、何かの事情で限界に突き当たったとしても、それはそれでいいんじゃないかなぁと、思っています。Rubyとパフォーマンスの話をすると、よく引き合いに出されるのがTwitterの話ですね。2007年にTwitterが始まったときは、あれはRuby on Railsのアプリケーションだったんです。冷静に考えるとわかりますが、TwitterのようなタイプのアプリケーションをRuby on Railsの得意なデータベースに対するCRUDで書こうと思うと、正直向いてないんですよね。

Twitterは限界に突き当たって、コアから書き直して、Scalaを使ってコアを書き直しました。これをしてTwitterがRubyをやめたので、「もうRubyはだめだ」と言った方がたくさんいましたが、それからもう10年近く経ちますけど、まだ「ダメだ」という感じではないですね、少なくとも私の周りでは。

思い出していただきたいのは、Twitterはずっと1.8を使い続けていたんですね。「パフォーマンス遅い、遅い」って文句言ってるわりには、ずっと1.8を使い続けて、あろうことか1.8を改造して「kiji」という独自のバージョンを作りかけてやめちゃったことがあります。

まず、彼らがやるべきだったことは、1.8はやめることはなかったかな、と思わないでもないんですが(笑)。Ruby1.9以降は随分速くなったので、トラフィックが大きくなることによって突き当たる限界というのは昔に比べて随分高くなっているんじゃないかなと思います。

Ruby2.0が出たのは2013年でした。それから今までの間、少しずつ性能は改善していました。

このグラフを見ると、バージョンごとに数パーセントぐらいずつ性能が良くなってきています。

Ruby3の3つの柱

私たちはこのように性能の改善を続けてきましたが、これより多くの改善をRuby3で行おうと思っています。過去のKeynoteでも話しましたが、Ruby3では大きな3つの柱を考えています。

1つがパフォーマンスの改善。もう1つがConcurrency、実際はこのパフォーマンスと改善とConcurrencyは1つのものなんですけれども。それから静的解析です。先に静的解析のほうから説明させてください。

最近は静的型がすごくホットで、2010年代のプログラミング言語はみんな静的型言語で、まぁ「そうなの」って感じですけど。プロジェクトが大きくなるに従って、テストがだんだん苦痛になってくるんですね。テストのサイズが大きくなったりとか、あるいはテストの実行時間が長くなったりとかして、だんだんだんだん苦痛になってくることが多いですね。

告白すると、私はテストが嫌いなんです。

(会場笑)

本当はテストを書きたくないんですよ。というのもDRYじゃないですよね。私たちはプログラムを書きたいんであって、テストを書きたいわけではないわけですよ。ただ、人類はまだプログラムを書くだけで、正しいプログラムを作る方法を知らないので、仕方がないのでテストを書いているのが現状だと思います。

もし、もっとテストを書く量が少なくてすむのであればそちらのほうが生産的なので、「もっと生産的になりたい」と思っているわけです。他の言語はどうしてるかというと、だいたい静的型っぽいアプローチを使っていることが多くて、例えばPHPでは「Type Hinting」というかたちで型宣言を導入するようになりました。Python3も「Type Annotation」を導入して、型宣言を入れています。JavaScriptもTypeScriptによって、「静的型を導入しましょう」というトレンドになってきて、Rubyと同じぐらい古いプログラミング言語においても型を言語に導入して、静的型チェックによってより多くのチェックをコンパイル時に、というのがトレンドな気がします。

「われわれどうしようか?」という話になるんですけど……。型宣言、Pythonも入れたし、PHPも入れたし、Perlも入ってますね。なので「もう、われわれもやろうか。PHPもPythonもやってるし」って言うんだけど、正直やりたくない……。型宣言嫌いなんですよね。

(会場笑)

なんでかというと、DRYじゃないからなんですよ。

(会場笑)

今私たちが書いてるRubyのプログラムには、型宣言は書いてないんですよ。型宣言、言語にないんでしょうがないんですけど。それでもちゃんと動いてるわけです。それに対して、さらに情報を付け加えるというのは、コンピューターに仕事をさせられている感じがするわけです。本当はコンピューターが私たちのために働いてほしい。

なので、テストは仕方なしに書いているんですけど、「型宣言については、妥協しないようにしようかなぁ」といまだに思っています。いらない、というか型宣言なしでも出来そうな気がするからです。

Ruby3の型については、実は今回の会議の中でもいくつかセッションがありますので、今日がエンドウさんの「Type Profilerについて」、それから明日はStripeの人たちが「Sorbet」というタイプチェッカーについて説明しますし、3日目に、松本宗太郎さんが、「Steep」などの型の話をしてくださいます。3つもセッションがあるので、そちらを詳しくは聞いていただければと思います。

型チェックについて

Rubyの型チェックに関して言うと、やろうと思ってます。

なんだけど、型宣言、型のアノテーションを導入する代わりに、言語とは別に4つのコンポーネントを導入しようと思っています。最初の1つは、型定型文法、型定義、フォーマットですけど、「rbiファイル」というものを考えています。それから、それぞれのライブラリの型定義をrbiファイルで記述したもの。それから「Type Profiler」、それから「静的型チェッカー」ですね。この4つについて考えています。

型定義シンタックスですが、通常のプログラムとは別に、型を表現するための情報が入ったファイルになります。これには、引数の型であるとか、戻り値の型であるとか、クラスやモジュールの情報とか、あるいはジェネリックなクラスなどの情報や、インターフェースなどの定義を含みます。

すごくラフに書くとこんな感じで、Rubyにちょっと似てるんだけど、Rubyじゃないという感じの文法ですね。このパーザーも用意します。

今プロトタイプがあって、ここに用意されてます。このフォーマットを使って標準ライブラリであるとか、それから将来的にはGemsも、この型定義ファイルを一緒に配布するようにすれば、既存のアプリケーションについては、「型情報がある」という状態が実現できます。

Type Profiler

次のコンポーネントは「Type Profiler」です。Type Profilerは、抽象解釈というテクニックを使ってプログラムを解析します。

例えば、こんなすごいつまらない感じのRubyのプログラムがあったとすると、これをこう見ます。

fooというメソッドが、15を引数として呼ばれたので、fooの引数は整数で呼ばれたと。なので、fooの引数aは、整数であると。aは整数で、プラスメソッドを整数で持ってるから、2と足し算したらOKなので「型情報問題ない」と。fooは整数を受けて整数を返すメソッドであるということが分かります。

すごくシンプルなかたちだとこんな感じ。これによって型宣言はないけれども、fooの型は分かったわけです。

Type Profilerはこのようにして型の情報を集めて、それからすでに定義されている、rbiファイルによってすでに定義されている型情報との矛盾があれば、エラーとして報告しますと。矛盾がなければそのまま通して、集めた型情報を使って、そのrbiファイルの含まれているクラスやメソッドについてのrbiファイルを出力することができます。

その出力した情報をもとに、次の段の型チェックを行うことができる、というツールになります。

正直言うと、型プロファイラは万能ではないので、例えば間違ったりとか、あるいはテストが十分に行われていなければ、カバレッジが高くなければ、正しい情報を受け取るはずの型を受け取らないって書いたりすることがあるので、そういうことにならないために、例えば自分のライブラリのrbiファイルを作りたいときには、型プロファイラを走らせて、テストプログラムを使って型プロファイラを作らせて、その作ったrbiファイルが十分であればそれでOKだけど、十分でなければ自分で書き換えて、より正確なものに変えるということができるようになります。

あと、YARDファイルには型情報が入っているので、そのYARDファイルからrbiファイルを出力するようなツールが作れないかなぁということが計画中ではありますが、ぜんぜん手がついてないですね、はい。

現在開発されている2つの静的型チェッカー

こうやって型情報を集めると、クラスの、いや、メソッドの型情報が全部集まっているので、「Static Type Checker」を走らせることができます。現在Rubyに対して開発されている静的型チェッカーは、2種類あります。

さっきちょっと話をした「Sorbet」。Stripeのチームが作っているSorbetと、それからマツモトソウタロウさんが作ってる「Steep」という2種類があります。

これらは最終的に、現在はrbiファイルのフォーマットが決まったばかりなので、まだ対応していないんですけども、最終的には両方ともrbiファイルのタイプの型定義をインプットして取るようになります。

この2つの型チェッカーは、それぞれ性質が異なっていて、「Sorbet」はC++で実行されていますし、非常に高速に実行されます。その型チェックは、だいたいNominalに行われます。さらにプログラムにDSLとして型情報を追加することができると。

私の目から見ると、ちょっと好みではないかもしれないんですけど、より正確でインラインな型を書くことができるというのが、Sorbetの特長になっています。

「Steep」のほうはRubyで書かれていて、より柔軟な型チェックについて実験することができるようになっています。こちらはStructural Typingを実装しています。

型宣言を書かなくとも型チェックができるようになる

最終的にはこれらのコンポーネントが完成して揃った暁には何ができるかというと、みなさんの普通のRubyのプログラムが、とくにRubyのプログラムに型宣言を書かなくても、型チェックができるようになります。さらにrbiファイルの型情報を、より正確なものに書き換えてやると、よりよい型チェックを行うことができると。

ということで、今までみなさんの普通に書いているRubyのプログラムが、型宣言を書かなくても普通に型チェックができるようになる。ということで「すごくよくない?」というところですね。

さらに、そのrbiファイルの型情報は、例えば未来のID開発環境が、例えば保管やデバッグに使うことができるんじゃないかなぁと思っています。

今回、さらに3つも発表があることがわかるように、この点については非常に多く働いています。非常に大きな動きがあったのは昨年なので、去年から今年にかけてRubyの静的型チェックについては非常に大きな動きがありました。

その結果、それからRuby3.0に含まれるであろうその未来については、非常に期待が持てると思っています、少なくとも私の視点からは。