Created
January 14, 2014 03:08
-
-
Save booch/8412360 to your computer and use it in GitHub Desktop.
What if we could check type signatures in Ruby? A proof of concept.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Allow multiple classes to be declared via A|B instead of [A, B]. | |
class Class | |
def |(*other_classes) | |
SetOfClasses.new(self, other_classes) | |
end | |
end | |
require 'set' | |
class SetOfClasses | |
attr_reader :classes | |
def initialize(*classes) | |
@classes = Set.new(classes.flatten) | |
end | |
def |(*other_classes) | |
SetOfClasses.new([self, *other_classes]) | |
end | |
def none?(&block) | |
classes.none?(&block) | |
end | |
end | |
class Class | |
def signature(*arg_types) | |
@method_signature = arg_types | |
end | |
def method_added(method_name) | |
puts 'method_added' | |
if @method_signature | |
method_signature = @method_signature | |
@method_signature = nil | |
original_method_name = "#{method_name}_without_type_checking" | |
self.send(:alias_method, "#{original_method_name}", "#{method_name}") | |
define_method("#{method_name}".to_sym) do |*args, &block| | |
check_method_signature(method_signature, args) | |
self.send(original_method_name, *args, &block) | |
end | |
end | |
end | |
end | |
class Object | |
def check_method_signature(arg_types, args) | |
puts "Checking signature: #{arg_types}: #{args}" | |
arg_types.each_with_index do |arg_type, index| | |
var = args[index] | |
if arg_type.kind_of?(SetOfClasses) | |
if arg_type.none?{|type| var.kind_of?(type)} | |
raise ArgumentError.new("Argument '#{var}' must be one of the types #{arg_type}") | |
end | |
elsif arg_type.kind_of?(Class) | |
unless var.kind_of?(arg_type) | |
raise ArgumentError.new("Argument '#{var}' must be of type #{arg_type}") | |
end | |
else | |
raise ArgumentError.new("Arguments to signature() must be classes.") | |
end | |
end | |
end | |
end | |
class Xyz | |
signature(Integer|Fixnum, String, Symbol) | |
def xyz(a, b, c) | |
puts 'xyz:' | |
puts a.inspect | |
puts b.inspect | |
puts c.inspect | |
end | |
end | |
Xyz.new.xyz(1,'b',:c) # Should work. | |
Xyz.new.xyz('1','b',:c) # Should raise an exception. |
This is kind of cool, but kind of makes me sick.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
No idea how this ended up with 4-space tabbing.