October 06, 2020
On Tuesday, 6 October 2020 at 13:35:25 UTC, jmh530 wrote:
> On Tuesday, 6 October 2020 at 13:20:43 UTC, John Colvin wrote:
>> [snip]
>>
>> If you look at the history of std.meta you'll see it was me who recently made Filter so ugly, the previous version was simple but too slow.
>
> To get Filter/staticMap to have improved performance (and it looks to me like Stefan copied the phobos version in his example), you were forced to make them uglier. But, isn't Stefan's point that his version has even better performance than your ugly version? Is your argument that the simple version would have better performance in this case?

That is a newbie thing to say. The big complication swept under the rug is the language. If you make the language bigger you can always makes things look nicer. It doesn't mean you should.
To moderators: Are you seriously rejecting my posts for overquoting? What happened to this community?

October 06, 2020
On Tuesday, 6 October 2020 at 12:35:15 UTC, Stefan Koch wrote:
> On Tuesday, 6 October 2020 at 11:34:10 UTC, foobar wrote:
>> On Tuesday, 6 October 2020 at 03:50:11 UTC, Stefan Koch wrote:
>>> [...]
>>
>> 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?
>
> It only gives access to what the compiler has to do anyway.
> It gives you an interface to what must exist within.
> You could even say this makes it easier to provide a library implementation by using the compiler as a built-in library.
> No, type functions don't do anything original they mirror how type manipulation works within templates providing a familiar and usable interface.
> It is an explicit goal of mine to have type functions look just like any other D code.

Last time I looked D was a small language but very powerful. It innovated and did things you could not do in C++. Now this forum has been taken over by beginners who want a big language which does the same as before, only differently. Yawn.
October 06, 2020
On Tuesday, 6 October 2020 at 17:50:33 UTC, foobar wrote:
> [snip]
>
> That is a newbie thing to say. The big complication swept under the rug is the language. If you make the language bigger you can always makes things look nicer. It doesn't mean you should.

Honestly, I was just trying to figure out what he was trying to say. He subsequently wrote that he was just providing some background information (or something to that effect).

> To moderators: Are you seriously rejecting my posts for overquoting? What happened to this community?

It's automated and has been like that as long as I've been here.
October 06, 2020
On Monday, 5 October 2020 at 11:44:34 UTC, Stefan Koch wrote:
> Maybe you like the template version more?
> Let me know.

I love the premise. Like watching people in TV ads compare the knife of the competition with their own. Completely mutilating a loaf of bread with the competition.

What if we make an honest comparison?
I spent 15 minutes writing this:

template Tuple(T...)
{
    alias Tuple = T;
}

alias basic_types = Tuple!(bool, ubyte, char, byte, ushort, wchar, short, uint, dchar, int, ulong, long);

template ImplicitConvertible(T, Candidate, Candidates...)
{
	static if (is (T : Candidate))
		alias Include = Candidate;
	else
		alias Include = Tuple!();

	static if (Candidates.length > 0)
		alias ImplicitConvertible = Tuple!(Include, ImplicitConvertible!(T, Candidates));
	else
		alias ImplicitConvertible = Include;
}

template ImplicitConversionTargets(T)
{
	alias ImplicitConversionTargets = ImplicitConvertible!(T, basic_types);
}

pragma (msg, ImplicitConversionTargets!(long));

That's 16 lines of code. Heck it even compiles in D1 if only the alias declarations are written in the old style.

Personally I prefer using existing language features.

/Daniel K
October 06, 2020
On Tuesday, 6 October 2020 at 15:24:31 UTC, Bruce Carneal wrote:
> I believe we should aim for the simplest code that admits the desired performance.

Sure, but the desired performance here, for this stuff, is the clear priority. Especially at lower levels where the costs compound. Here's some princely advice: it is better to be both feared and loved, but if you can be only one or the other, it is better to be feared than to be loved. That's the reason why Phobos does it the way it does. It'd love to have both, but if it is one or the other, fast compiles are far more important than pretty code.

