December 21, 2018
On Friday, 21 December 2018 at 11:25:18 UTC, Walter Bright wrote:
> On 12/21/2018 2:42 AM, Atila Neves wrote:
>> I don't think any lisper has ever complained about Lisp macros - on the contrary, it's considered one of Lisp's killer features. It's a shame they're inextricably linked to the syntax (or lack thereof) and that nobody has yet figured out a good macro system that doesn't depend on S-expressions.
>
> Probably because Lisp is unusable without macros,

Lisp is perfectly usable without macros, it's just that it would never occur to anyone to do that. D is usable without `foreach`, but why would I program with that handicap? In Lisp, not using macros usually means typing "lambda" more than one would like. `foreach` isn't even the best example in D-land, `mixin` is. I think we can agree that `mixin` is a huge win for D. Lisp macros are like that, but much better.

> sort of like how C and C++ are unusable without the preprocessor.

That's completely different - C/C++ have no way to reuse code other than the preprocessor. One technically could write a whole project without it, but it'd be madness. It's not at all like the Lisp case.

> Lisp is a great language for its ideas, and is a fine language for research purposes. But somehow it just never catches on.

That depends on the definition of "catches on". Every Emacs user has to write Lisp (if they don't they might as well use notepad++); Common Lisp, Scheme and Clojure are all on Tiobe's top 50; Emacs Lisp and Clojure both outrank D in Github popularity (Common Lisp isn't that far behind).

I dare say there are orders of magnitude more programmers using Lisp in the browser (via Clojurescript) than there are D programmers doing the same.


December 21, 2018
On 21.12.18 05:09, Walter Bright wrote:
> On 12/20/2018 7:21 PM, Timon Gehr wrote:
>> But it's a solved problem. Just make `alias this` exactly like `import`.
> 
> Imports don't have adjuster thunks,

Neither does `alias this`.

> implicit cast issues,

Treat implicit casting with `alias this` like an invocation of an imported function.

> slicing,

`alias this` does not have the slicing problem because it can't return something that shares the vtable with the original reference but has a different layout.

> or empty base optimizations.
With `alias this`, empty base optimization has to be done manually.

> Import lookup in a class is different because of the two-phase nature of it (people complained bitterly about the earlier single phase lookup).
> ...

Why should it be different? It's a "look up names in one of those scopes" problem. Same problem, same solution.

> Import lookup doesn't have to conform to the relevant undocumented C++ ABI, either.> (It was about a year of bug reports until I was finally able to match Microsoft C++'s MFC behavior for MI struct layout.)
> ...

Why would multiple `alias this` have to conform to any C++ ABIs?


>    class C : B {
>      B b;
>      alias b this;
>    }
> 
> Now what? (This is not specified in the dlang spec.)

This is a single alias this problem. But checking imports, this should give ambiguity errors by default (for both member access and implicit/explicit casting) and there should be a way to explicitly disambiguate.
December 21, 2018
On Friday, 21 December 2018 at 07:23:41 UTC, Manu wrote:
> On Thu., 20 Dec. 2018, 7:35 pm Walter Bright via Digitalmars-d < digitalmars-d@puremagic.com wrote:
>
>> On 12/20/2018 10:38 AM, H. S. Teoh wrote:
>> > There has been multiple attempts to implement it, but none were persistent enough to see it through to the finish line.
>>
>> Nobody has figured out the semantics of single alias this, either (when
>> faced
>> with class inheritance). The bugzilla issues for it are ample evidence.
>>
>> MI is a Hansel and Gretel feature, where it appears to be all gingerbread
>> and
>> candy, but the witch inevitably appears to pop you in the oven.
>>
>>
>> Other Hansel and Gretel features:
>>
>> 1. monkey patching
>> 2. macros
>> 3. implicit variable declaration
>> 4. preprocessor
>>
>
> I don't think I've ever wanted multiple inheritance in D, but I do want implicit conversions very often, and occasionally multiple implicit conversions.
I want implicit conversions here as well.What I don't want is this to be achievable via alias this, as you have no control over how it implicit case. I rather to do it via opCast.

Alex

December 21, 2018
On Thursday, 20 December 2018 at 03:42:08 UTC, Walter Bright wrote:
>
> Because polymorphism makes little sense for a value type.
>

What happens is in D we often want:
A - deterministic destruction
AND
B - reference semantics
AND
C - sub-typing (interfaces / alias this)

`class` does B and C, and even A when emplaced, but not that easy to use. Don't work in -betterC. GC will call destructors which breaks (A) if we aren't careful.

`struct` does A, B when wrapped, that not C very well.
December 21, 2018
On 12/20/18 10:08 PM, Walter Bright wrote:
> On 12/20/2018 6:55 AM, Steven Schveighoffer wrote:
>> really we should implement multiple-alias-this and that problem would be solved.
> 
> And create a host of other problems. Multiple alias this is multiple inheritance with all of C++'s problems with it, and more, because then you wind up with two (count 'em, 2!) multiple inheritance hierarchies. Madness. Even single alias this has spawned a fair amount of wreckage in bugzilla, because people use it for multiple inheritance which it was never intended for.

