The Case for Multiple-Inheritance

A

Austin Ziegler

See the top of my previous post: you're talking about duck typing, which
does not apply in my case since I *need* to manipulate the models, and I
need (for instance) to be able to check that an objet is kind_of? a
particular class/module.

You could, of course, override #kind_of? to do what you want.
Sorry, I don't think I have the time/motivation for that. I don't mind
discussion (and I don't mind being proved wrong if I learn something in
the process), but I thought a lot about it and *yes* I'm using inheritance
for 'is-a' relations. I'm finishing my PhD write now and I don't have a
lot of time for that.

What Mentalguy's suggesting is that the relationships may not be best
modeled as an is-a relationship, as I suggested to Trans early on in
the discussion. Is-a relationships are very tightly coupled and should
only be used where such a tight relationship is warranted.

-austin
 
A

Austin Ziegler

I think that programmers should have the right to do their own decisions.
If they don't feel like using MI, fine. But an anti-MI religion.

They do, but not all of those ways are possible in Ruby. C++ (as
noted) is really bad at delegation; Ruby excels at it. C++ is *better*
for MI (because of the explicit support and such) than Ruby, even with
mixins. But Ruby tends to be more compact and useful for most models.
All powerful programming tools have drawbacks. The question is whether or
not you gain more by taking the risk of using them than by avoiding them.
I already used MI in C++ (and yes, for "is-a" relations, cut the
you-should-not-use-inheritance crap) and I have seen people using MI in
very elegant ways. It is useful in some cases, can be avoided in others,
but it is useful sometimes.

I'd say it should be avoided 98% of the time (MI). Inheritance in
general, is probably overused by at least 50%. (Most is-a
relationships aren't useful ones and are caused by people who aren't
used to thinking of OOP in other terms. I'm not suggesting this for
you, but is-a has a lot of implications that has-a doesn't. Therefore,
it's not "you-should-not-use-inheritance crap", it's sound advice from
years in the industry.)

-austin
 
M

MenTaLguY

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

What Mentalguy's suggesting is that the relationships may not be best
modeled as an is-a relationship, as I suggested to Trans early on in
the discussion. Is-a relationships are very tightly coupled and should
only be used where such a tight relationship is warranted.

Also, even when you have a true "is-a" relationship, "is-a" in the class
domain doesn't always correspond to "is-a" in the domain being modeled.=20

This is because the requirements of dynamic method dispatch introduce
additional constraints on subclassing which violate some of the laws a
simple transitive relationship like "is-a" might reasonably be expected
to obey.

Single-inheritance is an obvious example of one such constraint, but
allowing multiple inheritance simply means replacing that constraint
with others.

-mental

--=-VKs9TzM47RkJXhINeGV5
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)

iD8DBQBHCBx4SuZBmZzm14ERAoNSAKDeCpdoY6mHgKn0EUvwCTovjkYO3QCgrD/h
ISeBBIzQ0v4tTSsRaJDoLr4=
=4sAe
-----END PGP SIGNATURE-----

--=-VKs9TzM47RkJXhINeGV5--
 
M

MenTaLguY

--=-ekyV+iJIpOhezfxJ5hop
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

This is because the requirements of dynamic method dispatch introduce
additional constraints on subclassing which violate some of the laws a
simple transitive relationship like "is-a" might reasonably be expected
to obey.

Actually, I guess this applies to having methods in general. The
classic ellipse-versus-circle thing is a great example: "is-a" in the
model domain (shapes) is exactly the opposite from "is-a" in the class
domain, at least in terms of Liskov substitutability.

-mental

--=-ekyV+iJIpOhezfxJ5hop
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)

iD8DBQBHCB84SuZBmZzm14ERAqjGAJ9rkEuy8AVX8DJsl94rIdXsNlxvoACfU/fQ
YMfB7xHDp8MMdkn3wmQickU=
=8BZF
-----END PGP SIGNATURE-----

--=-ekyV+iJIpOhezfxJ5hop--
 
