-
-
Save peterc/3026651 to your computer and use it in GitHub Desktop.
# encoding: utf-8 | |
# This is a very early scrappy step in building an automatic memoizer (think IncPy) | |
# in pure Ruby. Is it possible? Who knows. A first step, then, is to rule out any | |
# methods that mutate their parameters.. which I attempt to do here. | |
# Added: | |
# Added clever define_method and 'store the old method in a closure' trickery | |
# from Caius Durling's fork at https://gist.github.com/3027213/f900b1ec8e04736267fe445607a4aeb3feea9b54 | |
# Added: | |
# Now using Object#hash rather than a deep copied version of the arguments | |
module AutoMemo | |
CaughtMutation = Class.new(StandardError) | |
def monitor(meth) | |
unbound_method = instance_method(meth) | |
define_method(meth) do |*args, &blk| | |
old_hash = args.hash | |
unbound_method.bind(self).call(*args, &blk).tap do | |
btrace = (unbound_method.source_location + ["in `#{meth}'"]) * ':' | |
raise CaughtMutation, nil, btrace if args.hash != old_hash | |
end | |
end | |
end | |
def method_added(meth_name) | |
return false if self == Class || (@meths ||= {})[meth_name] | |
@meths[meth_name] = true | |
monitor(meth_name) | |
end | |
end | |
Class.send :include, AutoMemo | |
# --- the above would all be in a library/separate file that you required in first | |
# --- the below is 'normal' application code | |
class Adder | |
def add(a, b) | |
a.push("oh dear") # comment this out to 'fix' the problem | |
a + b | |
end | |
end | |
a = Adder.new | |
p a.add(%w{a b c}, %w{d e f}) |
Comment on your cleverness over at that gist - thanks! Implemented its main ideas into this as well as a new one of my own (using Object#hash instead of a deep copy).
๐
You can lose the check in method_added
to see if the method name starts with __
as well now - as you're no longer defining __#{original_method_name}
as the over-ridden method. Otherwise you're accidentally ignoring any methods anyone names as __*
for other reasons - which may not be intentional?
Since this is just playing round / prototyping, I might. That said, in most cases I think __ methods are "special" enough to probably not be suitable for memoization (or memoization of them would cause confusion). I'll drop it for now though as it's not too important and having cleaner code is a bonus.
Ultimately, raising an exception is also not required - instead, it should just mark the method as not to be memoized.. but handy to see while fleshing out the idea for now.
Had a go at writing a version that doesn't use class_eval or alias_method - https://gist.github.com/3027213 ๐