## digitalmars.D - Challenge: solve this multiple inheritance problem in your favorite

• mw (126/126) Jun 04 Problem:
• mw (4/6) Jun 04 Note: the `--` commented out line in class BANK and DOCTOR, if
• Simen =?UTF-8?B?S2rDpnLDpXM=?= (43/52) Jun 04 This is all fairly reasonable, but why use multiple inheritance?
• mw (92/132) Jun 04 The Diamond problem is a well-known issue, e.g. the majority of
• Jacob Carlborg (12/30) Jun 04 It's already possible to do that today:
• mw (16/25) Jun 05 Thank you for letting me know.
mw <mingwu gmail.com> writes:
```Problem:

Suppose a person who has both US & UK residence, travel to Paris,
and feel ill need to withdraw some money and see a doctor:

1) the person can only have 1 (one) name
2) the person has 3 addresses: one in US, one in UK, and a temp
hotel address in Paris
3) the person's bank account that can only be read by the bank
4) the person's health info that can only be read by doctor

I will show the Eiffel program, with the compiler ensures all
these constraints.

First, let me show your the program running result:
--------------------------------------------------------------------------------------

\$ ./app
A person have only *one* name
My hotel in Paris
US addr: London
UK addr: NewYork
bank_acct: only view-able by bank
health_info: only view-able by doctor

--------------------------------------------------------------------------------------

This is the multiple inheritance implementation in Eiffel: with
some comments

--------------------------------------------------------------------------------------
class PERSON

feature {ANY}   -- ANY, basically means `public` in

name: STRING is do Result := "A person have only *one* name"
end
addr: STRING is do Result := "A person can have *multi*.addr"
end

feature {BANK}
bank_acct: STRING is do Result := "bank_acct: only view-able
by bank" end

feature {DOCTOR}
health_info: STRING is do Result := "health_info: only
view-able by doctor" end

end

--------------------------------------------------------------------------------------
class UK_RESIDENT
inherit PERSON redefine addr end       -- redefine == override in

feature {ANY}
addr: STRING is do Result := "London" end
end

--------------------------------------------------------------------------------------
class US_RESIDENT
inherit PERSON redefine addr end       -- redefine == override in

feature {ANY}
addr: STRING is do Result := "NewYork" end
end

--------------------------------------------------------------------------------------
class VISITOR
inherit             -- Note: inherit PERSON 3 times! but treat
each 3 address individually
UK_RESIDENT rename addr as uk_addr end
US_RESIDENT rename addr as us_addr end
PERSON      redefine addr select addr end

make

feature {ANY}
make is do end

addr: STRING is
do
Result := "My hotel in Paris"
end
end

--------------------------------------------------------------------------------------
class BANK

create {ANY}
make

feature
make is do end

read_bank_acct(u: PERSON) is
do
io.put_string(u.bank_acct    + "%N")
--io.put_string(u.health_info  + "%N") -- ****** Fatal Error:
This feature is only exported to {DOCTOR}.
end
end

--------------------------------------------------------------------------------------
class DOCTOR

create {ANY}
make

feature
make is do end

read_health_info(u: PERSON) is
do
--io.put_string(u.bank_acct    + "%N") -- ****** Fatal Error:
This feature is only exported to {BANK}.
io.put_string(u.health_info  + "%N")
end
end

--------------------------------------------------------------------------------------
-- to build: compile  app.e -o app
class APP

create {ANY}
main

feature {ANY}
visitor: VISITOR
bank: BANK
doctor: DOCTOR

print_uk_addr(u: VISITOR) is do io.put_string("US addr: " +
u.uk_addr + "%N") end
print_us_addr(u: VISITOR) is do io.put_string("UK addr: " +
u.us_addr + "%N") end

main is
do
create bank.make
create doctor.make
create visitor.make

io.put_string(visitor.name + "%N")
io.put_string(visitor.addr + "%N")
print_uk_addr(visitor)
print_us_addr(visitor)

bank.read_bank_acct(visitor)
doctor.read_health_info(visitor)
end

end
--------------------------------------------------------------------------------------

Note: the `--` commented out line, followed by the compiler error
message: "Fatal Error ...."

(I'm a bit busy today, stop here. I will continue tomorrow).

Feel free to add your implementation in your favorite programming
language.
```
Jun 04
mw <mingwu gmail.com> writes:
```On Thursday, 4 June 2020 at 07:11:26 UTC, mw wrote:
Note: the `--` commented out line, followed by the compiler
error message: "Fatal Error ...."

Note: the `--` commented out line in class BANK and DOCTOR, if
un-commented, will get the compiler error message "Fatal Error
...."
```
Jun 04
Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
```On Thursday, 4 June 2020 at 07:11:26 UTC, mw wrote:
Problem:

Suppose a person who has both US & UK residence, travel to
Paris, and feel ill need to withdraw some money and see a
doctor:

1) the person can only have 1 (one) name
2) the person has 3 addresses: one in US, one in UK, and a temp
hotel address in Paris
3) the person's bank account that can only be read by the bank
4) the person's health info that can only be read by doctor

This is all fairly reasonable, but why use multiple inheritance?
I mean, it might be the logical way to do it in Eiffel, but in D
that's just not the right way.

For that matter, it reads as a very artificial situation:
- What happens when the person buys a holiday home in Italy?
- Do we need to define a separate inheritance tree for all
possible combinations?

Now, for showing off some of Eiffel's features, there's some good
stuff here - the feature export system is kinda interesting, and
doesn't really have a good analog in D, but may be approximated
with non-creatable types:

module person;
import bank;
class Person {
string bankingDetails(Bank.Token) {
return "Account number readable only by Bank";
}
}

module bank;
import person;
class Bank {
struct Token {
disable this();
private this(int i) {}
}
string personBankingDetails(Person person) {
return person.bankingDetails(Token(0));
}
}

module test;
import bank;
import person;
unittest {
Person p = new Person();
Bank b = new Bank();

// Won't compile - only a Bank can create a Token
//p.bankingDetails();

// Works fine
b.personBankingDetails(p);
}

--
Simen
```
Jun 04
mw <mingwu gmail.com> writes:
```On Thursday, 4 June 2020 at 09:37:38 UTC, Simen Kjærås wrote:
This is all fairly reasonable, but why use multiple
inheritance? I mean, it might be the logical way to do it in
Eiffel, but in D that's just not the right way.

For that matter, it reads as a very artificial situation:

The Diamond problem is a well-known issue, e.g. the majority of
the wiki article on multiple inheritance is dedicated to it:
https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem

I don't think my example is more artificial for the diamond
problem than the dining-philosophers problem for concurrent
programming (why not the philosophers simply ask the waitress for
more forks? :-)

Anyway, you can try to solve the Object => (Button, Clickable)
=>=> Button.equals() problem on the wiki page if you think it's
less artificial; I won't argue in this direction any further.

- What happens when the person buys a holiday home in Italy?
- Do we need to define a separate inheritance tree for all
possible combinations?

(As a side note: just because Eiffel solved the Diamond problem
so successfully, in Eiffel programmers are *encouraged* to use
(abuse :-) multiple inheritance as much as they can -- taking
into account it's a pure OO language. This is in contrast in
other languages, multiple inheritance usage is discouraged.)

Although D intendeds to have single-inheritance with multiple
interfaces, but the multiple inheritance problems have crept into
D already, because of the introduction of `mixin` and `alias xxx
this`. As a simple example, when a class has multiple interfaces
and multiple mixins, we may run into issues:

\$ cat multi.d
----------------------------------------------------------------------
interface NameI { string name(); }
interface AddrI { string addr(); }

mixin template NameT(T) {
string name() {return "name";}
bool equals(T other) {
return this.name() == other.name();
}
}

mixin template AddrT(T) {
string addr() {return "addr";}
bool equals(T other) {
return this.addr() == other.addr();
}
}

class Person : NameI, AddrI {
mixin NameT!Person;
mixin AddrT!Person;
}

void main() {
Person p1 = new Person();
Person p2 = new Person();

p1.equals(p2); // Error: function
multi.Person.AddrT!(Person).equals at multi.d(13) conflicts with
function multi.Person.NameT!(Person).equals at multi.d(6)
}
----------------------------------------------------------------------
(BTW, without the last line, the program compiles.)

And ideally, the function NameT.equals() and AddrT.equals()
should be reused, and be combined to define a new Person.equals().

From C++'s way of thinking, multiple inheritance (read D's mixin)
is all-or-none: either *all* the attributes of common ancestor
are separate copy, or be joined as one copy (called `virtual
inheritance`); but this didn't fully solve the problem.

So Walter said:
https://forum.dlang.org/post/rb4seo\$bfm\$1 digitalmars.com
"""
The trouble was, it was inserted without realizing it was
multiple inheritance, meaning its behaviors are ad-hoc and don't
make a whole lot of sense when examined carefully.
"""

Actually, I think it still solvable: by dealing with each
attribute from the parent class individually (instead of as a
whole), just follow Eiffel's method, adding language mechanism
(esp `rename`) to allow programmer to decide how to resolve the
conflict.

I'd imaging something like this:

----------------------------------------------------------------------
class Person : NameI, AddrI {
mixin NameT!Person rename equals as name_equals;
mixin AddrT!Person rename equals as addr_equals;

bool equals(Person other) {
return this.name_equals(other) &&
this.addr_equlas(other);
}
}
----------------------------------------------------------------------

Now, for showing off some of Eiffel's features, there's some
good stuff here - the feature export system is kinda

the world), protected (to the world), private (to the world),
coarse-grained -- even C++'s `friend` can access the declaring
class' *all* attributes; v.s Eiffel's access-control: just name
the outside class in an access list {Bank, WillExecutor}, it's
fine-grained.

interesting, and doesn't really have a good analog in D, but
may be approximated with non-creatable types:

module person;
import bank;
class Person {
string bankingDetails(Bank.Token) {
return "Account number readable only by Bank";
}
}

module bank;
import person;
class Bank {
struct Token {
disable this();
private this(int i) {}
}
string personBankingDetails(Person person) {
return person.bankingDetails(Token(0));
}
}

module test;
import bank;
import person;
unittest {
Person p = new Person();
Bank b = new Bank();

// Won't compile - only a Bank can create a Token
//p.bankingDetails();

// Works fine
b.personBankingDetails(p);
}

ok, essentially one line change here:

------------------------
feature {BANK, WillExecutor}
bank_acct: STRING is do Result := "bank_acct: only view-able
by bank" end
------------------------

Now, it's your turn now :-)
```
Jun 04
Jacob Carlborg <doob me.com> writes:
```On 2020-06-04 19:25, mw wrote:

Actually, I think it still solvable: by dealing with each attribute from
the parent class individually (instead of as a whole), just follow
Eiffel's method, adding language mechanism (esp `rename`) to allow
programmer to decide how to resolve the conflict.

I'd imaging something like this:

----------------------------------------------------------------------
class Person : NameI, AddrI {
mixin NameT!Person rename equals as name_equals;
mixin AddrT!Person rename equals as addr_equals;

bool equals(Person other) {
return this.name_equals(other) &&
this.addr_equlas(other);
}
}
----------------------------------------------------------------------

It's already possible to do that today:

class Person : NameI, AddrI {
mixin NameT!Person Name;
mixin AddrT!Person Addr;

bool equals(Person other) {
return Name.equals(other) &&
Addr.equals(other);
}
}

--
/Jacob Carlborg
```
Jun 04
mw <mingwu gmail.com> writes:
```On Friday, 5 June 2020 at 06:40:06 UTC, Jacob Carlborg wrote:
It's already possible to do that today:

class Person : NameI, AddrI {
mixin NameT!Person Name;
mixin AddrT!Person Addr;

bool equals(Person other) {
return Name.equals(other) &&
Addr.equals(other);
}
}

Thank you for letting me know.

This alleviates the name clashing problem, but didn't completely
solve it, this renaming is still coarse-grained all-or-none, e.g:

class Visitor {
mixin UKResident UKR;
mixin USResident USR;
}

all the attributes in UKResident.<attr> is rename to UKR.<attr>
and
all the attributes in USResident.<attr> is rename to USR.<attr>

While we want to achieve:

UKR.name  === USR.name (have the same  storage)
UKR.addr !=== USR.addr (have different storage)

i.e. fine-grained control on each mixin's attribute to be either
joined or separated.
```
Jun 05