Ruby でも型チェック

動的型付け (スクリプト) 言語では、データ型のチェックが実行時にしか行われないため、プログラムの妥当性検証・デバッグといった作業が困難になります。
例えば、Ruby でプログラムを書いていて、次のようなバグに悩まされたことのある人は多いのではないでしょうか。

  • Integer オブジェクトを参照しているべき変数が、他の型のオブジェクトを参照している。
  • そのオブジェクトが「いつ」「どこで」代入されたものなのか分からない。

この手のバグは、問題の発生 (不正な型の代入) と発覚 (エラーの発生) の位置が離れてしまうので、非常に厄介。 発生箇所を絞り込むのが難しいため、プログラムを広範囲に渡って見直すハメになります。

check_type の導入

こうしたバグへの対処を容易にするため、私はメソッドの冒頭で引数の型をチェックするようにしています。

student.rb
require 'date'
require 'check_type'
# 「学生」クラス
class Student
attr :id
attr :fname
attr :lname
attr :birthday
# 初期化
def initialize(id, lname, fname, birthday)
@id       =check_type(Integer, id,       false);
@lname    =check_type(String,  lname,    false);
@fname    =check_type(String,  fname,    false);
@birthday =check_type(Date,    birthday, false);
end
end # class Stundent
check_type.rb
# 型チェック
def check_type(type, instance, nullable =false)
if (instance.nil?)
unless (nullable)
raise ArgumentError.new("non-nil constraint vioration")
end
else
unless (instance.kind_of?(type))
raise(ArgumentError::new("type mismatch: #{instance.class} for #{type}"))
end
end
return instance
end

メソッドに不正な型の引数を渡すと、ArgumentError が発生します。

test.rb
require 'student'
s =Student::new(200, 'Narita', 'Sho', '1981/07/05')
% student.rb:20:in `check_type': type mismatch: String for Date (ArgumentError)
from student:13:in `initialize'
from test.rb:3:in `new'

また、check_type を各メソッドの冒頭に配置することで、そのメソッドがどんな型の引数をとるのか明示することにもなるため、オススメです。

担当: 成田 (型チェックの鬼)