From: Intransition on
Sorry about the delay... you know how it is!

On Jun 25, 8:30 am, Robert Klemme <shortcut...(a)googlemail.com> wrote:
> I always thought the proper idiom was
>
> # lib/foo.rb
>
> class Foo
>  ...
> end
>
> # optionally:
> require 'foo/baz'
>
> # lib/foo/baz.rb
>
> require 'foo'
>
> class Foo::Baz
>  ...
> end

Not always feasible, especially if you are loading a whole directory
of "pluggable" files. In which case you either bottom require of use
a separate file. I just as soon not have to worry about it either way
and if class and module where the same type of object then we wouldn't
have to.

> Alternative:
>
> # lib/foo.rb
>
> class Foo
>  ...
>   autoload :Baz, 'foo/baz'
> end

I do not ever use autoload, as much as I might like to. It has a known
bug --it will not use a customized require. I used a customized
require in my development environment, so it is useless to me. (and
really the one thing that pisses the most... but thats' another story)

> # lib/foo/baz.rb
> (as above)
>
> >> > 2) The developer is forced to choose between two arbitrarily limited
> >> > mechanisms for modeling behavior. Classes can only re-used once per
> >> > subclass and not at all via modules. Modules can be reused extensively
> >> > but do not naturally include their singleton methods. These
> >> > limitations lead to a number of code smells from overly limited use of
> >> > inheritance to included/ClassMethods hooks.
>
> >> Why is not including their singleton methods a strong limitation?
>
> > Well it is strong.
>
> That's a strange answer to my question...
>
> Let my first try to understand what you mean.  If I get you properly you want
>
> module A
>   def self.wok
>     puts "I work for the class"
>   end
>
>   def foo
>     puts "happy instance"
>     self.class.wok
>   end
> end
>
> class X
>   include A
> end
>
> X.new.foo ->
> "happy instance"
> "I work for the class"
>
> I personally would write
>
>   def foo
>     puts "happy instance"
>     A.wok
>   end
>
> and be done so I suspect you mean something else.

Well that's one thing. But look what happens:

class Y < X
def self.wok
"ain't going to happen"
end
end

You've strapped the implementation to a specific module, so there is
no way to override behavior in a subclass (short of overriding every
method that uses #wok).

> > If it were the other way around, and the singleton methods
> > were passed along, but you didn't want them to be... well I would like
> > to know under what circumstances that it would really be a problem. If
> > those singleton methods were simply being used as functions (M.f) then
> > they can easily be placed in a separate module. If they were being
> > used as a DSL for the module itself, why would the including class be
> > any worse off by gaining the DSL too?  Maybe there is some reason one
> > can find, but even so it seems to me that it must be the rarity.
>
> If I get your drift you want to use a single module to define a DSL
> and apply it at the same time, e.g.
>
> module DSL
>   def self.smart_attribute(sym)
>     class_eval "def #{sym}; puts 'Look Ma, how smart I am!'; @#{sym}; end"
>   end
>
>   smart_attribute :name
> end
>
> and then
>
> class Foo
>   include DSL
>
>   smart_attribute :age
> end
>
> f = Foo.new
> f.name
> f.age
>
> I personally would separate the DSL out into another module because
> that is more modular.  Not all classes might need smart attribute
> "name" so this would be a better choice IMHO.

Well, you could do that. But that's not really the main issue.
Probably the most significant issue is with super:

module M
def self.x; "M"; end
end

class X
include M
def self.x
super
end
end

You get an error. If it wasn't for this class inheritable attributes
would be completely trivial to implement.

Another more clear case is where the DSL defines a method that uses
another method the instance level as an overridable piece of
functionality.

module DSL
def self.smart_attribute(sym)
class_eval "def #{sym}; puts message + ' ' + @#{sym}.to_s; end"
end

# Default message is 'Look Ma, a smart:'.
def message
'Look Ma, a smart:'
end
end

Not being able to do this, you have to do more difficult meta-
programming tricks to achieve the same result.

> > all my use of modules I can't think of case where having the singleton
> > level would have been a problem and in many, if not most, I actually
> > wanted it and had to resort to some hoop jumping.
>
> > In fact, and I think this is worth pointing out... I never ever design
> > a module to be utilized via #extend. In such usecases I actually do
> > this:
>
> >  module M
> >    def self.append_features(base)
> >      base.extend self
> >    end
> >  end
>
> > Of course, rather than fuss about it, we could also have the best of
> > both worlds (imagine that).  I, for one, would be happy with a new
> > method to go along with #include and #extend that provided both
> > singleton and instance levels.
>
> I believe automatically propagating class methods might cause
> confusion because it is not obvious. At least I haven't had the need
> for this (which might be due to the different tasks we tackle with
> Ruby).

To me, not propagating them is "not obvious" --anything that creates
more work for me, when the alternative has no significant issues,
seems "not obvious" ;)