T

Trans

Linearization has nothing to do with modules per se. It's one
well-known technique for addressing ambiguity in the presence of
multiple inheritance. See for instance:

http://www.webcom.com/haahr/dylan/linearizations.html

The reason linearization is used is that partial ordering is not a
strong enough constraint to avoid ambiguity in method dispatching.
While linearization can be avoided, you would need to make other
trade-offs to compensate (i.e. you can't have your multiply inherited
cake and eat it too).

No no. It's not the linearization. The linearzation is what so great
about how Ruby does inheritance --it's what makes the MI (using
mixins) manageable! Yes, there's some debate about including modules
more than once in the same hierarchy. I've never really run into a
case where I've needed that, but I suppose it's possible. But it
doesn't really matter. The important thing is just concretely knowing
where in the hierarchy a class/module is. The linearization provides
that.

The thing is (and maybe this will help clarify how I see this) the
more I work with Ruby the more I feel like the best code is always of
the form:

module M
...
end

module X
...
end

module Y
...
end

class Z
extend M
include X
include Y
end

Such that everything is a module and classes are always just
compositions of modules. This provides maximum code reusability.

However, as one does this it becomes clear that it's more involved
then it needs to be. One has to name many more abstractions than the
model you have in mind actually has. For instance, because you have
the heart of a class in a module, you get something like:

module MyClassModule
..

class MyClass
include MyClassModule

and also b/c you have to define meta-modules separately for use with
#extend.

But if we instead remove the distinction between module and class,
i.e. one can include a class or subclass a module, then these extra
abstractions become completely unnecessary --our code can remain just
as we are acustomed to coding it, but we also have full code re-
usability at our disposal if wish to use it.

T.
 
M

Michael T. Richter

--=-prpIPDRQuhnEQkfS4zh5
Content-Type: multipart/alternative; boundary="=-9+KiqJnSOawTO1chDbQJ"


--=-9+KiqJnSOawTO1chDbQJ
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

I can see limited use for it; I'm more referring to what seems to be
an absolute mania about it. (That is, wanting to do it EVERY TIME.)


This goes back to my rant about "just because you have a language
feature doesn't mean you should use it all the time".

I have similar thoughts about Lisp-alikes and macros. C++ and
templates. Haskell and combinators. Etc., etc., etc. ad nauseum.

--=20
Michael T. Richter <[email protected]> (GoogleTalk:
(e-mail address removed))
Never, ever, ever let systems-level engineers do human interaction
design unless they have displayed a proven secondary talent in that
area. Their opinion of what represents good human-computer interaction
tends to be a bit off-track. (Bruce Tognazzini)

--=-9+KiqJnSOawTO1chDbQJ
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; CHARSET=3DUTF-8">
<META NAME=3D"GENERATOR" CONTENT=3D"GtkHTML/3.12.1">
</HEAD>
<BODY>
On Sun, 2007-07-10 at 07:16 +0900, Austin Ziegler wrote:
<BLOCKQUOTE TYPE=3DCITE>
<PRE>
<FONT COLOR=3D"#000000">&gt; &gt; You can, sort of. However, I don't get th=
e current mania for appending</FONT>
<FONT COLOR=3D"#000000">&gt; &gt; methods to the class-object and the insta=
nce-object at the same time.</FONT>
</PRE>
</BLOCKQUOTE>
<BR>
<BLOCKQUOTE TYPE=3DCITE>
<PRE>
<FONT COLOR=3D"#000000">&gt; It's nice for metaprogramming. However, in ma=
ny cases these things</FONT>
<FONT COLOR=3D"#000000">&gt; should really have been singleton factory obje=
cts, rather than classes.</FONT>
</PRE>
</BLOCKQUOTE>
<PRE>

