September 28, 2020
On Monday, 28 September 2020 at 18:44:51 UTC, H. S. Teoh wrote:
> [...]
>
> `alias this` is one of the things that seemed like a good idea at the time, but is turning out to be something that leads to code smells.  If anything, we'd like to get rid of it rather than extend it!
>
>
> T

So what is the alternative? I'm not that enthusiastic of mixin templates because it just inserts everything like you would copy and paste text. That way you have to be careful when using several mixin templates that they don't use the exact same variable name for example so it kind of becomes inconvenient. What happens with private: and public:, do they bleed over into the non mixin code?

I like alias this because it still offers a way to encapsulation what you want to insert.

Multiple inheritance isn't that much of a problem as interfaces solves that but still you want to be able to reuse implementations and not create wrappers for inner structs/classes by hand in every instance you use it.

September 28, 2020
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?

Regardless of how Eiffel does it, supporting either case in D is going to be a mess, because it changes the memory layout of B and C, meaning you'd have to decide beforehand which solution is desired, even if D hasn't even been written yet (and the need to implement D hasn't even come up yet).  The only way I can think of to support both cases without requiring foretelling the future is to access A's members via a virtual table, which will basically break a lot of the ABI, introduce another layer of indirection (with its performance implications), and require extensive rewriting of D's codegen, etc..  Basically, a HUGE amount of work to implement it and fix the resulting breakage, all for the marginal (I'd even say questionable) benefit of being able to use multiple inheritance.

I honestly doubt this will ever happen in D, even if you set aside the question of whether this is even a good idea to begin with.


T

-- 
For every argument for something, there is always an equal and opposite argument against it. Debates don't give answers, only wounded or inflated egos.
September 28, 2020
On 9/28/20 2:44 PM, H. S. Teoh wrote:
> On Mon, Sep 28, 2020 at 06:28:05PM +0000, mw via Digitalmars-d wrote:
>> On Monday, 28 September 2020 at 18:13:15 UTC, H. S. Teoh wrote:
>>> On Mon, Sep 28, 2020 at 05:59:19PM +0000, mw via Digitalmars-d wrote:
>>>
>>> As somebody already said, that's all well and good for Eiffel. It's
>>> not so clear how to integrate this nicely in D.
>>
>> D has `alias` already,
> [...]
> 
> `alias this` is one of the things that seemed like a good idea at the
> time, but is turning out to be something that leads to code smells.  If
> anything, we'd like to get rid of it rather than extend it!

I'd love to get a better idea about this, e.g. a few clear examples of bad uses and other few good examples where it's a win. My intuition vaguely revolves around "aliasing to an rvalue is bad and lvalue is good" but I year for clarity.

September 28, 2020
On 9/28/20 3:55 PM, Andrei Alexandrescu wrote:
> On 9/28/20 2:44 PM, H. S. Teoh wrote:
>> On Mon, Sep 28, 2020 at 06:28:05PM +0000, mw via Digitalmars-d wrote:
>>> On Monday, 28 September 2020 at 18:13:15 UTC, H. S. Teoh wrote:
>>>> On Mon, Sep 28, 2020 at 05:59:19PM +0000, mw via Digitalmars-d wrote:
>>>>
>>>> As somebody already said, that's all well and good for Eiffel. It's
>>>> not so clear how to integrate this nicely in D.
>>>
>>> D has `alias` already,
>> [...]
>>
>> `alias this` is one of the things that seemed like a good idea at the
>> time, but is turning out to be something that leads to code smells.  If
>> anything, we'd like to get rid of it rather than extend it!
> 
> I'd love to get a better idea about this, e.g. a few clear examples of bad uses and other few good examples where it's a win. My intuition vaguely revolves around "aliasing to an rvalue is bad and lvalue is good" but I year for clarity.
> 