> >> > 3) Using tools for conceptually different cases does not require that
> >> > their be physically different tools for each case. I wrench is just as
> >> > good for tightening a nut as it is for loosening one. If I had to use
> >> > two separate tools it would be quite annoying.
>
> >> I believe this is a bad analogy.  If at all you would have to take
> >> "tightening and loosing nuts" and "hammering".  In that case it's
> >> quite obvious that you would not normally use the same tool for the
> >> job.

> I believe this points into the right direction.  I'm not doing as much
> meta programming - probably because I am not involved in creating
> frameworks in Ruby.  I rather use the language to solve day to day
> problems.  So far my need for DSL's has been scarce.  So where were
> you really hurt by not automatically propagating class methods?  I
> can't think of a case but obviously you have one in mind.

Look at Anise, look at Facets Inheritor. And wherever you find the
ClassMethods hack, that is a case, and there are many of those around.

From: Robert Klemme on
On 27.06.2010 21:26, Intransition wrote:
> Sorry about the delay... you know how it is!

No problem.

> On Jun 25, 8:30 am, Robert Klemme<shortcut...(a)googlemail.com> wrote:
>> I always thought the proper idiom was
>>
>> # lib/foo.rb
>>
>> class Foo
>> ...
>> end
>>
>> # optionally:
>> require 'foo/baz'
>>
>> # lib/foo/baz.rb
>>
>> require 'foo'
>>
>> class Foo::Baz
>> ...
>> end
>
> Not always feasible, especially if you are loading a whole directory
> of "pluggable" files. In which case you either bottom require of use
> a separate file. I just as soon not have to worry about it either way
> and if class and module where the same type of object then we wouldn't
> have to.

Hmm, personally I would prefer loading those pluggable files explicitly
- even if via a special method that only passes a directory name.
Library code typically resides in a different location than user
extensions so for me this makes more sense. In that case I would do

class Foo
def self.load_plugins(dir_or_file)
...
end
end

and then

require 'foo'

Foo.load_plugins ENV["HOME"] + "/foo_extension"

You could even make it more implicit by defaulting to an environment
variable.

class Foo
def self.load_plugins(dir_or_file = ENV['FOO_PLUGINS'])
...
end
end

>> Alternative:
>>
>> # lib/foo.rb
>>
>> class Foo
>> ...
>> autoload :Baz, 'foo/baz'
>> end
>
> I do not ever use autoload, as much as I might like to. It has a known
> bug --it will not use a customized require. I used a customized
> require in my development environment, so it is useless to me. (and
> really the one thing that pisses the most... but thats' another story)

Indeed, that's a downside. OTOH, I'd rather opt for fixing that then
neglecting autoload. It is apparent: I don't do gem development and I
rarely use gems. :-)