</PRE>
<BLOCKQUOTE TYPE=3DCITE>
<PRE>
<FONT COLOR=3D"#000000">I can see limited use for it; I'm more referring to=
what seems to be</FONT>
<FONT COLOR=3D"#000000">an absolute mania about it. (That is, wanting to do=
it EVERY TIME.)</FONT>
</PRE>
</BLOCKQUOTE>
<BR>
This goes back to my rant about &quot;just because you have a language feat=
ure doesn't mean you should use it all the time&quot;.<BR>
<BR>
I have similar thoughts about Lisp-alikes and macros.&nbsp; C++ and templat=
es.&nbsp; Haskell and combinators.&nbsp; Etc., etc., etc. ad nauseum.<BR>
<BR>
<TABLE CELLSPACING=3D"0" CELLPADDING=3D"0" WIDTH=3D"100%">
<TR>
<TD>
-- <BR>
<B>Michael T. Richter</B> &lt;<A HREF=3D"mailto:[email protected]">ttmri=
(e-mail address removed)</A>&gt; (<B>GoogleTalk:</B> (e-mail address removed))<BR>
<I>Never, ever, ever let systems-level engineers do human interaction desig=
n unless they have displayed a proven secondary talent in that area. Their =
opinion of what represents good human-computer interaction tends to be a bi=
t off-track. (Bruce Tognazzini)</I>
</TD>
</TR>
</TABLE>
</BODY>
</HTML>

--=-9+KiqJnSOawTO1chDbQJ--

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

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

iD8DBQBHCGgbLqyWkKVQ54QRAv0gAKDKrZjF4Bqj9oF09jedgKB6gZWAawCggeGE
7SVwR1EL8skaHFibEKs36+E=
=VejZ
-----END PGP SIGNATURE-----

--=-prpIPDRQuhnEQkfS4zh5--
 
M

Michael T. Richter

--=-8ATCcdtClfT3OTXr4aSU
Content-Type: multipart/alternative; boundary="=-a5Y6GSRzGWi4pCIIf3nq"


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

I'd say it should be avoided 98% of the time (MI). Inheritance in
general, is probably overused by at least 50%. (Most is-a
relationships aren't useful ones and are caused by people who aren't
used to thinking of OOP in other terms. I'm not suggesting this for
you, but is-a has a lot of implications that has-a doesn't. Therefore,
it's not "you-should-not-use-inheritance crap", it's sound advice from
years in the industry.)


Going back to the original model that sparked this: inheritance reflects
an "is-a" relationship. Someone wants to make an application object
that:

1. IS-A Linux application; and
2. IS-A video player application.


What stymies me here is... exactly which runtime is this going to be
running in where you can plug in a Linux application, a Windows
application or a ... Symbian, say, application and need the transparent
dispatching to the right functionality? Just the "LinuxApplication"
class by itself makes me suspicious of the model behind it: what useful
abstraction do you get from this that you don't get from an
"Application" that has a "POSIXFileSystem" (instead of "NTFSFileSystem")
and a "LinuxSecurityModel" (instead of "NTSecurityModel") or whatever?
Just this core has me scratching my head.

Adding the "VideoPlayerApplication" to the mix only raises even more
questions, all related, once again, to the nature of a runtime where you
need transparent dispatching to a VideoPlayerApplication over ... what,
exactly? A VideoRecorderApplication? An AudioPlayerApplication? A
RealTimeAutomobileAssembleyApplication?

Basically, I'm failing to see any useful "IS-A" relationships at all in
the original poster's model barring an awfully convoluted run-time with
some questionable approaches to things built into it. What he's viewing
as inheritance (IS-A) situations to me look like composition (HAS-A)
situations.

And this is what strikes me about most C++ code: people using IS-A left,
right and centre because that's what the language supports best; C++ is
positively lousy at supporting HAS-A relationships (and Java isn't much
better).

--=20
Michael T. Richter <[email protected]> (GoogleTalk:
(e-mail address removed))
A well-designed and humane interface does not need to be split into
beginner and expert subsystems. (Jef Raskin)

