January 04, 2021
On 04.01.21 14:47, Ola Fosheim Grøstad wrote:
> Indirection through a parametric alias. This is the simplest I have come up with so far:
> 
> 
>   struct Foo(T) {}
> 
>   alias Bar(T) = Foo!T;
> 
>   void f(T)(Bar!T x) {}
> 
>   void main() {
>     f(Bar!int());
>   } 

On 04.01.21 14:54, Ola Fosheim Grøstad wrote:
> Typo: "discriminate between". An alias should be indistinguishable from the object, you are only naming something. You should be able to use whatever names you fancy without that having semantic implications, that's the core PL design principle.

`Bar!int` is an alias. It's indistinguishable from `Foo!int`. The code fails in the same manner when you replace "Bar!int" with "Foo!int".

`Bar!T` is not yet an alias. You're asking the compiler to find a `T` so that `Bar!T` becomes an alias to `Foo!int`. The compiler doesn't know how to do that.

Issue 1807 is well worth fixing/implementing. But it's not a case of DMD making a difference between an alias and its source.
January 04, 2021
On Monday, 4 January 2021 at 14:11:28 UTC, ag0aep6g wrote:
> `Bar!int` is an alias. It's indistinguishable from `Foo!int`. The code fails in the same manner when you replace "Bar!int" with "Foo!int".


Wrong. This succeeds:

 struct Foo(T) {}

  alias Bar(T) = Foo!T;

  void f(T)(Foo!T x) {}

  void main() {
    f(Bar!int());
  }


January 04, 2021
On 04.01.21 15:37, Ola Fosheim Grøstad wrote:
> On Monday, 4 January 2021 at 14:11:28 UTC, ag0aep6g wrote:
>> `Bar!int` is an alias. It's indistinguishable from `Foo!int`. The code fails in the same manner when you replace "Bar!int" with "Foo!int".
> 
> 
> Wrong. This succeeds:
> 
>   struct Foo(T) {}
> 
>    alias Bar(T) = Foo!T;
> 
>    void f(T)(Foo!T x) {}
> 
>    void main() {
>      f(Bar!int());
>    }

You didn't replace "Bar!int" with "Foo!int". You replaced "Bar!T" with "Foo!T". That's something else entirely.
January 04, 2021
On Monday, 4 January 2021 at 14:40:31 UTC, ag0aep6g wrote:
> You didn't replace "Bar!int" with "Foo!int". You replaced "Bar!T" with "Foo!T". That's something else entirely.

No, it isn't. When it is instantiated you get "Bar!int" and then the unification would substitute that with "Foo!int".

This is basic type system design. Nothing advanced. Just plain regular unification.

This should even be worth discussing... the fact that it is being debated isn't promising for D's future...


January 04, 2021
On Monday, 4 January 2021 at 14:44:00 UTC, Ola Fosheim Grøstad wrote:
> On Monday, 4 January 2021 at 14:40:31 UTC, ag0aep6g wrote:
>> You didn't replace "Bar!int" with "Foo!int". You replaced "Bar!T" with "Foo!T". That's something else entirely.
>
> No, it isn't. When it is instantiated you get "Bar!int" and then the unification would substitute that with "Foo!int".
>
> This is basic type system design. Nothing advanced. Just plain regular unification.
>
> This should even be worth discussing... the fact that it is being debated isn't promising for D's future...

Also, keep in mind that the type isn't "Foo", that is also just a name! The true type would be a nominal "struct _ {}". If you through alias say that an object has two equivalent names, then the type system better behave accordingly.


January 04, 2021
On Monday, 4 January 2021 at 14:40:31 UTC, ag0aep6g wrote:
> On 04.01.21 15:37, Ola Fosheim Grøstad wrote:
>> On Monday, 4 January 2021 at 14:11:28 UTC, ag0aep6g wrote:
>>> `Bar!int` is an alias. It's indistinguishable from `Foo!int`. The code fails in the same manner when you replace "Bar!int" with "Foo!int".
>> 
>> 
>> Wrong. This succeeds:
>> 
>>   struct Foo(T) {}
>> 
>>    alias Bar(T) = Foo!T;
>> 
>>    void f(T)(Foo!T x) {}
>> 
>>    void main() {
>>      f(Bar!int());
>>    }
>
> You didn't replace "Bar!int" with "Foo!int". You replaced "Bar!T" with "Foo!T". That's something else entirely.

IMO, this is a better example, even if it's a little more verbose.

