On 10/8/07, Trans <transfire / gmail.com> wrote:
> On Oct 7, 8:45 pm, "Austin Ziegler" <halosta... / gmail.com> wrote:
>> On 10/7/07, Trans <transf... / gmail.com> wrote:
>>> On Oct 7, 8:42 am, "Austin Ziegler" <halosta... / gmail.com> wrote:
>>>> No, actually, it doesn't provide maximum code reusability. Ruby
>>>> supports a rich vocabulary and you're choosing to use a form that,
>>>> while clearly useful in some cases, is not right.
>>> Yes it does, b/c if methods are placed in a class, the only reuse if
>>> via a subclass.
>> You are 100% wrong here. Not all reuse is defined by mixin or
>> inheritance. Delegation and composition are also reuse.
> Fair enough, although that's only 50% wrong ;)

No, it's 100% wrong. You said "the only reuse [is] via a subclass." Just
using the instance as an object is reuse (composition).

[snip]

> modules are more versatile than classes. Thus by using all modules for
> behavior (and classes as only compositions of modules) maximizes
> reusability. And for this simple fact: classes cannot be mixed-in.

Wrong. Look VERY CAREFULLY at your code, Trans. How many of your modules
are used in more than one place?

If the answer is few, then you're doing it wrong and you're introducing
negative consequences in terms of maintainability, while not actually
increasing reuse. Most methods are specific behaviour for a given state.
Pretending that by extracting that into modules that youre increasing
reuse is nonsense. Unless you're actually reusing the code, you're not
increasing reuse.

>>> If they are in a module they are not limited by that constraint. I
>>> haven't lost anything at all by putting all behavior in modules, I
>>> have only gained. The approach is a superset.

You've lost maintainability and clarity. You've lost any sense of good
design.

>> The approach is a mess and tries to abstract things out in a
>> nonsensical way. Essentially, you're saying that UP FRONT you divide
>> things into one or more "class modules" and one or more "instance
>> modules". With few exceptions, modules should be extracted from
>> classes when you can think of a more-generic case for it. And you
>> don't extract the whole damned class to pull it off; you pull the
>> special-purpose-for-similar-objects code out.
> Huh? It's a mess to divide these things into modules for mixin, but
> its ok to divide them as classes and delegate? The point is to make
> less mess by dividing things up into more manageable pieces. Those
> pieces may prove nicely reusable, too, if well thought out, btw.

Yeah, it's much cleaner to think in terms of objects. It's much
preferable to compose than inherit. Delegation is only necessary when
you need to Act As something else.

> But my example of extraction of the *whole* damned class was to make a
> point about the limitations of class vs module reuse. For instance,
> here's an example I didn't mention before. Did you know that if you
> use all modules then AOP coding is very simple? It's easy to prepend a
> module relative to other modules in the inheritance chain. But not so
> for classes. Dynamic AOP for classes takes some mind-numbing meta-
> coding for sure.

That's part of the problem, I guess: I don't think that AOP is
worthwhile in Ruby. AOP doesn't add any readability to the code, in the
end, than doing it a bit smarter. AOP only sort-of makes sense in Java
because Java's (otherwise) a static language. But even there, it's not
all that useful.

>>> I seem to be the only one who ever gives code examples. How about
>>> you show me an example of how you'd do it?
>> You've got plenty of examples of how I'd do it: it's called my
>> existing Ruby code that's out there publicly on RubyForge. (This is
>> probably close to ten thousand lines of code ... that doesn't do what
>> you're talking about except in one instance I can think of that I
>> only did it that way for clarity and wouldn't do it that way again.)
> Of course I've seen some of your code, Austin. And you're quite a
> capable coder. But your Ruby code has a very classic feel to it. Like
> you were programming in C. You're very conservative and don't much
> venture into the meta-coding deep. I'm not insinuating anything
> negative in that, btw. I think it's good that there are all types of
> coders. But you seem almost religious about your way of doing things.

That's because you're tone-deaf. I'm saying that from experience with
other people actually having to maintain code or having to maintain
other peoples' code, the way that you're doing things is nonsense and
reduces overall maintainability.

If you think my code looks like C, you'd be quite wrong; I simply don't
do metaprogramming where it isn't appropriate. (I have an experimental
branch of PDF::Writer that used quite a bit of metaprogramming to
encapsulate a number of rules that PDFs impose on objcts; if I ever pick
up PDF::Writer again, then I will probably be going from that
direction.) Most of the problems that I've solved haven't needed meta-
programming and I'm not going to inflict it on others just to show how
kewl I am.

