Scala, dartなどの静的型付けをやってきて、久しぶりにRubyを触ってみると動的型付けが辛い。
Ruby3からは静的型付けが公式で入るらしいけど、それまではSorbetで凌ぐのが良さげ。
Sorbet
Ruby2系でも使える静的型チェックをやってくれるgem。
段階的に既存プロジェクトに導入していけるのが強み。
Install
gem 'sorbet', :group => :development
gem 'sorbet-runtime'
bundle install
srb init
srb tc
srb init
で初期化すると、srb関連の機能が使えるようになる。
srb tc
は後述するsrbを有効化したものについては型チェックして、エラーを出してくれる。
Use
# typed: true
require 'sorbet-runtime'
module A extend T::Sig
sig {params(left: Float, right: Float).returns(Float)}
def div(left, right)
return left / right;
end
module_function :div
end
puts A.div(1.0, 2.0)
まずは型チェックが通るものから。
1行目は型チェックの有効無効制御で、明示的に指定する必要がある。明示的に向こうにする場合はfalseを指定すればいい。
4行目のT::Sig
はsorbet-runtime
に含まれるものだが、これをextendsすることで関数パラメータのIFを記述するsig
が使えるようになる。
公式でよくやっているtop-levelにextends T::Sig
する方法もシンタックスとしては通るが、バグで動かないのでmoduleかclassにextendsさせる。
ちなみにメソッドのIFを記述しないなら、1行目の記述だけで良い。
例えば、上記divでString型を返したりするとこんな感じのエラーが出てくる。
lib/main.rb:7: Returning value that does not conform to method result type https://srb.help/7005
7 | return "";
^^^^^^^^^
Expected Float
lib/main.rb:6: Method div has return type Float
6 | def div(left, right)
^^^^^^^^^^^^^^^^^^^^
Got String("") originating from:
lib/main.rb:7:
7 | return "";
ただし、Floatに対してIntegerみたいなものだと、実行時までわからないものもある。
$ srb tc
No errors! Great job.
$ ruby lib/main.rb
Traceback (most recent call last):
...
Expected type Float, got type Integer with value 1 (TypeError)
Caller: lib/main.rb:12
Definition: lib/main.rb:6
IDE Support
vscodeの場合はsorbet-lspを入れるだけでいい。
最もvscodeのプラグイン自体はまだ開発中らしく不安定だが、十分役に立ってくれる。