はじめに
Ruby 3.0ではRubyのコードに型定義情報を提供するRBSという仕組みが導入されます。
この記事では簡単なサンプルプログラムを通して、RBSとその周辺ツールの使い方や役割を説明します。
なお、説明する内容はあくまで初歩的な内容です。予めご了承ください。
動作確認時の実行環境
本記事の執筆時点ではまだRuby 3.0は正式にリリースされていません。
正式リリース時、または今後のバージョンアップによってこの記事の内容と実際の挙動が異なる可能性もあります。
本記事の執筆時に使用した実行環境は以下のとおりです。
- Ruby 3.0.0dev (2020-11-13T16:46:08Z master 7826210541) [x86_64-darwin19]
- rbs 0.17.0
- typeprof 0.4.2
- steep 0.36.0
ただし、型チェックに関わる機能はgemとして提供されているため、gemをインストールすればRuby 2.7でも動かすことができます。詳しくは後述します。
サンプルコード
本記事で使用したサンプルコードは以下のリポジトリに置いています。
本記事の位置づけ
この記事はRuby 3.0 Advent Calendar 2020 7日目の記事です。
また、Ruby 2.3のリリース時から毎年続けている、「サンプルコードでわかる!Ruby 2.xの主な新機能と変更点」シリーズのRuby 3.0バージョンでもあります。(RBS以外の新機能をまとめた記事も後日公開する予定です)
- サンプルコードでわかる!Ruby 2.3の主な新機能 - Qiita
- サンプルコードでわかる!Ruby 2.4の新機能と変更点 - Qiita
- サンプルコードでわかる!Ruby 2.5の主な新機能と変更点 - Qiita
- サンプルコードでわかる!Ruby 2.6の主な新機能と変更点 - Qiita
- サンプルコードでわかる!Ruby 2.7の主な新機能と変更点 - Qiita
Part 2も書きました!
Ruby 3.0のその他の新機能と変更点は以下の記事にまとめました。
こちらもあわせてどうぞ。
サンプルコードでわかる!Ruby 3.0の主な新機能と変更点 Part 2 - 新機能と変更点の総まとめ
それでは以下が本編です。
自分で作ったコードに型定義を付ける
自分で簡単なRubyのクラスを作り、そのクラスに型情報を定義してRubyの型チェックがどのように実現されるのかを見ていきましょう。
以下の手順に従えば、みなさんもご自身でRuby 3.0の型チェックを体験することができます。
まず、適当なディレクトリを作って、そこに移動します。
ここでは rbs-sandbox
というディレクトリを作りました。
$ mkdir rbs-sandbox
$ cd rbs-sandbox
lib
ディレクトリを作り、その中にfizz_buzz.rb
を作ります。
$ mkdir lib
$ touch lib/fizz_buzz.rb
lib/fizz_buzz.rb
には以下のようなコードを書くことにします。
class FizzBuzz
def self.run(n)
1.upto(n).map do |n|
if n % 15 == 0
'FizzBuzz'
elsif n % 3 == 0
'Fizz'
elsif n % 5 == 0
'Buzz'
else
n.to_s
end
end
end
end
さて、このFizzBuzz.run
メソッドに対して、自力で型定義を書いてもいいのですが、ここではRuby 3.0に付属するtypeprof
コマンドを使って型定義を自動生成してみます。
typeprof
で型定義を自動生成するためには、目的のコードを実行するRubyコードが必要になります。
そこで、runner/fizz_buzz_runner.rb
に次のようなコードを書いてみましょう。(注:runner
というディレクトリ名は筆者が適当に付けた名前です。RBSの予約語とかではありません)
$ mkdir runner
$ touch runner/fizz_buzz_runner.rb
runner/fizz_buzz_runner.rb
には次のようなコードを書きます。
require_relative '../lib/fizz_buzz'
results = FizzBuzz.run(15)
puts results
念のため、上のコードが動作することも確認しましょう。以下のような実行結果が得られればOKです。
$ ruby runner/fizz_buzz_runner.rb
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
準備ができたので、typeprof
を実行しましょう。
以下のコマンドを入力します。すると以下のような出力結果が得られます。
(注:本記事執筆時点ではunknown RBS type: RBS::Types::Bases::Top
という謎の警告も大量に出力されます)
$ typeprof runner/fizz_buzz_runner.rb
# Classes
class FizzBuzz
def self.run : (Integer) -> Array[String]
end
この出力結果が、typeprof
によって自動的に型推論されたFizzBuzzクラスの型情報です。
(Integer) -> Array[String]
とあるように、Integer型の引数を渡すと、Stringを要素に含むArrayが戻り値として返ってくることが表現されています。
次に、Steepというgemを使って、プログラムの型チェックを行ってみましょう。
SteepはRuby 3.0には付属しないため、別途インストールする必要があります。
$ gem install steep
gemをインストールしたら、steep init
コマンドを実行します。
$ steep init
Writing Steepfile...
すると、次のようなSteepfile
が作成されます。
# target :lib do
# signature "sig"
#
# check "lib" # Directory name
# check "Gemfile" # File name
# check "app/models/**/*.rb" # Glob
# # ignore "lib/templates/*.rb"
#
# # library "pathname", "set" # Standard libraries
# # library "strong_json" # Gems
# end
# target :spec do
# signature "sig", "sig-private"
#
# check "spec"
#
# # library "pathname", "set" # Standard libraries
# # library "rspec"
# end
Steepfile
はSteepを実行する際に必要な設定ファイルです。
設定例がコメントアウトされていますが、ここではSteepfile
の中身を以下のように書き換えることにします。
target :lib do
check "lib"
check "runner"
signature "sig"
end
上の設定では、lib
ディレクトリとrunner
ディレクトリを型チェックの対象とし、型定義ファイルをsig
ディレクトリに配置するという意味になっています。(注:本記事執筆時点ではSteepfile
のDSLに関する説明が見つけられなかったので、設定の意味については筆者の推測で書いています)
型定義ファイルをsig
ディレクトリに配置しましょう。
次のようにsig
ディレクトリを作り、そこにfizz_buzz.rbs
ファイルを作成します。
$ mkdir sig
$ touch sig/fizz_buzz.rbs
sig/fizz_buzz.rbs
に先ほどtypeprof
で出力された型定義情報を書き込みます。
class FizzBuzz
def self.run : (Integer) -> Array[String]
end
これでSteepを使う準備ができました。では、steep check
とコマンドを入力してください。
$ steep check
何か起きましたか?何も起きませんね。
何も起きないということは型チェックに成功したことを意味しています。
ちゃんと不正な型を検出できるかどうかを確認するために、fizz_buzz_runner.rb
を次のように書き換えてみてください。
require_relative '../lib/fizz_buzz'
-results = FizzBuzz.run(15)
+results = FizzBuzz.run('abc')
puts results
こうすると、型チェック時にエラーが発生するはずです(引数にIntegerではなくStringを渡したため)。
$ steep check
runner/fizz_buzz_runner.rb:3:23: ArgumentTypeMismatch: receiver=singleton(::FizzBuzz), expected=::Integer, actual=::String ('abc')
エラーが出力されたことを確認したら、fizz_buzz_runner.rb
のコードは元に戻してください。
Rubyの組み込みライブラリも型チェックの対象になります。たとえば、fizz_buzz.rb
で以下のようにわざと文字列に対してupto
メソッドを呼び出してみましょう。
class FizzBuzz
def self.run(n)
- 1.upto(n).map do |n|
+ '1'.upto(n).map do |n|
if n % 15 == 0
これもやはり型チェックに引っかかります。(注:本記事執筆時点ではなぜか同じエラーメッセージが2回出力されます)
$ steep check
lib/fizz_buzz.rb:3:4: UnresolvedOverloading: receiver=::String, method_name=upto, method_types=(::string, ?::boolish) -> ::Enumerator[::String, ::String] | (::string, ?::boolish) { (::String) -> void } -> ::String ('1'.upto(n))
エラーが出力されたことを確認したら、fizz_buzz.rb
のコードは元に戻してください。
Ruby 3.0の型チェックの基本説明は以上になります。
ここまでの説明でわかること
ここまでの説明でわかることを以下にまとめます。
- Rubyの型情報はrbsと呼ばれるファイルに定義する(Rubyスクリプト本体に型情報は書かない)
- rbsは自力で書いてもよいが、
typeprof
を使うと型推論でrbsを自動生成できる(ただし目的のコードを動かすコードが必要) - 型チェックの実行はRuby本体の機能でなく、外部のgemを利用する。Steepはそのうちのひとつ
- Rubyの組み込みライブラリは最初から型情報が提供されるので型チェックの対象となる
続いてもうちょっと応用的な使い方を見ていきます。
標準ライブラリの型チェックをする
この記事ではStringやIntegerのようにrequire
なしで使えるクラスを「組み込みライブラリ」(コアライブラリともいう)、DateクラスやPathnameクラスのようにrequire
が必要なクラスを「標準ライブラリ」と呼ぶことにします。
この項では標準ライブラリの型チェックを利用する手順を説明します。
fizz_buzz_runner.rb
を次のように修正してみましょう。
ここでは15回という固定の回数ではなく、今日の日付の日にちぶん(つまり1〜31回)FizzBuzzを実行します。
+require 'date'
require_relative '../lib/fizz_buzz'
-results = FizzBuzz.run(15)
+results = FizzBuzz.run(Date.today.day)
puts results
この状態でsteep check
を実行します。
$ steep check
コードに問題がないので何も起きませんね。
ではわざと.day
を.dy
に変えてみましょう。
require 'date'
require_relative '../lib/fizz_buzz'
-results = FizzBuzz.run(Date.today.day)
+results = FizzBuzz.run(Date.today.dy)
puts results
.dy
は存在しないメソッドなので、steep check
で怒られるはずです。どうでしょうか?
$ steep check
あれ?またまた何も起きませんでした……。
実は標準ライブラリを型チェックの対象にするためには、Steepfile
にそのライブラリ名を指定する必要があります。
というわけで、Steepfile
に以下の一行を追加します。
target :lib do
check "lib"
check "runner"
signature "sig"
+ library "date"
end
こうするとsteep check
で型エラーを検知できます。
$ steep check
runner/fizz_buzz_runner.rb:4:23: NoMethodError: type=::Date, method=dy (Date.today.dy)
gemの型チェックをする
Ruby 3.0リリース時にはRubyの組み込みライブラリや標準ライブラリについては、最初から型定義情報が同梱されます。
しかし、Ruby標準ではないサードパーティー製のgemについては、別途型定義が必要になります。
TypeScriptのDefinitelyTypedと同じように、RBSにはgem_rbsというリポジトリがあります。
本記事執筆時点では、listen、rainbow、redis、retryableという限られた種類のgemしか型情報が登録されていませんが、今後RBSが普及するにつれて、型情報が増えていくことが期待されます。(もちろんみなさん自身がコントリビュートすることも可能です!)
ここでは試しにRetryable gemの型定義ファイルを使ってみましょう。
まず、Retryable gemをインストールします。
$ gem install retryable
次にfizz_buzz_runner.rb
を次のように書き換えます。(Retryableの型チェックをしたいだけなので、コード自体にあまり意味はありません)
require 'date'
require 'retryable'
require_relative '../lib/fizz_buzz'
Retryable.retryable(tries: 3) do
results = FizzBuzz.run(Date.today.day)
puts results
end
さらにgit submodule
を使って、gem_rbsをインポートします。
# git initしてなかったら、まずgit initを実行
$ git init
# git submoduleコマンドでgem_rbsをインポート
$ git submodule add https://github.com/ruby/gem_rbs.git vendor/rbs/gem_rbs
Steepfile
を開いて、Retryableの型情報を読み込むようにします。
なお、library "forwardable"
も追加しているのは、Retryableが内部的にForwardableモジュールを使っているため、この記述がないとsteep check
時にUnknownTypeNameError: name=Forwardable
というエラーが出るためです。
target :lib do
check "lib"
check "runner"
signature "sig"
+ repo_path "vendor/rbs/gem_rbs/gems"
+ library "retryable"
+ library "forwardable"
end
この状態でsteep check
を実行すると、型チェックに成功=つまり何も起きないはずです。
$ steep check
ためしに.retryable
を.retryablee
に書き換えて見ましょう。
require 'date'
require 'retryable'
require_relative '../lib/fizz_buzz'
-Retryable.retryable(tries: 3) do
+Retryable.retryablee(tries: 3) do
results = FizzBuzz.run(Date.today.day)
puts results
end
こうすると型チェックに引っかかるはずです。
$ steep check
runner/fizz_buzz_runner.rb:5:0: NoMethodError: type=singleton(::Retryable), method=retryablee (Retryable.retryablee(tries: 3) do)
キーワード引数も型チェックされます。3
を文字列の"3"
に変えてみましょう。
require 'date'
require 'retryable'
require_relative '../lib/fizz_buzz'
-Retryable.retryable(tries: 3) do
+Retryable.retryable(tries: "3") do
results = FizzBuzz.run(Date.today.day)
puts results
end
steep check
を実行すると、キーワード引数の型が違うと怒られます。
$ steep check
runner/fizz_buzz_runner.rb:5:20: ArgumentTypeMismatch: receiver=singleton(::Retryable), expected=::Retryable::Configuration::options, actual={ :tries => ::String } (tries: "3")
VS Code用のエクステンションで型チェックする
SteepはVS Code用のエクステンションも提供しています。
VS Codeで"Steep"という名前のエクステンションを検索すると、以下のエクステンションが見つかるはずです。
まずこれをインストールしましょう。
このエクステンションを使うためにはBundlerをセットアップする必要があります。
というわけで、bundle init
を実行してGemfile
を作成します。
$ bundle init
bundle init
が終わったらGemfile
を次のように編集し、bundle install
を実行してください。
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
gem "steep"
gem "retryable"
Bundlerのセットアップが終わったら、VS Codeでrbs-sandbox
ディレクトリを開いてみましょう。
コードが1つ前の節で使った(tries: "3")
の状態になっていれば、以下のように型エラーが通知されているはずです。
メソッド名のオートコンプリート機能も働きます(同時に型情報も表示されています)。
カーソルをメソッドの上にhoverすると、そのメソッドの型情報も表示されます。
steep-vscodeを使った感想
静的型付け言語を使ったときのような開発体験をRubyでできるのは、なかなか新鮮でした!
ただ、筆者はRubyMineユーザーなので、個人的にはRubyMineでも早く同じような機能が提供されることを期待しています。
(こちらのページによると、「コーディング支援などの機能や RBS のサポートは、Ruby 3.0 のリリースバージョンが公開された後に実装される見込み」だそうです)
typeprofと既存のrbsファイルを使って型情報の差分を取得する
すでに説明した通り、typeprof
を利用すると型情報を自動生成することができます。
ただし、typeprof
はコードから型を推測しているので、推測がうまくいかないときはコードの書き手が意図した通りの型情報が出力されない場合があります。
たとえば以下のようなコードがあったとします。
# lib/person.rb
class Person
def initialize(name)
@name = name
end
end
# runner/person_runner.rb
require_relative '../lib/person'
Person.new('Alice')
この場合、typeprof
の実行結果は以下のようになります。
$ typeprof runner/person_runner.rb
# Classes
class Person
@name : String
def initialize : (String) -> String
end
initialize
の戻り値がStringになっていますが、コードの書き手の意図としては「インスタンス変数に名前をセットしただけ」であり、それを戻り値として返そうとしたわけではありません。
こういう場合はtypeprof
の出力結果を自分で編集する必要があります。そこで、sig/person.rbs
は次のように戻り値をStringからvoidに変えて保存しました。
class Person
@name : String
def initialize : (String) -> void
end
続けて、Personクラスにhello
メソッドを実装します。
class Person
def initialize(name)
@name = name
end
+ def hello
+ puts "Hello, I'm #{@name}!"
+ end
end
person_runner.rb
でもhello
メソッドを呼び出すようにします。
require_relative '../lib/person'
person = Person.new('Alice')
person.hello
しかし、この状態でtypeprof
を再実行すると、先ほど修正したinitialize
メソッドの戻り値がまたStringに戻ってしまいます。
$ typeprof runner/person_runner.rb
# Classes
class Person
@name : String
def initialize : (String) -> String
def hello : -> nil
end
typeprof
を実行するたびに以前修正した内容を何度も修正するのは面倒です。
この問題を避けるため、typeprof
では実行用のコードだけでなく、既存のrbsファイルも型推論のインプットとして与えることができます。
というわけで、typeprof
の引数に先ほど作ったperson.rbs
を指定して実行してみましょう。
こうすると、既存のrbsファイルに定義されていない型情報だけが出力されます(既存の型情報はコメントアウトされて出力されます)。
$ typeprof sig/person.rbs runner/person_runner.rb
# Classes
class Person
# @name : String
# def initialize : (String) -> void
def hello : -> nil
end
あとは差分として出力されたdef hello : -> nil
をsig/person.rbs
に追加すればOKです(ただし、ここも戻り値はnil
ではなくvoid
に変えた方がいいかもしれません)。
なお、typeprof
は-o
オプションを付けることで、自動生成された型情報をファイルに出力することもできます。
$ typeprof sig/person.rbs runner/person_runner.rb -o sig/person.gen.rbs
rbsコマンドで型定義ファイルの雛形を作成する
ここまでtypeprof
コマンドを使って型定義ファイルを作成してきましたが、typeprof
の代わりにrbs
コマンドを使って型定義ファイルの雛形(プロトタイプ)を生成することができます。
以下はrbs
コマンドでfizz_buzz.rb
の型定義情報を生成する例です。
$ rbs prototype rb lib/fizz_buzz.rb
class FizzBuzz
def self.run: (untyped n) -> untyped
end
rbs
コマンドを使った場合はtypeprof
のように型推論はされませんが(引数も戻り値もuntyped
と出力される)、その代わりに対象のファイルを実行するプログラム(この記事でいうところのrunnerスクリプト)を用意する必要がなくなります。
rbsコマンドでメソッドシグニチャを確認する
ちなみに、rbs
コマンドは雛形作成以外にもいくつか機能があります。
以下はrbs
コマンドを使ってメソッドシグニチャを確認する例です。
# String#rjustのシグニチャを確認する
$ rbs method String rjust
::String#rjust
defined_in: ::String
implementation: ::String
accessibility: public
types:
(::int integer, ?::string padstr) -> ::String
# Math.sin(クラスメソッド)のシグニチャを確認する
$ rbs method --singleton Math sin
::Math.sin
defined_in: ::Math
implementation: ::Math
accessibility: public
types:
(::Numeric x) -> ::Float
# Date#month(requireが必要な標準ライブラリ)のシグニチャを確認する
$ rbs -r date method Date month
::Date#month
defined_in: ::Date
implementation: ::Date
accessibility: public
types:
() -> ::Integer
# Retryable.retryable(gemのクラスメソッド)のシグニチャを確認する
$ rbs --repo vendor/rbs/gem_rbs/gems -r retryable -r forwardable method --singleton Retryable retryable
::Retryable.retryable
defined_in: ::Retryable
implementation: ::Retryable
accessibility: public
types:
[X] (?::Retryable::Configuration::options options) ?{ (::Integer, ::Exception?) -> X } -> X?
テストコードとTypeProfを組み合わせてrbsを作成する
この記事ではfizz_buzz_runner.rb
のような実行スクリプトを作りましたが、Minitestのようなテストコードを実行スクリプト代わりに使ってもかまいません。
以下はテストコードからFizzBuzzクラスのrbsを生成する例です。
require 'minitest/autorun'
require_relative '../lib/fizz_buzz'
class RubyTest < Minitest::Test
def test_run
expected = ['1', '2', 'Fizz', '4', 'Buzz', 'Fizz', '7', '8', 'Fizz', 'Buzz', '11', 'Fizz', '13', '14', 'FizzBuzz']
assert_equal expected, FizzBuzz.run(15)
end
end
$ typeprof test/fizz_buzz_test.rb
# Classes
class FizzBuzz
def self.run : (Integer) -> Array[String]
end
class FizzBuzzTest
def test_run : -> untyped
end
FizzBuzzTestクラスのrbsも一緒に出力されていますが、FizzBuzzクラスもfizz_buzz_runner.rb
を使ったときと同じようにrbsが出力されています。
その他
Railsで使うにはどうしたらいいの?(未検証)
pockeさん(@p_ck_)が開発されているrbs_railsというgemを使うと、Rails用のrbsを生成できるみたいですが、まだ未検証です。すいません。
詳しくはrbs_railsのリポジトリや、各種Web記事を参照してください。
- https://github.com/pocke/rbs_rails
- rbs_railsにRailsの型を取り込む手順 2020-07-16版 - pockestrap
- rbs_rails & steep で型のある Rails 開発を体験しよう - stmn tech blog
- Railsで型チェックを導入してみる - Qiita
SorbetとRBSの関係はどうなってるの?
Rubyの型チェックに以前から興味を持っていた方は、Sorbet(ソルベ)をご存じかもしれません。
SorbetはRBSやSteepより先行して開発・運用されている型チェックツールです。
基本的にSorbetとRBS/Steepは「Rubyの型チェックをする」という目的は同じであるものの、それを実現する仕組みは別物です。
とはいえ、SorbetとRubyの開発チームは互いに協力しながら、相互に互換性のある型チェックの仕組みを構築していく予定だそうです。
詳しくは下記のブログ記事(英語)をご覧ください。
実はRuby 2.7でも使えるRBS
Steepだけでなく、RBSとTypeProfもgemとして提供されています。そのため、これらのgemさえインストールすればRuby 3.0だけでなくRuby 2.7でも動作させることが可能です。
以下はRuby 2.7.2で各ツールを動かした際の実行結果です。
$ ruby -v
ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-darwin19]
$ gem install rbs
$ gem install typeprof
$ gem install steep
$ rbs prototype rb lib/fizz_buzz.rb
class FizzBuzz
def self.run: (untyped n) -> untyped
end
$ typeprof runner/fizz_buzz_runner.rb
# Classes
class FizzBuzz
def self.run : (Integer) -> Array[String]
end
$ steep check
ちなみにTypeProfはRuby 2.7以上が必要ですが(参照)、RBSとSteepはRuby 2.6でも動作するようです(参考-1、参考-2)。
ところでRBSって何の略?
RBSは何の略語なのか、はっきりわからないのですが、こちらのスライドから想像するに、"Ruby Signature language"の略かな?と思ったりしています。
RBS/TypeProf/Steepに関する情報源
この記事で紹介したのはHello, worldレベルのごく簡単なRBS/TypeProf/Steepの使い方です。
本格的にRubyで型チェックをしようとすると、もっともっと深い知識が必要になります。
より詳しく知りたい方は、以下の資料を参考にしてください。
RBS/TypeProf/Steepの公式リポジトリ
各リポジトリのREADMEにはさらに詳しい情報へのリンクが載っています。
設計思想に触れる
RBSとSteepの開発は松本宗太郎さん(@soutaro)が、TypeProfの開発は遠藤侑介さん(@mametter)がそれぞれ中心になって開発されています。
各ツールの設計思想はお二人の登壇スライドやWeb記事等で確認することができます。
- https://speakerdeck.com/soutaro
- https://www.slideshare.net/mametter
- Ruby 3の静的解析機能のRBS、TypeProf、Steep、Sorbetの関係についてのノート - クックパッド開発者ブログ
- Ruby 3 の静的解析ツール TypeProf の使い方 - クックパッド開発者ブログ
型定義ファイルのサンプルを探す
型定義ファイルの書き方がわからない場合は、実際の型定義ファイルを見てみると参考になるかもしれません。
ブラウザ上でTypeProfを試す
下記サイトに行くと、ブラウザ上でTypeProfの実行結果を確認することができます。
まとめ(というか個人的な感想)
というわけで、この記事ではRuby 3.0から提供されるRBSとその関連ツールを使って、Rubyの型チェックの概要を説明しました。
個人的に他にもいくつかのユースケースでRBSを試してみたのですが、少し凝ったコードを書くと、型定義の書き方がよくわからなかったり、型チェック時に予期しない型エラー(従来の感覚からいうと、ちょっと厳格すぎるチェック)が発生してコードの修正方法に悩んだりしました。
なので、正直なところ、今すぐ開発の現場に投入できるかというと、まだちょっとハードルが高いんじゃないかなという印象です。
めちゃくちゃ柔軟で何でもできちゃうRubyだからこそ、外からカッチリとした型定義を与えるというのは、なかなか難易度が高い(柔軟さを取ると型定義が書きにくく、型定義をシンプルにすると柔軟さが失われやすい)、という感想を持ちました。
とはいえ、Rubyにようやく型チェックの土台が公式に整えられた、というのはとても大きな一歩だと思います。
これから徐々にgemの型定義が増えていったり、型定義を書くための知見や型を利用するノウハウが溜まっていたりしていくことで、数年後にはRubyでも「型のある世界」が当たり前になっているかもしれません。
@soutaroさんと@mametterさんの活躍に感謝しつつ、今後のRubyプログラミングの進化を楽しみにしたいと思います!
あわせて読みたい
Ruby 3.0のその他の新機能と変更点は以下の記事にまとめました。
こちらもあわせてどうぞ。