finding one's self in the strangest places

T

Trans

Hello,

Thought this was interesting for a numbner of reasons, not the least of
which is that I am suprised it even works. What it is is whay to do
dynamic module mixins, i.e. including modules with macro-like changes
in behavior. Note that the first three lines simply add those well
known methods to the Binding class.

require 'nano/binding/self'
require 'nano/binding/%5B%5D%3D' # []=
require 'nano/binding/local_variables'

class Module

def module_options
@module_options ||= {}
end

alias_method :include_without_options, :include

def include(*args)
base = self
options = args.last.is_a?(Hash) ? args.pop : {}
mods = args.collect do |mod|
mod.append_dynamic_features( self, options )
mod
end
include_without_options(*mods)
end

# Note: Is this the best name for this callback?
def append_dynamic_features( base, options )
mod = self
base.module_options[mod] = options
end

# vars is just a dummy slot, does nothing
def dynamic_mixin( space, *vars )
mod = space.self
space.local_variables.each { |v|
default = space[v.to_sym]
space[v.to_sym] = lambda { |base|
base.class.module_options[mod][v.to_sym] || default }
}
end

end


# try it

if $0 == __FILE__

module MyDynamix

dynamic_mixin( binding, name='Tom' ) # name is local var

define_method( :hello ) {
puts "Hello from #{name[self]}"
}

define_method( :goodbye ) {
puts "Goodbye from #{name[self]}"
}

end

class MyClass
include MyDynamix, :name => 'George'
end

m = MyClass.new
m.hello #=> Hello from George
m.goodbye #=> Hello from George

class MyClass2
include MyDynamix
end

m = MyClass2.new
m.hello #=> Hello from Tom
m.goodbye #=> Hello from Tom

end

What amazes me about this is that 'name' in 'name[self]' is a local
variable assigned to a lambda, yet 'self' is not and rather belongs to
the initialized instance of the class. How is that possible? How is
execution of the lambda deferred til later? I think this is a
facinating example of Ruby's "juggling" abilities.

Also, it would be nice to be able to take this one step further, if it
were possible NOT to have to explictly pass 'binding' via
#dyanmic_mixin or pass 'self' via the local var lambda, then it would
be perfect.

T.
 
R

Robert Klemme

Trans said:
What amazes me about this is that 'name' in 'name[self]' is a local
variable assigned to a lambda, yet 'self' is not and rather belongs to
the initialized instance of the class. How is that possible? How is
execution of the lambda deferred til later? I think this is a
facinating example of Ruby's "juggling" abilities.

The concept is generally known as "closure". The closure captures variable
bindings so you can access them later:

12:53:16 [source]: ruby -e 'x=10;f=lambda { x }; p f.call;x=20;p f.call'
10
20
12:53:23 [source]:
Also, it would be nice to be able to take this one step further, if it
were possible NOT to have to explictly pass 'binding' via
#dyanmic_mixin or pass 'self' via the local var lambda, then it would
be perfect.

There is a module that allows access to the bindinf of the caller. This
would help you as it would make passing a binding explicit unnecessary.

Kind regards

robert
 
T

Trans

Robert said:
The concept is generally known as "closure". The closure captures variable
bindings so you can access them later:

Right. In this case it just seems more facinating in how the two
closure-spaces (I don't know the proper terminology) are intermingling.
12:53:16 [source]: ruby -e 'x=10;f=lambda { x }; p f.call;x=20;p f.call'
10
20
12:53:23 [source]:
Also, it would be nice to be able to take this one step further, if it
were possible NOT to have to explictly pass 'binding' via
#dyanmic_mixin or pass 'self' via the local var lambda, then it would
be perfect.

There is a module that allows access to the binding of the caller. This
would help you as it would make passing a binding explicit unnecessary.

Thanks. Binding.of_caller is of some, but limited use. Not sure how to
rid '[self]' with it. In playing with this I again find another of
these methods whose "receiver" is actually the point-of-execution, in
this case #binding. (I was recently talking Module.nesting which is one
too). Yet with #binding, it sure would be nice if it weren't so. Then
Binding.of_caller wouldn't be needed, and one could just do
binding_of_caller = mod.binding.

T.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,900
Latest member
Nell636132

Latest Threads

Top