Repeated inheritance

79 views
Skip to first unread message

Louis M

unread,
May 14, 2018, 12:38:45 PM5/14/18
to Eiffel Users
Hello everyone.

I have an error with a weird, diamond-like, repeated inheritance. The result I get seems totally illogical to me (with my conception of inheritance) and if somebody is able to explain it to me, I would appreciate. So here is what I have. First, I have the ancestor:

deferred class
    MY_CLASS_1

feature

    make
       
do
            value
:= "1"
       
end

    value
:STRING

end

Then, I have a descendant of this class that redefine the make:

deferred class
    MY_CLASS_2

inherit
    MY_CLASS_1
        redefine
            make
       
end

feature

    make
       
do
            value
:= "2"
       
end

end

Finally, I have an other class that inherit from MY_CLASS_1 and MY_CLASS_2:

class
    MY_CLASS_3

inherit
    MY_CLASS_1
        rename
            value
as value_1,
            make
as make_1
       
select
            value_1
,
            make_1
       
end
    MY_CLASS_2
        rename
            value
as value_2,
            make
as make_2
       
end

create
    make

feature
{NONE} -- Initialization

    make
           
-- Initialization of Current
       
do
            make_1
            make_2
       
end
end

My initial understanding is that in the class MY_CLASS_3, I have two string attributes ('value_1' and 'value_2') and that the 'make_1' was going to initialize the 'value_1' with the string "1" and the 'make_2' was going to initialize the 'value_2' with the string "2". The problem is that my assumption is wrong. I have a "Variable is not properly set." error for 'value_2'. If I look at the flat view of 'make_2' I see that it initialize 'value_1' instead:

    make_2
           
-- (from MY_CLASS_2)
       
do
            value_1
:= "2"
       
end

This is what seems completely illogical to me and make repeated inheritance not really usable for perfectly logical case. So maybe I do not understand the logic or I missed a step to make it work (with the select clause maybe). Is there somebody that can help me with that.

Thanks,

Louis M

Colin Adams

unread,
May 14, 2018, 1:05:05 PM5/14/18
to eiffel...@googlegroups.com
That will be the effect of the select, I guess

--
You received this message because you are subscribed to the Google Groups "Eiffel Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users...@googlegroups.com.
Visit this group at https://groups.google.com/group/eiffel-users.
For more options, visit https://groups.google.com/d/optout.

Finnian Reilly

unread,
May 14, 2018, 1:36:04 PM5/14/18
to Eiffel Users
I think you should just accept that you cannot use rename to make one thing (value: STRING) into two things and design your classes accordingly.

Alexander Kogtenkov

unread,
May 15, 2018, 4:54:50 AM5/15/18
to eiffel...@googlegroups.com
My answer might sound contradictory at first glance, but it might explain why "select" works this way.

Let'e add one more replication for features `value` and `make`:


MY_CLASS_1
   rename
      value as value_1,
      make as make_1
   select
      value_1,
      make_1
   end
MY_CLASS_2
   rename
      value as value_2,
      make as make_2
   end
MY_CLASS_2
   rename
      value as value_3,
      make as make_3
   end

This is equivalent to


MY_CLASS_1
   rename
      value as value_1,
      make as make_1
   select
      value_1,
      make_1
   end
MY_CLASS_2 -- (A)
   rename
      value as value_2,
      make as make_3
   end
MY_CLASS_2 -- (B)
   rename
      value as value_3,
      make as make_2
   end

In other words, it does not matter from which parent `value_2` and `make_2` come from - from (A) or from (B). What should `make_2` do now? Should it initialize `value_2` or should it initialize `value_3`? What about `make_3`?

The language has no magic to distinguish whether `value_2` is more appropriate in one case than in another. a reasonable decision would be "take something distinct", and that distinct feature is `value_1` because it is selected. And this is what is done by the compiler.

Whether the behavior is reasonable - is another point. But it is consistent. So, I would say, consistency takes precedence here even though the behavior becomes not that intuitive.

Regards,
Alexander Kogtenkov


'Louis M' via Eiffel Users <eiffel...@googlegroups.com>:

Louis M

unread,
May 15, 2018, 12:35:28 PM5/15/18
to Eiffel Users
Hello. Thanks for all your response.

But I am still not convince. Maybe I don't see the whole thing, but for me, it is still strange. My understanding of object oriented paradigm with class is that classes have attributes (that contain data) and method that process those attributes. When another class inherit from this class, it get every features (methods and attributes) it contain. But, unless you actually redefine things in the desendant, the methods and attributes of the ancestors should work the same way it does in the ancestor. I always taught that to make methods used a feature of another ancestor class, you have to redefine, undefine or put the feature deferred to make it work.

I really thing that in this exemple (of Alexander):


MY_CLASS_1
   rename
      value
as value_1,
      make
as make_1
   
select
      value_1
,
      make_1
   
end
MY_CLASS_2
-- (A)
   rename
      value
as value_2,
      make
as make_3
   
end
MY_CLASS_2
-- (B)
   rename
      value
as value_3,
      make
as make_2
   
end

