はじめに
ついに前々から今年の夏リリースされるとアナウンスされていたstripe製のRuby型解析ライブラリがリリースされました。
個人的にRubyは型が加われば最強の言語なので、これは本当に嬉しい。
導入方法として、公式のGetting Startedをまとめていく。
Overview
Sorbetは2つのコンポーネントが中心
srb
- SorbetのCLI
- 静的解析(コード実行前解析)を行う
- Sorbetを始めるときにも活躍
sorbet-runtime
- Pure Rubyにアノテーションを加えるgem
- 名前空間
T
の中にsig
メソッドを定義- Signaturesに関わる
- https://sorbet.org/docs/sigs
- 実行中も動的にコードの型チェック
- 2つはお互いを補う
- 実行中の型予測を行う
- 同時に実行結果が型予測を補強
- Sorbetの一例
# typed: true require 'sorbet-runtime' class A extend T::Sig sig {params(x: Integer).returns(String)} def bar(x) x.to_s end end def main A.new.barr(91) # error: Typo! A.new.bar("91") # error: Type mismatch! end
play groud%7D%0A%20%20def%20bar(x)%0A%20%20%20%20x.to_s%0A%20%20end%0Aend%0A%0Adef%20main%0A%20%20A.new.barr(91)%20%20%20%23%20error%3A%20Typo!%0A%20%20A.new.bar(%2291%22)%20%20%23%20error%3A%20Type%20mismatch!%0Aend)
Adopting Sorbet in an Existing Codebase
Step 1: 必要gemのインストール
- 必要なgemは2つ
- cli
- ランタイムシステム
手順
Gemfileに追加
# -- Gemfile -- gem "sorbet", :group => :development gem "sorbet-runtime"
インストール
bundle install
うまく動いているかの検証
❯ srb
[help output]
❯ srb typecheck -e ‘puts “Hello, world!”‘ No errors! Great job. ❯ bundle exec ruby -e ‘puts(require “sorbet-runtime”)’ true
Step 2: Sorbetのイニシャライズ
イニシャライズコマンド
❯ srb init
なんかいろいろ出てくる
終了後sorbet
ディレクトリが作成される
Gitなどでバージョン管理が必要
イニシャライズの検証
sorbet/
ディレクトリの構成
sorbet/ ├── config └── rbi/ └── ···
sorbet/config
はSorbetの設定ファイル- https://sorbet.org/docs/cli
sorbet/rbi/
はRBIファイルが定義されている- Ruby Interfaceファイルのこと
- https://sorbet.org/docs/rbi
Step 3: まずはsrb tc
超簡単
❯ srb tc
デフォルトではカレントディレクトリのRuby fileすべての型解析を行う
Step 4: constant resolutionエラーの修正
この時点でのエラー
- 基本的にはSorbetはそいつらを無視
- 無視しないで修正することが大事
1. Parse errors
- Rubyの文法が適正であることの要請
2. Dynamic constant references
- 定数アクセスパターンは禁止
- 例えば
foo.bar::A
- ほとんどの場合メソッド呼び出しパターンで解決
foo.bar.get_A
3. Dynamic includes
動的なinclude
は静的解析不可能
module A; end module B; end def x rand.round == 0 ? A : B end class Main include x end
メソッドx
の内容によらず上のincludeそのものが良くないみたい
4. Missing constants
- Gemを含めたすべてのクラス、モジュール、定数について知ることがSorbetの効率性に重要
- 定数は継承階層、型の互換性を知る上で中心的な役割を果たす
3と4の解決方法
- RBIファイルを使用
- 型アノテーションだけが書かれたファイル
- pythonでいうstubファイルみたいなもの
srb init
が自動でRBIファイルを作成してくれる- 完璧じゃないので3と4のエラーを解決するには手書きが必要
Step 5: 型検査の適用
- Step 4まででSorbetが保証したもの
- すべてのRubyファイルの解析の完了
- すべてのクラス、モジュール、定数の解析完了
- コードの自動補完の完全正確性
- 型アノテーションで使用可能
- すべてのgemが明示的ななインターフェイスを持つ
- 型検査適用の超まとめ
# typed: true
をファイルに記述- メソッドのシグネチャを書いていく
Quick Reference
型検査の実行
# typed: true
をRubyファイルに追加
あとは自作の関数にアノテーションをつけていくだけ
# typed: true class Main # (2) sig アノテーションを利用するためT::Sigをextend: extend T::Sig # (3) メソッドにsigアノテーションを追加: sig {params(x: String).returns(Integer)} def self.main(x) x.length end # 引数がないメソッドはこうも書ける: sig {returns(Integer)} def no_params 42 end end
srb
とsorbet-runtime
がMain.main
メソッドを検査し、引数がString
であることとInteger
をreturnすることを確認するsrb
はこれを静的解析するsorbet-runtime
は同時にメソッドが呼ばれるたび動的に検査する
よく使う型
- Integer, String, T::Boolean – Class Types
- T.nilable – Nilable Types
- T.any – Union Types
- T.let, T.cast, T.must, T.assert_type! – Type Assertions
- [Type1, Type2] – Tuple Types
- {key1: Type1, key2: Type2} – Shape Types
- T.untyped
- T.noreturn
- T.type_alias – Type Aliases
- T::Array, T::Hash, T::Set, T::Enumerable – Generics in the Standard Library
- T.proc – Proc Types
- T.class_of
- T.self_type
- T.all – Intersection Types
https://sorbet.org/docs/quickref#type-system
よくある質問
何かうまくいかない
トラブルシューティングの項目を参照
https://sorbet.org/docs/troubleshooting
Sorbetを実行できる環境はある?
playgroundがある
https://sorbet.run/
一つのファイルだけ実行できる?
できる
# -- foo.rb -- # typed: true require 'sorbet-runtime' class Main extend T::Sig sig {void} def self.main puts 'Hello, world!' end end Main.main
❯ srb tc foo.rb
実行時解析のテスト
❯ bundle exec ruby foo.rb
プロジェクト全体への実行は?
簡単
❯ srb
まとめ
導入簡単。
いろいろできそうなこと有るし、結構これRubyにとって革命じゃないのか。
追加・修正あればコメントお願いします。