> Well, what is a "real problem"?

That code compiles slowly and/or with excessive memory. That's why type functions are being investigated.

There's no new functionality gained by the type functions. Their whole reason for existing is to make faster, less memory hungry builds. (right now anyway, that might change if it gains capabilities, but right now an explicit design goal is to make them a limited subset of template functionality in the name of improved performance).

I frequently take Stefan's examples, copy/paste them into a template, and use them. The code doesn't even look that different.

The problem is there's several usages where this version is slow.

type function version:

alias type = alias;
type[] basicTypeConvTargets(type T)
{
    type[] targets;
    targets.length = basic_types.length;
    size_t n = 0;
    foreach(t;basic_types)
    {
        if (is(T : t))
        {
            targets[n++] = t;
        }
    }
    return targets[0 .. n];
}

template version:

alias type = string;
type[] basicTypeConvTargets(T)()
{
    type[] targets;
    targets.length = basic_types.length;
    size_t n = 0;
    foreach(t;basic_types)
    {
        if (is(T : mixin(t)))
        {
            targets[n++] = t;
        }
    }
    return targets[0 .. n];
}


Very little difference! The makeConvMatix was *identical* except for the function prototype (and even there, both return strings!). Filter's guts can be:

        size_t[Args.length] keep;
        size_t pos = 0;
        foreach(idx, alias arg; Args)
                if(Pred!arg) keep[pos++] = idx; // note idx, not arg.
        return makeResult(keep[0 .. pos]);

In today's D. Again, *almost identical* to the typefunction version, just using an index into the list instead of storing the alias in the array directly.


What kills this approach is *not* the code being hideous. It is the performance aspect - additional CTFE code is necessary to convert it back to a type tuple, and secondarily, the foreach being unrolled leads to extra work. A type function can simply do `returned.tupleof` and keep the foreach how it is. Here, we'd have to `mixin(returned.doConversion)`. (Where doConversion is a similarly reusable lib function.)

Again, minor syntax difference, but significant performance hit because doConversion must rebuild a new string out of stuff the compiler already knows. Phobos' current implementation is uglier, but also faster and uses less memory than the mixin version. So it wins over it.

This is the place the typefunction has a potential win. It might combine the nice code AND get a performance improvement. But if the TF ends up slower than Phobos has now... it is going to be rewritten into the faster version, even if uglier, because it is the compile performance driving this evolution.

The Phobos implementation started life with a very simple implementation too. It became what it is because it *had to*, specifically for performance reasons.

October 06, 2020
On 10/6/20 1:51 PM, foobar wrote:
> On Tuesday, 6 October 2020 at 12:35:15 UTC, Stefan Koch wrote:
>> On Tuesday, 6 October 2020 at 11:34:10 UTC, foobar wrote:
>>> On Tuesday, 6 October 2020 at 03:50:11 UTC, Stefan Koch wrote:
>>>> [...]
>>>
>>> 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?
>>
>> It only gives access to what the compiler has to do anyway.
>> It gives you an interface to what must exist within.
>> You could even say this makes it easier to provide a library implementation by using the compiler as a built-in library.
>> No, type functions don't do anything original they mirror how type manipulation works within templates providing a familiar and usable interface.
>> It is an explicit goal of mine to have type functions look just like any other D code.
> 
> Last time I looked D was a small language but very powerful. It innovated and did things you could not do in C++. Now this forum has been taken over by beginners who want a big language which does the same as before, only differently. Yawn.

Everything you just said is wrong.

-Steve
October 06, 2020
On 10/6/20 2:09 PM, Adam D. Ruppe wrote:
> Filter's guts can be:
> 
>          size_t[Args.length] keep;
>          size_t pos = 0;
>          foreach(idx, alias arg; Args)
>                  if(Pred!arg) keep[pos++] = idx; // note idx, not arg.
>          return makeResult(keep[0 .. pos]);
> 
> In today's D. Again, *almost identical* to the typefunction version, just using an index into the list instead of storing the alias in the array directly.

