jll63 / openmethods.d

Open multi-methods for the D language. OPEN! Multi is cool. Open is great.
44 stars 8 forks source link

brain storming idea: implement Eiffel's OO, esp. multiple inheritance, and use it with openmethods #19

Open mw66 opened 3 years ago

mw66 commented 3 years ago

This is the idea from the discussion:

https://forum.dlang.org/post/ltkcbcwtgrxceesnnxqk@forum.dlang.org

Background:

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 (multiple-) alias this, which caused many unsolvable troubles in current D.

Actually the diamond problem is a solved problem by Eiffel language, which won the 2006 ACM Software System Award:

https://en.wikipedia.org/wiki/ACM_Software_System_Award

And I showed the concrete example here: https://forum.dlang.org/thread/obqthozmxwzhvrafothw@forum.dlang.org

https://forum.dlang.org/post/rb4seo$bfm$1@digitalmars.com The two hallmarks of Eiffel, is design-by-contract and multiple inheritance, which is known in academics (in my former life).

it's a pity that

D only picked design-by-contract from Eiffel, but throw away multiple inheritance,

... and instead introduced sub-typing, mixin,

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

Proposal:

On Tuesday, 29 September 2020 at 09:56:47 UTC, Petar Kirov [ZombineDev] wrote:

I think one extravagant, but definetely workable solution would be create a template DSL (ofc it could also be string mixin-based) in D to prototype a multiple inheritance system (with e.g. the Eiffel features rename, export, undefine, redefine / override, and select). Being just a prototype, it should be acceptable to go with custom functions like isSubTypeOf, asBaseType, etc., instead of using the built-in language syntax and semantics. (Sort of like https://code.dlang.org/packages/openmethods adds multiple dispatch.)

This is an interesting idea, basically implement the Eiffel compiler (the OO part) as a D library, and perhaps also use openmethods, then we will have a Lisp's multi-methods + Eiffel OO inheritance.

I'm not sure how complex this implementation is going to look like.

mw66 commented 3 years ago

https://forum.dlang.org/thread/obqthozmxwzhvrafothw@forum.dlang.org

Challenge 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:

https://github.com/mingwugmail/dlang_tour/tree/master/eiffel/visitor

mw66 commented 3 years ago

Another example, in pseudo D code:

https://forum.dlang.org/post/pidvdadmyqceqqkdfkcv@forum.dlang.org

On Monday, 28 September 2020 at 19:41:07 UTC, H. S. Teoh wrote:

On Mon, Sep 28, 2020 at 06:56:40PM +0000, mw via Digitalmars-d wrote: [...]

It's all about resolve name clashing: alias means synonyms; let's just borrow from Eiffel, instead of re-invent the wheels: the main concepts to solve multiple inheritance are these 5 keywords:

https://www.eiffel.org/doc/eiffel/Eiffel_programming_language_reserved_words

rename export undefine redefine -- D's override select

I don't know Eiffel; could you enlighten me as to how it solves the following instance of the diamond problem?

struct Resource { this(...) { acquireResource(); } ~this() { releaseResource(); } }

class A { Resource x; }

class B : A { ... }

class C : A { ... }

class D : B, C { // Should D have one instance of A.x, or two instances // of A.x? (Putting aside the question of naming for the // time being -- let's pretend we have a way of // addressing x somehow in either case.) }

I can see some situations for which you want two distinct instances of A.x (there should be two distinct resources acquired by D), and some other situations for which you want them to be the same (the same resource should be shared by B and C). How does Eiffel cater to both cases?

The Resource A.x in your example is what I have shown in my example PERSON.addr and PERSON.name here:

https://forum.dlang.org/thread/obqthozmxwzhvrafothw@forum.dlang.org

1) In Eiffel, by default the attribute is shared/joined, meaning in your above code as it its, there is only 1 x in D; and that's the PERSON.name in my example (no special treatment, it's just joined in D).

2) If the application do want separate instances of one attribute, PERSON.addr in my example, then use rename / select to choose the one the programmer wanted semantics in mind. So in this case, your example is:

class B : A {...}
class C : A {...}

// let's suppose the application's semantics want to use C.x, but still keep B.x
class D1 : B(rename x as bx)    // (rename ...) is my invented Eiffel syntax in D
         , C(select x) {        // (select ...) is my invented Eiffel syntax in D
  ...
}

// let's suppose the application's semantics want to use B.x, but still keep C.x
class D2 : B(select x)
         , C(rename x as cx) {
  ...
}

// let's suppose the application's semantics want to use B.x, but remove C.x
class D3 : B(select x)
         , C(undefine x) {
  ...
}

// let's suppose the application's semantics want to use C.x, but remove B.x
class D4 : B(undefine x)
         , C(select x) {
  ...
}

// let's suppose the application's semantics want to remove both B.x and C.x
class D5 : B(undefine x)
         , C(undefine x) {
  redefine x;  // need to redefine x
}

// let's suppose the application's semantics want to keep  both B.x and C.x
class D6 : B(rename x as bx)
         , C(rename x as cx) {
  redefine x;  // need to redefine x
}

... as you can see, all different application semantics can be specified by the programmer.

Please check my previous PERSON.addr example more carefully.

PERSON.addr UK_RESIDENT(: PERSON).addr US_RESIDENT(: PERSON).addr VISITOR(: UK_RESIDENT, US_RESIDENT).addr

https://forum.dlang.org/post/obqthozmxwzhvrafothw@forum.dlang.org