--=-a5Y6GSRzGWi4pCIIf3nq
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; CHARSET=3DUTF-8">
<META NAME=3D"GENERATOR" CONTENT=3D"GtkHTML/3.12.1">
</HEAD>
<BODY>
On Sun, 2007-07-10 at 07:21 +0900, Austin Ziegler wrote:
<BLOCKQUOTE TYPE=3DCITE>
<PRE>
<FONT COLOR=3D"#000000">I'd say it should be avoided 98% of the time (MI). =
Inheritance in</FONT>
<FONT COLOR=3D"#000000">general, is probably overused by at least 50%. (Mos=
t is-a</FONT>
<FONT COLOR=3D"#000000">relationships aren't useful ones and are caused by =
people who aren't</FONT>
<FONT COLOR=3D"#000000">used to thinking of OOP in other terms. I'm not sug=
gesting this for</FONT>
<FONT COLOR=3D"#000000">you, but is-a has a lot of implications that has-a =
doesn't. Therefore,</FONT>
<FONT COLOR=3D"#000000">it's not &quot;you-should-not-use-inheritance crap&=
quot;, it's sound advice from</FONT>
<FONT COLOR=3D"#000000">years in the industry.)</FONT>
</PRE>
</BLOCKQUOTE>
<BR>
Going back to the original model that sparked this: inheritance reflects an=
&quot;is-a&quot; relationship.&nbsp; Someone wants to make an application =
object that:
<OL TYPE=3D1>
<LI TYPE=3D1 VALUE=3D1>IS-A Linux application; and
<LI TYPE=3D1 VALUE=3D2>IS-A video player application.
</OL>
<BR>
What stymies me here is... exactly which runtime is this going to be runnin=
g in where you can plug in a Linux application, a Windows application or a =
... Symbian, say, application and need the transparent dispatching to the r=
ight functionality?&nbsp; Just the &quot;LinuxApplication&quot; class by it=
self makes me suspicious of the model behind it: what useful abstraction do=
you get from this that you don't get from an &quot;Application&quot; that =
has a &quot;POSIXFileSystem&quot; (instead of &quot;NTFSFileSystem&quot;) a=
nd a &quot;LinuxSecurityModel&quot; (instead of &quot;NTSecurityModel&quot;=
) or whatever?&nbsp; Just this core has me scratching my head.<BR>
<BR>
Adding the &quot;VideoPlayerApplication&quot; to the mix only raises even m=
ore questions, all related, once again, to the nature of a runtime where yo=
u need transparent dispatching to a VideoPlayerApplication over ... what, e=
xactly?&nbsp; A VideoRecorderApplication?&nbsp; An AudioPlayerApplication?&=
nbsp; A RealTimeAutomobileAssembleyApplication?<BR>
<BR>
Basically, I'm failing to see any useful &quot;IS-A&quot; relationships at =
all in the original poster's model barring an awfully convoluted run-time w=
ith some questionable approaches to things built into it.&nbsp; What he's v=
iewing as inheritance (IS-A) situations to me look like composition (HAS-A)=
situations.<BR>
<BR>
And this is what strikes me about most C++ code: people using IS-A left, ri=
ght and centre because that's what the language supports best; C++ is posit=
ively <B>lousy</B> at supporting HAS-A relationships (and Java isn't much b=
etter).<BR>
<BR>
<TABLE CELLSPACING=3D"0" CELLPADDING=3D"0" WIDTH=3D"100%">
<TR>
<TD>
-- <BR>
<B>Michael T. Richter</B> &lt;<A HREF=3D"mailto:[email protected]">ttmri=
(e-mail address removed)</A>&gt; (<B>GoogleTalk:</B> (e-mail address removed))<BR>
<I>A well-designed and humane interface does not need to be split into begi=
nner and expert subsystems. (Jef Raskin)</I>
</TD>
</TR>
</TABLE>
</BODY>
</HTML>

--=-a5Y6GSRzGWi4pCIIf3nq--

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

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

