The dark side of inherited methods

On 02.11.2010 17:37, Intransition wrote:

tool. That said, I will agree that “is a” relationships come up far less
incompatibly from super class to sub class) and in those languages
inheritance can be used more often than in language which lack these
features (e.g. Java).

Indeed, I think inheritance gets a really bad rap in Ruby b/c Ruby’s
base classes and inheritance system are so poorly designed to handle
it.

I would not subscribe to that. Although Bertrand Meyer is a big fan of
implementation inheritance I am not yet convinced that it is such a good
idea so often. Of course, there are always uses for any technique
present, but I find the “is a” relationship interpretation of
inheritance so clear and obvious that I somehow feel bad about polluting
inheritance with other uses. I cannot really put forward a more
concrete argument or even back this up by some hard (business) numbers,
but a world in which “A inherits B” <=> “A is a B” is so much simpler.
And in Ruby, whenever you need implementation inheritance you can use a
mixin module. Enumerable is a very good example for that.

The other day I was talking to my Father, a Cobol programmer from back
in the day, and he was telling me that when he retired, OOP was just
starting to get hyped. He was quite interested in it at the time and
then rattled off some of the advantages he remembered it was to bring
to the field. Inheritance for code reuse was high on the list and

Well, there are other forms of code reuse - even in procedural
languages. It’s not that you need inheritance to have code reuse. I
would even go as far as to claim that it is more difficult to implement
classes intended solely for implementation inheritance than classes
which implement a particular real world concept and which are only
inherited if “is a” is needed. I say this because a class intended for
implementation inheritance needs other criteria for including
functionality than a class which implements a particular concept.

related to that, one point eally struck me – instead of versioning
code as we have become accustomed, the idea was to subclass the
original class and implement changes in the subclass. Using some sort
of class name referencing scheme you could use any version.

That does not sound like a, um, proper application of inheritance to me.
It might be useful to do it that way in some rare conditions but I
would not promote this as a big feature of inheritance.

Sadly, I had to inform him that OOP did not quite prove to be the
godsend everyone originally thought it might be.

Well, silver bullets - if you know one, you know them all. :slight_smile:

Btw, I find that we have this type of discussion far too seldom here
nowadays and I imagine that we did that more often earlier on. But that
is totally subjective.

Cheers

robert

On Nov 2, 2010, at 12:02 PM, Josh C. wrote:

On Tue, Nov 2, 2010 at 11:19 AM, James Edward G. II <[email protected]

wrote:

Of course, inheritance has it’s place and uses but I feel it’s definitely
overused.

Can you give some examples of where it has its place?

I have to lay down the law, because there will always be exceptions, but
I thought Robert K. describes it pretty good a few messages back
with the “is a” check.

I realized the only time I ever subclass anything
is ActiveRecord::Base (and I’m not really sure what that offers over
modules, DataMapper uses modules, for example).

ActiveRecord is an interesting case of Rubyish inheritance too. It’s
really a focused DSL, I think. You inherit from ActiveRecord::Base to
get a bunch of macros (has_many(), validates_presence_of(), etc.).
Using this mini-DSL you describe your model and that changes how the
objects produced from that class will behave. It’s a
build-your-on-class-with-shortcuts system, I think.

James Edward G. II

On Nov 2, 2010, at 1:25 PM, Joel VanderWerf wrote:

def length
a.x = 1 # NoMethodError
Good question. It works to replace super with:

self[:x] = new_x

I realize that’s not an ideal solution though.

James Edward G. II

On 02/11/2010 17:40, Robert K. wrote:

good idea so often. Of course, there are always uses for any
technique present, but I find the “is a” relationship interpretation
of inheritance so clear and obvious that I somehow feel bad about
polluting inheritance with other uses. I cannot really put forward a
more concrete argument or even back this up by some hard (business)
numbers, but a world in which “A inherits B” <=> “A is a B” is so much
simpler. And in Ruby, whenever you need implementation inheritance you
can use a mixin module. Enumerable is a very good example for that.
There are, IIRC, two places where is a, is not well implemented by
inheritance.

One is the classic square and rectangle problem. In a graphics library,
both square and rectangle are graphic objects that can be drawn on the
screen, moved and resized etc. But what is the relationship between
square and rectangle? Is a square a rectangle with height = width, or is
a rectangle a square without the constraint of height = width, or does
is-a not apply at all? Whatever the author of the package chooses the
users have to know if they have a square or a rectangle, or some very
reasonable code sequences will fail to do what is expected.