s/year/yearn/
September 28, 2020
On Monday, 28 September 2020 at 19:55:10 UTC, Andrei Alexandrescu wrote:
> On 9/28/20 2:44 PM, H. S. Teoh wrote:
>> On Mon, Sep 28, 2020 at 06:28:05PM +0000, mw via Digitalmars-d wrote:
>>> On Monday, 28 September 2020 at 18:13:15 UTC, H. S. Teoh wrote:
>>>> On Mon, Sep 28, 2020 at 05:59:19PM +0000, mw via Digitalmars-d wrote:
>>>>
>>>> As somebody already said, that's all well and good for Eiffel. It's
>>>> not so clear how to integrate this nicely in D.
>>>
>>> D has `alias` already,
>> [...]
>> 
>> `alias this` is one of the things that seemed like a good idea at the
>> time, but is turning out to be something that leads to code smells.  If
>> anything, we'd like to get rid of it rather than extend it!
>
> I'd love to get a better idea about this, e.g. a few clear examples of bad uses and other few good examples where it's a win. My intuition vaguely revolves around "aliasing to an rvalue is bad and lvalue is good" but I year for clarity.

An example of bad use is when you "alias this" a class inside a class. A good case is when you "alias this" a struct inside a struct.

An ideal case for me is allowing classes to implement opimplicit that strictly returns a copy of a value type, so that we can get rid alias this for classes.

-Alex
September 28, 2020
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 remove keep 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

September 28, 2020
On Monday, 28 September 2020 at 20:28:17 UTC, mw wrote:
> On Monday, 28 September 2020 at 19:41:07 UTC, H. S. Teoh wrote:
>> 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?

I didn't handle just 2 cases, but all kinds of cases, whether in the Derived class D.x. you want *any* or even *none* of A.x, B.x, C.x, all kinds of combination, the programmer can specify exactly what s/he wanted using

-- undefine
-- redefine
-- rename
-- select

I showed 5 examples in Eiffel way, you can do it as an exercise of all the other combinations.

September 28, 2020
On Mon, Sep 28, 2020 at 08:45:33PM +0000, mw via Digitalmars-d wrote:
> On Monday, 28 September 2020 at 20:28:17 UTC, mw wrote:
> > On Monday, 28 September 2020 at 19:41:07 UTC, H. S. Teoh wrote:
> > > 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?
> 
> I didn't handle just 2 cases, but all kinds of cases, whether in the Derived class D.x. you want *any* or even *none* of A.x, B.x, C.x, all kinds of combination, the programmer can specify exactly what s/he wanted using
> 
> -- undefine
> -- redefine
> -- rename
> -- select
> 
> I showed 5 examples in Eiffel way, you can do it as an exercise of all the other combinations.

Good. Now make it work for D. :-P

Note that this is easy to do in Eiffel because it has a dynamic memory layout; D does not.  Making it work for D will be extremely complex, and probably will not justify the comparatively meager benefits relative to the cost.


T

-- 
Today's society is one of specialization: as you grow, you learn more and more about less and less. Eventually, you know everything about nothing.
September 28, 2020
On Monday, 28 September 2020 at 21:11:04 UTC, H. S. Teoh wrote:
>
> Good. Now make it work for D. :-P
>
> Note that this is easy to do in Eiffel because it has a dynamic memory layout; D does not.  Making it work for D will be

Not sure what you mean by dynamic memory layout.

Eiffel is a statically compiled language. All the class's memory layout is know at compile time (as it's fully specified by the programmer in the source code).

BTW, many (if not all) Eiffel compilers actually compile Eiffel program to C (as target language).

I just uploaded SmartEiffel (open source) 1.1 compiler, and my previous visitor example to:

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

You can play with it.

And the generated C code of my example is:

https://github.com/mingwugmail/dlang_tour/blob/master/eiffel/visitor/app1.c

You can study it.

> extremely complex, and probably will not justify the comparatively meager benefits relative to the cost.

Well, we are solving the sub-typing `alias this` & various mixin problems here in Dlang in the very thread.

September 28, 2020
On 9/28/2020 12:55 PM, Andrei Alexandrescu wrote:
> I'd love to get a better idea about this, e.g. a few clear examples of bad uses and other few good examples where it's a win. My intuition vaguely revolves around "aliasing to an rvalue is bad and lvalue is good" but I year for clarity.

There are several bugzilla issues on alias this that aren't fixed because there was no clear way to do it that both made sense and did not break existing code.

If a class hierarchy has alias this, when does the compiler go looking down the alias this, and when does it go looking at the base class and interfaces? (Of course the base class can have an alias this, and an alias this can have a base class.) You have essentially *two* multiple inheritance systems in play at the same time, each obeying different rules.

Couple that with the current arbitrary random rules about how alias this behaves for classes, and any coherent MI alias this scheme would randomly break existing code.