目次
Rubyの型解析ライブラリSorbet事始め
Kazuki Moriyama (森山 和樹)
はじめに
ついに前々から今年の夏リリースされるとアナウンスされていた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
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
srb tc
Step 3: まずは超簡単
❯ 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
よくある質問
何かうまくいかない
トラブルシューティングの項目を参照
Sorbetを実行できる環境はある?
playgroundがある
一つのファイルだけ実行できる?
できる
# -- 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にとって革命じゃないのか。
追加・修正あればコメントお願いします。