SlideShare a Scribd company logo
Ruby 3の型推論やってます
遠藤 侑介
Ruby 3 さみっと
1
雑談:endless新作
• ruby masterに新文法提案して仮採択されました
• Endless method definition [Feature #16746]
• この資料でもさっそく使っていきます!
• と思ったけど、脳負荷がありそうなので控えめに
2
def foo = 42
def foo
42
end
= と同じ
Ruby 3の型を1ページで
Ruby 3は型記述言語・型推論・型検査を提供したい
3
def inc:
(Integer) -> Integer
① 型記述言語
(RBS; ruby-signature)
def inc(n) = n+1
コード
② 型推論
(ruby-type-profiler)
③ 型検査
(Steep, Sorbet, …)
この発表では
②型推論を
話します
Ruby 3の型推論を1ページで
• 型プロファイラ:型レベルのRubyインタプリタ
• メソッドが受け取った型・返した型を集めて表示する
4
def foo(n)
n.to_s
end
foo(42)
Integer
String
def foo:
(Integer) -> String
いい感じのデモ:ao.rb
• 3Dレイトレーサ
• 300行のトイプログラム
• Syoyo Fujita, Hideki Miura作
• https://code.google.com/
archive/p/aobench/
• 若干改変あり
5
Demo: ao.rb
6
class Vec
@x : Float
@y : Float
@z : Float
initialize : (Float, Float, Float) -> Float
x : () -> Float
x= : (Float) -> Float
...
vadd : (Vec) -> Vec
vsub : (Vec) -> Vec
vcross : (Vec) -> Vec
vdot : (Vec) -> Float
vlength : () -> Float
vnormalize : () -> Vec
3Dベクトルのクラス
ベクトル演算たち
座標
attr_accessor
Demo: ao.rb
7
class Scene
@spheres : [Sphere, Sphere, Sphere]
@plane : Plane
initialize : () -> Plane
ambient_occlusion : (Isect) -> Vec
render : (Integer, Integer, Integer) -> Integer
end
class Sphere
@center : Vec
@radius : Float
initialize : (Vec, Float) -> Float
intersect : (Ray, Isect) -> (NilClass | Vec)
end
3つの球
球は中心と半径
Demo: ao.rb
8
class Ray
@org : Vec
@dir : Vec
initialize : (Vec, Vec) -> Vec
org : () -> Vec
dir : () -> Vec
end
class Isect
@t : Float
@hit : FalseClass | TrueClass
@pl : Vec
@n : Vec
initialize : () -> Vec
光線は起点と方向
交点の判定・計算
交わるか否か
boolean相当
Demo: ao.rb
• いい感じでは?
9
アジェンダ
• ➔型プロファイラの設計と性質
• 目標とアプローチ
• 普通の型システムとの違い
• デモと考察
• 型プロファイラの実装や工夫
• 今後
10
型プロファイラの目標とアプローチ
• 目的:型注釈なしRubyコードを静的解析する
• 主目的:型記述(RBS)のプロトタイプを作る
• 副目的:型エラーの可能性も指摘したい
• アプローチ:抽象解釈ベースの解析
11
def foo(n)
n.to_s
end
foo(42)
Integer
String
def foo:
(Integer) -> String
普通の型システムとの違い
• 普通の型システムはメソッド単位で解析する
• 仮引数の型はどうする?
• 手書きする(TypeScript他)
• 我々の目的には合わない
• 使われ方を見て決める(ML他)
• 右のような例で難しい
12
class A
def foo = 42
end
class B
def foo = "str"
end
def f(n)
n.foo #=> 何?
end
型プロファイラの pros/cons
pros
•型注釈なしで解析可能
•型注釈なしで解析可能
•型注釈なしで解析可能
•型注釈なしで解析可能
•型注釈なしで解析可能
※ただし~(後で)
cons
•解析が遅い
•テストが必要
•誤推定はある
•解析の理解が難しい
•開発体験が未知
めちゃくちゃ挑戦的!
13
アジェンダ
• 型プロファイラの設計と性質
• ➔デモと考察
• diff-lcs
• ruby-type-profiler
• 型プロファイラの実装や工夫
• 今後
14
事例:diff-lcs
• 最長共通部分列ライブラリ
• ダウンロード数4位の人気gem→
• 簡単なテスト↓を起点に解析
15
https://bestgems.org/
require_relative "diff-lcs/lib/diff/lcs"
class T; end
Diff::LCS.diff([T.new]+[T.new], [T.new]+[T.new]) {}
diff-lcs解析結果(そこそこいい感じの例)
16
class Diff::LCS::Change
include Comparable
@element : NilClass | T | any
@position : Integer | any
@action : :+ | :- | any
self.valid_action? :
(:! | :+ | :- | :< | :== | :> | any) -> (FalseClass | TrueClass)
action : () -> (String | any)
position : () -> (Integer | any)
element : () -> (NilClass | T | any)
initialize : (String | any, Integer | any, NilClass | T | any) -> NilClass
to_a : () -> ([String | any, Integer | any, NilClass | T | any])
unchanged? : () -> (FalseClass | TrueClass | any)
end
列の中の要素
追加削除の位置
追加 or 削除 ※元コードはStringでしたがデモのためSymbolに書き換えた
誤推定っぽいのは薄くしてます
diff-lcs解析結果(難しい例)
• 引数に依存して返り値の型が変わるメソッド
• diff(ary, ary, DiffCallbacks) ➔ Array[Array[Diff::LCS::Change]]
• diff(ary, ary, SDiffCallbacks)➔ Array[Diff::LCS::ContextChange]
•オーバーロードのRBSは手書きしてください
17
module Diff::LCS
self.diff :
(Array[T] | Diff::LCS, Array[T] | any, ?NilClass)
-> (Array[Array[Diff::LCS::Change | NilClass | any] |
Diff::LCS::Change | Diff::LCS::ContextChange | NilClass | any] | any)
end
diff-lcsで出た警告の例
• flow-sensitiveな解析が必要
※実際にはもっといっぱい出てます
個別に原因究明して、修正や改善検討をする……
18
if callbacks.respond_to?(:finished_a) and …
…
callbacks.finished_a(event)
#=>「NilClass#finished_aを呼ぶかも」警告が出る
…
else
事例:type-profiler
• 型プロファイラのコード
• Rubyで書かれている(5000行くらい)ので
• 型プロファイルできる
19
type-profiler解析結果(いい感じの例)
20
class TP::Type
include TP::Utils::StructuralEquality
self.any : () -> TP::Type::Any
self.bool : () -> TP::Type::Union
self.nil : () -> TP::Type::Instance
self.optional :
(TP::Type | TP::Type::Any | TP::Type::Array | … | any) ->
(TP::Type | TP::Type::Any | TP::Type::Array | … | any)
self.guess_literal_type :
(any) ->
(TP::Type::Any | TP::Type::Array | … | TP::Type::Symbol)
…
end
type-profiler解析結果(難しい例)
• 再帰構造がfalse positiveになる例
• ExecutionPoint (EP)は外側のEPへの参照を持つ
21
class TypeProfiler::ExecutionPoint
…
@outer : NilClass | TypeProfiler::ExecutionPoint | any
# 一番外側のEPをたどるコード
ep = EP.new(…)
while ep.outer
ep = ep.outer #=>「NilClass#outerを呼ぶかも」警告が出る
end
ep.pc #=>「NilClass#pcを呼ぶかも」警告が出る
type-profilerその他問題点(抜粋)
• 巨大なUnionが出てきてつらい
• 継承関係を利用してまとめる?
• 型のエイリアスをうまく作る?
• Object#method経由の呼び出しが追えない
• 作り込みが足らない
※実際にはもっといっぱい(略)
22
(TP::Type | TP::Type::Any | TP::Type::Array | … | any)
FAQ
• X | any は any と同じでは?
• おっしゃるとおり
• でもRBSプロトタイプ生成には便利なので
あえて潰さずに残している
23
アジェンダ
• 型プロファイラの設計と性質
• デモと考察
• ➔型プロファイラの実装や工夫
• 扱う型
• 分岐、可変長引数やキーワード引数
• ダミー実行や診断機能
• 今後
24
扱う型:普通のやつ
• 普通のインスタンス
• NilClass、Integer、String、Objectなど
• ユーザ定義クラス
• 普通のクラスオブジェクト
• クラスメソッド定義などで必要
• クラスのクラスや、特異クラスは扱わない
• any型
• 未定義メソッド呼出、ダミー実行(後述)などで発生
• anyのメソッド呼び出し(any.foo())は無視する
25
扱う型:コンテナ周り
• 具体型:Symbol、リテラル型
• :key などは具体値として扱う
• 整数 42 などは同一メソッド内では具体値として扱う
• コンテナ型:配列とハッシュ
• [X, Y, *Z]: 先頭はX型、次はY型、残りはZ型の配列
• [Integer, String]: 整数と文字列のタプル(ary[0]は先頭)
• [*Integer]: 長さ不明の整数シーケンス
• (RBSより表現力が少し強いので、RBSにするとき情報が落ちる)
• {X=>Y, Z=>W}: キーがX型なら値はY型…、なハッシュ
• {:key1=>Integer, Symbol=>String}
26
扱う型:その他
• Proc型: ブロック、具体値として扱う
• callやyieldをなるべく正確にトレースするため
• Union型: 型の和集合の型
• (Integer | String) : 整数か文字列
• 工夫(というか制限)
• ([*Integer] | [*String])は[*(Integer|String)]に潰す
• 不正確だけど、組合せ爆発を避けて解析高速化を選んだ
27
分岐の扱い
• 両方実行して、Union型で合流する
※解析を確実に合流させるのは意外と難しく、ヒューリスティクスを使ってる(PCが小さい方から解析する)
28
x = nil
if rand < 0.5
x = 42
else
x = "str"
end
p(x) #=> Integer|String
可変長引数とキーワード引数
• なんかそれっぽく動く(実装は地味に大変だった)
29
def foo(a, *r, z)
r
end
foo(1, 2, 3)
foo(1, 2, "S", 3)
def foo:
(Int, *Int|Str, Int)
-> Array[Int|Str]
def foo(n:, s:)
{ N: n, S: s }
end
foo(n: 42, s: "str")
def foo:
(n: Int, s: Str)
-> {:N=>Int, :S=>Str}
「ダミー実行」(1)
• 解析が到達できないメソッドは解析できない
• ダミー実行:未到達メソッドを無理やり実行する
• 引数はすべてanyとする
30
def foo(n)
n
end
def bar(a, b, c)
foo(42)
end
fooもbarも
テストがない
ここにfooのヒントが
あるけど使えない
「ダミー実行」(2)
• ダミー実行は良し悪し
• ゴミ情報が波及することも
31
# ao.rb抜粋
class Vec
# このメソッドは使われない
def vadd(b) # b: any
Vec.new(
@x + b.x, # any
@y + b.y, # any
@z + b.z, # any
)
end
end
class Vec
@x: Float | any
@y: Float | any
@z: Float | any
# 今はチートコードを追加(広義の型注釈?)
if _ = false
v = Vec.new(0.0,0.0,0.0)
v.vadd(v)
end
vaddをanyで呼び出すので
@xにanyが記録される
記録されない特殊な
anyで解決できる?
解析の診断機能
• pで型をrevealできる
• 「ここにどんな型が来ると思ってる?」とか調べる
• 解析到達経路をバックトレース風に表示する機能
32
def foo(n)
p n
end
foo(1)
foo("str")
# Revealed types
# reveal.rb:2 #=> Integer | String
# Classes
class Object
foo : (Integer | String) -> (Integer | String)
end
困難に対して
• 解析が遅い ➔ (精度を犠牲にしつつ)高速化してきた
• テストが必要 ➔ (荒削りだけど)ダミー実行
• 誤推定・誤検出 ➔ (限界はあるけど)改善中
• 解析の理解が難しい ➔ (簡単だけど)診断機能
• 開発体験が未知 ➔ ?
33
アジェンダ
• 型プロファイラの設計と性質
• デモと考察
• 型プロファイラの実装や工夫
• ➔今後
• 型プロファイラによるプログラミング体験
• 開発進捗と今後の予定
34
polyglot
• 2つの言語で解釈可能なプログラム
• 例:RubyとJavaScriptのpolyglot
• コツ:片方の言語だけで解釈されるように頭を使う
35
if (0)
print("Hello Ruby") [ここはRuby]
else
console.log("Hello JS") [ここはJS]
// end
型はpolyglot
• 型付き言語のプログラムはpolyglot
• インタプリタが動的な意味で解釈できる
• さらに、型システムが静的な意味で解釈できる
• 型注釈はチート(polyglotの観点では)
• 型システムだけに指示を与えられるずるい記述
36
型プロファイラの開発体験?
• ずるくないpolyglot
• 普通の実行に加え、型レベル実行を意識して書く
• 別言語ではないのでそこまでつらくないと思う
• それでもメリットはある(はず)
• 型注釈なしの記述は疑いなくシンプル
• 型プロファイラが解析できる≒素直な良いコード?
37
def foo(n: Integer | String) : Integer | String
p n
end
foo(1)
foo("str")
def foo(n)
p n
end
foo(1)
foo("str")
vs.
進捗と今後
• 現状:やっとスタート地点
• 解析器の基本設計ができた
• Rubyのおおよその言語機能がサポートできてきた
• 組み込みクラスの知識をRBSから取り込んだ
• 今後:実験と改善を繰り返す
• バグの洗い出しと修正
• プログラミング体験の設計と不足機能の実装
• 診断機能、差分更新機能
• Railsアプリ解析用のドライバ開発
• など
38
まとめ
• Ruby 3は型記述言語・型推論・型検査を提供し
たい
• 型推論担当の型プロファイラをやってます
• (寛容な心で)手伝ってくれる人たのむ!
• https://github.com/mame/ruby-type-profiler
• ruby-jp slack の #types でも
39
説明しなかったこと
• オーバーロードの推定は諦めた
• 爆発する
• オーバーロードするときは基本的に手書きして
• 再帰呼び出しはいい感じにできる
• でも再帰的なデータ構造のハンドリングは微妙
• カスタムメソッド
• 型プロファイラプラグイン
• インスタンス変数の配列の破壊
• を説明するには、まずコンテナ型がメソッドを跨がらな
いことを説明しないと……
40

More Related Content

PDF
minne の API 改善
PDF
淺談 Java GC 原理、調教和 新發展
PDF
libpgenでパケット操作
PDF
FPGAX2019
PDF
証明プログラミング超入門
PPTX
【DL輪読会】AUTOGT: AUTOMATED GRAPH TRANSFORMER ARCHITECTURE SEARCH
PDF
2015年度GPGPU実践プログラミング 第5回 GPUのメモリ階層
PPTX
【DL輪読会】言語以外でのTransformerのまとめ (ViT, Perceiver, Frozen Pretrained Transformer etc)
minne の API 改善
淺談 Java GC 原理、調教和 新發展
libpgenでパケット操作
FPGAX2019
証明プログラミング超入門
【DL輪読会】AUTOGT: AUTOMATED GRAPH TRANSFORMER ARCHITECTURE SEARCH
2015年度GPGPU実践プログラミング 第5回 GPUのメモリ階層
【DL輪読会】言語以外でのTransformerのまとめ (ViT, Perceiver, Frozen Pretrained Transformer etc)

What's hot (20)

PDF
大規模ネットワークの性質と先端グラフアルゴリズム
PDF
[Track2-2] 最新のNVIDIA AmpereアーキテクチャによるNVIDIA A100 TensorコアGPUの特長とその性能を引き出す方法
PDF
スペクトラル・クラスタリング
PDF
BERTに関して
PPTX
Unicode文字列処理
PDF
不遇の標準ライブラリ - valarray
PDF
NetworkXによる語彙ネットワークの可視化
PDF
LLVM最適化のこつ
PPTX
契約プログラミング
PPTX
ホモトピー型理論入門
 
PPTX
[DL輪読会]Object-Centric Learning with Slot Attention
PDF
条件分岐とcmovとmaxps
PPTX
[研究室論文紹介用スライド] Adversarial Contrastive Estimation
PDF
Tensorflow Lite and ARM Compute Library
PDF
PRMU 201312 subpixel object detection survey
PDF
Proof summit 2017 for slideshare
PDF
よくわかるCoqプログラミング
PPTX
DockerコンテナでGitを使う
PDF
CF-FinML 金融時系列予測のための機械学習
PPTX
第3回関西NIPS読み会:Temporal Regularized Matrix Factorization for High dimensional T...
大規模ネットワークの性質と先端グラフアルゴリズム
[Track2-2] 最新のNVIDIA AmpereアーキテクチャによるNVIDIA A100 TensorコアGPUの特長とその性能を引き出す方法
スペクトラル・クラスタリング
BERTに関して
Unicode文字列処理
不遇の標準ライブラリ - valarray
NetworkXによる語彙ネットワークの可視化
LLVM最適化のこつ
契約プログラミング
ホモトピー型理論入門
 
[DL輪読会]Object-Centric Learning with Slot Attention
条件分岐とcmovとmaxps
[研究室論文紹介用スライド] Adversarial Contrastive Estimation
Tensorflow Lite and ARM Compute Library
PRMU 201312 subpixel object detection survey
Proof summit 2017 for slideshare
よくわかるCoqプログラミング
DockerコンテナでGitを使う
CF-FinML 金融時系列予測のための機械学習
第3回関西NIPS読み会:Temporal Regularized Matrix Factorization for High dimensional T...
Ad

Similar to Ruby 3の型推論やってます (20)

KEY
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
PDF
すごいHaskell読書会 第7章 (前編)
PPT
Pythonintro
PDF
Python勉強会2-数値と文字列
PPT
12-11-30 Kashiwa.R #5 初めてのR Rを始める前に知っておきたい10のこと
PDF
中3女子でもわかる constexpr
PDF
リテラル文字列型までの道
PDF
すごいHaskell 第7章 型や型クラスを自分で作ろう(前編)
PDF
数式をnumpyに落としこむコツ
PDF
超初心者がローマ数字をいろいろパースしてみる
PDF
型プロファイラ:抽象解釈に基づくRuby 3の静的解析
KEY
PPTX
(Lambdaだけで) 純LISPのような ナニかを作る
PPTX
Ruby講座第二回
PPTX
純粋関数型アルゴリズム入門
PDF
関数プログラミング入門
PDF
2011年11月11日
PPTX
Ruby4章
PPTX
Ruby4章
PDF
Python勉強会3-コレクションとファイル
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
すごいHaskell読書会 第7章 (前編)
Pythonintro
Python勉強会2-数値と文字列
12-11-30 Kashiwa.R #5 初めてのR Rを始める前に知っておきたい10のこと
中3女子でもわかる constexpr
リテラル文字列型までの道
すごいHaskell 第7章 型や型クラスを自分で作ろう(前編)
数式をnumpyに落としこむコツ
超初心者がローマ数字をいろいろパースしてみる
型プロファイラ:抽象解釈に基づくRuby 3の静的解析
(Lambdaだけで) 純LISPのような ナニかを作る
Ruby講座第二回
純粋関数型アルゴリズム入門
関数プログラミング入門
2011年11月11日
Ruby4章
Ruby4章
Python勉強会3-コレクションとファイル
Ad

More from mametter (20)

PDF
error_highlight: User-friendly Error Diagnostics
PDF
TRICK 2022 Results
PDF
クックパッド春の超絶技巧パンまつり 超絶技巧プログラミング編 資料
PDF
Enjoy Ruby Programming in IDE and TypeProf
PDF
TypeProf for IDE: Enrich Development Experience without Annotations
PDF
Ruby 3の型解析に向けた計画
PDF
emruby: ブラウザで動くRuby
PDF
Type Profiler: Ambitious Type Inference for Ruby 3
PDF
マニアックなRuby 2.7新機能紹介
PDF
A Static Type Analyzer of Untyped Ruby Code for Ruby 3
PDF
A Plan towards Ruby 3 Types
PDF
Ruby 3 の型解析に向けた計画
PDF
A Type-level Ruby Interpreter for Testing and Understanding
PDF
本番環境で使える実行コード記録機能
PDF
Transcendental Programming in Ruby
PDF
Cookpad Hackarade #04: Create Your Own Interpreter
PDF
Ruby 3のキーワード引数について考える
PDF
TRICK 2018 results
PDF
Type Profiler: An Analysis to guess type signatures
PDF
Esoteric, Obfuscated, Artistic Programming in Ruby
error_highlight: User-friendly Error Diagnostics
TRICK 2022 Results
クックパッド春の超絶技巧パンまつり 超絶技巧プログラミング編 資料
Enjoy Ruby Programming in IDE and TypeProf
TypeProf for IDE: Enrich Development Experience without Annotations
Ruby 3の型解析に向けた計画
emruby: ブラウザで動くRuby
Type Profiler: Ambitious Type Inference for Ruby 3
マニアックなRuby 2.7新機能紹介
A Static Type Analyzer of Untyped Ruby Code for Ruby 3
A Plan towards Ruby 3 Types
Ruby 3 の型解析に向けた計画
A Type-level Ruby Interpreter for Testing and Understanding
本番環境で使える実行コード記録機能
Transcendental Programming in Ruby
Cookpad Hackarade #04: Create Your Own Interpreter
Ruby 3のキーワード引数について考える
TRICK 2018 results
Type Profiler: An Analysis to guess type signatures
Esoteric, Obfuscated, Artistic Programming in Ruby

Ruby 3の型推論やってます