メソッドの型指定

Ruby のメソッドの引数や戻り値には型指定がないけど、それを指定できるような仕組みをつくってみました。

module Strict
  def strict(name, &block)
    @returns = Object
    @arguments = []
    block.call
    m = instance_method(name)
    ret_type, arg_type = @returns, @arguments
    define_method name do |*args|
      c = caller
      args.each_with_index do |arg, i|
        next unless arg_type[i]
        raise TypeError, "argument #{i+1}: #{arg.inspect} is not kind of #{arg_type[i]}", c unless arg_type[i] === arg
      end
      ret = m.bind(self).call(*args)
      raise TypeError, "return value #{ret.inspect} is not kind of #{ret_type}", c unless ret_type === ret
      ret
    end
  end
  def returns(type)
    @returns = type
  end
  def argument(type)
    @arguments.push type
  end
end

Strict モジュールを extend して、メソッドを定義した後、strict でメソッドの戻り値の型と引数の型を指定できます。

class Hoge
  extend Strict

  def hoge(a, b)
    return a
  end

  strict :hoge do
    returns Integer
    argument Integer
    argument String
  end
end

h = Hoge.new
h.hoge 123, "abc"   # => 123
h.hoge "123", "abc" # TypeError: argument 1: "123" is not kind of Integer

いまいち不格好ですけど、一応目的は達成できてるかな。

実際に使うかというと使わないような気がしますが、まあこんなこともできるという実験ということで。