I think Filter is a much more apt example than the implicit targets.

Not just because you can make it faster, but because with type functions, you should be able to actually *use filter*:

return Args.filter!Pred.array;

I mean, we have filter for a reason. You can easily write filter out into a loop, instead of using filter. But there it is, and it makes coding so much more pleasant. If the compiler and CTFE can be fast enough to make this pleasant (I'm not 100% convinced, but it looks promising), then I'm on board.

It comes down to one thing -- arrays vs. tuples. In type functions, a tuple is an array, and you can do all the things you can do with a normal array: mutate, sort, shrink, grow, loop, use as a range, etc.

With a Tuple, everything is immutable, and each change needs to go across a new template boundary. Even a loop is not really a loop.

That being said, I think the only way type functions make a difference is if they perform WELL. Even if they perform *as well* as templates (though indications are they perform better), I'd rather write code in an imperative style than recursive.

-Steve
October 06, 2020
On Tuesday, 6 October 2020 at 18:00:29 UTC, Daniel K wrote:
> On Monday, 5 October 2020 at 11:44:34 UTC, Stefan Koch wrote:
>> [...]
>
> I love the premise. Like watching people in TV ads compare the knife of the competition with their own. Completely mutilating a loaf of bread with the competition.
>
> [...]

As is your right.
I spent 5 minutes on the type-function, because there's nothing to think about.
You just loop and that's it :)
October 06, 2020
On Tuesday, 6 October 2020 at 18:30:15 UTC, Steven Schveighoffer wrote:
> On 10/6/20 2:09 PM, Adam D. Ruppe wrote:
>> Filter's guts can be:
>> 
>>          size_t[Args.length] keep;
>>          size_t pos = 0;
>>          foreach(idx, alias arg; Args)
>>                  if(Pred!arg) keep[pos++] = idx; // note idx, not arg.
>>          return makeResult(keep[0 .. pos]);
>> 
>> In today's D. Again, *almost identical* to the typefunction version, just using an index into the list instead of storing the alias in the array directly.
>
> I think Filter is a much more apt example than the implicit targets.
>
> Not just because you can make it faster, but because with type functions, you should be able to actually *use filter*:
>
> return Args.filter!Pred.array;
>
> I mean, we have filter for a reason. You can easily write filter out into a loop, instead of using filter. But there it is, and it makes coding so much more pleasant. If the compiler and CTFE can be fast enough to make this pleasant (I'm not 100% convinced, but it looks promising), then I'm on board.
>
> With a Tuple, everything is immutable, and each change needs to go across a new template boundary. Even a loop is not really a loop.

Excellent point; the only reason Filter, staticSort, staticMap, Reverse, Repeat, etc., etc. exist is because of the limitations of working with AliasSeq. If type functions can allow us to replace most of these uses with good old std.range/algorithm code, that's a huge win in my book.

We have a big chance to go left when C++ went right and went all-in on template metaprogramming. Let's leave templates to do what they were designed for - genericizing structures and algorithms, and leave the compile-time computation to CTFE + type functions.


October 06, 2020
On Tuesday, 6 October 2020 at 18:43:42 UTC, Stefan Koch wrote:
> On Tuesday, 6 October 2020 at 18:00:29 UTC, Daniel K wrote:
>> On Monday, 5 October 2020 at 11:44:34 UTC, Stefan Koch wrote:
>>> [...]
>>
>> I love the premise. Like watching people in TV ads compare the knife of the competition with their own. Completely mutilating a loaf of bread with the competition.
>>
>> [...]
>
> As is your right.
> I spent 5 minutes on the type-function, because there's nothing to think about.
> You just loop and that's it :)

I like how you concisely argued technical aspects. Only people with weak arguments would have picked irrelevant points. Wait... oh...

/Daniel K