the method `make_2' should use `value_3' and `make_3' should use `value_2'. I think that because they clearly came from the same classes and they are not deferred nor redefine nor undefine. The example I give was of course a minimal example base on a true, real life problem that I have. Now, to make it work, I had to do an ugly, java like, class with duplicate code.

For the fact that I try to duplicate feature using this inheritance, this is true. But I don't think that it is wrong. When I use multiple inheritance, it is of course because I want a class to have the features of multiple classes. It should be the same with repeated inheritance.

Then again; maybe I miss the point or something.

Good day,

Louis M

r...@amalasoft.com

unread,
May 15, 2018, 7:49:13 PM5/15/18
to eiffel...@googlegroups.com
Hi Louis
 
It's quite old (but I did a quick update to it today), and exactly contrary to Bertrand's excellent observations/advice (it's long and takes forever to get to the point), this might actually help (if you can wade through it).  Have a look.
Thanks
R
 
--
Adapting_Inherited_Features_in_Eiffel-lores.pdf

Bernd Schoeller

unread,
May 18, 2018, 4:22:30 AM5/18/18
to eiffel...@googlegroups.com

Hi Louis,

Sorry to be a little harsh, but "replicating inheritance" is IMHO a broken concept that should not be used. Replicating inheritance always happens when you have a single parent concept multiple times in the same class. In Eiffel, it is very simple to detect, as it forces you to use the 'select' keyword.

In this case, 'value' and 'make' are declared in MY_CLASS_1, but then in MY_CLASS_3 you have two of each. As you have correctly observed, the polymorphic binding within each method is non-intuitive. Alexander has given a good description of what actually happens.

In theory, replicated inheritance can be a language feature, but that would require a much more complicated way of polymorphic binding, and that is probably not worth it. Non-polymorphic inheritance might be another way to deal with it.

In every serious code base that I have seen in Eiffel, the use of 'select' was a code smell, mean that the developer used inheritance where actually some kind of aggregation was more appropriate.

Regards,
Bernd

PS: A classic example for replicating inheritance is that a two-dim ARRAY2 inherits from ARRAY twice, once for the horizontal, once for the vertical direction. This is a nice idea, but just not implementable in Eiffel. Eiffel misses language features to support this.

Larry Rix

unread,
May 22, 2018, 9:11:27 AM5/22/18
to Eiffel Users
Louis,

The notion of "MY_CLASS_n" is arbitrary. I'd rather see an example that has some real world context. Why? Because in pragmatic practice, I tend to use a HAS-A relationship rather than inheritance (IS-A).

In the case you've presented then—I tend to have:

class
MY_CLASS_3

feature
{NONE}

my_thing_1
: MY_CLASS_1

my
_thing_2: MY_CLASS_2


Using attributes exported to {NONE} means I can write access features which are semantically named to the purpose of MY_CLASS_3.

The code is cleaner, easier to follow and understand, and is simpler to maintain. I don't think the creation is any more computationally expensive.

I agree with Bernd that the use of "select" tends towards a code smell.


Just my two cents worth.

Larry

Louis M

unread,
May 22, 2018, 3:51:04 PM5/22/18
to Eiffel Users
Hi everyone,

Thanks for all your answers. My real world design is a little complex. It is in a game I make. I will try to make it clear and brief.

I have a class {SOUNDABLE} for every game object that can play sound. In this class, I have a sound, a sound source (mixing unit) and some methods like play, stop, pause, etc.

Next, I have {INFINITE_SOUNDABLE} that inherit from {SOUNDABLE}, but redefine some method (like play) to make the sound repeat itself indefinitely. I use this class for some game object (like engine motor) and for game music.

Then I have the class {MENU} that have a sound to play when I select a menu item and the {MENU} have a background music. So the {MENU} inherit from {SOUNDABLE} for the menu item selection sound and from {INFINITE_SOUNDABLE} for the background music.

So, can I use a client/supplier relationship rather than inheritance; yes I can. But, if I do it for the {MENU}, I must do it for every class in my program and I love the fact that game object are responsible for the sound they have to play.

Thanks again,

Louis M

r...@amalasoft.com

unread,
May 24, 2018, 8:31:08 AM5/24/18
to eiffel...@googlegroups.com
Hi Louis
 
Setting aside the repeated inheritance question, it seems you would be better served by a minor change to your model.  I think the INFINITE_SOUNDABLE idea is going to get you into trouble at some point.  In an alternate model, there is SOUNDABLE, as before, but it is SOUND that has the descendant, that being REPEATING_SOUND.  That class would have something like 'max_times' and 'interval' as features, along with a flag 'is_indefinite'.  The 'play' procedure in SOUND is redefined in REPEATING_SOUND to loop until max_times, but skips the index increment in the loop when is_indefinite is True.  Interestingly perhaps, setting that flag to False once the repeating sound is looping will terminate play, making it, in effect, interruptable, but not requiring any heroics to do so.  This could be, presumably, triggered by a procedure called 'stop'.  For one-and-done sounds, there is no need to complicate the client logic with a play-then-stop sequence.
I've attached a skeleton of a project with the suggested relationships, along with a very simple diagram.
Hope this helps.
R
 
-------- Original Message --------
Subject: [eiffel-users] Re: Repeated inheritance
From: "'Louis M' via Eiffel Users" <eiffel...@googlegroups.com>
Date: Tue, May 22, 2018 3:51 pm
To: Eiffel Users <eiffel...@googlegroups.com>

--
soundable.zip
Reply all
Reply to author
Forward
0 new messages