x.width *= 2;
x.height *= 2;
If x is a rectangle this doubles both dimensions.
if x is a square, this will quadruple the dimensions, or fail.

The other is where the inheriting object has to learn a new skill at run
time. A teachable foo is not a foo.

implement classes intended solely for implementation inheritance than

That does not sound like a, um, proper application of inheritance to
me. It might be useful to do it that way in some rare conditions but
I would not promote this as a big feature of inheritance.
IIRC, (I started back in the day also), it was promoted as a way to
achieve data migration. You altered the schema, and altered the class to
match, but only converted the data if it was edited. It was helpful in
situations where just linking the OLTP took 36 hours, and updating all
the data would have meant the service was down for days - too expensive
to contemplate.

Regards
Ian

On Tue, Nov 2, 2010 at 11:44 PM, Ian H. [email protected]
wrote:

I would not subscribe to that. Although Bertrand Meyer is a big fan of
There are, IIRC, two places where is a, is not well implemented by
inheritance.

One is the classic square and rectangle problem. In a graphics library, both
square and rectangle are graphic objects that can be drawn on the screen,
moved and resized etc. But what is the relationship between square and
rectangle? Is a square a rectangle with height = width, or is a rectangle a
square without the constraint of height = width, or does is-a not apply at
all?

The rule is pretty easy: state of subclasses can be separated in two
groups:

  1. state present in superclass
  2. everything not in 1

Now the rule to maintain “is a” relationship is simple: the set of
allowed states in group 1 must be a subset of the set of allowed
states in the superclass (not a proper subset, so both may be
identical). Allowed state in group 2 is totally governed by the
subclass’s invariant.

From this it follows immediately that, if inheritance is used here,
Square can only be a subclass of Rectangle and not the other way
round.

Whatever the author of the package chooses the users have to know if
they have a square or a rectangle, or some very reasonable code sequences
will fail to do what is expected.

x.width *= 2;
x.height *= 2;
If x is a rectangle this doubles both dimensions.
if x is a square, this will quadruple the dimensions, or fail.

A square is a rectangle where width == height. That’s the
definition in math and that’s how I would implement it usually. The
simplest version would be

class Rectangle
attr_reader :width, :height

def initialize(width, height)
@width = width
@height = height
end

def outline
2 * ( width + height )
end
end

class Square < Rectangle

def initialize(x)
@width = x
end

def height; width; end
end

Note that in Ruby, different than in other programming languages,
@height is not wasted in a Square because it is never allocated. In
other languages you would simply ensure that both members had the same
value all the time in class Square.

Now if you need mutable squares and rectangles you need to devise a
method to manipulate Rectangle which can be used by subclasses as
well, e.g.

class Rectangle
def resize(width, height)
raise ArgumentError if width < 0 || height < 0
@width = width
@height = height
self
end
end

class Square
def resize(width, height)
raise ArgumentError if width < 0 || height < 0 || width != height
@width = width
self
end
end

In a more realistic example you would probably make Rectangle inherit
Polygon and have completely different code for outline calculation and
manipulation. Then also there would be a generalized storage of side
lenghts and angles which would cope with arbitrary many sides thus not
wasting space for a member that is not used.

It’s not that you need inheritance to have code reuse. I would even go

of class name referencing scheme you could use any version.

That does not sound like a, um, proper application of inheritance to me.
It might be useful to do it that way in some rare conditions but I would
not promote this as a big feature of inheritance.

IIRC, (I started back in the day also), it was promoted as a way to achieve
data migration. You altered the schema, and altered the class to match, but
only converted the data if it was edited. It was helpful in situations where
just linking the OLTP took 36 hours, and updating all the data would have
meant the service was down for days - too expensive to contemplate.

Wow, that’s an amazing roundtrip time! I guess that situation has
changed dramatically nowadays (unless, that is, you have to compile
and link SAP maybe :-)).

Kind regards

robert

On Nov 3, 3:20am, Robert K. [email protected] wrote:

2 * ( width + height )
end
class Square
def resize(width, height)
raise ArgumentError if width < 0 || height < 0 || width != height
@width = width
self
end
end

I find it interesting that you change a two-argument Rectangle
initialization method to take one argument in Square, but you keep the
resize method as two arguments.