The issues I've seen in bugzilla are how alias this overrides default expected behavior of classes. In other words, if you have a base class and want to cast it to a derived, and the base class has an alias this, it stupidly tries the alias this, and won't even consider a dynamic cast.

It's not madness so much as poor implementation. Saying there are bugs in the alias this implementation is not the same as saying the feature is problematic. All that is needed is a good definition of what takes precedence -- we already have that, it's just not implemented properly.

> MI is just a bad idea. It's best to just think of another way to structure the data - even if you get it to work, it'll just confuse the poor schlub who has to maintain the code.

But MI is not multiple alias-this. alias-this is more like an implicit cast, where as multiple inheritance has to deal with the diamond problem, and other issues (like the expectation of base types living at the same address as the derived one). Would you say that C++ operator conversions has all the same problems as MI?

-Steve
December 21, 2018
On 12/21/2018 3:54 AM, Atila Neves wrote:
> On Friday, 21 December 2018 at 11:25:18 UTC, Walter Bright wrote:
>> Probably because Lisp is unusable without macros,
> 
> Lisp is perfectly usable without macros,

Technically true. Doubtful that is pragmatically true once a program exceeds a certain size.


>> sort of like how C and C++ are unusable without the preprocessor.
> 
> That's completely different - C/C++ have no way to reuse code other than the preprocessor. One technically could write a whole project without it, but it'd be madness. It's not at all like the Lisp case.

In my subjective opinion, it is :-)


