Created
July 1, 2012 03:15
-
-
Save peterc/3026651 to your computer and use it in GitHub Desktop.
Argument mutation detection in Ruby (rough experiment)
This file contains hidden or 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
# 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}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.