October 05, 2020
On 10/5/2020 2:20 PM, Stefan Koch wrote:
> Now post it with all transitive dependencies.

I'm not sure how transitive dependencies applies here. A illuminating test case would be helpful.
October 06, 2020
On Tuesday, 6 October 2020 at 00:25:40 UTC, foobar wrote:
> On Monday, 5 October 2020 at 21:20:36 UTC, Stefan Koch wrote:
>> On Monday, 5 October 2020 at 21:13:09 UTC, Paul Backus wrote:
>>> On Monday, 5 October 2020 at 20:57:04 UTC, Stefan Koch wrote:
>>>> On Monday, 5 October 2020 at 12:50:39 UTC, Andrei Alexandrescu wrote:
>>>>
>>>>> [...]
>>>>
>>>> This code does not work.
>>>> I don't even need to compile it to see that.
>>>
>>> It has some simple mistakes, but the fundamental idea is sound. Here's a version that actually compiles:
>>>
>>> import std.meta;
>>>
>>> alias Numerics = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real, char, wchar, dchar);
>>> enum convertsTo(T, U) = is(T : U);
>>> alias ImplicitConversionTargets(T) = Filter!(ApplyLeft!(convertsTo, T), Numerics);
>>>
>>> // prints: (int, uint, long, ulong, float, double, real, dchar)
>>> pragma(msg, ImplicitConversionTargets!int);
>>> // prints: (float, double, real)
>>> pragma(msg, ImplicitConversionTargets!double);
>>
>> Now post it with all transitive dependencies.
>> And we have a fair comparison.
>
> Post your code with all changes to the language and compiler. Then we have a fair comparison.

Actually no.
The compiler changes don't affect the user.
They're for a small number of people to know about and support.
Typefunctions and CTFE are together still much less complicated than the template system is.

I have nothing to hide though here it is
https://github.com/dlang/dmd/compare/master...UplinkCoder:talias_master

650 lines of rather clean code which can in the future be factored with the respective semantic routines.
October 06, 2020
On Tuesday, 6 October 2020 at 01:12:01 UTC, Walter Bright wrote:
> On 10/5/2020 2:20 PM, Stefan Koch wrote:
>> Now post it with all transitive dependencies.
>
> I'm not sure how transitive dependencies applies here. A illuminating test case would be helpful.

Transitive dependencies apply, because it shows how much work has to be done in the library.
As opposed to the type-function code which works without library support (except for the druntime array handling of course)
October 06, 2020
On Tuesday, 6 October 2020 at 01:12:01 UTC, Walter Bright wrote:
> On 10/5/2020 2:20 PM, Stefan Koch wrote:
>> Now post it with all transitive dependencies.
>
> I'm not sure how transitive dependencies applies here. A illuminating test case would be helpful.

Because Stefan didnt rely on external code, he showed *all* the code needed, so a counter example should have been the same. Since the point is to compare the two *language* features, not compare library calls. I mean it's like someone posted a new sort algorithm and Andrei replied with....

import std.algorithm;
result = sort!(foo);

October 06, 2020
On Tuesday, 6 October 2020 at 01:12:01 UTC, Walter Bright wrote:
> On 10/5/2020 2:20 PM, Stefan Koch wrote:
>> Now post it with all transitive dependencies.
>
> I'm not sure how transitive dependencies applies here. A illuminating test case would be helpful.

Okay let me show you the transitive dependencies of the "3 liner" suggested by Andrei and executed by Paul Backus.

--- phobos dependencies ---

template ApplyLeft(alias Template, args...)
{
    alias ApplyLeft(right...) = SmartAlias!(Template!(args, right));
}

private template SmartAlias(T...)
{
    static if (T.length == 1)
    {
        alias SmartAlias = Alias!T;
    }
    else
    {
        alias SmartAlias = T;
    }
}

alias Alias(alias a) = a;
alias Alias(T) = T;

template Filter(alias pred, TList...)
{
    static if (TList.length < filterExpandFactor)
    {
        mixin(FilterShortCode[TList.length]);
    }
    else
    {
        template MaybeNothing(Q...)
        {
            static if (pred!(Q[0]))
                alias MaybeNothing = AliasSeq!(Q[0]);
            else
                alias MaybeNothing = Nothing;
        }

        alias Filter = staticMap!(MaybeNothing, TList);
    }
}

