January 04, 2021
On Monday, 4 January 2021 at 17:24:42 UTC, John Colvin wrote:
> On Monday, 4 January 2021 at 17:22:55 UTC, John Colvin wrote:
>> On Monday, 4 January 2021 at 13:47:17 UTC, Ola Fosheim Grøstad wrote:
>>> [...]
>>
>> I have a longer reply I'm trying to write, but just to make sure I'm on the right track:
>>
>>     template Foo(T) {
>>         alias Foo = T;
>>     }
>>
>>     template Q(A : Foo!T, T) {
>>         pragma(msg, A.stringof, " ", T.stringof);
>>     }
>>
>>     alias X = Q!(Foo!int);
>>
>> in your opinion, this should compile and msg `int int`, yes?
>>
>> I'm trying to make a really concise example without using IFTI.
>
> and presumably the same for
>     alias X = Q!(int);
> yes?

Would this also imply:

enum bool Bar(A) = is(A : Foo!T, T);

void main() {
    static assert(Bar!(Foo!int));
    static assert(Bar!(int));
}
January 04, 2021
On Monday, 4 January 2021 at 01:19:12 UTC, jmh530 wrote:
> it makes things overly complicated
Just because a feature makes something simpler is not enough of an argument of why it should be added. Case and point C, lua and Go languages. They are popular in part because they are simple. That's why I said that no good arguments came after. This is not a good argument. A good argument would be one that shows that benefit is worth increased complexity.

> If you aren't writing templates, then it wouldn't affect you.
I know how to write simple template. I just dont use them recursively. I find it hard to reason about. Also I'm concerned about compilation speed and resulting code bloat. Maybe I'm just not experienced enough

> However, it was deemed beneficial enough that a form of it was added to C++ as part of C++ 11
Well D already support that just with different syntax.

struct too_long_name(T) {}
alias bar = too_long_name;
bar!(int);

> I wonder if the inability to do this would inhibit the ability of D code to interact with C++ code bases.

The way interop between D and C++ works is that you need to match function signatures and struct/class data layout on binary level. Then you just link. There is no cross language template instantiation and there will never be one. So it will not affect interop.

P.s. Thank you for a well written post with a link to useful read.


January 04, 2021
On Monday, 4 January 2021 at 21:07:49 UTC, welkam wrote:
> [snip]
> P.s. Thank you for a well written post with a link to useful read.

Thanks for reading it.
January 04, 2021
On Sunday, 3 January 2021 at 22:50:16 UTC, Ola Fosheim Grøstad wrote:
> YOU DO HAVE TO ACKNOWLEDGE A TYPE SYSTEM BUG!
>
> If an indirection through an alias causes type unification to fail then that is a serious type system failure. No excuses please...

Different people have different definitions of words. It's clear that your definition of bug does not match other people definition so instead of forcing other people to conform to your definition it would be beneficial if you could express your ideas using other words.

Secondly lets talk about

alias Bar!int = Foo!int;

or is it

alias Bar(T) = Foo!T;

Whatever. You want to alias template and assign a new name and then use the new name. Because you were not able to do that you say its obvious type system bug. I mean "You should be able to use whatever names you fancy without that having semantic implications". I guess type checking occurs during semantic analysis so its connected.

Anyway you want assign template name. Spoiler alert Bar!int is not a name. It's also not a type or even an object. You might used another term for how alias should work but I cant track them all. Its template instantiation.

Instead of
alias Bar!int = Foo!int;

use

alias Bar = Foo;
//or
alias Bar = foo!int;

for more read [1]

What you tried before was an attempt to assign template instantiation to another template instantiation or function call. If you want to assign name to name then write it that way.

When I got into personality types and typed myself I found out that my type doesnt respect physical world and details. And its true. I struggle with who where when. I some times get out of home without clipping my nails because I forgot to clip them. And I forgot because I was lost in my thoughts. Analyzing patterns is more important than being physically in the world. But what you displayed here is criminal neglect of details. There is difference between types, objects, names and symbols. There is difference between template declaration and initialization. There are differences between type system and language semantics. If you wont pay attention to these details ofcourse you will have problems communicating with people. And you failure to effectively communicate to others is not indication that its bad for D's future.