iD8DBQBHCGryLqyWkKVQ54QRApPgAJ4iAujVDnsv3M9JhzEAbTQ3uLLEpgCaAo/p
LLqZveVwGD4fZ460VmoPUoU=
=pz5Z
-----END PGP SIGNATURE-----

--=-8ATCcdtClfT3OTXr4aSU--
 
S

Sylvain Joyeux

Delegation does not mean duck typing. You are confusing your
terminology.
Tell me if I'm wrong, but from a type point of view, delegation is only
usable in the context of duck-typing. If you don't think as 'my type is
defined as what messages I'm able to answer to', then you cannot replace
one object with one delegator since they won't have the same type.
 
S

Sylvain Joyeux

You could, of course, override #kind_of? to do what you want.
Yes. I could build a MI-like pattern from scratch. I does not seem to be
the "right way" ...
What Mentalguy's suggesting is that the relationships may not be best
modeled as an is-a relationship, as I suggested to Trans early on in
the discussion. Is-a relationships are very tightly coupled and should
only be used where such a tight relationship is warranted.
I'm modelling activities (moving to a point, moving the camera, taking a
picture at a location, this kind of things). To keep things manageable,
use the inheritance relationship to express that we have abstract actions
(the generic MoveTo(x, y)) and implementation of those actions
(MyWayToMoveTo(x, y)).

I need to reason about these relationships because you can use a specific
movement method in place of a generic one, but you can't use a
TakePictureNow activity in place of a MoveTo(x, y). However, you could use
a TakePictureAt(x, y) in place of a MoveTo(x, y) since it is both
modelling a MoveTo(x, y) *and* a TakePicture.

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

Now: why classes ? Some code is tied to the various activities (code to
start the activity, to stop it, handlers when some event appear) and I do
want the children models to inherit the methods of their parent models.

I actuall started to write parts of my framework without using method
dispatch and inheritance. It began quickly horrible, so I rewrote it using
it. I'm more than happy now. I would just be happier with MI.

Sylvain
 
T

Trans

Going back to the original model that sparked this: inheritance reflects
an "is-a" relationship. Someone wants to make an application object
that:

1. IS-A Linux application; and
2. IS-A video player application.

What stymies me here is... exactly which runtime is this going to be
running in where you can plug in a Linux application, a Windows
application or a ... Symbian, say, application and need the transparent
dispatching to the right functionality? Just the "LinuxApplication"
class by itself makes me suspicious of the model behind it: what useful
abstraction do you get from this that you don't get from an
"Application" that has a "POSIXFileSystem" (instead of "NTFSFileSystem")
and a "LinuxSecurityModel" (instead of "NTSecurityModel") or whatever?
Just this core has me scratching my head.

Adding the "VideoPlayerApplication" to the mix only raises even more
questions, all related, once again, to the nature of a runtime where you
need transparent dispatching to a VideoPlayerApplication over ... what,
exactly? A VideoRecorderApplication? An AudioPlayerApplication? A
RealTimeAutomobileAssembleyApplication?

Basically, I'm failing to see any useful "IS-A" relationships at all in
the original poster's model barring an awfully convoluted run-time with
some questionable approaches to things built into it. What he's viewing
as inheritance (IS-A) situations to me look like composition (HAS-A)
situations.

And this is what strikes me about most C++ code: people using IS-A left,
right and centre because that's what the language supports best; C++ is
positively lousy at supporting HAS-A relationships (and Java isn't much
better).

You're assuming too much from the names. I'm just working on improving
Launchy to add a video player launcher, in the process I worked on the
overall code to make it more OOP.

http://rubyforge.org/projects/copiousfreetime/

T.
 
P

Pat Maddox

Tell me if I'm wrong, but from a type point of view, delegation is only
usable in the context of duck-typing. If you don't think as 'my type is
defined as what messages I'm able to answer to', then you cannot replace
one object with one delegator since they won't have the same type.

What? Half of the Design Patterns book is about how to use
composition & delegation instead of inheritance.

Pat
 
S

Sylvain Joyeux

