The Case for Multiple-Inheritance

T

Trans

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.

T.
 
J

Jason Perkins

Sure, I'll try.

Thanks very much for taking the time to elaborate! There seems to be
two libraries for delegation in Ruby: forwardable and delegator. Is
one preferred over the other?
 
J

James Edward Gray II

Thanks very much for taking the time to elaborate! There seems to
be two libraries for delegation in Ruby: forwardable and delegator.
Is one preferred over the other?

Use delegator when you want to forward the entire interface of an
object and forwardable when you want to handle delegation method by
method. Also, remember that neither are needed in some cases. Ruby
is so dynamic it makes tasks like this quite easy.

James Edward Gray II
 
S

Simon Krahnke

* Sylvain Joyeux said:
In most cases, it is not a problem: I represent the fact that
TakePictureAt(x, y) is in fact a sequence of MoveTo(x, y) and a
TakePictureNow. It is therefore possible to repesent the thing with
single inheritance.

That is not a is-a relationship, it is a sequence that contains
TakePictureNow and MoveTo. Can't you freely arrange other Actions in
sequences? How do you model that?
But there are cases where this kind of separation cannot be done --
hence the need for one class to be an implementation of multiple
models.

And how do they look like?

And where do you need kind-of? Such a question is not very OOP.

mfg, simon .... l
 
M

MenTaLguY

--=-qJyQhYY0D03rJoQgjSiZ
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

Here's something off the top of my head, that allows you to reason
about "types" without needing MI.
=20
module LinuxApplication; end
module VideoApplication; end
module WindowsApplication; end
=20
class LinuxVideoApplication
include LinuxApplication
include VideoApplication
end
=20
class WindowsVideoApplication
include WindowsApplication
include VideoApplication
end
=20
def only_linux_video_please(app)
raise "Must be a LinuxVideoApplication" unless LinuxApplication =3D=3D= =3D
app && VideoApplication =3D=3D=3D app
end

I'm not sure this is a particularly good example; it's based off of
Trans' example, not Sylvain's, and it also does use multiple inheritance
inasmuch as modules amount to restricted MI.

-mental

--=-qJyQhYY0D03rJoQgjSiZ
Content-Type: application/pgp-signature; name=signature.asc
Content-Description: This is a digitally signed message part

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD4DBQBHCYWjSuZBmZzm14ERAtLWAJjQDKkkPg/BZRo3wn7gxZsK+71PAJ9e8iar
yQRQYeDfXG10JCasYiZh2w==
=Pkv1
-----END PGP SIGNATURE-----

--=-qJyQhYY0D03rJoQgjSiZ--
 
M

MenTaLguY

--=-WUI5aIkbD4PrNVhDi++z
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

It's possible to use delegation with non-dynamic languages. Hard, but pos=
sible.

I dunno, it mostly just means writing wrapper methods. I actually do
"static" delegation in Ruby more often than I do "dynamic" delegation.

-mental

--=-WUI5aIkbD4PrNVhDi++z
Content-Type: application/pgp-signature; name=signature.asc
Content-Description: This is a digitally signed message part

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQBHCYXoSuZBmZzm14ERAoLEAJ9I+8o4GyQG+KnNKgIN59wuSoDEiQCeMJDi
6m4OuGI6EjIHmPZnVeG1a2A=
=v2OH
-----END PGP SIGNATURE-----

--=-WUI5aIkbD4PrNVhDi++z--
 
P

Pat Maddox

I'm not sure this is a particularly good example; it's based off of
Trans' example, not Sylvain's, and it also does use multiple inheritance
inasmuch as modules amount to restricted MI.

-mental

Well, neither one gave examples, afaik, and I think this whole thing
is kind of stupid. I have a feeling that when these guys post their
examples, people will show them much better ways to design their code,
so I wanted to get the ball rolling.

Pat
 
T

Trans

Well, neither one gave examples, afaik, and I think this whole thing
is kind of stupid. I have a feeling that when these guys post their
examples, people will show them much better ways to design their code,
so I wanted to get the ball rolling.

Pat, my program is almost as simple as the first example I gave. Since
I can't inherit from two classes, in the end I just made the
LinuxApplication a module. If it were a bigger deal I would go ahead
and make the VideoApplication a module too, just as you describe, but
it's not that important. It just seemed like a good example to share
in support of MI because Ruby's SI forced me to make a rather
arbitrary choice of which gets to be the class and which doesn't.

