I’ve become intrigued by the idea of unextending objects – that is,
removing capabilities from them module-wise.
A couple of things that have been mentioned here recently that relate
at least very indirectly to this are: the idea of a “clean” root class
for objects; and the discussion about inheritance (especially from
core classes) leaving an object with “extra” methods. In general, all
of these thoughts seem to pertain to the idea of a the granularity of
the modular, “plug-in” capabilities of Ruby objects.
Anyway, I don’t have any big idea in mind. Just playing around with
the idea of being able to add and remove modules at run-time. My
current little testbed for this looks like this:
Object#stretch(aModule) – extend only for duration of block
class Object
def stretch(mod)
c = class << self; self; end
before = methods
extend(mod)
yield
(methods - before).each { |m| c.class_eval {undef_method(m)} }
end
end
Demo
module Stuff
def talk; puts “hi”; end
end
a = [1,2,3]
a.stretch(Stuff) { a.talk } # hi
p a.respond_to?(:talk) # false
This has a kind of aesthetic or maybe narrative appeal to me: the
capabilities required of an object for a given purpose exist only at a
kind of flashpoint, the exact place and time where they’re needed, and
then they disappear again.
I’m interested in whether people have done things like this before,
whether it looks like the kind of thing one would ever actually use,
etc.
I’ve become intrigued by the idea of unextending objects – that is,
removing capabilities from them module-wise.
A couple of things that have been mentioned here recently that relate
at least very indirectly to this are: the idea of a “clean” root class
for objects; and the discussion about inheritance (especially from
core classes) leaving an object with “extra” methods. In general, all
of these thoughts seem to pertain to the idea of a the granularity of
the modular, “plug-in” capabilities of Ruby objects.
Anyway, I don’t have any big idea in mind. Just playing around with
the idea of being able to add and remove modules at run-time. My
current little testbed for this looks like this:
Object#stretch(aModule) – extend only for duration of block
class Object
def stretch(mod)
c = class << self; self; end
before = methods
extend(mod)
yield
(methods - before).each { |m| c.class_eval {undef_method(m)} }
end
end
Demo
module Stuff
def talk; puts “hi”; end
end
a = [1,2,3]
a.stretch(Stuff) { a.talk } # hi
p a.respond_to?(:talk) # false
This has a kind of aesthetic or maybe narrative appeal to me: the
capabilities required of an object for a given purpose exist only at a
kind of flashpoint, the exact place and time where they’re needed, and
then they disappear again.
I’m interested in whether people have done things like this before,
whether it looks like the kind of thing one would ever actually use,
etc.
I like this.
I’ve been thinking for a while that it would be nice to be able to
un-extend an object. A while back I was doing something where I was
passing an object around and extending it when it arrived somewhere, but I
wanted to un-extend it prior to passing it on the next receiver.
Phil
–
“Or perhaps the truth is less interesting than the facts?”
Amy Weiss (accusing theregister.co.uk of engaging in ‘tabloid journalism’)
Senior VP, Communications
Recording Industry Association of America
# Object#stretch(aModule) -- extend only for duration of block
class Object
def stretch(mod)
c = class << self; self; end
before = methods
extend(mod)
yield
(methods - before).each { |m| c.class_eval {undef_method(m)} }
end
end
Nice, very nice :-)))
pigeon% cat b.rb
#!/usr/bin/ruby
class Object
def stretch(mod)
c = class << self; self; end
before = methods
extend(mod)
yield
(methods - before).each { |m| c.class_eval {undef_method(m)} }
end
end
The idea of allowing just about everything, of assuming the programmer is
brilliant, is a neat idea.
</side_note>
In any case, you could create a subclass of the object’s class with whatever
functionality or mixins you wanted, then switch the object’s class to the
new class, run the block, and switch it back. Assuming this were part of
Ruby, it would allow us to do this without having to grab the singleton
class (which I think is what matz didn’t like, for some reason).
Actually, if the object didn’t already have a singleton class, you wouldn’t
have to create one… though you do have to create the new subclass, so I
don’t see what that buys you, except that you can delete a class.
Actually on a somewhat related idea, I wanted to have an object be able
to mixin an eval’ed set of methods. That way I could dynamically change
them by re-eval’ing changed behavior. Its for a software agent system I
am playing with…anyway…what I did was have a random module created
for my target “Agent” object which was empty of methods. I then eval’ed
the method definitions (String) into that module. Whenever the methods
definitions changed I could then remove all the methods on the module
cleanly and re-eval the defintions. It works really well, but not at
the granularity your describe below (during a method call).
I’ve become intrigued by the idea of unextending objects –
that is, removing capabilities from them module-wise.
A couple of things that have been mentioned here recently
that relate at least very indirectly to this are: the idea of
a “clean” root class for objects; and the discussion about
inheritance (especially from core classes) leaving an object
with “extra” methods. In general, all of these thoughts seem
to pertain to the idea of a the granularity of the modular,
“plug-in” capabilities of Ruby objects.
Anyway, I don’t have any big idea in mind. Just playing
around with the idea of being able to add and remove modules
at run-time. My current little testbed for this looks like this:
Object#stretch(aModule) – extend only for duration of block
class Object
def stretch(mod)
c = class << self; self; end
before = methods
extend(mod)
yield
(methods - before).each { |m| c.class_eval {undef_method(m)} }
end
end
Demo
module Stuff
def talk; puts “hi”; end
end
a = [1,2,3]
a.stretch(Stuff) { a.talk } # hi
p a.respond_to?(:talk) # false
This has a kind of aesthetic or maybe narrative appeal to me:
the capabilities required of an object for a given purpose
exist only at a kind of flashpoint, the exact place and time
where they’re needed, and then they disappear again.
I’m interested in whether people have done things like this
before, whether it looks like the kind of thing one would
ever actually use, etc.
class Object
def stretch(mod)
c = class << self; self; end
before = methods
cache = {}
mod.instance_methods.select {|m| respond_to?(m)}.each do |m|
alt = “#{id}#{m}”
c.send(:alias_method,alt,m)
cache[alt] = m
end
extend(mod)
yield
cache.each {|alt,m| c.send(:alias_method,m,alt)}
(methods - before).each { |m| c.class_eval {undef_method(m)} }
end
end
I’d rather do this by binding and unbinding methods… but that technique
doesn’t seem to fit exactly.
And there must be thread-related problems with this (aren’t there
always?
Indeed – very interesting. I admit I hadn’t scoured RAA, though I’m
not sure the names of those programs would have clued me in to the
similarity of at least a couple of them to what I’ve been hacking at.
class Object
def stretch(mod)
c = dup
c.extend(mod)
yield(c)
end
end
I thought about dup, and avoided it partly because it always seems to
raise issues about deep/shallow copy as well as just being
(potentially) expensive. In this case, also, the resulting semantics
have a different feel:
obj.stretch(mod) {|o| o.blah … }
rather than:
obj.stretch(mod) { obj.blah … }
The first one, to me, doesn’t convey the sense that this object is
being stretch’ed. And I think it would behave differently if, say,
there were multiple references to the object, and you expected all
references to be stretched.
Picking up on the block param thing, though: in the non-dup version,
perhaps ‘yield self’ would be a good idea, so one could chain them:
arr.stretch(ArrayExtras) {
arr.to_hash.stretch(HashExtras) { |h| p h.some_new_thing… }
}
The problem with this is that it's not thread safe and I don't really want
to debug a script which use #stretch in 2 differents threads because I
think that it will be difficult to find the error.
The first one, to me, doesn't convey the sense that this object is
being stretch'ed.
Not a problem for me : I just see a strange combination of the letters
's', 't', 'r', 'c', 'h' and 'e'