Tell me if I'm wrong, but from a type point of view, delegation is
^^^^^^^^^^^^^^^^^^^^^^^^^
What? Half of the Design Patterns book is about how to use
composition & delegation instead of inheritance.
I don't say that delegation does not exist outside duck-typing. And I never
said that only inheritance is usable from an *engineering* point of view.

However, the "model(a) == model(b)" comparison does NOT cooperate well with
delegation unless your definition of model is the duck-typing one (for
instance if you define the type as the class of the object).

Sylvain
 
A

Austin Ziegler

The thing is (and maybe this will help clarify how I see this) the
more I work with Ruby the more I feel like the best code is always of
the form:

module M; end
module X; end
module Y; end

class Z
extend M
include X
include Y
end

Such that everything is a module and classes are always just
compositions of modules. This provides maximum code reusability.

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.

Modules in Ruby are useful for adding new "generic" class capabilities.

Enumerable is a way of using the ability to iterate over a collection
object (and a collection is defined by #each, which goes through the
*state values* of the object in turn). It's like a Java interface except
that you don't need to reimplement each and every piece of the interface
because the interface is always expressed in terms of #each.

Transaction::Simple is a way of adding "extra" state to an object that
allows it to be versioned in memory. All of that extra state is managed
by the methods defined in the module.

What you're talking about is on par with components and graphical
development. They can work in simple cases, but the moment you start
having complex cases, you start realizing that the code is becoming a
bit more complex to integrate that way. You can either refactor your
code, or start looking at ways that the language can change to
accomodate your model's inflexibility.

Modules aren't really meant for providing state that the object depends
on normally. They're *extra*. So you should be working with classes that
have implementations; you should refactor out common functionality (and
I mean REALLY common functionality) into modules; you should have a
class hierarchy if it's appropriate; you should compose multiple objects
into a single object where appropriate.

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.

There are legitimate arguments (made by matju) for the unification of
class and module; your case is not one of them.

-austin
 
A

Austin Ziegler

Tell me if I'm wrong, but from a type point of view, delegation is only
usable in the context of duck-typing. If you don't think as 'my type is
defined as what messages I'm able to answer to', then you cannot replace
one object with one delegator since they won't have the same type.

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

-austin
 
P

Pat Maddox

I don't say that delegation does not exist outside duck-typing. And I never
said that only inheritance is usable from an *engineering* point of view.

However, the "model(a) == model(b)" comparison does NOT cooperate well with
delegation unless your definition of model is the duck-typing one (for
instance if you define the type as the class of the object).

Sylvain

I really really wish you would provide a quick example of what you
actually want. I know you're quite busy, but you've managed to find
plenty of time for this discussion.

Here's something off the top of my head, that allows you to reason
about "types" without needing MI.

module LinuxApplication; end
module VideoApplication; end
module WindowsApplication; end

class LinuxVideoApplication
include LinuxApplication
include VideoApplication
end

class WindowsVideoApplication
include WindowsApplication
include VideoApplication
end

def only_linux_video_please(app)
raise "Must be a LinuxVideoApplication" unless LinuxApplication ===
app && VideoApplication === app
end
 
P

Pat Maddox

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.

Agreed. That looks painful.

Pat
 
T

Trans

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

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?
Modules in Ruby are useful for adding new "generic" class capabilities.

Enumerable is a way of using the ability to iterate over a collection
object (and a collection is defined by #each, which goes through the
*state values* of the object in turn). It's like a Java interface except
that you don't need to reimplement each and every piece of the interface
because the interface is always expressed in terms of #each.

Transaction::Simple is a way of adding "extra" state to an object that
allows it to be versioned in memory. All of that extra state is managed
by the methods defined in the module.

What you're talking about is on par with components and graphical
development. They can work in simple cases, but the moment you start
having complex cases, you start realizing that the code is becoming a
bit more complex to integrate that way. You can either refactor your
code, or start looking at ways that the language can change to
accomodate your model's inflexibility.