private enum staticMapExpandFactor = 150;
private string generateCases()
{
    string[staticMapExpandFactor] chunks;
    chunks[0] = q{};
    static foreach (enum i; 0 .. staticMapExpandFactor - 1)
            chunks[i + 1] = chunks[i] ~ `F!(Args[` ~ i.stringof ~ `]),`;
        string ret = `AliasSeq!(`;
        foreach (chunk; chunks)
            ret ~= `q{alias staticMap = AliasSeq!(` ~ chunk ~ `);},`;
        return ret ~ `)`;
        }

        private alias staticMapBasicCases = AliasSeq!(mixin(generateCases()));

        template staticMap(alias F, Args...)
        {
            static if (Args.length < staticMapExpandFactor)
                mixin(staticMapBasicCases[Args.length]);
            else
                alias staticMap = AliasSeq!(staticMap!(F, Args[0 .. $ / 2]),
                    staticMap!(F, Args[$ / 2 .. $]));
        }


private alias FilterShortCode = AliasSeq!(
    q{
        alias Filter = Nothing;
    },
    q{
        static if (pred!(TList[0]))
            alias Filter = AliasSeq!(TList[0]);
        else
            alias Filter = Nothing;
    },
    q{
        static if (pred!(TList[0]))
        {
            static if (pred!(TList[1]))
                alias Filter = AliasSeq!(TList[0], TList[1]);
            else
                alias Filter = AliasSeq!(TList[0]);
        }
        else
        {
            static if (pred!(TList[1]))
                alias Filter = AliasSeq!(TList[1]);
            else
                alias Filter = Nothing;
        }
    },
    q{
        static if (pred!(TList[0]))
        {
            static if (pred!(TList[1]))
            {
                static if (pred!(TList[2]))
                    alias Filter = AliasSeq!(TList[0], TList[1], TList[2]);
                else
                    alias Filter = AliasSeq!(TList[0], TList[1]);
            }
            else
            {
                static if (pred!(TList[2]))
                    alias Filter = AliasSeq!(TList[0], TList[2]);
                else
                    alias Filter = AliasSeq!(TList[0]);
            }
        }
        else
        {
            static if (pred!(TList[1]))
            {
                static if (pred!(TList[2]))
                    alias Filter = AliasSeq!(TList[1], TList[2]);
                else
                    alias Filter = AliasSeq!(TList[1]);
            }
            else
            {
                static if (pred!(TList[2]))
                    alias Filter = AliasSeq!(TList[2]);
                else
                    alias Filter = Nothing;
            }
        }
    },
    q{
        static if (pred!(TList[0]))
        {
            static if (pred!(TList[1]))
            {
                static if (pred!(TList[2]))
                {
                    static if (pred!(TList[3]))
                        alias Filter = AliasSeq!(TList[0], TList[1], TList[2], TList[3]);
                    else
                        alias Filter = AliasSeq!(TList[0], TList[1], TList[2]);
                }
                else
                {
                    static if (pred!(TList[3]))
                        alias Filter = AliasSeq!(TList[0], TList[1], TList[3]);
                    else
                        alias Filter = AliasSeq!(TList[0], TList[1]);
                }
            }
            else
            {
                static if (pred!(TList[2]))
                {
                    static if (pred!(TList[3]))
                        alias Filter = AliasSeq!(TList[0], TList[2], TList[3]);
                    else
                        alias Filter = AliasSeq!(TList[0], TList[2]);
                }
                else
                {
                    static if (pred!(TList[3]))
                        alias Filter = AliasSeq!(TList[0], TList[3]);
                    else
                        alias Filter = AliasSeq!(TList[0]);
                }
            }
        }
        else
        {
            static if (pred!(TList[1]))
            {
                static if (pred!(TList[2]))
                {
                    static if (pred!(TList[3]))
                        alias Filter = AliasSeq!(TList[1], TList[2], TList[3]);
                    else
                        alias Filter = AliasSeq!(TList[1], TList[2]);
                }
                else
                {
                    static if (pred!(TList[3]))
                        alias Filter = AliasSeq!(TList[1], TList[3]);
                    else
                        alias Filter = AliasSeq!(TList[1]);
                }
            }
            else
            {
                static if (pred!(TList[2]))
                {
                    static if (pred!(TList[3]))
                        alias Filter = AliasSeq!(TList[2], TList[3]);
                    else
                        alias Filter = AliasSeq!(TList[2]);
                }
                else
                {
                    static if (pred!(TList[3]))
                        alias Filter = AliasSeq!(TList[3]);
                    else
                        alias Filter = Nothing;
                }
            }
        }
    }
);

private enum filterExpandFactor = FilterShortCode.length;

package alias Nothing = AliasSeq!(); // yes, this really does speed up compilation!

---

--- actual 3 liner ---
   alias Numerics = AliasSeq!(byte, ubyte, short, ushort, int, uint, long,
            ulong, float, double, real, char, wchar, dchar);
        enum convertsTo(T, U) = is(T : U);
        alias ImplicitConversionTargets(T) = Filter!(ApplyLeft!(convertsTo, T), Numerics);
        pragma(msg, ImplicitConversionTargets!uint);
---

