黒曜 / @kokuyouwind
Rubyと関数型言語チョットワカル
(最近は関数型言語全然触ってない)
カンファレンス実況勢
初参加の RubyKaigi 2015 から11年
実況芸で食わせていただいております(?)
「入門 関数型-ish プログラミング on Ruby」で登壇
名古屋といえば関数型プログラミングと型でしょ!!!
TypeScriptが完全にデファクトスタンダード化
2019年はまだ過渡期(Babel, ts-loader, webpack, …)
今や型レベルプログラミングや型安全性の話が一般的に
Rubyでも型システムの整備が進んだ
RBS(型記述言語)がbundled gem化 (2020/12)
Steep, TypeProfなどの周辺ツールが継続開発
インラインRBS記法の実験的サポート (2026/03)
そもそも型ってなんだっけ?
なんでRubyに型システムがあると嬉しいんだっけ?
Ruby言語と別に型専用のRBS言語があるのはなぜ?
🤔
型・型システムの基礎
型を後付けする難しさ
Rubyの型システムの歴史振り返り
まとめ
型・型システムの基礎
型を後付けする難しさ
Rubyの型システムの歴史振り返り
まとめ
型をつける・型がある・型がほしい
型推論・型安全・型注釈
いろんな語があるけど「型」って結局なに???
🤔
Pierce 著 / 住井 監訳, オーム社, 2013 (和訳版)
「型システムとは、プログラムの各部分を、
それが計算する値の種類に沿って分類することにより、プログラムがある種の振る舞いを起こさないことを保証する、
計算量的に扱いやすい構文的手法である。」(TAPL p.1)
※「十分に意味のある具体的な定義を与えるのが難しい」と前置きあり
プログラムの各部分を、値の種類に沿って分類する
この「分類」を「型」と呼んでいることが多そう
→ 以降のスライドでは「型」をこの意味で用いる
「型システムとは、プログラムの各部分を、
それが計算する値の種類に沿って分類することにより、プログラムがある種の振る舞いを起こさないことを保証する、
計算量的に扱いやすい構文的手法である。」(TAPL p.1)
型 =「プログラムが表現しうるすべての値から、
ある性質を満たす値を集めたもの」
「#each に応答する」性質を満たす集合(型)も考えられる
→ def obj.each したオブジェクトもすべて要素になる
クラスは型だが、クラスだけが型ではない
プログラム実行時に「ある性質を満たす値か」をチェックする
1.each → NoMethodError
「1 が #each に応答する集合の要素か」を動的に検査している
(動的型付けでは) ヒープ中の異なる種類の構造を区別するのに実行時の型タグが利用される。「動的型付けされる」といった言い回しは誤っているといって差し支えなく、おそらく「動的検査される」と言い換えるべきであるが、標準的に使われる用語法である。(TAPL p.2)
→ これは(構文的手法としての)型システムではない
「型システムとは、プログラムの各部分を、
それが計算する値の種類に沿って分類することにより、プログラムがある種の振る舞いを起こさないことを保証する、
計算量的に扱いやすい構文的手法である。」(TAPL p.1)
「値そのもの」ではなく「プログラムの各部分」を型で分類する
→ 実行前のプログラムに対する手法が「型システム」
型付け規則によって項につけるラベル = 型
型付け規則は何でも自由に決めればいいというものではない
「すべての項に untyped をつける」型付け規則はなんの役にも立たない
型安全性(型健全性) を満たすよう型システムを設計する
型付け規則によってすべての項に型をつけられるなら、
実行時に各項がその型の値になり、実行時に型エラーにならない
例: 「x と y が整数ならば x + y も整数」はあらゆる x と y で成立する
何を実行時エラーとするかは言語次第
型システムの設計によって、どういう種類の「不正な振る舞い」を
防げるかが変わる
TAPL「型システムは、項が実行時にどう振る舞うかの静的な近似」(p.2)
「実行時エラーにならないコードが型付け規則を満たす」とは限らない
上記を満たすなら「型完全」という
項に対してどう型をつけるか
型注釈 = 人が書く
型推論 = ツールが導出する
基本は上記の組み合わせ
静的型検査
型付け規則で項に正しく型がつけられないなら、実行時エラーを起こす可能性がある
型システム: 項を値の種類で分類する構文的手法
プログラム全体に型がつけられたときに、
実行時の型エラーが発生しないことを保証する
型: その分類のラベル
直感的には値の集合
形式的には型付け規則に用いるラベル
「なにを実行時型エラーとするか」は言語しだい
それに合わせて型システムを設計する必要がある
型・型システムの基礎
型を後付けする難しさ
Rubyの型システムの歴史振り返り
まとめ
「型検査を考慮して設計されていない言語に型システムを組み込むのは困難な問題である」(TAPL p.7)
理由1: 型検査を困難にする機能・イディオムが推奨されがち
例) ダックタイピング、define_method、ブロックの制御フロー
理由2: 型注釈を考慮した具象構文は複雑になりがち
後付けだと「注釈をどこに、どう書くか」が問題になる
推論以前に「型」をどう設計するかがそもそも難しい
tap メソッドは break するブロックを渡されたときだけ自身を返さない
x, *y, z = a のような代入は配列内位置で異なる型が必要
「Rubyらしいプログラム」を拒否せず型を付けるには高い表現力が必要
推論だけで導出するのは難しい (型注釈が必要)
表現力を上げると、型システムの「計算量的に扱いやすい」から遠ざかる
推論が難しくなる
注釈が長く複雑になる
言語と型システムを合わせて設計すれば一定回避できることもある
Rubyのイディオムは型推論と相性が悪く、型注釈が必要
誰が書くのか?
どうやって型を表現するのか?
Gradual Typing [Siek & Taha 2006]
分からない部分は未定にし、分かっている部分同士の矛盾だけ拒否
近年、既存の言語に型を後付けする標準的な方法になっている
TypeScript any
Python Any
RBS untyped
出典: Siek & Taha 2006
全部 untyped = 何も検査していないのと同じ
untyped を受け取った式の結果も untyped (規則で型が決まらない)
効くのは「untyped と接しない、まとまった領域」を作れたとき
その領域では結局「高い表現力の型システム」が必要
型システムを後付けするのは難しい
型推論しづらい機能・イディオム
複雑な型注釈の構文設計
Rubyは普通のプログラムでも表現力の高い型が必要
複雑な型注釈を誰がどう書くか
漸進的型付けが後付の標準手法になっている
型検査の恩恵を受けたい領域では結局ちゃんと型を付ける必要がある
型・型システムの基礎
型を後付けする難しさ
Rubyの型システムの歴史振り返り
まとめ
Matz RubyKaigi 2014: Ruby 3へ向けて実装したいアイデア
Concurrency, JIT, 静的型付け
静的型付けのメリット
型情報を利用したパフォーマンス改善
実行前の静的型検査によるエラーチェック
ドキュメンテーションとしての型
「無駄だと思うものは誰も書きたくない」
型指定は「むしろ積極的に外すべき」
Matz 2016 "We are not going to add any kind of type annotation to Ruby"
「Ruby本体に型注釈の記法は足さない」というスタンス
目指すのは Structural Subtyping + 型推論で、注釈なしの型検査
Steep (Soutaro, 2017)
sig/*.rbi の別ファイルシグネチャ + コメント注釈
Sorbet (Stripe, 2017)
sig { } DSL + 実行時検査
「人間が注釈を書く」前提
「注釈なしの型検査」を目指すMatzの思想と別ルートではある
Ruby自体には型注釈記法を導入しない
2019-03 ruby-signature が ruby org 配下に誕生
Steep のシグネチャ言語を切り出したもの
後に RBS にリネーム (2020)
検査器は統一せず、シグネチャ言語だけ共通化 = RBSの原点
Steep は移行、Sorbet は互換を表明 (実対応は2025)
Rubyの構文に型注釈記法は入れない(Matzの意向)
人間が書かなくていいものを書きたくない
構文を変えると特定バージョンへの移行を強いる
コメントでなく別ファイルにする Steep の設計を継承 (2017年から sig/*.rbi)
C実装の組み込みライブラリや他人のgemにも、外から型を付けられる
コードを変えずに始められる (.d.ts / .h と同じ発想)
ツールに依存しない中立な言語
型情報をツール間で共有するため
2020-12 Ruby 3.0.0
rbs / typeprof が bundled gems になった
Steep・Sorbet は非同梱
Matz が、「型注釈を書くことを Ruby本体として推進しない」と判断
2021-05 「別ファイルは不便、インラインで書きたい」という要望が出る
Matz「型宣言のための新構文は追加しない。
yard コメント内の RBS 構文なら yard 側の判断」
2024: rbs-inline (soutaro)
RBS を Ruby コードのコメントとして書けるように
Ruby の構文は変えないまま、別ファイルの書きにくさを回避
2025: Sorbet が RBS コメント記法をサポート (Shopify)
既存のDSL記法と併用可(opt-in、experimental)
RBS で Sorbet の全機能を表せるわけではない
2026: RBS 4.0, Steep 2.0
Ruby 3 の目標として「静的型検査」が立てられた
Matz「型情報は欲しいが、型注釈をRuby構文には入れない」
複数の型検査器が独立して開発
ツール間での連携のため、中立のシグネチャ言語が必要に
Steepのシグネチャ言語を ruby-signature として切り出し、後のRBSに
現在はRBSのインライン記法が実験的にサポートされている
型・型システムの基礎
型を後付けする難しさ
Rubyの型システムの歴史振り返り
まとめ
型・型システムの基礎
型システム = 項を分類して不正な振る舞いを防ぐ構文的手法
型はその分類のラベル
Rubyに型を後付けする難しさ
普通のRubyコードを拒否しないには高い型の表現力が要る
Rubyらしいイディオムは型推論と相性が悪い
Rubyの型システムの歴史
Matz「型情報は欲しい、でも型注釈は構文に入れない」
シグネチャ言語 (RBS) を別ファイルやコメントにする判断