There's also code that I have written that I can't show (it's
work-related) that does stuff within a particular domain that I would
never advocate in any other way. (I have one, in particular, that
overrides const_missing to return a string value representing the name
of the const just tried. The code where you'd use this expects that.)

>> I mostly do it as classes. I don't screw around with modularizing
>> things that simply *aren't* modules. Just because you CAN extract all
>> of the purpose out of a class and put it into a module just to
>> include it right back in doesn't mean you SHOULD.
> Of course not. And of course I don't. But I wish Ruby didn't make me
> have to choice one over the other. It reduces the flexibility of it's
> otherwise extremely elegant inheritance model; and likewise makes
> delegation the better choice in more cases than otherwise would be
> necessary.

"Necessary" is a matter of opinion. To pick up on a different message
(to which I'll be replying to here, as well), I consider an object with
50 attributes an abomination. I don't care whether you've gotten those
attributes by mixins or manually writing them; it means you haven't
thought about your class design well enough. You seem to prefer flat
classes; I think that flat classes are a disaster in waiting.

[snip]

>>> You are constraining the use of modules according to your own view
>>> of how they "should" be used, and in the process I think you're
>>> missing my point.
>> No, I didn't miss your point at all. I think your approach is stupid
>> and shortsighted. Sorry for the harshness, but the reality is that
>> using modules this way is nonsense and you're running into
>> limitations because you're trying to do something that doesn't make
>> sense. I'm not saying that Ruby's perfect or that everything in it
>> makes sense, but if it *hurts* to do something one way in Ruby,
>> that's usually a sign that you're doing it wrong.
> Ah yea, and "If God meant for men to fly, he would have given them
> wings."
>
> You're arguing that I have the problem, not Ruby. But I'm not arguing
> Ruby has a problem, I'm arguing that maybe it could be even better.
> Nor do I have a problem. I finished the program yesterday. I didn' run
> into a limitation per-se, I ran in to a "why am I having to make an
> ultimately pointless distinction?"

Yeah. You do have a problem and you *have* been arguing that Ruby has a
problem. If one is suggesting an improvement to something, it's because
one has hit a pain point. Sometimes those pain points are legitimate;
sometimes they're like the old joke:

  Man: Doctor, it hurts when I put my arm behind my back.
  Doctor: Well, don't put your arm behind your back.

Your example is precisely one of those situations. The distinction, by
the way, MAY be pointless. Inasmuch as it would encourage the sort of
design decisions you're making and have demonstrated in this discussion?
I'm more than happy to have it there, because if you're doing it, far
less capable programmers than you would make even dumber decisions from
it.

>>> You've created an artificial separation between forms of code
>>> encapsulation because, you reason to yourself, one is for handling
>>> state and one if for generic capabilities.
>> It's not artificial. In Ruby, modules do not contain state. Period.
>> Only objects, which are instances of classes, contain state.
> Now you're knit picking words. Of course only object instances have
> state. But classes, and modules!, define objects.

I'm not nit-picking at all. It's the fundamental distinction between the
two right now. Classes can be turned into things that have state;
modules can't. Modules can only add features to objects.

>> The canonical examples of modules (Enumerable, Comparable) don't deal
>> with state directly, but only through a defined interface that DOES
>> deal with state in an object. Transaction::Simple is a module because
>> it adds functions to objects that allow those objects to manipulate
>> state in different ways.
>>
>> Transaction::Simple has generic applicability. Text::Format does not.
> Yep. A well defined interface is an important aspect of code reuse --
> that's true for all forms of composition.

Not even related to what I said. Modules have some generic
applicability.

>>> But that is putting a hard line in a soft world. There really is no
>>> need to make that hard distinction. If a coder, like yourself, wants
>>> to do that you can do so just as easily with a single form of
>>> encapsulation....
>> [snip the rest of this nonsensical paragraph and the entire next
>> paragraph that has nothing to do with what I said]
>>
>> Do NOT try to put words in my mouth.
> What words? You always do this Austin. You makes some admonishing
> remark and then never explain what your talking about. It's a very
> deceptive conversational tactic. And I wish you'd cut it out. Explain
> yourself. What words do you mean? What I have I said that is
> nonsensical? And why do you think it's all in reference to what you
> said. What about the point *I* trying to make?

"If a coder, like yourself, wants to ... with a single form of
encapsulation." You rambled on for two paragraphs as if I had said
something about a single form for development. Which I didn't. Never
have, never will. (In fact, it's pretty much *you* that's talked about
only using one form for development. I know you don't, but you're still
talking nonsense here.)

>>>> I'll put it clearly: if I were running a Ruby consultancy and a
>>>> programmer of mine used your methodology for everything (e.g.,
>>>> classes defined by composing modules), I would not keep that
>>>> programmer on. It's a bad style. Sorry.
>>> Clearly there is a reason you aren't running a consultancy.
>> ...which has nothing to do with this stance. You write bad code and
>> design badly. You always want to change the language to fix the fact
>> that you've coded yourself into a corner. You've done this for years,
>> Trans.
> No. For years I have explored the language. Pushed up against it's
> boundaries. Investigate what it can and can't do. I bring up ideas and
> make suggestions to explore those further, not because I'm stuck, but
> because I'm curious.

Really? If that's the case, then you really need to work on your
presentation skills, because you've yet to present something in a way
that says "I'm exploring this..." and instead nearly always say "Ruby
should do this..." while presenting examples that in every single case
could be coded easier and cleaner if you simply stepped back and thought
a little more. The example you opened this discussion with
(LinuxVideoPlayerApplication) is such BAD design that you SHOULD have
been able to see it.

Like I said, Ruby isn't perfect and there are things that can improve
about it -- and yes, you've pointed out a few of those areas. But when
it "hurts" to do something one way in Ruby, it usually means that you're
doing something suboptimally.

> I'm forever learning. I didn't come from a C background and just
> started writing my C program's in Ruby. I learned Ruby as Ruby. And I
> try to use Ruby for what it is, and enjoy considering what more it
> could be.

Nice misdirection and assumption of the martyr position.

> Does this exploration lead me down rough roads sometimes? Damn right
> it does. But in the end I'm a better coder for it. Am I the best coder
> around here? Of course not, I don't pretend to be. But you sure do.

Again, nice misdirection. I don't pretend to be the best coder. I am,
however, a professional software developer that has to deliver a lot of
code on a regular basis. There are things that I know I wouldn't put up
with from people who worked with me if I had to maintain their code.
There are things that I don't care about.

>> Classes defined by composing modules is bad practice. There are times
>> for doing things like this, but they are few and far between and
>> people who do it should be prepared to defend their reasons for doing
>> so because it smells like bad code.
> Good gracious. You have so utterly missed the whole point I'm not sure
> why I continue to type. What was my point Austin? Please, I want you
> to put words in my mouth so I can see if there's actually any
> communication going on here.
I haven't missed the point. You believe that modules are more flexible
than classes and that things would be better if you could mix classes in
the same way you can mix modules in, or at least if you could "properly"
instantiate modules. You may believe that, but in Ruby at least, you're
wrong.

On 10/8/07, Trans <transfire / gmail.com> wrote:
> On Oct 7, 8:57 pm, "Austin Ziegler" <halosta... / gmail.com> wrote:
>> On 10/7/07, Trans <transf... / gmail.com> wrote:
>>> On Oct 7, 10:42 am, "Pat Maddox" <perg... / gmail.com> wrote:
>>>> You misunderstand me.  What looks painful to me is having the
>>>> behavior of a class tucked away behind 15 different modules, instead
>>>> of being right in the class where it belongs.
>>> I see. Well it's a trade-off. Would you prefer a class with 50
>>> attributes or one that divides those into a few groups based on what
>>> they concern? For instance I have a Package class, it consists of
>>> General, Version, Content and Control components. I use that as a
>>> library reference object. However when I want to use it for creating
>>> distributable packages I mixin Distribution and Dependency modules.
>> Personally? I'd prefer the former. If I was dumb enough to design
>> something that required 50 attributes. Let's pick on your example:

[snip layered redesign]

>> There we go. It's now a properly composed object that doesn't mix in
>> multiple pieces of functionality into a flat access hierarchy. I now
>> have a Package::Version *class* to reuse elsewhere if I want, and it's a
>> full-on black box. I don't have any worries that Package::Version the
>> class will have any instance variables that conflict with another item
>> because I used a module.
>>
>> It took me all of thirty seconds to fix your design and make it
>> class-oriented rather than module-oriented. My objects are now more
>> usable as a whole and the overall interface substitution capabilities
>> are improved because now my Package class doesn't HAVE fifty attributes;
>> it's got FIVE. The Distributable package is properly specialized, too.
>> It might be possible to use a module to extend a package object into a
>> distributable package object rather than a subclass, but I'm not sure
>> that the design for that is a good one.
>>
>> Because each of my classes is smaller, my code is likely to be
>> significantly more robust, and I have functionality that I can test
>> independently of the whole Package class without trying to mix it
>> into something different.
>>
>> That's what I mean, Trans: your design approach for mixing in 50
>> attributes or writing 50 attributes is bad. If you need that many,
>> your object is PROBABLY way too big. (Yes, there are times when such
>> things are necessary; they're extremely uncommon, though.)
>
> Fantastic jobs Austin. Now initialize the class from a YAML file for
> me. Did I mention the file spec looks like this:

[...]
> It has some 35+ possible entires and some of those have aliases
> besides (because I like giving my users some flexibility). Plus it
> should be extensible because other utilities (like the distributable
> packaging tool) will need additional information.
>
> Come on, show the dumb-dumb how it's *properly* done.

  class Package
    def self.from_yaml(yaml)
      pkg = Package.new

      YAML.load(yaml).each do |key, value|
        case key
        when 'package' then # ...
        when 'version' then # ...
        when 'created' then # ...
        when 'homepage' then # ...
        when 'devsite' then # ...
        when 'authors' then # ...
        when 'description' then # ...
        when 'libpaths' then # ...
        else
          # save the unknown pairs somewhere so that subclasses can deal
          # with them, too.
        end
      end
    end
  end

Just because your storage representation is flat doesn't mean your
in-memory object has to be. (And you can define your own #to_yaml on the
Package class that flattens things for saving.)

This is no different than what I wrote for Ruwiki four years ago (I
eschewed YAML because Syck was broken in a couple of different releases
of Ruby.)

-austin
-- 
Austin Ziegler * halostatue / gmail.com * http://www.halostatue.ca/
               * austin / halostatue.ca * http://www.halostatue.ca/feed/
               * austin / zieglers.ca