Modules aren't really meant for providing state that the object depends
on normally. They're *extra*. So you should be working with classes that
have implementations; you should re-factor out common functionality (and
I mean REALLY common functionality) into modules; you should have a
class hierarchy if it's appropriate; you should compose multiple objects
into a single object where appropriate.

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. 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. 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. However
the world is not always so clear cut. Modules sometimes deal with
state. If we were to follow your logic all the way through, then
modules shouldn't be allowed to address state at all. Would we want
that? But if we follow my logic all the way through, we have lost
nothing. We have only gained. Whether someone else avoids it or shoots
themselves in the foot with it is their own problem. I see very good
ways to put the new capabilities to elegant use.

What we're talking about here ultimately is just a matter of coupling
between components. You don't think modules should have anything to do
with this and that we should all be using classes and composition
instead b/c it doesn't couple the components as tightly together. But
I think that's incorrect. Modules are a perfectly valid way of
creating components, and coupling behaviors. As along as you avoid
shared state (and by that I mean specifically instance vars) then the
coupling is being managed quite well. The trade off is just a matter
of managing a greater potential for method name clashes, but you save
yourself the complexities of method routing. You pick your poison and
I'll pick mine. In the mean time your insistence on it being your way
or no way is really unfortunate. You might not agree with my
techniques but why are you trying to stop me from doing it? Hey, I'm
all "Burger King" 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.

T.
 
T

Trans

Agreed. That looks painful.

Bahahahahahahahahaha!

You're right! But it's only painful b/c it's what you HAVE to do today
to achieve this level of code reuse. If I had my way, it would as
simple as:

class X
def self.foo # instead of needing M
...
end

class Y; end

class Z
is X
is Y
end

And we'd still have complete code reuse.

T.
 
P

Pat Maddox

Bahahahahahahahahaha!

You're right! But it's only painful b/c it's what you HAVE to do today
to achieve this level of code reuse. If I had my way, it would as
simple as:

class X
def self.foo # instead of needing M
...
end

class Y; end

class Z
is X
is Y
end

And we'd still have complete code reuse.

T.

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.

Pat
 
J

James Edward Gray II

Delegation does not mean duck typing. You are confusing your
terminology.

[offlist]

I'm enjoying this thread. Would you please consider elaborating on
this notion in the thread itself?

Sure, I'll try.

For the record though, the issues I'm about to discuss don't seem to
relate to Sylvain's complaints about a lack of MI. For that reason,
I've renamed this subthread.

Delegation is a form of composition, like inheritance. The design
patterns movement has pushed that delegation is is more flexible to
inheritance and should be preferred when we are designing objects.

It comes in many, many forms but one example might be that we want a
strict Queue object. We want to allow enqueueing and dequeuing items
and peeking at the top item, but we don't want users accessing items
in the middle of the Queue. One way to create this is to delegate
the needed operations to an Array:

require "forwardable"

class Queue
def initialize
@queue = Array.new
end

extend Forwardable

def_delegators :mad:queue, :shift, :<<
alias_method :enqueue, :<<
alias_method :dequeue, :shift

def peek
@queue.first # don't delegate because it allows: first(n)
end
end

if __FILE__ == $PROGRAM_NAME
q = Queue.new
%w[one two three].each { |num| q.enqueue num }
4.times { p q.dequeue }
end

__END__

Duck typing is a concept that says we should view the type of an
object by what it can do, more than what it is. The deeper you go
with this line of thought you generally come to realize than type
checking can often be avoided and leads to more powerful code when it
is. For example, an add_event() method could work with an Array, our
new Queue, or something else entirely:

$ irb -r queue
def add_event(event_list)
event_list << :event
end => nil
add_event(Array.new) => [:event]
q = Queue.new
=> # said:
add_event(q) => [:event]
q.peek
=> :event

This are simplified explanations, but hopefully the begin to
illustrate the differences.

James Edward Gray II
 

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

Forum statistics

Threads
473,968
Messages
2,570,149
Members
46,695
Latest member
StanleyDri

Latest Threads

Top