Ruby + Type = Rubype
既存のコードとの互換性を崩すことなく、選択的にメソッドに型保証を与えるGemです.
ここで指す'メソッドの型保証'とは実行時レベルでメソッドの引数返値のクラス型や反応すべきメソッドを保証する事です.
得られる一般的な型保証のメリット
Executableなドキュメントをコード内に付与出来る.
エラーがより意味のあるエラーになる.
Rubypeが確かであるならば引数返値チェックの処理は一切不要となる
この様な形で型情報付与するからこそ出来ること.
実行時に動的に型の付与変更、型情報の確認がする事も出来る.(型情報もオブジェクト)
クラス型のみではなく、反応すべきメソッドをシンボルで指定する事が可能
このGemのデメリット、至らぬところ
当該メソッドを呼び出す度に型チェックを行うのでオーバヘッドが生まれる.
(しかし多くの場合は気にされるほどではない)
実行前に行われるいわゆる型チェックによるメリットは享受出来ない.
選択的にメソッドの引数と返値のクラスを保証
型保証を与えないメソッドとの共存が可能で漸進的に型情報を付与していく事が可能です.
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型やシンボルを使えばダックタイピングも可能
class People
def marry(people)
# 何を返値にしても大丈夫です!
end
typesig :marry, [People] => Any
end
#to_i
に必ず反応する事を保証
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
明快!
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
型情報もオブジェクトなので動的に付与変更確認する事も出来る.
動的な型の付与や変更の意味は確かでないですが、実行中に型情報を確認出来ることはメリットだと思います.
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の使用者が使用すると想定されるメソッドを以下の4つ
Module.typesig
Method#type_info
Method#arg_types
Method#return_type
Module.typesig
型情報を付与する.
typesig(メソッド名, 型情報)
class MyClass
def typed_method(arg)
1
end
typesig :typed_method, [String] => Fixnum
end
型情報
の一般形は
[(第一引数の型), (第二引数の型) ... (第n引数の型)] => (返値の型)
として与えられる.
この文脈で型
とはオブジェクトのクラスまたは反応されるべきメソッドのどちらか.
同じメソッドに対してtypesig
が二度以上定義されたならば、より後に定義されたもののみを扱う.
Method#type_info
型情報をハッシュで返す
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
引数の型情報を配列で返す.
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
返値の型情報を返す.
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
クラス
エラーは
module Rubype
class ArgumentTypeError < ::TypeError; end
class ReturnTypeError < ::TypeError; end
end
の2つ
まだまだ始まったばかりなので
一緒にやってくれる人をガンガン募集しています!
みんなでワイワイとやりましょう〜!