2024年のrbsアップデートまとめ

ruby 3.4リリース

Ruby 3.4が出て2月ほどが経ちましたね。 私は去年のRuby 3.3リリースに合わせたRBSの記事を書かせていただきました。

新機能ラッシュ! RBS最新情報をキャッチアップ | gihyo.jp

今年はこの企画自体がなかったので個人のブログで簡単に書いておくことにします。

RBS::UnitTestの追加

RBS記述が実際のRuby許動に沿ったものなのかテストするためのフレームワークが提供されるようになりました。 これまでrbsリポジトリー内でだけ使われていたものを、gemなどでも利用できるようになっています。

  def test_foo
    assert_send_type(
      "(Integer) -> String",
      YourGemClass.new, :foo, 123
    )
  end

サンプルコードを読み解くと、YourGemClassというclassのインスタンスメソッドfoo123という引数を与えて動かすと、(Integer) -> String、つまり引数にはInteger、返り値にはStringが返ることを期待するテストコードになります。 さらに裏では記述されたRBSもチェックしています。RBSの記述、実装者の期待値、実際のRubyの挙動の3点から整合性を確認できます。

Recordタイプにオプショナルなキーを指定できるように

Recordタイプ(e.g. { foo: Integer }。固定のHash的なやつ)で、あってもなくてもどっちでもいいけどもしもあったら型チェックしてほしい。。。そんな指定ができるようになりました。

# aaa({ foo: 123 }) => OK
# aaa({ foo: 123, bar: 'zzz' }) => OK
# aaa({ foo: 123, bar: 234 }) => Error
# aaa({ foo: 123, zzz: 'zzz' }) => Error
def aaa: ({
  foo: Integer, # これまで通りの必須キー
  ?bar: String # 必須ではないオプショナルキー
}) -> void

引数が全くわからない場合のメソッド記述が可能に

メソッドに対して、引数が全くないのか、なんでも受け付けるのか、まったく分からないことを表すことができるようになりました。移譲などで引数の数や形式が変わる場合に便利です。

# 従来のあらゆる引数を受け入れる記述
# これでは引数がまったくないことを表せない
def foo: (*untyped, **untyped) -> void

# `(?)`と書くと「引数まったくわからんから何でもおk」を表現できる
def foo: (?) -> String

# Procやブロックでも`(?)`が使える
def bar: (^(?) -> void) { (?) -> void } -> void

RBSトークン列が取得できるように

RBS::Parser.lex(source)

RBSライブラリーのRuby APIとして抽象構文木(AST)を構築する前のトークン列を取得できるようになりました。 このAPIrubocop-on-rbsを開発していて欲しくなった機能で、抽象構文木では取りにくいトークンの位置情報を取得して問題を報告させるのに役立てています。APIはPrismを参考にしました。

def foo:  (aaa: Integer) -> void
         ^ スペースが2つ続いていることを指摘したい

Genericsの強化

型引数の制約条件が緩くなりました。あんまり使うことない?

interface _Foo[T < String?]
  def foo: () -> T
end

type foo = _Foo[String]     # OK
type bar = _Foo[Integer?]   # Error

また、型引数にデフォルトの型を指定できるようになりました。型引数は一度つけば毎回指定しなければいけなくなるのが面倒ですが、これで使いやすくなりそうです。

# `_Each[Integer]`だけで使えるようになる
interface _Each[Element, Return = void]
  def each: { (Element) -> void } -> Return
end

bundled gemが徐々にrbsリポジトリーを卒業していくことに

bundled gemはRuby本体でのメンテナンスを離れてgemとして独立していったgem達です。つまり普通のgemにかなり近い存在です。これをrbsリポジトリーで持つのはもう違うよねーということで、rbsリポジトリーからbundled gemをなくしていくことになっています。ユーザー的にはあんまり意識することはないかもしれません。PR送る先で困るかも。。。わかりやすく整理します。

もしmanifest.yamlを書いている上級者の方々は、ここにbundled gemを書かないようにしていってくれると助かります。

Kernel#NamespaceKernel#TypeName がdeprecatedに

RBSに関心がなくても遭遇する可能性があります。 Namespace()というメソッドとTypeName()というメソッドがあるのですが、今後RubyでNamespace機能が搭載される予定ですしややこしいということでdeprecatedになり、使用しているコードで警告が出るようになりました。 もし警告を見たらライブラリーをアップデートするか、警告を出している(TypeName()を使用している)ライブラリーにPRを送ってみてください。

今後

2024年の出来事ということでここまでで終わりですが、RubyKaigi 2024でも話されたrbs-inlineに対する界隈の関心は高く、rbsリポジトリーにいつかmergeされる予定とどこかで聞いたことがあるので注目ですね。