洋食の日記

「だ・である」調ではなく「です・ます」調で書きはじめれば良かったなと後悔してる人のブログです

RBSでメソッドをオーバーロードするときは三点リーダーを書く

はじめに

タイトルのとおり。RBSによる型注釈が提供されているメソッドをmonkey patchして、その型注釈をpatch.rbsに書きたい時がある。Rubyのノリで書くと、重複定義エラーになる。

ディレクトリ構成

以降の文章は、次のようなディレクトリ構成で、

|- lib
|   `- hoge.rb
|- sig
|   |- patch.rbs
|   `- hoge.rbs
|- Gemfile
`- Steepfile   

bundle exec steep check が動くような感じで、ファイルを用意している。

# Gemfile
source 'https://rubygems.org'

gem 'rbs', '~> 1.2'
gem 'steep', '~> 0.44'
# Steepfile
target :lib do
  signature "sig"

  check "lib"
end

たとえば

Javascriptみたいに実数と文字列の足し算をRubyでもしたいとする(本当はしたくない)。

> 3.1 + '4'
"3.14"

そのために、Floatクラスをmonkey patchする。

# lib/hoge.rb

class Float < Numeric
  alias_method :_org_add, :+

  def + (b)
    return self.to_s + b if b.is_a?(String)
    _org_add b
  end

  private :_org_add
end

これで実数と文字列の足し算ができる。

$ irb
irb(main):001:0> require_relative 'lib/hoge.rb'
=> true
irb(main):002:0> 3.1 + '4'
=> "3.14"
irb(main):003:0> 3.1 + 4
=> 7.1

型注釈を書く

以上のmonkey patchに対してRBSを書くとすると、まず、以下のようなものが思いつく。

# sig/patch.rbs

class Float < Numeric
  alias _org_add +

  def +: (Complex) -> Complex
       | (Numeric) -> Float
       | (String) -> String
end

これで bundle exec steep check すると、重複定義エラーになる。 Floatがcore/float.rbshttps://github.com/ruby/rbs/blob/master/core/float.rbs)で定義されているためである。

(前後省略)

gems/rbs-1.2.1/core/float.rbs:40:2: [error] Non-overloading method definition of `+` in `::Float` cannot be duplicated
│ Diagnostic ID: RBS::DuplicatedMethodDefinition
│
└   def +: (Complex) -> Complex

解決策

三点リーダーを書いて、オーバーロードであることを示す。

# sig/patch.rbs

class Float < Numeric
  alias _org_add +

  def +: (Complex) -> Complex
       | (Numeric) -> Float
       | (String) -> String
       | ... # オーバーロードであると示す
end

これで重複定義エラーがでなくなる。

$ bundle exec steep check
# Type checking files:

..........................................................

No type error detected. 🫖

おわりに

Rubyはmonkey patchが容易なので、よく出くわすエラーだと思う。公式の文法ドキュメントを見て考えたが、実はガイドの方に書いてありました: rbs/sigs.md at master · ruby/rbs · GitHub