Skip to content

Instantly share code, notes, and snippets.

@sawaken
Created December 23, 2014 15:57
Show Gist options
  • Save sawaken/9ea85db936de747ec12f to your computer and use it in GitHub Desktop.
Save sawaken/9ea85db936de747ec12f to your computer and use it in GitHub Desktop.
Rubyでレコード型っぽいデータ型を作るテスト
class RecordInstance
attr_reader :klass, :members
def initialize(klass, hash)
unless klass.members.all?{|name, type| hash.has_key?(name) && hash[name].is_a?(type)}
raise "Instance initialization error!!"
end
@klass = klass
@members = klass.members.keys.inject({}){|acc, name| acc.merge(name => hash[name])}
end
def is_like?(klass)
klass.members.all?{|name, type| @klass.members.has_key?(name) && @klass.members[name] == type}
end
def alt(alternative)
RecordInstance.new(@klass, @members.merge(alternative))
end
def method_missing(message, *args, &block)
if args.length == 0 && @members.has_key?(message)
return @members[message]
end
super
end
end
class RecordClass
attr_reader :members
def initialize(hash)
unless hash.values.all?{|type| type.instance_of?(Class)}
raise "Invalid specification of member's Type!!"
end
@members = hash
end
def +(other)
unless @members.all?{|name, type| !other.members[name] || other.members[name] == type}
raise "Conflict merging!!"
end
return RecordClass.new(other.members.merge(@members))
end
def new(hash)
return RecordInstance.new(self, hash)
end
end
def Record(hash)
RecordClass.new(hash)
end
def Def(name, signature, &definition)
arg_type = signature.keys.first
ret_type = signature.values.first
define_method(name) do |arg|
unless arg.instance_of?(RecordInstance) && arg.is_like?(arg_type)
p arg, arg_type
raise "Apply Error: cannot apply #{name} to #{arg}!!"
end
res = definition.call(arg)
unless res.instance_of?(RecordInstance) && res.is_like?(ret_type)
raise "Apply Error: evaluated value is invalid!!"
end
break res
end
end
#-----------------------------------------------------------------------------------------
Animal = Record(age: Integer, sex: Symbol)
Human = Animal + Record(name: String)
Something = Record(age: Integer)
me = Human.new(name: "GOMIKUZU", age: 12, sex: :male)
Def :aging, Something => Something do |r|
r.alt(age: r.age * 2)
end
p aging(me)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment