On Thu, 15 Jun 2006, Daniel S. wrote:
hi daniel-
I can of course only speak for myself, but I’m not fond of the idea of
reserving constant names when it’s really not needed – “class extension”,
as it seemingly has come to be named, should be handled internally, within
the module that defines such an extension. Even if the ClassMethods and
InstanceMethods modules are private, they would still clutter the namespace.
By using anonymous modules in instance variables you avoid collisions (not
completely of course.)
Furthermore, I feel that it’s redundant to have an entire “child” module for
the instance methods.
indeed. read the code though, it’s not needed - it’s just for symmtry.
Not that the implementation is bad at all; I just don’t think it’s
streamlined enough to make it to the core, although that obviously isn’t my
call to make.
here’s my complaint against any non-module based solution: it wildly
violates
POLS due to the change in scoping. i’m not saying it can’t be done, but
read
over these tests/demos carefully and you’ll see it’s not quite as
straightforward as you’re suggesting - espcially if you want ‘normal’
class
method definition semantics.
i think you may be able to work around some of these issues, but some
are part
of ruby.
harp:~ > ruby a.rb
recursive inclusion : meta_module
a.rb:140:in included': stack level too deep (SystemStackError) from a.rb:140:in
included’
from a.rb:9
from a.rb:197:in show' from a.rb:194:in
show’
from a.rb:4
recursive inclusion : mixable
success
double inclusion : meta_module
a.rb:140:in append_features': cyclic include detected (ArgumentError) from a.rb:140:in
included’
from a.rb:37
from a.rb:197:in show' from a.rb:194:in
show’
from a.rb:23
double inclusion : mixable
success
namespace pollution : meta_module
a.rb:73: N polluted! (RuntimeError)
from a.rb:197:in show' from a.rb:194:in
show’
from a.rb:60
namespace pollution : mixable
success
constant scoping : meta_module
a.rb:104:in const_get': uninitialized constant #<Module:0xb75ccc50>::C (NameError) from a.rb:104 from a.rb:131:in
meta_module’
from a.rb:102
from a.rb:197:in show' from a.rb:194:in
show’
from a.rb:100
constant scoping : mixable
success
harp:~ > cat a.rb
recursive inclusion
show(‘recursive inclusion’, :meta_module){
module M
meta_module{
def foo() :foo end
}
include M
end
}
show(‘recursive inclusion’, :mixable){
module M
module ClassMethods
def foo() :foo end
end
mixin M
end
}
double inclusion
show(‘double inclusion’, :meta_module){
module M
meta_module{
def foo() :foo end
}
end
module N
meta_module{
def bar() :bar end
}
include M
end
class C
include M
include N
end
}
show(‘double inclusion’, :mixable){
module M
module ClassMethods
def foo() :foo end
end
end
module N
module ClassMethods
def bar() :bar end
end
mixin M
end
class C
mixin M
mixin N
end
}
namespace pollution
show(‘namespace pollution’, :meta_module){
module N
meta_module{
def foo() :foo end
}
end
module M
include N
meta_module{
def bar() :foo end # defined in N!
}
end
if N.respond_to? ‘bar’
raise ‘N polluted!’
else
true
end
}
show(‘namespace pollution’, :mixable){
module N
module ClassMethods
def foo() :foo end
end
end
module M
mixin N
module ClassMethods
def bar() :foo end
end
end
if N.respond_to? ‘bar’
raise ‘N polluted!’
else
true
end
}
constant scoping
show(‘constant scoping’, :meta_module){
module N
meta_module{
C = true
const_get :C
}
end
true
}
show(‘constant scoping’, :mixable){
module N
module ClassMethods
C = true
const_get :C
end
end
true
}
BEGIN {
define two impls of class method mixin
META_MODULE_IMPL = lambda {
class Module
def meta_module(&block)
@meta_module ||= Module.new
@meta_module.module_eval(&block)
extend(@meta_module)
@meta_module
end
def included(mod)
mod.extend(@meta_module ||= Module.new)
if mod.kind_of? Module
if mod.instance_variables.include? “@meta_module”
other_meta_module =
mod.instance_variable_get(:@meta_module)
other_meta_module.send(:include, @meta_module)
else
mod.instance_variable_set(:@meta_module, @meta_module)
end
end
end
end
}
MIXABLE_IMPL = lambda {
module Mixable
Mixin = lambda do |this, other|
cm = this.const_get ‘ClassMethods’ rescue nil
im = this.const_get ‘InstanceMethods’ rescue nil
other.extend cm if cm
other.module_eval{
include im if im
extend RecursiveMixin
}
end
module RecursiveMixin
def included other
Mixin[self, other]
super
end
end
extend RecursiveMixin
end
class Object
def mixin other
sc =
class << self
self
end
sc.module_eval{ mixin other }
end
end
class Module
def mixin other
other.module_eval{ include Mixable } unless
Mixable > other
include other
end
end
}
demonstrate implications of two implimentations of class method mixin
def show label, which, &code
div = ‘_’ * 79
puts div
puts “#{ label } : #{ which }”
puts div
impl = Object.const_get(which.to_s.upcase << ‘_IMPL’)
fork {
STDOUT.sync = STDERR.sync = true
impl.call
ret = code.call
puts(ret ? ‘success’ : ‘failed’)
}
Process.wait
ensure
2.times{ puts }
end
}
regards.
-a