I would consider this illuminating in a way.
October 06, 2020
On Tuesday, 6 October 2020 at 10:22:44 UTC, Stefan Koch wrote:
> On Tuesday, 6 October 2020 at 01:12:01 UTC, Walter Bright wrote:
>> On 10/5/2020 2:20 PM, Stefan Koch wrote:
>>> Now post it with all transitive dependencies.
>>
>> I'm not sure how transitive dependencies applies here. A illuminating test case would be helpful.
>
> Okay let me show you the transitive dependencies of the "3 liner" suggested by Andrei and executed by Paul Backus.
>
> --- phobos dependencies ---
> ...

Ah dang.
This won't compile ...
I forgot
---
AliasSeq(T...) = T;
---

with that added it will compile.
October 06, 2020
On Monday, 5 October 2020 at 11:44:34 UTC, Stefan Koch wrote:
> Hi,
>
> I've posted an incomplete version of a semantic translation of ImplictConvTargets from a template into a type function.
>
> After a few rather trivial fixes let me show you what the code looks like now.
>
> ---
> alias type = alias;
>
> // needed to avoid deeper changes ... in the future it may be unnecessary.
> auto makeAliasArray(type[] types ...)
> {
>     return types;
> }
>
> enum basic_types = makeAliasArray(bool, ubyte, char, byte, ushort, wchar, short, uint, dchar, int, ulong, long);
>
> type[] convTargets(type T)
> {
>     if (isBasicType(T))
>         return basicTypeConvTargets(T);
>     return null;
> }
>
> bool isBasicType(type T)
> {
>     foreach(t;basic_types)
>     {
>         if (is(T == t))
>             return true;
>     }
>     return false;
> }
>
> type[] basicTypeConvTargets(type T)
> {
>     type[] targets;
>     targets.length = basic_types.length;
>     assert(isBasicType(T), "You may not call this function when you don't have a basic type ... (given: " ~ T.stringof ~ ")");
>     size_t n = 0;
>     foreach(t;basic_types)
>     {
>         if (is(T : t))
>         {
>             targets[n++] = t;
>         }
>     }
>     return targets[0 .. n];
> }
> // 42 lines including whitespace and comments
>
> pragma(msg, convTargets(long)); // outputs [(ulong), (long)]
> ---
>
> And again here is the part of the template that we just re-implemented:
> ---
>     static if (is(T == bool))
>         alias ImplicitConversionTargets =
>             AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, CentTypeList,
>                        float, double, real, char, wchar, dchar);
>     else static if (is(T == byte))
>         alias ImplicitConversionTargets =
>             AliasSeq!(short, ushort, int, uint, long, ulong, CentTypeList,
>                        float, double, real, char, wchar, dchar);
>     else static if (is(T == ubyte))
>         alias ImplicitConversionTargets =
>             AliasSeq!(short, ushort, int, uint, long, ulong, CentTypeList,
>                        float, double, real, char, wchar, dchar);
>     else static if (is(T == short))
>         alias ImplicitConversionTargets =
>             AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real);
>     else static if (is(T == ushort))
>         alias ImplicitConversionTargets =
>             AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real);
>     else static if (is(T == int))
>         alias ImplicitConversionTargets =
>             AliasSeq!(long, ulong, CentTypeList, float, double, real);
>     else static if (is(T == uint))
>         alias ImplicitConversionTargets =
>             AliasSeq!(long, ulong, CentTypeList, float, double, real);
>     else static if (is(T == long))
>         alias ImplicitConversionTargets = AliasSeq!(float, double, real);
>     else static if (is(T == ulong))
>         alias ImplicitConversionTargets = AliasSeq!(float, double, real);
>     // part omitted because we don't have ucent and cent in our list
>     else static if (is(T == char))
>         alias ImplicitConversionTargets =
>             AliasSeq!(wchar, dchar, byte, ubyte, short, ushort,
>                        int, uint, long, ulong, CentTypeList, float, double, real);
>     else static if (is(T == wchar))
>         alias ImplicitConversionTargets =
>             AliasSeq!(dchar, short, ushort, int, uint, long, ulong, CentTypeList,
>                        float, double, real);
>     else static if (is(T == dchar))
>         alias ImplicitConversionTargets =
>             AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real);
>     // 41 lines including white-space and comments (only that there is no white-space or comments)
> ---
>
> I leave it up to you to decide which version is more understandable and extendable (should we ever get another basic type :))
>
> As noted previously please do discuss!
> Maybe you like the template version more?
> Let me know.

And here is the performance comparison:

uplimk@uplimk-virtual-machine:~/d/dmd$ hyperfine -w 10 "generated/linux/release/64/dmd xx_tf.d -sktf -c" "generated/linux/release/64/dmd xx.d -c " "generated/linux/release/64/dmd xx_adam.d -c"
Benchmark #1: generated/linux/release/64/dmd xx_tf.d -sktf -c
  Time (mean ± σ):       9.1 ms ±   0.4 ms    [User: 5.8 ms, System: 3.2 ms]
  Range (min … max):     8.5 ms …  10.7 ms    288 runs