T.
 
P

Pat Maddox

Pat, my program is almost as simple as the first example I gave. Since
I can't inherit from two classes, in the end I just made the
LinuxApplication a module. If it were a bigger deal I would go ahead
and make the VideoApplication a module too, just as you describe, but
it's not that important. It just seemed like a good example to share
in support of MI because Ruby's SI forced me to make a rather
arbitrary choice of which gets to be the class and which doesn't.

T.

The point is that if you "have to make an arbitrary choice of which
gets to be the class and which doesn't" then it's likely that neither
one "has" to be the class nor *should* one be the class. You've
created objects with composable behavior, so you're better off passing
in objects and using delegation, or using modules.

Pat
 
A

Austin Ziegler

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.
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.

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.
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.)

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.
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.

There's HUGE code smell in what you've shown.
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. 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.
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.
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.

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.

-austin
 
A

Austin Ziegler

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:

class Package
class Description end
# Should be Package::Description
attr_accessor :description

class Version; end
# Should be Package::Version
attr_accessor :version

class Contents; end
# Should be Package::Contents
attr_accessor :contents

class Controller; end
# Should be Package::Controller
attr_accessor :controller

class Distributable < Package
class Distribution; end
attr_accessor :distribution

class Dependency; end
# Probably should be specialized further so that it's a list of
# dependencies, so it should be attr_reader with a good initialize
# to fix this issue.
attr_accessor :dependencies
end
end

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.)

-austin
 
A

Austin Ziegler

but possible.
I dunno, it mostly just means writing wrapper methods. I actually do
"static" delegation in Ruby more often than I do "dynamic" delegation.

I do both; it depends on how much I need Container to Act Like Containee.

But you're right.

-austin
 
A

Austin Ziegler

Why hard? If you've got function pointers or their equivalent, you have
delegation, no?

I was referring to Ruby's #method_missing capabilities more than
anything else. You can't do that in C++. (As I said to MentalGuy, it's
something that depends on how much I need a containing object to Act
As a contained object. Ruby doesn't care because of its fully dynamic
dispatch; C++ cares because of the static typing limitations.)

-austin
 
T

Trans

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 ;) But the point was the
restriction of having to pick one construct (class vs. module) over
the other. A module can *almost* do anything a class can. One can even
effectively initialize a method via Class.new{ include Mod }. Of
course this is less convenient and one must fuss with ClassMethods and
be careful of the Double Inclusion Problem, but my point clearly
stands: 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.
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. 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.
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.
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.
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?"
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.
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.
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?
...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. 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. 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.
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.

T.
 
T

Trans

Personally? I'd prefer the former. If I was dumb enough to design
something that required 50 attributes. Let's pick on your example:

class Package
class Description end
# Should be Package::Description
attr_accessor :description

class Version; end
# Should be Package::Version
attr_accessor :version

class Contents; end
# Should be Package::Contents
attr_accessor :contents

class Controller; end
# Should be Package::Controller
attr_accessor :controller

class Distributable < Package
class Distribution; end
attr_accessor :distribution

class Dependency; end
# Probably should be specialized further so that it's a list of
# dependencies, so it should be attr_reader with a good initialize
# to fix this issue.
attr_accessor :dependencies
end
end

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:

---
package:
version:
created:
homepage:
devsite:
authors:
description:
libpaths:

etc...

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.

T.
 
R

Robert Dober

Well, I have to manipulate both "models" and "instances of these models". I
could re-write a whole type system -- that's how people doing that from
C++ do because of the lack of introspection. Instead, I decided to use the
model/instance of Ruby. Why ? Because I can *build* models (i.e. add
methods, inherit methods from parent models, in short: walk the
inheritance chain) and then ask ruby to build objects from those models.
It's not only about state, it's also about sharing code and inheriting
properties from a *code* point of view. And this is called the
class/object relationship.

The bottom line being: if I was to *not* use inheritance for my purposes, I
would have to reinvent the "inheritance wheel".

What I'm stuck with is having "first class models" (classes) and "second
class models" (modules). Know what ? What I gained by using Ruby and the
single inheritance *still* outperfoms the bunch of code I would have had
to write without them. Still, I'd like to have MI :p
That is funny Sylvain, the more I program in Ruby the less I want to have MI and
actually I could perfectly dispose of SI too as long as Mixins behaved
a tiny little bit different, well I have expressed the thoughts about
this in my Blog (URL in sign.)
but if you want some more serious ideas about this you might want to read this
maybe.
http://www.iam.unibe.ch/~scg/Archive/Papers/Scha03aTraits.pdf
HTH
Robert
 