>> # lib/foo/baz.rb
>> (as above)
>>
>>>>> 2) The developer is forced to choose between two arbitrarily limited
>>>>> mechanisms for modeling behavior. Classes can only re-used once per
>>>>> subclass and not at all via modules. Modules can be reused extensively
>>>>> but do not naturally include their singleton methods. These
>>>>> limitations lead to a number of code smells from overly limited use of
>>>>> inheritance to included/ClassMethods hooks.
>>
>>>> Why is not including their singleton methods a strong limitation?
>>
>>> Well it is strong.
>>
>> That's a strange answer to my question...
>>
>> Let my first try to understand what you mean. If I get you properly you want
>>
>> module A
>> def self.wok
>> puts "I work for the class"
>> end
>>
>> def foo
>> puts "happy instance"
>> self.class.wok
>> end
>> end
>>
>> class X
>> include A
>> end
>>
>> X.new.foo ->
>> "happy instance"
>> "I work for the class"
>>
>> I personally would write
>>
>> def foo
>> puts "happy instance"
>> A.wok
>> end
>>
>> and be done so I suspect you mean something else.
>
> Well that's one thing. But look what happens:
>
> class Y< X
> def self.wok
> "ain't going to happen"
> end
> end
>
> You've strapped the implementation to a specific module, so there is
> no way to override behavior in a subclass (short of overriding every
> method that uses #wok).

Hmm, I see your point. I am wondering though whether that might not be
too much magic.

>>> If it were the other way around, and the singleton methods
>>> were passed along, but you didn't want them to be... well I would like
>>> to know under what circumstances that it would really be a problem. If
>>> those singleton methods were simply being used as functions (M.f) then
>>> they can easily be placed in a separate module. If they were being
>>> used as a DSL for the module itself, why would the including class be
>>> any worse off by gaining the DSL too? Maybe there is some reason one
>>> can find, but even so it seems to me that it must be the rarity.
>>
>> If I get your drift you want to use a single module to define a DSL
>> and apply it at the same time, e.g.
>>
>> module DSL
>> def self.smart_attribute(sym)
>> class_eval "def #{sym}; puts 'Look Ma, how smart I am!'; @#{sym}; end"
>> end
>>
>> smart_attribute :name
>> end
>>
>> and then
>>
>> class Foo
>> include DSL
>>
>> smart_attribute :age
>> end
>>
>> f = Foo.new
>> f.name
>> f.age
>>
>> I personally would separate the DSL out into another module because
>> that is more modular. Not all classes might need smart attribute
>> "name" so this would be a better choice IMHO.
>
> Well, you could do that. But that's not really the main issue.
> Probably the most significant issue is with super:
>
> module M
> def self.x; "M"; end
> end
>
> class X
> include M
> def self.x
> super
> end
> end
>
> You get an error. If it wasn't for this class inheritable attributes
> would be completely trivial to implement.

Ah, I see! It seems for the class level methods one needs a second
module which is automatically used to extend a class that includes the
visible module. I agree, that's a bit clumsy.


module X
module Xcl
def bar
puts "X::bar"
end
end

def self.included cl
cl.extend Xcl
end

def foo
puts "foo"
self.class.bar
end
end

class A
include X

def self.bar
puts "A::bar"
super
end

end

irb(main):042:0* A.new.foo
foo
A::bar
X::bar
=> nil

But not really hard.

> Another more clear case is where the DSL defines a method that uses
> another method the instance level as an overridable piece of
> functionality.
>
> module DSL
> def self.smart_attribute(sym)
> class_eval "def #{sym}; puts message + ' ' + @#{sym}.to_s; end"
> end
>
> # Default message is 'Look Ma, a smart:'.
> def message
> 'Look Ma, a smart:'
> end
> end
>
> Not being able to do this, you have to do more difficult meta-
> programming tricks to achieve the same result.
>
>>> all my use of modules I can't think of case where having the singleton
>>> level would have been a problem and in many, if not most, I actually
>>> wanted it and had to resort to some hoop jumping.
>>
>>> In fact, and I think this is worth pointing out... I never ever design
>>> a module to be utilized via #extend. In such usecases I actually do
>>> this:
>>
>>> module M
>>> def self.append_features(base)
>>> base.extend self
>>> end
>>> end
>>
>>> Of course, rather than fuss about it, we could also have the best of
>>> both worlds (imagine that). I, for one, would be happy with a new
>>> method to go along with #include and #extend that provided both
>>> singleton and instance levels.
>>
>> I believe automatically propagating class methods might cause
>> confusion because it is not obvious. At least I haven't had the need
>> for this (which might be due to the different tasks we tackle with
>> Ruby).
>
> To me, not propagating them is "not obvious" --anything that creates
> more work for me, when the alternative has no significant issues,
> seems "not obvious" ;)

:-) I think we agree to disagree here.

>>>>> 3) Using tools for conceptually different cases does not require that
>>>>> their be physically different tools for each case. I wrench is just as
>>>>> good for tightening a nut as it is for loosening one. If I had to use
>>>>> two separate tools it would be quite annoying.
>>
>>>> I believe this is a bad analogy. If at all you would have to take
>>>> "tightening and loosing nuts" and "hammering". In that case it's
>>>> quite obvious that you would not normally use the same tool for the
>>>> job.
>
>> I believe this points into the right direction. I'm not doing as much
>> meta programming - probably because I am not involved in creating
>> frameworks in Ruby. I rather use the language to solve day to day
>> problems. So far my need for DSL's has been scarce. So where were
>> you really hurt by not automatically propagating class methods? I
>> can't think of a case but obviously you have one in mind.
>
> Look at Anise, look at Facets Inheritor. And wherever you find the
> ClassMethods hack, that is a case, and there are many of those around.

Ok, I'll try to.

Kind regards

robert


--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
From: Intransition on
Just a quick follow up to this --I would love to hear a clear case
where passing along the singleton methods would cause a serious issue
that couldn't easily be overcome.