LoginSignup
2
0

More than 3 years have passed since last update.

SorbetでRubyに型を付けてみた

Last updated at Posted at 2020-09-29
1 / 10

SorbetでRubyに型を付けてみた

sorbetというGemと遊んでみた。

Rubyに漸進的に型を付けるライブラリーだ。

自己紹介


アメリカ人で、日本は3年目。

1年強、東京のユニークビジョンで働いている。

きっかけ

実は、RubyもRailsも苦手!全部魔法的に見える。型で探検したい。

そしてsorbetでRailsにも型が定義されている。


「漸進的型付け」とは?

プログラミング言語の中で、「強い型付け」と「弱い型付け」言語が存在する。

「強い型付け」は例えば、型が合わないとコンパイルできない言語。

それに引き換え、「弱い型付け」言語は型が使えるが、別に頑張って合わせる必要がない。

また、その方が実行時(dynamic)、コンパイル時(static)にチェックされると言う軸もある。


1_BddwVWW6hFU0miT9DCbUWQ.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

まとめ/感想

いいところ

  1. sorbetはRubyの型チェックを開発時にしてくれる。
  2. 型を定義する仕組みはたくさんある。TypeScriptに近い!
  3. ツールがそろっているみたい(VSCode拡張機能など)。

どうかな、というところ

  1. 個人的に、型定義の構文が読みにくいと思う。
  2. 当然だが導入後は学習コストはある。

以上

ご清聴、ありがとうございました!

機会があれば、ぜひ使ってみてください。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0