On Tue, Nov 2, 2010 at 7:51 PM, James Edward G. II
[email protected] wrote:

a = A.new
a.x = 1 # NoMethodError

Good question. It works to replace super with:

self[:x] = new_x

I realize that’s not an ideal solution though.

… as is wasting a class because one wants to spare the typing IMHO.
:slight_smile:

Kind regards

robert

On 11/03/2010 01:22 AM, Robert K. wrote:

… as is wasting a class because one wants to spare the typing IMHO. :slight_smile:

My suggestion is not just for reducing typing: the super construct gives
you a clean, idiomatic way to wrap the methods provided by Struct.

Anyway, it’s not as if Class.new were depleting the ozone layer or
something :slight_smile:

Scala solved this problem (which can viewed as a lack of the “uniform
return type principle”) on its collection library on version 2.8:
http://www.scala-lang.org/docu/files/collections-api/collections_2.html

It used implicits arguments to do this. It is not a simple design, but
it is quite general.

On Wed, Nov 3, 2010 at 9:58 PM, Joel VanderWerf
[email protected] wrote:

On 11/03/2010 01:22 AM, Robert K. wrote:

… as is wasting a class because one wants to spare the typing IMHO. :slight_smile:

My suggestion is not just for reducing typing: the super construct gives you
a clean, idiomatic way to wrap the methods provided by Struct.

That’s true. It’s just that it feels itchy to me to use a class this
way.

Anyway, it’s not as if Class.new were depleting the ozone layer or something
:slight_smile:

Hmm… What about global warming? :wink:

Cheers

robert

On Nov 2, 1:40pm, Robert K. [email protected] wrote:

inheritance with other uses. I cannot really put forward a more
concrete argument or even back this up by some hard (business) numbers,
but a world in which “A inherits B” <=> “A is a B” is so much simpler.
And in Ruby, whenever you need implementation inheritance you can use a
mixin module. Enumerable is a very good example for that.

Mixins are just another way to do inheritance. It has the same issues.
The only difference is the module level --and that’s just an arbitrary
deviation often circumvented by “ClassMethods” hacks…

To the main point of Ruby’s poor handling of inheritance, I should
give an an example. Consider creating a subclass of Numeric. We have
no access to the Numeric.new, so a subclass of Numeric is actually a
trick. Such a class would (by necessity) work just as well without the
numeric superclass. The only reason for the superclass is so that
is_a?(Numeric) will work.

which implement a particular real world concept and which are only
inherited if “is a” is needed. I say this because a class intended for
implementation inheritance needs other criteria for including
functionality than a class which implements a particular concept.

I think implementation inheritance becomes easier if classes are kept
small with simple APIs and focused on a limited goal. Then larger
“real world” classes are built up by inheriting from these smaller
abstractions. But to have a really robust system to do this, one needs
some tools that Ruby doesn’t provide, such a private inheritance,
probably class private instance variables and methods, and better
multiple inheritance than Ruby’s mixin system.

I think there is a place for both inheritance and composition. It’s
not an all or nothing deal. I think Enumerable is actually a great
example of the power of inheritance when used well. And I think Ruby
would be even better if it took that idea and ran with it a bit
further.

Btw, I find that we have this type of discussion far too seldom here
nowadays and I imagine that we did that more often earlier on. But that
is totally subjective.

Agreed.

Intransition [email protected] wrote:

Mixins are just another way to do inheritance. It has the same issues.
The only difference is the module level --and that’s just an arbitrary
deviation often circumvented by “ClassMethods” hacks…

What I would like to have are Mixins (especially Enumerable) behaving
some
more clever.

Asume I have some MyCollection class. I implement #each and include
Enumerable. Now I can do:

mc = MyCollection.new(‘some’, ‘data’, ‘here’, ‘and’, ‘there’)
mc4 = mc.select { |s| s.length != 4 }

But, unfortunately, mc4.class is Array and not MyCollection. I have to
implement select, reject and others by myself, loosing some of the nice
help
Enumerable gives me.

I often want to chain things and call some mapping method on some
collection
class without falling back to Array.

This could be solved if Enumerable looks for some MyCollection.newEnum()
class method and calls it if present to create a new object for
collecting
the results of the select (or some other mechanism, not sure how to go
best. Any way to tell Enumerable how to collect the results of the
mapping
method in a new MyCollection object will do).

Klaus

The Answer is 42. And I am the Answer. Now I am looking for the
Question.

