黒曜 / @kokuyouwind
名古屋在住
Leaner Technologies Inc. 所属
Railsエンジニア
Next.js とか AWS 周りも触ってる
We're Hiring!!!
Ruby の静的型検査機能の概要
静的型検査を利用した開発手順
RBSの機能と利用例
ライブラリ利用時の静的型検査
まとめ
Ruby の静的型検査機能の概要
静的型検査を利用した開発手順
RBSの機能と利用例
ライブラリ利用時の静的型検査
まとめ
動的型付け
事前の型検査は行わず、実行時に型を検査する
型エラーは実行時にしかわからない
動的なメソッド追加などに対応しやすい
静的型付け
コンパイル時など、実行前に型を検査する
型エラーを早期に検知できる
動的型付け言語への静的型検査機能追加が増えている
Rubyは生まれた時から動的型付け言語で,
型指定が無くてもこれまで動いているため,
型指定はむしろ積極的に外すべきだと
まつもとさんは主張します。
ー Ruby Kaigi 2016 基調講演レポートより
RBS: Rubyのための型記述言語
Steep: 静的型検査器
TypeProf: 静的型解析器
class Stack
def push(e)
@stack << e
end
end
stack.rb
class Stack
def push: (Integer) -> untyped
end
stack.rbs
TypeProfで
型推論
Steepで
型検査
RBS: Rubyのための型記述言語
.rb ごとに、対応する .rbs ファイルに記述
TypeScriptの.d.tsのようなイメージ
class Stack
def push(e)
@stack << e
end
end
stack.rb
class Stack
def push: (Integer) -> untyped
end
stack.rbs
TypeProfで
型推論
Steepで
型検査
Steep: 静的型検査器
RBS を元に、Rubyコードの型エラーを検査
tsc --noEmit のようなイメージ
class Stack
def push(e)
@stack << e
end
end
stack.rb
class Stack
def push: (Integer) -> untyped
end
stack.rbs
TypeProfで
型推論
Steepで
型検査
TypeProf: 静的型解析器
Rubyコードを元に、型を推論してRBSを生成
あまり他の言語では見ない仕組み
class Stack
def push(e)
@stack << e
end
end
stack.rb
class Stack
def push: (Integer) -> untyped
end
stack.rbs
TypeProfで
型推論
Steepで
型検査
Stripe製の静的型検査ツール
RBSではなくRBIという独自形式を用いる
インラインで型注釈を書くことも可能
RBSを用いるツール群とは現状で互換性がない
# typed: true
extend T::Sig
sig {params(name: String).returns(Integer)}
def main(name)
puts "Hello, #{name}!"
name.length
end
Ruby は型宣言のための文法は導入しない方針
代わりに型宣言専用のRBSが導入された
RBSを用いる代表的なツールが Steep, TypeProf
Steep を用いて静的型検査が行える
TypeProf を用いて型推論と簡易的な検査が行える
Sorbet はRBIという独自記法を用いる
RBSとは現状で互換性がない
Ruby の静的型検査機能の概要
静的型検査を利用した開発手順
RBSの機能と利用例
ライブラリ利用時の静的型検査
まとめ
大まかに2つの手法が存在する
RBS をすべて記述する
Steep による厳密な型検査が可能
他の利用者にも型情報を提供できる
(TypeProfでRBSの雛形を作ることも可能)
RBS を記述せず、Ruby コードから推論させる
TypeProf による簡易的な型検査が可能
作業量を増やさず型の恩恵が得られる
stack = IntStack.new
stack.push(1)
stack.push(2)
stack.push(10)
p stack.pop # => 10
p stack.pop + stack.pop # => 3
target :app do
# 検査対象ディレクトリ
check "lib"
# RBS配置ディレクトリ
signature "sig"
# 検査レベルの指定
configure_code_diagnostics(
Steep::Diagnostic::Ruby.all_error
)
end
RBSにpushの定義を記述すると、Ruby側でエラー表示
マウスホバーで型エラー内容を表示
push, pop の型を記述し、型だけ合わせて実装
(まだスタックとしては動かない)
インスタンス変数に配列を持つように変更
(push, popそれぞれで型エラー)
pushはnilを返すよう変更
popは @stack が空のときにデフォルト値を返すよう変更
型を推論させるため、利用側コードから記述
(この時点では undefined method)
型をあわせてpush, popを定義
Stackの機能を実装
Steepを利用した実装から、RBSをすべて消した状態
(型エラーが出ている)
TypeProf で 型解析して RBS に出力
RBSを書くとコストは掛かるが各種メリットがある
Steepで静的型検査を厳密に行える
型定義ファイルをライブラリ利用者にも提供できる
TypeProfのみでもある程度の型エラーが検知できる
型記述のコストなしで恩恵が得られる
IDE拡張でエラー表示などの開発サポートが得られる
Language Server Protocolで実現されているため、
VSCode以外でも利用可能
Ruby の静的型検査機能の概要
静的型検査を利用した開発手順
RBSの機能と利用例
ライブラリ利用時の静的型検査
まとめ
RBSには高度な型を表現するための各種機能がある
Union Type ( T | U )
Generics ( T[U] )
Covariant, Contravariant( out T, in T )
etc...
機能を知るための資料
rbsリポジトリのdocs/syntaxに文法がまとまっている
rbsリポジトリのcoreに標準ライブラリの型定義がある
stack1 = Stack.new
stack.push(1)
stack.push(2)
p stack.pop + stack.pop # => 3
stack2 = Stack.new
stack.push('a')
stack.push('b')
p stack.pop + stack.pop # => 'ba'
stack1.push('a') # TypeError
IntStack を Stack にリネーム
Stack#push に文字列を渡すと型エラー
Stack の型定義を Stack[T] にし、Integer を T に置き換え
(pop でデフォルト0を返すところが型エラー)
initialize で default を受け取るように変更
Integer でも String でも動くようになり、
Integer をデフォルトにしたstackに String を push すると型エラー
class PrettyPrint[T < _Output]
interface _Output
def <<: (String) -> void
end
attr_reader output: T
end
昨年末リリースされた Ruby 3.1 (RBS 2.0) で追加
(Steepでの型検査はまだ動かない模様)
RBS には高度な型を表現するための各種機能がある
rbsのsyntaxドキュメントを見ると一通り把握できる
一例としてジェネリクスを紹介した
「任意の型に関する型」を表現できる
Ruby 3.0 ではBounded Genericsが入り強化された
Ruby の静的型検査機能の概要
静的型検査を利用した開発手順
RBSの機能と利用例
ライブラリ利用時の静的型検査
まとめ
外部ライブラリの型情報をどうするか
gem_rbs_collection に型情報レジストリがある
型が提供されていないものは、自分で書くか
その部分の型検査を緩める必要がある
rbs collection コマンドで Gemfile から依存を解析し、
対応する型情報を gem_rbs_collection から取得できる
Steep などのツールの依存性解決もやってくれる
外部Gemの ULID と KSUID を使うコード
(いずれも型情報がないため untyped でエラー)
rbs_collection.yaml に設定を記述し、
rbs collection install を実行
コミュニティで型情報が提供されている ULID は
型が解決できるようになった
KSUID は自分で RBS を記述することで型を解決
( vendor/rbs 以下も sig ソースとして Steepfile に設定)
Steepの検査設定をstrictなどにして
untyped の型検査をしないという選択肢もある
Ruby の静的型検査機能の概要
静的型検査を利用した開発手順
RBSの機能と利用例
ライブラリ利用時の静的型検査
まとめ
静的型検査を活用したRubyの開発について紹介した
RBS を書いて Steep で型検査すると、
静的型付け言語のような開発体験・安全性が得られる
TypeProf を使うと、これまでと同じ感覚で
Rubyを書きながら型の恩恵が(多少)得られる
IDE拡張を利用すると良好な開発体験を得られる
RBS自体や周辺ツールもどんどん進化している
ruby-jp Slackの #types チャンネルに入ろう!