People say that you notice in others what you dont like in yourself.
1. https://dlang.org/spec/template.html#aliasparameters
January 04, 2021
On Monday, 4 January 2021 at 22:14:12 UTC, welkam wrote:
> Anyway you want assign template name. Spoiler alert Bar!int is not a name. It's also not a type or even an object. You might used another term for how alias should work but I cant track them all. Its template instantiation.

It is a name, e.g.:

alias BarInt = Bar!int;


"BarInt", "Bar!int" and "Foo!int" are all names, or labels, if you wish. And they all refer to the same object: the nominal type. Which you can test easily by using "is(BarInt==Foo!int)".


> When I got into personality types and typed myself I found out that my type doesnt respect physical world and details.

Drop ad hominem. Argue the case.

January 04, 2021
On Monday, 4 January 2021 at 22:55:28 UTC, Ola Fosheim Grøstad wrote:
> "BarInt", "Bar!int" and "Foo!int" are all names, or labels, if you wish. And they all refer to the same object: the nominal type. Which you can test easily by using "is(BarInt==Foo!int)".


If the terminology is difficult, let' call them "signifiers". If D add type-functions, then another signifier for the same type could be "Combine(Foo,int)".

It should not matter which signifier you use, if they all yield the exact same object (in the mathematical sense): the same nominal type "struct _ {}", then they should be interchangeable with no semantic impact.

This is a very basic concept in PL design. If you name the same thing several ways (any way you like), then the effect should be the same if you swap one for another. It should be indistiguishable.

January 04, 2021
On 1/4/2021 4:11 AM, 9il wrote:
> [...]
The reason those switches are provided is because the write/read is a performance hog.

D provides a couple functions in druntime which guarantee rounding intermediate values to float/double precision. Those can be used as required. This is better than a compiler switch because having compiler switches that influence floating point results is poor design.

> Since C99 the default x87 behavior is precise.

Not entirely:

 float f(float a, float b) {
    float d = (a + b) - b;
    return d;
 }

 f:
        sub     esp, 4
        fld     DWORD PTR [esp+12]
        fld     st(0)
        fadd    DWORD PTR [esp+8]
        [no write/read to memory here, so no round to float]
        fsubrp  st(1), st
        fstp    DWORD PTR [esp]
        fld     DWORD PTR [esp]
        add     esp, 4
        ret

In any case, let's try your example https://cpp.godbolt.org/z/7sa8dP with dmd for 32 bits:

                push    EAX
                push    EAX
                fld     float ptr 010h[ESP]
                fadd    float ptr 0Ch[ESP]
                fstp    float ptr [ESP]     // there's the write
                fld     float ptr [ESP]     // there's the read!
                fsub    float ptr 0Ch[ESP]
                fstp    float ptr 4[ESP]    // the write
                fld     float ptr 4[ESP]    // the read
                add     ESP,8
                ret     8

It's semantically equivalent to the godbolt asm you posted.
January 05, 2021
On Tuesday, 5 January 2021 at 03:20:16 UTC, Walter Bright wrote:
> On 1/4/2021 4:11 AM, 9il wrote:
>> [...]
> The reason those switches are provided is because the write/read is a performance hog.
>
> D provides a couple functions in druntime which guarantee rounding intermediate values to float/double precision. Those can be used as required. This is better than a compiler switch because having compiler switches that influence floating point results is poor design.
>
> > Since C99 the default x87 behavior is precise.
>
> Not entirely:
>
>  float f(float a, float b) {
>     float d = (a + b) - b;
>     return d;
>  }
>
>  f:
>         sub     esp, 4
>         fld     DWORD PTR [esp+12]
>         fld     st(0)
>         fadd    DWORD PTR [esp+8]
>         [no write/read to memory here, so no round to float]
>         fsubrp  st(1), st
>         fstp    DWORD PTR [esp]
>         fld     DWORD PTR [esp]
>         add     esp, 4
>         ret
>
> In any case, let's try your example https://cpp.godbolt.org/z/7sa8dP with dmd for 32 bits:
>
>                 push    EAX
>                 push    EAX
>                 fld     float ptr 010h[ESP]
>                 fadd    float ptr 0Ch[ESP]
>                 fstp    float ptr [ESP]     // there's the write
>                 fld     float ptr [ESP]     // there's the read!
>                 fsub    float ptr 0Ch[ESP]
>                 fstp    float ptr 4[ESP]    // the write
>                 fld     float ptr 4[ESP]    // the read
>                 add     ESP,8
>                 ret     8
>
> It's semantically equivalent to the godbolt asm you posted.