On Thu, Nov 4, 2010 at 1:24 AM, Intransition [email protected]
wrote:

inheritance so clear and obvious that I somehow feel bad about polluting
To the main point of Ruby’s poor handling of inheritance, I should
give an an example. Consider creating a subclass of Numeric. We have
no access to the Numeric.new, so a subclass of Numeric is actually a
trick. Such a class would (by necessity) work just as well without the
numeric superclass. The only reason for the superclass is so that
is_a?(Numeric) will work.

I am not sure I get your point. What do you mean by access to
Numeric.new? Method new is defined in Class and Numeric does not have
a method #initialize:

irb(main):028:0> Numeric.ancestors.map {|cl| cl.method(:new) rescue cl}
=> [#<Method: Class#new>, Comparable, #<Method: Class#new>, Kernel,
#<Method: Class#new>]

irb(main):029:0> Numeric.ancestors.map {|cl|
cl.instance_method(:initialize) rescue cl}
=> [#<UnboundMethod: Numeric(BasicObject)#initialize>, Comparable,
#<UnboundMethod: Object(BasicObject)#initialize>, Kernel,
#<UnboundMethod: BasicObject#initialize>]

Did you mean Integer? I describe the issue here
http://blog.rubybestpractices.com/posts/rklemme/019-Complete_Numeric_Class.html

Even that would only be a case of a specific class and not something
about inheritance in Ruby in general.

What is it that you find “poor” about inheritance in Ruby?

which implement a particular real world concept and which are only
inherited if “is a” is needed. I say this because a class intended for
implementation inheritance needs other criteria for including
functionality than a class which implements a particular concept.

I think implementation inheritance becomes easier if classes are kept
small with simple APIs and focused on a limited goal. Then larger
“real world” classes are built up by inheriting from these smaller
abstractions.

Makes me think of traits. Scala has some nice features in that area
IIRC.

http://www.scala-lang.org/node/126

But to have a really robust system to do this, one needs
some tools that Ruby doesn’t provide, such a private inheritance,
probably class private instance variables and methods, and better
multiple inheritance than Ruby’s mixin system.

Exactly.

I think there is a place for both inheritance and composition. It’s
not an all or nothing deal. I think Enumerable is actually a great
example of the power of inheritance when used well. And I think Ruby
would be even better if it took that idea and ran with it a bit
further.

What exactly would you like to have changed in the language? So far I
always liked the simplicity and cleanness of Ruby. But maybe
evolutions of the language are possible that will maintain the balance
yet get more out of inheritance. I am really curios what you are
imagining.

Kind regards

robert

On Thu, Nov 4, 2010 at 9:45 AM, Klaus S. [email protected] wrote:

Enumerable. Now I can do:

This could be solved if Enumerable looks for some MyCollection.newEnum()
class method and calls it if present to create a new object for collecting
the results of the select (or some other mechanism, not sure how to go
best. Any way to tell Enumerable how to collect the results of the mapping
method in a new MyCollection object will do).

Of course there are ways that could be used to implement what you want,
e.g.

module Enumerable

default: new behavior

def select_2(cl = self.class)
r = (cl.new rescue [])
each {|x| r << x if yield x}
r
end
end

But this does not solve the major issue that you change the interface.
Currently there is a ton of code around that works with the
assumption (explicit or implicit) that #select returns an Array. I
would not dare judge how much code will be broken if we change the
default behavior.

A solution would be this:

module Enumerable

default: old behavior

def select_2(cl = Array)
r = cl.new
each {|x| r << x if yield x}
r
end
end

But then you loose the convenience to not have to provide the type.

Kind regards

robert

Robert K. [email protected] wrote:

But this does not solve the major issue that you change the interface.
Currently there is a ton of code around that works with the
assumption (explicit or implicit) that #select returns an Array. I
would not dare judge how much code will be broken if we change the
default behavior.

This is why I would not want this as default behavior. I would expect
select, reject etc return an Array as it is now. But if the class
including
Enumerable provides a special MyCollection::enumNew method it would be
used instead.

So something like

module Enumerable
def select
r = (self.class.enumNew(self) rescue [])
# [ rest of the select code ]
end
end

would do. This should not break old code.
I am not sure how to implement this best, as creating an empty
collection
and adding elements by << ma be not the most efficient way to go.

(please note that MyCollection.enumNew takes the original object as
parameter to be able to copy other instance variables etc)

kind regards,

Klaus

The Answer is 42. And I am the Answer. Now I am looking for the
Question.

On Nov 4, 4:47am, Robert K. [email protected] wrote:

I am not sure I get your point. What do you mean by access to
Numeric.new? Method new is defined in Class and Numeric does not have
a method #initialize:

That is what I am referring.

irb(main):029:0> Numeric.ancestors.map {|cl|
cl.instance_method(:initialize) rescue cl}
=> [#<UnboundMethod: Numeric(BasicObject)#initialize>, Comparable,
#<UnboundMethod: Object(BasicObject)#initialize>, Kernel,
#<UnboundMethod: BasicObject#initialize>]

Did you mean Integer? I describe the issue
herehttp://blog.rubybestpractices.com/posts/rklemme/019-Complete_Numeric_…

No not Integer, although this holds for it too. Your HexNum is a
perfect example. Notice you never use #super in it’s implementation.
Because you can’t. Also try calling a Numeric method that you haven’t
defined for HexNum, like #ceil.

Even that would only be a case of a specific class and not something
about inheritance in Ruby in general.

What is it that you find “poor” about inheritance in Ruby?

There are different things. For one I don’t think the core classes are
well honed for inheritance, hence the original topic of this thread.

which implement a particular real world concept and which are only

not an all or nothing deal. I think Enumerable is actually a great
example of the power of inheritance when used well. And I think Ruby
would be even better if it took that idea and ran with it a bit
further.

What exactly would you like to have changed in the language? So far I
always liked the simplicity and cleanness of Ruby. But maybe
evolutions of the language are possible that will maintain the balance
yet get more out of inheritance. I am really curios what you are
imagining.

  • Getting rid of class vs. module distinction.
  • Have #include (or another method) provide “class level” methods in
    inheritance chain.
  • Private inclusion, such that all methods of class/module being
    included are private to the including class/module.
  • (Maybe) class private methods and variables, i.e. methods not
    visible to the inheritance chain.
  • Built-in method to mean self.class.new() --I think people forget
    to do it that way.
  • Built-in support for parametric mixins.
  • Work on improving modularity of core library (e.g. Facet’s Indexable
    and Hashery’s Ostructable).

I’m sure there are others too. And it’s not an all or nothing deal.
Any one of them would improve upon things.

I agree about the simplicity. In fact, in some ways I want it to be
even more so.

On Thu, Nov 4, 2010 at 1:24 AM, Intransition [email protected]
wrote:

inheritance so clear and obvious that I somehow feel bad about polluting
To the main point of Ruby’s poor handling of inheritance, I should

to the field. Inheritance for code reuse was high on the list and
I think implementation inheritance becomes easier if classes are kept
would be even better if it took that idea and ran with it a bit
further.

Btw, I find that we have this type of discussion far too seldom here
nowadays and I imagine that we did that more often earlier on. But that
is totally subjective.

Agreed.

Completely share this impression and I agree that it is a pity.
I am not sure I completely agree with any opinion stated here but most
of them have made some points I was not completely aware of.

Cheers
R.

On Thu, Nov 4, 2010 at 10:55 AM, Klaus S. [email protected] wrote:

Robert K. [email protected] wrote:

So something like

module Enumerable
def select
r = (self.class.enumNew(self) rescue [])

[ rest of the select code ]

end
end

would do. This should not break old code.

It will, because classes can be modified in Ruby.

require ‘set’

class Set
def self.enum_new(other_set)
new
end
end

require ‘a_library_that_uses_set’

x = a_set.select {|x| x > 0}
assert { Array === x } # boom!

Again, global state (in this case a method in class Set) bytes. My
suggested approach with providing the class as argument is better IMHO
because

  • It is more flexible (at one time you can map an Array to a Set,
    another time to an Array etc.).

  • It does not break old code (see above).

Actually you do not want to determine the return type of Set#map
globally but rather per use.

I am not sure how to implement this best, as creating an empty collection
and adding elements by << ma be not the most efficient way to go.

There is no other way because you do not know beforehand whether all
or only part of the elements of the current collection go into the new
one.

(please note that MyCollection.enumNew takes the original object as
parameter to be able to copy other instance variables etc)

I noticed that but this is useless without knowing what the copy will
be used for. Enumerable#map and #select have vastly differing
semantics!

Cheers

robert