Benchmark #2: generated/linux/release/64/dmd xx.d -c
  Time (mean ± σ):      17.3 ms ±   0.6 ms    [User: 11.3 ms, System: 5.9 ms]
  Range (min … max):    16.5 ms …  19.5 ms    166 runs

Benchmark #3: generated/linux/release/64/dmd xx_adam.d -c
  Time (mean ± σ):      20.4 ms ±   0.7 ms    [User: 15.2 ms, System: 5.0 ms]
  Range (min … max):    19.5 ms …  22.7 ms    144 runs

Summary
  'generated/linux/release/64/dmd xx_tf.d -sktf -c' ran
    1.91 ± 0.11 times faster than 'generated/linux/release/64/dmd xx.d -c '
    2.24 ± 0.13 times faster than 'generated/linux/release/64/dmd xx_adam.d -c'


The source code can be found here:

https://gist.github.com/UplinkCoder/d29dd143b352d28390426a3ffedf9521

So can the benchmark results.
Note: not importing std.meta gives the template version (xx.d) a speed boost of 3 milliseconds.
October 06, 2020
On Tuesday, 6 October 2020 at 11:14:05 UTC, Stefan Koch wrote:
> [snip]
> The source code can be found here:
>
> https://gist.github.com/UplinkCoder/d29dd143b352d28390426a3ffedf9521
>
> So can the benchmark results.
> Note: not importing std.meta gives the template version (xx.d) a speed boost of 3 milliseconds.

Thanks for providing these results.

You had previously provided some evidence of a significant reduction in memory consumption. You might consider adding that information (using these examples) to this gist to bolster your argument.
October 06, 2020
On Tuesday, 6 October 2020 at 11:25:41 UTC, jmh530 wrote:
> On Tuesday, 6 October 2020 at 11:14:05 UTC, Stefan Koch wrote:
>> [snip]
>> The source code can be found here:
>>
>> https://gist.github.com/UplinkCoder/d29dd143b352d28390426a3ffedf9521
>>
>> So can the benchmark results.
>> Note: not importing std.meta gives the template version (xx.d) a speed boost of 3 milliseconds.
>
> Thanks for providing these results.
>
> You had previously provided some evidence of a significant reduction in memory consumption. You might consider adding that information (using these examples) to this gist to bolster your argument.

Memory consumption is reduced by 2x roughly.

(adams and pauls version are about the same, with pauls sucking a bit more memory, perhaps because there is more to parse?)

I'll add it to the gist.
October 06, 2020
On Tuesday, 6 October 2020 at 03:50:11 UTC, Stefan Koch wrote:
> On Tuesday, 6 October 2020 at 00:25:40 UTC, foobar wrote:
>> On Monday, 5 October 2020 at 21:20:36 UTC, Stefan Koch wrote:
>>> On Monday, 5 October 2020 at 21:13:09 UTC, Paul Backus wrote:
>>>> On Monday, 5 October 2020 at 20:57:04 UTC, Stefan Koch wrote:
>>>>> On Monday, 5 October 2020 at 12:50:39 UTC, Andrei Alexandrescu wrote:
>>>>>
>>>>>> [...]
>>>>>
>>>>> This code does not work.
>>>>> I don't even need to compile it to see that.
>>>>
>>>> It has some simple mistakes, but the fundamental idea is sound. Here's a version that actually compiles:
>>>>
>>>> import std.meta;
>>>>
>>>> alias Numerics = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real, char, wchar, dchar);
>>>> enum convertsTo(T, U) = is(T : U);
>>>> alias ImplicitConversionTargets(T) = Filter!(ApplyLeft!(convertsTo, T), Numerics);
>>>>
>>>> // prints: (int, uint, long, ulong, float, double, real, dchar)
>>>> pragma(msg, ImplicitConversionTargets!int);
>>>> // prints: (float, double, real)
>>>> pragma(msg, ImplicitConversionTargets!double);
>>>
>>> Now post it with all transitive dependencies.
>>> And we have a fair comparison.
>>
>> Post your code with all changes to the language and compiler. Then we have a fair comparison.
>
> Actually no.
> The compiler changes don't affect the user.
> They're for a small number of people to know about and support.
> Typefunctions and CTFE are together still much less complicated than the template system is.
>
> I have nothing to hide though here it is
> https://github.com/dlang/dmd/compare/master...UplinkCoder:talias_master
>
> 650 lines of rather clean code which can in the future be factored with the respective semantic routines.

Actuallly yes.
The language changes affect the user. This is a large change to the language which puts back in the compiler what we do in libraries. Whoop-de-do.
Do type functions do anything new?