Ruby 3の静的解析機能のRBS、TypeProf、Steep、Sorbetの関係についてのノート

こんにちは、フルタイムRubyコミッタとして働いてる遠藤(@mametter)です。

Ruby 3 は「静的型解析」を備えることが目標の 1 つになっています。遠藤が開発してる TypeProf は Ruby 3 の静的型解析エコシステムの中の 1 ツールです。しかし Ruby 3 の静的解析というと、RBS、TypeProf、Steep、Sorbet などいろいろなツール名が出てきてよくわからない、という声を何回か聞いたので、かんたんにまとめておきます。

3 行まとめ

  • RBS:Ruby の型情報を扱う言語。Ruby 3 にバンドルされる。
  • TypeProf:型注釈のない Ruby コードを型解析するツール。Ruby 3 にバンドルされる。
  • Steep/Sorbet:Ruby で静的型付けのプログラミングができるツール。

詳しくはそれぞれ以下で解説します。

RBS とは

RBS は、Ruby 3 の型を扱うための基盤です。おおよそ、次の 4 種類のものからなります。

  • RBS 言語:Ruby プログラムの型情報を記述するための記法(拡張子 .rbs)
  • Ruby 組み込みライブラリの型情報:Ruby の組み込みクラス(ArrayStringなど)の型情報を書いた.rbsファイル群
  • RBS ライブラリ:.rbs ファイルのパースや解析などをするライブラリ
  • rbs コマンド:.rbs ファイルを扱うための便利コマンド

いろいろありますが、Ruby プログラマが直接意識するのは 1 つめの RBS 言語だけだと思います *1 。よって、単に RBS と言ったら「RBS 言語」、または「RBS 言語で書かれたソースコード」を指すと考えるのがよいと思います。

RBS 言語の例を示しておきます(core/string.rbs よりものすごく抜粋)。Ruby っぽいですが、Ruby ではない別の言語になっています。

class String
  def empty?: () -> bool
end

この記述は、Ruby の組み込みクラスである String クラスの型情報(RBS言語で書かれている)の抜粋で、empty? という無引数のメソッドを持っていて、bool を返すということを表しています。 RBS ライブラリを使うと、こういう .rbs ファイルをパースして抽象構文木を得ることができます。 rbs コマンドは、.rbs ファイルを読んでメソッドを検索するなどができます。

RBS はそれ単体で何かをするものではなく *2 、Ruby 3 の型情報を扱うツールが共通で使いたくなるものを集めた gem になっています。この gem は Ruby 3 に同梱されます。しかし基本的には型解析ツール向けの gem であり、普通の Ruby プログラマは RBS 言語を読み書きすることはあっても、RBS gem を直接使うことはあまりないと思います。

TypeProf とは

TypeProf は、型注釈のない Ruby コードを無理やり解析する静的型解析器です。Ruby 3 にバンドルされます。TypeProf は RBS 基盤を活用して作られています。

TypeProf の特徴はなんといっても、「型注釈を書かなくてもなんとなく型解析っぽいことができる」という性質に極振りして設計されているところです。 キーポイントは、メソッド呼び出しの情報を活用して解析するところです。 これにより、たとえばdef hello(user) ... endという型注釈が一切ないメソッドに対しても、hello(User.new)という呼び出しがあれば「メソッドhelloUserインスタンスを引数に取る」ということを推論します。

また、一部のクラスに RBS 言語で型情報を書いて TypeProf に与えることもできます。TypeProf はユーザが明示した型情報を無条件に信用するので、解析精度や解析速度が向上します。

TypeProf について詳しくは別の記事で解説しています。

techlife.cookpad.com

Steep/Sorbet とは

Steep は、Ruby の静的型検査器です。RBS を使って、伝統的な漸進的型付けによる型検査を行うことができます。 単に型エラーを検出できるだけではなく、LSP を実装しているので、エディタ上での型エラー表示、補完、ドキュメント表示なども実装されています。 現状で RBS を使って便利さを実感できるのは、Steep だけです。 このへんがわかりやすい記事にリンクしておきます。

qiita.com

Sorbet は、また別の Ruby の静的型検査器です。 こちらは RBS ではなく RBI という独自形式の型注釈を使います(RBS から RBI への変換器も開発されています)。 ものすごくざっくり言ってしまうと、できることは Steep とおおよそ同じです。 とはいえ、Stripe や Shopify という大企業ですでに数年ほど経験を積んでいるので、完成度はとても高いです。 解析器はC++で書かれていて、解析速度をものすごく重視しています。

Steep も Sorbet も Ruby 3 にバンドルされる予定はありません。 Ruby の設計者である matz が、「型注釈を書くことを Ruby本体として推進しない」と判断した結果です *3 。この判断と相性の良い TypeProf は将来の期待とともに Ruby 3 にバンドルされますが、型注釈を書くことをいとわない人は Steep や Sorbet を使うとよいと思います。

なお、Steep 自体はバンドルこそされませんが、RBS はもともと Steep の型注釈言語でした。 Ruby 組み込みライブラリや gem の型情報を各種ツール間で共通化したかったので、RBS という形で共通基盤として切り離され、Ruby本体に同梱されることになりました。

再度まとめ

  • RBS: Ruby 3 の型情報を扱う言語を始めとする基盤。Ruby 3 にバンドルされる。
  • TypeProf: 型注釈のない Ruby コードを型解析するツール。Ruby 3 にバンドルされる。現状の主機能は Ruby コードからの RBS スタブ生成。
  • Steep/Sorbet: Ruby の静的型検査器。型注釈を書く必要はあるが、Ruby で静的型の便利なプログラミング体験ができる。IDE での補完やドキュメント表示も。

*1:厳密に言うと、rbs コマンドは触ることもあるかも。

*2:厳密に言うと、単体でも動的型検査機能が使えます。

*3:これについてはいろいろな意見があると思いますが、個人的にはそういう言語も面白いと思っていて、そのために苦しみながら TypeProf を開発しています。