Rubypeを使ってRubyで型のメリットを享受する

# Ruby + Type = Rubype

既存のコードとの互換性を崩すことなく、選択的にメソッドに型保証を与えるGemです.

![210414.png](https://qiita-image-store.s3.amazonaws.com/0/30440/66e47595-ddd1-d18a-1d59-2a5f16801bcd.png)


[Github](https://github.com/gogotanaka/Rubype)
[HP](http://gogotanaka.github.io/rubype.github.io/)
[HackerNews](https://news.ycombinator.com/item?id=9345452)

ここで指す'メソッドの型保証'とは実行時レベルでメソッドの引数返値のクラス型や反応すべきメソッドを保証する事です.

### 得られる一般的な型保証のメリット
<i class="fa fa-check"></i> Executableなドキュメントをコード内に付与出来る.
<i class="fa fa-check"></i> エラーがより意味のあるエラーになる.
<i class="fa fa-check"></i> Rubypeが確かであるならば引数返値チェックの処理は一切不要となる

### この様な形で型情報付与するからこそ出来ること.
<i class="fa fa-check"></i> 実行時に動的に型の付与変更、型情報の確認がする事も出来る.（型情報もオブジェクト）
<i class="fa fa-check"></i> クラス型のみではなく、反応すべきメソッドをシンボルで指定する事が可能

### このGemのデメリット、至らぬところ
<i class="fa fa-times"></i> 当該メソッドを呼び出す度に型チェックを行うのでオーバヘッドが生まれる.
（しかし多くの場合は気にされるほどではない）

<i class="fa fa-times"></i> 実行前に行われるいわゆる型チェックによるメリットは享受出来ない.

## 選択的にメソッドの引数と返値のクラスを保証

型保証を与えないメソッドとの共存が可能で漸進的に型情報を付与していく事が可能です.

```rb
require 'rubype'

class MyClass
  # `sum`が2つの`Numeric`オブジェクトを取り、`String`オブジェクトを返す事を保証する
  def sum(x, y)
    (x + y).to_s
  end
  typesig :sum, [Numeric, Numeric] => String
end

MyClass.new.method(:sum).type_info
#=> [Numeric, Numeric] => String

MyClass.new.new(1, 2)
#=> '3'

# 
MyClass.new.sum(1, '2')
#=> Rubype::ArgumentTypeError: Expected MyClass#sum's 2nd argument to be Numeric but got "2" instead
```

## Any型やシンボルを使えばダックタイピングも可能

```rb
class People
  def marry(people)
    # 何を返値にしても大丈夫です！
  end
  typesig :marry, [People] => Any
end
```

##  `#to_i` に必ず反応する事を保証

```rb
class MyClass
  def sum(x, y)
    x.to_i + y
  end
  typesig :sum, [:to_i, Numeric] => Numeric
end

MyClass.new.sum(:has_no_to_i, 2)
#=> Rubype::ArgumentTypeError: Expected MyClass#sum's 1th argument to have method #to_i but got :has_no_to_i instead
```

## 明快！

```rb
class People
  def marry?(people)
    'not boolean'
  end
  typesig :marry?, [People] => Boolean
end

People.new.marry?(People.new)
#=> Rubype::ReturnTypeError: Expected People#marry? to return Boolean but got "not boolean" instead
```


## 型情報もオブジェクトなので動的に付与変更確認する事も出来る.

動的な型の付与や変更の意味は確かでないですが、実行中に型情報を確認出来ることはメリットだと思います.

```rb
require 'rubype'

class MyClass
  def string?(x)
    x.is_a?(String)
  end
  typesig :string?, [Any] => Boolean
end

typed_method = MyClass.new.method(:string?)

if typed_method.return_type == Boolean
  # メソッドの型情報をコードに組み込む事ができる.
  ...
end
```

# ドキュメント

Gemの使用者が使用すると想定されるメソッドを以下の４つ

`Module.typesig`

`Method#type_info`

`Method#arg_types`

`Method#return_type`

# Module.typesig

型情報を付与する.
`typesig(メソッド名, 型情報)`

```rb
class MyClass
  def typed_method(arg)
    1
  end
  typesig :typed_method, [String] => Fixnum
end
```

`型情報`の一般形は

`[(第一引数の型), (第二引数の型) ... (第n引数の型)] => (返値の型)`

として与えられる.

この文脈で`型`とはオブジェクトのクラスまたは反応されるべきメソッドのどちらか.

同じメソッドに対して`typesig`が二度以上定義されたならば、より後に定義されたもののみを扱う.

# Method#type_info

型情報をハッシュで返す

```rb
class MyClass
  def typed_method(arg)
    arg.to_i
  end
  typesig :typed_method, [:to_i] => Fixnum
end

MyClass.new.method(:typed_method).type_info
#=> {[:to_i]=>Fixnum}
```

# Method#arg_types

引数の型情報を配列で返す.

```rb
class MyClass
  def typed_method(arg)
    arg.to_i
  end
  typesig :typed_method, [:to_i] => Fixnum
end

MyClass.new.method(:typed_method).arg_types
#=> [:to_i]
```


# Method#return_type


返値の型情報を返す.

```rb
class MyClass
  def typed_method(arg)
    arg.to_i
  end
  typesig :typed_method, [:to_i] => Fixnum
end

MyClass.new.method(:typed_method).return_type
#=> Fixnum
```

Rubypeが定義する追加クラスとしては

* どんなオブジェクトに対しても型情報としてマッチする`Any`クラス
* TrueClassとFalseClassのオブジェクトに型情報としてマッチする`Boolean`クラス

エラーは

```rb
module Rubype
  class ArgumentTypeError < ::TypeError; end
  class ReturnTypeError   < ::TypeError; end
end
```

の２つ

# まだまだ始まったばかりなので

一緒にやってくれる人をガンガン募集しています！

みんなでワイワイとやりましょう〜！

https://github.com/gogotanaka/Rubype

![210414.png](https://qiita-image-store.s3.amazonaws.com/0/30440/0aafba03-1a4c-4676-5377-75f906aaeab9.png)