February 16, 2017
On Wednesday, 15 February 2017 at 19:39:52 UTC, Andrei Alexandrescu wrote:
> On 02/15/2017 06:20 AM, Daniel N wrote:
>> On Wednesday, 15 February 2017 at 09:22:14 UTC, Daniel N wrote:
>>> template every(T...)
>>> {
>>>     template satisfies(U...)
>>>     {
>>>         enum satisfies = true;
>>>     }
>>> }
>>
>> (lunch-break => time to hack D!)
>>
>> template every(T...)
>> {
>>   template satisfies(U...)
>>   {
>>     enum satisfies = {
>>       foreach(t; T)
>>         foreach(u; U)
>>       if(!u!t)
>>             return false;
>>       return true;
>>     }();
>>   }
>> }
>
> That looks pretty neat. Can you find 4-5 cases in which this could be used gainfully in Phobos? Thanks! -- Andrei

Slightly OT, but it is related so bear with me:

Design by introspection is great and prevent having to make new names for every possible combo of features. Unfortunately, it doesn't work so well for introspecting multiple types at once: normally you have to then make a new symbol, with a new name, to pass to e.g. allSatisfy. That annoys me, so I've been wanting this sort of thing for a while (which would also make `every` way better to use, instead of having to make a name for every predicate:

You know how we have named parameterised enums and aliases?
enum isInt(a) = is(a == int);
and
alias sizeof(a) = a.sizeof;

why not allow *anonymous* parameterised enums and aliases, like this:

enum areAllInt(TL ...) = allSatisfy!(enum(a) => is(a == int), TL);

void foo(TL...)(TL args)
if (allSatisfy!(enum(T) => T.sizeof <= 4, TL))
{
    // ...
}

an example from phobos:

bool ordered(alias less = "a < b", T...)(T values)
if (T.length == 2 && is(typeof(binaryFun!less(values[1], values[0])) : bool) || T.length > 2 && is(typeof(ordered!less(values[0..1 + $ / 2]))) && is(typeof(ordered!less(values[$ / 2..$]))))

becomes this:

bool ordered(alias less = "a < b", T...)(T values)
if (T.length > 2 &&
    allSatisfy!(enum(size_t i) =>
                   is(typeof(binaryFun!less(values[i], values[i+1])) : bool),
        Iota!(T.length - 1))

I admit it's not a huge win, but hiding the log_2(N) template depth behind allSatisfy is great: you shouldn't have to (and people wont) think about that when writing a template constraint like this.

There is a part of me that thinks all this meta-template stuff is madness though and that modifications to ctfe could make it mostly obsolete, but that's a story for later...
February 16, 2017
On Thursday, 16 February 2017 at 00:37:00 UTC, ZombineDev wrote:
> On Thursday, 16 February 2017 at 00:08:12 UTC, Walter Bright wrote:
>> On 2/15/2017 12:31 PM, Jonathan M Davis via Digitalmars-d wrote:
>>> It's one of those features that I was surprised when you couldn't do it.
>>
>> It was an oversight. We just never thought of it.
>
> What do you think about generalizing this feature to allow introducing template value parameters without default value, i.e.:
>
> // Note: `beg` and `end` have no default value
>
> auto bounded
>     (auto beg, auto end, auto onErrPolicy = Enforce("Value out of range"), T)
>     (T value)
> {
>     return Bounded!(beg, end, Policy)(value);
> }
>

note that this would still be usable with constraits via e.g. if(is(typeof(beg) : int))
February 16, 2017
On 2017-02-16 01:37, ZombineDev wrote:

> BTW, shouldn't we use `enum`, instead of `auto`, since everywhere else
> `enum` means guaranteed to be computed at compile-time whereas `auto`
> means the opposite?

Far enough, since it's not possible to change the parameter inside the function anyway.

Question though, what happens with an array literal, example:

void foo(int[] a = [1, 2, 3])()
{
    auto b = a; // allocation ?
    auto c = a; // allocation ?
}

As far as I understand, if an array is declared as a manifest constant it will cause a new allocation for each time it's used.

enum a = [1, 2, 3];
auto b = a; // new allocation
auto c = a; // new allocation

What happens when an array literal is a default value for a template parameter?

-- 
/Jacob Carlborg
February 16, 2017
On 2017-02-16 01:40, John Colvin wrote:

> Slightly OT, but it is related so bear with me:
>
> Design by introspection is great and prevent having to make new names
> for every possible combo of features. Unfortunately, it doesn't work so
> well for introspecting multiple types at once: normally you have to then
> make a new symbol, with a new name, to pass to e.g. allSatisfy. That
> annoys me, so I've been wanting this sort of thing for a while (which
> would also make `every` way better to use, instead of having to make a
> name for every predicate:
>
> You know how we have named parameterised enums and aliases?
> enum isInt(a) = is(a == int);
> and
> alias sizeof(a) = a.sizeof;
>
> why not allow *anonymous* parameterised enums and aliases, like this:
>
> enum areAllInt(TL ...) = allSatisfy!(enum(a) => is(a == int), TL);
>
> void foo(TL...)(TL args)
> if (allSatisfy!(enum(T) => T.sizeof <= 4, TL))
> {
>     // ...
> }

I've been thinking something similar. But this is the way I see it:

A lambda is an anonymous template function which is not yet instantiated. But with the lambda syntax only one the different template parameters are allowed. Why not generalize the lambda syntax to allow different kind of template parameters. Example:

What we have today:

[1, 2, 3].map!(e => e * 2);

Is basically the same as:

T __anonymous(T)(T e)
{
    return e * 2;
}

[1, 2, 3].map!(__anonymous!(int));

But today we cannot represent the following template with a lambda:

size_t sizeof(alias a)()
{
    return a.sizeof;
}

So why not allow this lambda syntax:

alias sizeof = (alias a) => a.sizeof;

It would be nice if we could reuse the existing syntax for regular template parameters as well:

alias isInt = t => is(t == int);
auto b = isInt!(int);

If the above is not possible, we could add an additional parameter list to lambdas:

alias isInt = (t)() => is(t == int);

-- 
/Jacob Carlborg
February 16, 2017
On Thursday, 16 February 2017 at 00:40:19 UTC, John Colvin wrote:
> On Wednesday, 15 February 2017 at 19:39:52 UTC, Andrei Alexandrescu wrote:
>> On 02/15/2017 06:20 AM, Daniel N wrote:
>>> On Wednesday, 15 February 2017 at 09:22:14 UTC, Daniel N wrote:
>>>> template every(T...)
>>>> {
>>>>     template satisfies(U...)
>>>>     {
>>>>         enum satisfies = true;
>>>>     }
>>>> }
>>>
>>> (lunch-break => time to hack D!)
>>>
>>> template every(T...)
>>> {
>>>   template satisfies(U...)
>>>   {
>>>     enum satisfies = {
>>>       foreach(t; T)
>>>         foreach(u; U)
>>>       if(!u!t)
>>>             return false;
>>>       return true;
>>>     }();
>>>   }
>>> }
>>
>> That looks pretty neat. Can you find 4-5 cases in which this could be used gainfully in Phobos? Thanks! -- Andrei
>
> Slightly OT, but it is related so bear with me:
>
> Design by introspection is great and prevent having to make new names for every possible combo of features. Unfortunately, it doesn't work so well for introspecting multiple types at once: normally you have to then make a new symbol, with a new name, to pass to e.g. allSatisfy. That annoys me, so I've been wanting this sort of thing for a while (which would also make `every` way better to use, instead of having to make a name for every predicate:
>
> You know how we have named parameterised enums and aliases?
> enum isInt(a) = is(a == int);
> and
> alias sizeof(a) = a.sizeof;
>
> why not allow *anonymous* parameterised enums and aliases, like this:
>
> enum areAllInt(TL ...) = allSatisfy!(enum(a) => is(a == int), TL);
>
> void foo(TL...)(TL args)
> if (allSatisfy!(enum(T) => T.sizeof <= 4, TL))
> {
>     // ...
> }
>
> [snip]
>

I have a library that allows just that, but with even shorter syntax :P

https://github.com/ZombineDev/rxd/blob/v0.0.3/source/rxd/xf/xform.d#L50
https://github.com/ZombineDev/rxd/blob/v0.0.3/source/rxd/meta2/type.d#L241

Though I have feeling that it will make uplinkCoder cringe, because of the pressure it is probably putting on the compiler :D
February 16, 2017
On Thursday, 16 February 2017 at 07:34:02 UTC, Jacob Carlborg wrote:
> On 2017-02-16 01:37, ZombineDev wrote:
>
>> BTW, shouldn't we use `enum`, instead of `auto`, since everywhere else
>> `enum` means guaranteed to be computed at compile-time whereas `auto`
>> means the opposite?
>
> Far enough, since it's not possible to change the parameter inside the function anyway.
>
> Question though, what happens with an array literal, example:
>
> void foo(int[] a = [1, 2, 3])()
> {
>     auto b = a; // allocation ?
>     auto c = a; // allocation ?
> }
>
> As far as I understand, if an array is declared as a manifest constant it will cause a new allocation for each time it's used.
>
> enum a = [1, 2, 3];
> auto b = a; // new allocation
> auto c = a; // new allocation
>
> What happens when an array literal is a default value for a template parameter?

enums are just literals - values without identity. This means that if you need them at runtime, the compiler will need to allocate storage for each time they are used. In cases where such a values is used in more places, it may be more economical to assign it to a static immutable variable and reference it in place of the enum.

In the case of template parameters I would expect this to be even more true, because an array by definition is a contiguous chunk of memory and a template parameter is something encoded in the mangled name of the symbol at runtime, so the compiler can't get away from allocating additional storage for each usage of the template value parameter at runtime.

(@compiler devs, please correct me if I am wrong)
February 16, 2017
On Thursday, 16 February 2017 at 07:48:37 UTC, Jacob Carlborg wrote:
> On 2017-02-16 01:40, John Colvin wrote:
>
>> Slightly OT, but it is related so bear with me:
>>
>> Design by introspection is great and prevent having to make new names
>> for every possible combo of features. Unfortunately, it doesn't work so
>> well for introspecting multiple types at once: normally you have to then
>> make a new symbol, with a new name, to pass to e.g. allSatisfy. That
>> annoys me, so I've been wanting this sort of thing for a while (which
>> would also make `every` way better to use, instead of having to make a
>> name for every predicate:
>>
>> You know how we have named parameterised enums and aliases?
>> enum isInt(a) = is(a == int);
>> and
>> alias sizeof(a) = a.sizeof;
>>
>> why not allow *anonymous* parameterised enums and aliases, like this:
>>
>> enum areAllInt(TL ...) = allSatisfy!(enum(a) => is(a == int), TL);
>>
>> void foo(TL...)(TL args)
>> if (allSatisfy!(enum(T) => T.sizeof <= 4, TL))
>> {
>>     // ...
>> }
>
> I've been thinking something similar. But this is the way I see it:
>
> A lambda is an anonymous template function which is not yet instantiated. But with the lambda syntax only one the different template parameters are allowed. Why not generalize the lambda syntax to allow different kind of template parameters. Example:
>
> What we have today:
>
> [1, 2, 3].map!(e => e * 2);
>
> Is basically the same as:
>
> T __anonymous(T)(T e)
> {
>     return e * 2;
> }
>
> [1, 2, 3].map!(__anonymous!(int));
>
> But today we cannot represent the following template with a lambda:
>
> size_t sizeof(alias a)()
> {
>     return a.sizeof;
> }
>
> So why not allow this lambda syntax:
>
> alias sizeof = (alias a) => a.sizeof;
>
> It would be nice if we could reuse the existing syntax for regular template parameters as well:
>
> alias isInt = t => is(t == int);
> auto b = isInt!(int);
>
> If the above is not possible, we could add an additional parameter list to lambdas:
>
> alias isInt = (t)() => is(t == int);

You can sort of do this even today. All you need is struct with template-ed opCall.

See https://github.com/ZombineDev/rxd/blob/v0.0.3/source/rxd/meta2/lambda.d#L12 and
https://github.com/ZombineDev/rxd/blob/v0.0.3/source/rxd/xf/xform.d#L54

This one of the few (only?) places that C++14 is better than D, because in C++14 polymorphic lamdas are first-class values, whereas in D alias function literals are templates (i.e. you can refer to them only by alias, can't assign them to enum or auto constants / variables).
February 16, 2017
On Wednesday, 15 February 2017 at 17:10:26 UTC, Adam D. Ruppe wrote:
> On Wednesday, 15 February 2017 at 07:56:00 UTC, Jacob Carlborg wrote:
>> Your documentation is an improvement but it doesn't help when reading the source code.
>
> Yeah, I think there's a few things we can do in the source too. We should find the common combinations and abstract them out, like I said before, isInputRangeOf is potentially useful.
>
> Though, like Andrei, I'm skeptical on doing too much of that, since the combinations can quickly explode and then you just have to search through more to figure out wtf they mean.
>
> That'd help the source and docs if we find the right balance.

I submitted a Phobos PR for `isInputRangeOf`. It was nuked from orbit. I understand why: as you mention, the combinatorial explosion of names is an issue. But: it's a mess rangeifying code when this:

void fun(Foo[] foos);  // yay! readable

becomes:

void fun(R)(R foos) if(isInputRange!R && is(Unqual!(ElementType!R) == Foo);

Ugh.

Atila
February 16, 2017
On 2/16/17 7:11 AM, Atila Neves wrote:
> On Wednesday, 15 February 2017 at 17:10:26 UTC, Adam D. Ruppe wrote:
>> On Wednesday, 15 February 2017 at 07:56:00 UTC, Jacob Carlborg wrote:
>>> Your documentation is an improvement but it doesn't help when reading
>>> the source code.
>>
>> Yeah, I think there's a few things we can do in the source too. We
>> should find the common combinations and abstract them out, like I said
>> before, isInputRangeOf is potentially useful.
>>
>> Though, like Andrei, I'm skeptical on doing too much of that, since
>> the combinations can quickly explode and then you just have to search
>> through more to figure out wtf they mean.
>>
>> That'd help the source and docs if we find the right balance.
>
> I submitted a Phobos PR for `isInputRangeOf`. It was nuked from orbit. I
> understand why: as you mention, the combinatorial explosion of names is
> an issue. But: it's a mess rangeifying code when this:
>
> void fun(Foo[] foos);  // yay! readable
>
> becomes:
>
> void fun(R)(R foos) if(isInputRange!R && is(Unqual!(ElementType!R) == Foo);

If you find a number of these, that would be good evidence. -- Andrei

February 16, 2017
On Wednesday, 15 February 2017 at 19:39:52 UTC, Andrei Alexandrescu wrote:
> On 02/15/2017 06:20 AM, Daniel N wrote:
>> On Wednesday, 15 February 2017 at 09:22:14 UTC, Daniel N wrote:
>>> template every(T...)
>>> {
>>>     template satisfies(U...)
>>>     {
>>>         enum satisfies = true;
>>>     }
>>> }
>>
>> (lunch-break => time to hack D!)
>>
>> template every(T...)
>> {
>>   template satisfies(U...)
>>   {
>>     enum satisfies = {
>>       foreach(t; T)
>>         foreach(u; U)
>>       if(!u!t)
>>             return false;
>>       return true;
>>     }();
>>   }
>> }
>
> That looks pretty neat. Can you find 4-5 cases in which this could be used gainfully in Phobos? Thanks! -- Andrei

Thanks, I'll get back to you. Unfortunately I just contracted fever, not fit to leave bed. Nothing dangerous, just am K.O.