コメントにRubyの型情報(RBS)を書けるgemを作った

はじめに

イタンジ株式会社の稲垣です。ノマドクラウドという不動産仲介向けのSaaSを開発しています。 趣味はFF14で本当はFF14の記事を書こうと思ったのですが、つい先日gemを作るというネタになることをしてしまったので作ったgemについて書きます。

作ったもの

rubygems.org github.com

これなに

rubyファイルのコメントに型情報を書くことで、rbsファイルを生成することができるgemです。

例えばこんなrubyコードを書いて

# example.rb

module App
  # @sig (Integer) -> String
  def some_method(arg)
    arg.to_s
  end

  private

  # @sig (String) -> Integer
  def some_private_method(arg)
    arg.to_i
  end
end

こんなコマンドを叩くと irbs example.rb -o example.rbs

こんなrbsファイルが生成されます。

# example.rbs

module ::App
  public def some_method: (Integer) -> String
  private def some_private_method: (String) -> Integer
end

RBSについて

「こんなrbsファイル」と言われてもrbsって何?と思う方も多いと思います。 なのでまずrbsについて簡単にですが説明します。

最近のRubyは型を取り扱うことができるようになりました。 rbsとはRuby 3.0から標準添付されるようになった、rubyの型情報を扱う言語(とそれらを扱うrbscliコマンド)のことです。

ちょっとややこしいのですが、rbsはrbとは独立して存在しており、それ自体はrubyの動作に影響を及ぼすことはありません。 あくまで型情報を記しているだけで、実際に型検査を行うためには別のツールが必要になります。

現状ではSteepがrbsを用いた実用的な唯一の型検査ツールです。

Rubyで型を使う開発フローをざっくり書くと、

  1. rbを書く
  2. 1の型情報のrbsを書く
  3. Steepでrbsを使ってrbの静的な型検査を行う

のようになります。

gemの話にもどると、irbsは上記開発フローの2. 1の型情報のrbsを書くを補助するツールと位置づけることができます。

Rubyの型周りについての包括的な説明はクックパッド開発者ブログ様の以下の記事が詳しいので、一読をおすすめします。 techlife.cookpad.com

また、弊社blogでも実際にrbsを活用した記事があるので、併せてご覧いただければと思います。 tech.itandi.co.jp

irbs

上述の通り、Rubyで型を用いた開発を行うためにはrbsファイルを作成する必要があります。 ただこれ実際にrbsファイルを手書き1するとわかるのですが、rbとrbsを行ったり来たりが非常にめんどうくさいです。 なので、rbファイルにインラインでrbsを書けるようにしました。2

記法

@sig

メソッド定義の上のコメントに、@sigに続けてメソッドに対する型注釈を書くと、そのメソッドに対するrbsコードが出力されます。

# @sig () -> void
def some_method; end
public def some_method: () -> void

メソッド名と、視認性(public|private)は自動的に補完されます。 型注釈については補完されることなく(たとえrbsとして間違っていたとしても)そのまま反映されます。

attr_*メソッドに対しても型注釈を書くことができます。

# @sig String
attr_accessor :foo
public attr_accessor foo: String

特殊なケースとして、動的に定義されているメソッドについて型注釈を書きたいときがあります。 そのような場合は、@!methodディレクティブ3を使います。

# @!method bar
#   @sig () -> void
delegate [:bar] => :other
public def bar: () -> void

@rbs

typeエイリアスなど、型注釈以外のrbsコードを書きたくなることがあります。 そのような場合は、module定義ののコメントに@rbsに続けて書くと、そのmoduleのに反映されます。4

# @rbs type key = String | Symbol
class App; end
class App
  type key = String | Symbol
end

使用例

実はirbsのソースコード自体がirbsとSteepによって型検査されています。

なのでソースコードと、irbsによって生成されたrbs型検査のためのRakeタスクを見ていただけると、なんとなく使い方がわかるかと思います。

余談

Sordについて

irbsと似たようなツールとして、YARDからrbsを生成するSordというものがあります。

github.com

Sordだと、現状privateなメソッドの型がprivateにならないという問題がありました。

また、型注釈を書くだけならYARDの記法は冗長だと感じます。

# YARD
# @param a [Integer]
# @param b [Integer]
# @return [Integer]
def add(a, b)
  a + b
end
# irbs
# @sig (Integer, Integer) -> Integer
def add(a, b)
  a + b
end

なので型注釈をコメントで書くことに特化したirbsを作ろうと思った次第です。

おわりに

型付きRuby流行ってほしいです。


  1. TypeProfrbs_railsといった自動生成ツールもあるが、完璧とは言い難かったりrailsのModel限定だったりでどうしても手書きする必要は出てくる。
  2. rbに型注釈を書かないというRubyの型に対するスタンスに反しているが、rbから見ればただのコメントなので許されるはず。
  3. File: Tags Overview — Documentation for yard (0.9.28)。 そうです実は内部的にはYARDを使っています。
  4. 直感的ではないですが、パーサーとして内部で使っているYARDの仕様上こうせざるを得ませんでした。遠い未来にはパーサーも自前実装したいです。