>> Lisp is a great language for its ideas, and is a fine language for research purposes. But somehow it just never catches on.
> 
> That depends on the definition of "catches on". Every Emacs user has to write Lisp (if they don't they might as well use notepad++); Common Lisp, Scheme and Clojure are all on Tiobe's top 50; Emacs Lisp and Clojure both outrank D in Github popularity (Common Lisp isn't that far behind).
> 
> I dare say there are orders of magnitude more programmers using Lisp in the browser (via Clojurescript) than there are D programmers doing the same.

I've written Elisp extensions in Emacs, too. But they were all just a dozen or two lines. It never inspired me to write a larger program in it.

I considered adding scripting to microEmacs, but I was going to use Javascript for that. :-)

Lisp is an easy language to implement, document, and sandbox, making it handy for use as an embedded scripting language. Javascript takes months to implement by someone experienced writing compilers, making it not the first thing an IDE developer would try.

D isn't any more suitable for in-browser code than C++ is. A native language with raw pointers would not be a good choice for an embeddable scripting language.

Javascript has been incredibly successful as an embedded language in web pages.
December 21, 2018
On 12/21/2018 11:08 AM, Steven Schveighoffer wrote:
>> The issues I've seen in bugzilla are how alias this overrides default expected 
> behavior of classes. In other words, if you have a base class and want to cast it to a derived, and the base class has an alias this, it stupidly tries the alias this, and won't even consider a dynamic cast.
> 
> It's not madness so much as poor implementation. Saying there are bugs in the alias this implementation is not the same as saying the feature is problematic. All that is needed is a good definition of what takes precedence -- we already have that, it's just not implemented properly.

The ones I reviewed do not have easy answers as to what the correct behavior "ought" to be, and it certainly is not spec'ed in the specification.


> But MI is not multiple alias-this.

It fundamentally is. (Though I agree it doesn't have the virtual base "diamond inheritance" issue.)

December 21, 2018
On 12/21/18 4:46 PM, Walter Bright wrote:
> On 12/21/2018 11:08 AM, Steven Schveighoffer wrote:
>>> The issues I've seen in bugzilla are how alias this overrides default expected 
>> behavior of classes. In other words, if you have a base class and want to cast it to a derived, and the base class has an alias this, it stupidly tries the alias this, and won't even consider a dynamic cast.
>>
>> It's not madness so much as poor implementation. Saying there are bugs in the alias this implementation is not the same as saying the feature is problematic. All that is needed is a good definition of what takes precedence -- we already have that, it's just not implemented properly.
> 
> The ones I reviewed do not have easy answers as to what the correct behavior "ought" to be, and it certainly is not spec'ed in the specification.

Which ones?

Fundamentally, alias this brings behavior for types where default behavior doesn't exist. For classes, casting to void * or another object is defined, so alias this should not override. Principal of least surprise. That's the only bug I've seen.

>> But MI is not multiple alias-this.
> 
> It fundamentally is. (Though I agree it doesn't have the virtual base "diamond inheritance" issue.)

In terms of which alias this is used, we have leeway to define whatever we want. Alias this acts like inheritance, but it's NOT inheritance. We can say there is a specific order of precedence between the alias this'd items (I wouldn't recommend that), or we could say more than one alias this being usable in the same expression is an ambiguity error.

I don't think the precedence rules are that complicated:

1. The type's members or base classes
2. The type's alias this'd members. Any ambiguities are a compiler error.
3. UFCS functions that accept the type or base classes.
4. UFCS functions that accept any alias this'd type. Any ambiguities are a compiler error.

Unlike MI, there is a specific member you can use to disambiguate, which is way less clunky than MI, where you need a cast, or to name the type itself.

My vision:

void foo(int x) { writeln("foo int"); }
void foo(T t) { writeln("foo T"); }

void bar(int x) { writeln("bar int"); }

void baz(int x) { writeln("baz int"); }
void baz(T t) { writeln("baz T"); }
void baz(S s) { writeln("baz S"); }

void foobar(int x) { writeln("foobar int"); }
void foobar(T t) { writeln("foobar T"); }

struct T
{
   void bar() { writeln("bar T"); }
}

struct S
{
   int x;
   T t;
   alias this x;
   alias this t;
   void foo() { writeln("S");}
}

S s;
s.foo(); // foo S
s.bar(); // bar T; members take precedence over UFCS
s.baz(); // baz S; main type takes precedence over alias this'd
s.foobar(); // Error, both alias this'd have same precednece for UFCS
s.x.foobar(); // foobar int
s.t.foobar(); // foobar T

Don't have the "both alias this'd have the same member" error, but you get the idea.

And your example from earlier:

class C : B
{
  B b;
  alias b this;
}

Basically, the alias this b is never used, because C will always take precedence.

If you think any of this is unintuitive, please let me know.

-Steve
December 21, 2018
On 12/21/2018 5:48 AM, Timon Gehr wrote:
> On 21.12.18 05:09, Walter Bright wrote:
>> On 12/20/2018 7:21 PM, Timon Gehr wrote:
>>> But it's a solved problem. Just make `alias this` exactly like `import`.
>>
>> Imports don't have adjuster thunks,
> 
> Neither does `alias this`.

That depends on how it is specified. For example, should functions found through 'alias this' go into the vtbl[]? (It's not a vacuous question, it has come up with people wanting that to happen. I said no.)


>> implicit cast issues,
> Treat implicit casting with `alias this` like an invocation of an imported function.

It's not that simple. There are issues with what is considered a "better" match, several of which are in bugzilla w.r.t. alias this.


>> slicing,
> `alias this` does not have the slicing problem because it can't return something that shares the vtable with the original reference but has a different layout.

That all depends on how one defines how alias this lookup works wrt providing functions to be installed in the vtbl[].


>> Import lookup in a class is different because of the two-phase nature of it (people complained bitterly about the earlier single phase lookup).
>> ...
> 
> Why should it be different? It's a "look up names in one of those scopes" problem. Same problem, same solution.

That isn't how import lookup works today when import declarations appear in classes. It originally worked that way, but nobody understood it. I was UNABLE to explain that import lookup worked exactly like member lookup. I even showed the code which was the same code. But everyone, simply could not understand this and insisted it was behaving bizarrely. So now it is a much more complex 2 phase lookup that behaves "intuitively". It's a great lesson in how "obvious" and "intuitive" behavior for a person is anything but as an algorithm.


>> Import lookup doesn't have to conform to the relevant undocumented C++ ABI, either.> (It was about a year of bug reports until I was finally able to match Microsoft C++'s MFC behavior for MI struct layout.)
> Why would multiple `alias this` have to conform to any C++ ABIs?

Because people can, will, and do (see Manu) use it to try to emulate struct inheritance for C++ structs.


>>    class C : B {
>>      B b;
>>      alias b this;
>>    }
>>
>> Now what? (This is not specified in the dlang spec.)
> 
> This is a single alias this problem. But checking imports, this should give ambiguity errors by default (for both member access and implicit/explicit casting) and there should be a way to explicitly disambiguate.

That isn't how imports work (the base class lookup takes precedence over import lookup).

To sum up, the behavior of single alias this is not spec'ed, and there are a lot of decisions to make to tighten that up that are not obvious. All that would have to be done before any multiple alias this can be considered. The experience with MI in C++ was that a seemingly simple idea turned out to have a great deal of unexpected complexity, most of it turning up later. And worse, even when all that was eventually worked out and the implementation bugs fixed, the benefits of MI to programming pretty much failed to materialize.
December 21, 2018
On 12/21/18 5:23 PM, Steven Schveighoffer wrote:
> 1. The type's members or base classes
> 2. The type's alias this'd members. Any ambiguities are a compiler error.
> 3. UFCS functions that accept the type or base classes.
> 4. UFCS functions that accept any alias this'd type. Any ambiguities are a compiler error.

A simple way to put this is -- if the code would compile WITHOUT alias this, then that's what happens.

-Steve