struct Foo(T) {}

alias Bar(T) = Foo!T;

void f(T)(Bar!T x) {}

void main() {
    auto x = Bar!int();
    f(x);
}
January 04, 2021
On Monday, 4 January 2021 at 15:03:05 UTC, jmh530 wrote:
> On Monday, 4 January 2021 at 14:40:31 UTC, ag0aep6g wrote:
>> On 04.01.21 15:37, Ola Fosheim Grøstad wrote:
>>> On Monday, 4 January 2021 at 14:11:28 UTC, ag0aep6g wrote:
>>>> `Bar!int` is an alias. It's indistinguishable from `Foo!int`. The code fails in the same manner when you replace "Bar!int" with "Foo!int".
>>> 
>>> 
>>> Wrong. This succeeds:
>>> 
>>>   struct Foo(T) {}
>>> 
>>>    alias Bar(T) = Foo!T;
>>> 
>>>    void f(T)(Foo!T x) {}
>>> 
>>>    void main() {
>>>      f(Bar!int());
>>>    }
>>
>> You didn't replace "Bar!int" with "Foo!int". You replaced "Bar!T" with "Foo!T". That's something else entirely.
>
> IMO, this is a better example, even if it's a little more verbose.
>
> struct Foo(T) {}
>
> alias Bar(T) = Foo!T;
>
> void f(T)(Bar!T x) {}
>
> void main() {
>     auto x = Bar!int();
>     f(x);
> }

Also, the typesystem clearly sees the same type with two names, so there is no new nominal type (obviously):

  struct Foo(T) {}
  alias Bar(T) = Foo!T;

  static assert(is(Bar!int==Foo!int));

We are talking unification over complete types, unification over incomplete types would be more advanced... but this isn't that. We don't start unification until we have a concrete complete type to start working with.



January 04, 2021
On 04.01.21 15:44, Ola Fosheim Grøstad wrote:
> On Monday, 4 January 2021 at 14:40:31 UTC, ag0aep6g wrote:
>> You didn't replace "Bar!int" with "Foo!int". You replaced "Bar!T" with "Foo!T". That's something else entirely.
> 
> No, it isn't.

Of course it is. Replacing foo with bar is not the same as replacing baz with qux. The resulting code is different. The compiler output is different.

My original post stands.

> When it is instantiated you get "Bar!int" and then the unification would substitute that with "Foo!int".

In `f(Bar!int());`, `Bar!int` is expanded to `Foo!int` before IFTI is attempted. When `f` is instantiated, any mention of `Bar` is long forgotten. The compiler sees the argument type as `Foo!int` (the type, not the string "Foo!int"). From there it deduces `T` = `int` when the parameter is `Foo!T x`, or it fails the instantiation when the parameter is `Bar!T x`. Either way, there's  no "Bar!int" anymore that would be replaced by anything.

> This is basic type system design. Nothing advanced. Just plain regular unification.

As far as I understand, describing what DMD does as "unification" would be a stretch. You might have a point saying that DMD should do "plain regular unification" instead of the ad hoc, undocumented hacks it does right now.
January 04, 2021
On Monday, 4 January 2021 at 15:15:50 UTC, ag0aep6g wrote:
> As far as I understand, describing what DMD does as "unification" would be a stretch. You might have a point saying that DMD should do "plain regular unification" instead of the ad hoc, undocumented hacks it does right now.

Unification is what you do with parametric types, even if it implemented in an ad hoc manner that turns out to not work...

The funny thing is that this would have worked with regular macro expansion.

January 04, 2021
On Tuesday, 29 December 2020 at 19:59:56 UTC, Ola Fosheim Grøstad wrote:
> On Tuesday, 29 December 2020 at 16:14:59 UTC, Atila Neves wrote:
>> [...]
>
> I am not speaking for Ilya, but from skimming through the dialogue it struck me that you didn't respond from the perspective of managing the process, but from a pure engineer mindset of providing alternatives.
>
> It would've been better if you started by 1. understanding the issue 2. acknowledging that the type system has an obvious bug 3. looking at the issue from the perspective of the person bringing attention to the issue. I don't think anyone was looking for workarounds, but looking for
>
> 1. acknowledgment of the issue
> 2. acknowledgment of what the issue leads to in terms of inconvenience
> 3. a forward looking vision for future improvements

Your two #1 points aren't the same - understanding/acknowledging the issue. I think I could have done more to acknowledge it now that you brought it up.