I can't reproduce the same DMD output as you.

DMD with flags -m32 -O generates

https://cpp.godbolt.org/z/9b4e9K
        assume  CS:.text._D7example1fFffZf
                push    EBP
                mov     EBP,ESP
                fld     float ptr 0Ch[ESP]
                fadd    float ptr 8[EBP]
                fsub    float ptr 8[EBP]
                pop     EBP
                ret     8
                add     [EAX],AL
                add     [EAX],AL

As you can see there are no write-read op codes.

DMD with flag -m32 generates

https://cpp.godbolt.org/z/GMGMra
        assume  CS:.text._D7example1fFffZf
                push    EBP
                mov     EBP,ESP
                sub     ESP,018h
                movss   XMM0,0Ch[EBP]
                movss   XMM1,8[EBP]
                addss   XMM0,XMM1
                movss   -8[EBP],XMM0
                subss   XMM0,XMM1
                movss   -4[EBP],XMM0
                movss   -018h[EBP],XMM0
                fld     float ptr -018h[EBP]
                leave
                ret     8
                add     [EAX],AL

It just uses SSE, which I think a good way to go, haha. Probably if no one has raised this bug then all real-world DMD targets have at least SSE support.

The only D compiler that uses excess precision is DMD and only if -O flag is passed. The same example compiled with GDC uses write-read codes. LDC uses SSE codes.

As for C, it allows an intuitive built-in way to work with exact precision when an assignment works like a directive to use exact precision for the expression result, unlike D. It doesn't cover all cases but an intuitive and very easy way to do things the right way.

January 05, 2021
On 1/4/2021 11:22 PM, 9il wrote:
> I can't reproduce the same DMD output as you.

I did it on Windows 32 bit. I tried it on Linux 32, which does indeed show the behavior you mentioned. At the moment I don't know why the different behaviors.

https://issues.dlang.org/show_bug.cgi?id=21526


> It just uses SSE, which I think a good way to go, haha.

As I mentioned upthread, it will use SSE when SSE is baseline on the CPU target, and it will always round to precision.


> The only D compiler that uses excess precision is DMD and only if -O flag is passed. The same example compiled with GDC uses write-read codes. LDC uses SSE codes.

DMD still supports baseline 32 bit Windows that does not have XMM registers.


> As for C, it allows an intuitive built-in way to work with exact precision when an assignment works like a directive to use exact precision for the expression result, unlike D. It doesn't cover all cases but an intuitive and very easy way to do things the right way.

It's a rarity of cases that require the rounding, and DMD does have the functions in druntime to do it for those cases. It's what people doing numerics have asked for. It means the write/read penalty is only there when it is actually required.

In any case, this is an issue for the past. The future is XMM code, and DMD does the round-to-precision in all XMM code generation. If you find a case where it isn't, please file a bugzilla and let me know so I can fix it.

January 05, 2021
On Tuesday, 5 January 2021 at 09:47:41 UTC, Walter Bright wrote:
> On 1/4/2021 11:22 PM, 9il wrote:
>> I can't reproduce the same DMD output as you.
>
> I did it on Windows 32 bit. I tried it on Linux 32, which does indeed show the behavior you mentioned. At the moment I don't know why the different behaviors.
>
> https://issues.dlang.org/show_bug.cgi?id=21526
>
>
>> It just uses SSE, which I think a good way to go, haha.
>
> As I mentioned upthread, it will use SSE when SSE is baseline on the CPU target, and it will always round to precision.

Does this mean that DMD Linux 32-bit executables should compile with SSE codes? I ask because DMD compiles Linux 32-bit executables with x87 codes when -O is passed and with SSE if no -O is passed. That is very weird.