SorbetでRubyに型を付けてみた
sorbet
というGemと遊んでみた。
Rubyに漸進的に型を付けるライブラリーだ。
自己紹介
アメリカ人で、日本は3年目。1年強、東京のユニークビジョンで働いている。
きっかけ
実は、RubyもRailsも苦手!全部魔法的に見える。型で探検したい。
そしてsorbet
でRailsにも型が定義されている。
「漸進的型付け」とは?
プログラミング言語の中で、「強い型付け」と「弱い型付け」言語が存在する。
「強い型付け」は例えば、型が合わないとコンパイルできない言語。
それに引き換え、「弱い型付け」言語は型が使えるが、別に頑張って合わせる必要がない。
また、その方が実行時(dynamic)、コンパイル時(static)にチェックされると言う軸もある。
![1_BddwVWW6hFU0miT9DCbUWQ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457797/31616832-fc68-3c64-43b6-1a3b1f064e7f.png)要するに、漸進的型付けとは、プロジェクトを部分的に「static」と「strong」の右上の隅に引っ張っていくことだ。
普段は関数単位あるいはファイル単位で行われていると思う。
sorbet
はRubyの型を強くしない。代わりに、ファイル単位での型チェックをコンパイル時に移動する。
sorbet
の例(1)
sig do
params(
name: String
)
.returns(String)
end
def get_welcome_message(name)
"Welcome, #{name}!"
end
bundle exec srb tc
No errors! Great job.
sorbet
の例(2)
sig do
params(
name: String
)
.returns(String)
end
def get_welcome_message(name)
43110
end
bundle exec srb tc
lib/main.rb:14: Returning value that does not conform to method result type https://srb.help/7005
14 | 43110
^^^^^
Expected String
lib/main.rb:13: Method `get_welcome_message` has return type `String`
13 | def get_welcome_message(name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Got Integer(43110) originating from:
lib/main.rb:14:
14 | 43110
^^^^^
Errors: 1
複雑な型
関数に型を付ける以外にも機能はたくさんある。例えば、nil
の場合:
sig {params(x: T.nilable(String)).void}
def foo(x)
if x
puts "x was not nil! Got: #{x}"
else
puts "x was nil"
end
end
# without this T.let, x would have the inferred type NilClass
x = T.let(nil, T.nilable(Integer))
(0..10).each do |n|
x = n if n % 3 == 0
end
ユニオン型
class A
extend T::Sig
sig {params(x: T.any(Integer,String)).void}
def self.foo(x); end
end
# 10 and "Hello, world" both have type `T.any(Integer, String)`
A.foo(10)
A.foo("Hello, world")
# error: Expected `T.any(Integer, String)` but found `TrueClass`
A.foo(true)
flow typing
sig {params(x: T.nilable(String), default: String).returns(String)}
def maybe(x, default)
# (1) Outside the if, x is either nil or a String
T.reveal_type(x) # => Revealed type: `T.nilable(String)`
if x
# (2) At this point, Sorbet knows `x` is not nil
T.reveal_type(x) # => Revealed type: `String`
x
else
# (3) In the else branch, Sorbet knows `x` must be nil
T.reveal_type(x) # => Revealed type: `NilClass`
default
end
end
まとめ/感想
いいところ
-
sorbet
はRubyの型チェックを開発時にしてくれる。 - 型を定義する仕組みはたくさんある。TypeScriptに近い!
- ツールがそろっているみたい(VSCode拡張機能など)。
どうかな、というところ
- 個人的に、型定義の構文が読みにくいと思う。
- 当然だが導入後は学習コストはある。
以上
ご清聴、ありがとうございました!
機会があれば、ぜひ使ってみてください。