A

Austin Ziegler

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.

You've lost maintainability and clarity. You've lost any sense of good
design.
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.
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.)
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]
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.
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.
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.)
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.
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.


[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
 
A

Austin Ziegler

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.

You've lost maintainability and clarity. You've lost any sense of good
design.
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.
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.)
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]
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.
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.
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.)
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.
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.


[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
 
M

MenTaLguY

--=-W7/urU3J20UL6XDxf76H
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

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.

On the other hand, if you're reusing them in multiple places, e.g.:

class FooWithBarAndBaz
include Foo
include Bar
include Baz
end

class FooWithBarAndHoge
include Foo
include Bar
include Hoge
end

...you've got to create a distinct class up front for every permutation
of features. If you were composing objects instead, you'd be able to
compose objects dynamically, instead of being limited to the particular
compositions that you've baked into classes.
Just because your storage representation is flat doesn't mean your
in-memory object has to be.

I've come to recognize a tying of the internal representation to the
serialized form as a code smell, because usually (especially) when the
serialized form is designed to be human-editable, the requirements for
the structure of the two can be very different.

-mental

--=-W7/urU3J20UL6XDxf76H
Content-Type: application/pgp-signature; name=signature.asc
Content-Description: This is a digitally signed message part

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQBHClc7SuZBmZzm14ERAi/rAJwOj8VS0LI2egNp9b5MaE0/yOVsSwCfe5p3
0nUJr2s/v4tfzL2av56bQ1k=
=Uykd
-----END PGP SIGNATURE-----

--=-W7/urU3J20UL6XDxf76H--
 
R

Rick DeNatale

I've been following the drama in this thread with great interest.

I tried to interject a thought a few days ago and it seems to have
fallen on deaf ears.

Let me try again.

The perceived 'need' for MI in Ruby comes from a
misunderstanding/ignorance of the difference of the meaning of
inheritance in languages like Ruby vs. languages like Java/C++.

In Java/C++ inheritance is primarily a means of expressing type
relationships, and secondarily a means of sharing implementation.
Because of the statically typed nature of these languages, related
types HAVE to be related by inheritance.

In Ruby, inheritance is solely a mechanism for sharing implementation.
Inheritance in Ruby does not build a type hierarchy, even though it
might superficially appear to.

1) It doesn't build a type hierarchy in the sense that two objects
which can be substituted for each other in a given context don't have
to have any kind of implementation relationship, the need only
correctly implement the set of behaviors required by the user.
2) It doesn't build a type hierarcy because in general the whole
notion of types in Ruby is rather flexible, recall the discussion of
whether or not it was 'proper' to make ranges of floats. Sometimes
range elements need to have a succ method, sometimes not, depending on
how you use the resulting Range.

In a statically typed language the coupling of type specification with
implementation leads to a particular set of pressures on the design of
both the language and programs in that language which are different
from the pressures on Ruby and it's programs. Many of the things
which drove statically typed languages come from addressing problems
caused when compiled code which expected an object to be laid out in
memory a certain way ran across an object which had been miscast,
leading to run-time crashes, or hard to debug behavior. Static typing
provides a mechanism to winnow out some of these bugs at compile time,
however most bugs in the development of any program are more subtle
and arise not from type but from logic errors.

Dynamic languages exhibit different failure behavior. Executable code
is less sensitive to the bits and bytes layout, and the kind of errors
caught by static typing tend to fail faster at run-time with better
debugging information available at the time of failure. The main
reason test-first techniques came out of the dynamic language
community was that it was a 'substitute' for static type checking
which actually covers a much broader spectrum of the kind of errors
programmers make.


I certainly understand how this flexibility and decoupling of the
notion of types from inheritance can seem wrong to one trained in
Java/C++ or the like, but the point is that these different
philosophies lead to different approaches to design. Trying to bend
Ruby inheritance and mixins as a means of typing is working against
the strengths of the language, just as trying to manually simulate
dynamic binding in C++ is an indication that you aren't working with
your technology.

As the old maxim goes, "When in Rome..."
 

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,968
Messages
2,570,149
Members
46,695
Latest member
StanleyDri

Latest Threads

Top