Jump to page: 1 2
Thread overview
Questions on the new __traits(parameters)
Mar 09, 2022
Quirin Schroll
Mar 09, 2022
Timon Gehr
Mar 10, 2022
bauss
Mar 10, 2022
Quirin Schroll
Mar 10, 2022
Adam D Ruppe
Mar 10, 2022
Timon Gehr
Mar 10, 2022
Adam Ruppe
Mar 10, 2022
Timon Gehr
Mar 10, 2022
Paul Backus
Mar 10, 2022
Adam D Ruppe
Mar 10, 2022
Timon Gehr
Mar 10, 2022
H. S. Teoh
Mar 10, 2022
Timon Gehr
Mar 10, 2022
Timon Gehr
Mar 10, 2022
Elronnd
Mar 10, 2022
MoonlightSentinel
Mar 11, 2022
max haughton
Mar 11, 2022
Timon Gehr
March 09, 2022

In the changelog it says:

>

When used inside a foreach using an overloaded opApply, the trait yields the parameters to the delegate and not the function the foreach appears within.

Why? This will rarely be wanted or be used intentionally. When a programmer uses __traits(parameters) in a foreach loop, it will for certain happen to someone not aware of this. The iteration implemention (opApply or range functions) is a detail one should not really have to care about on the usage side. This complicates the language. Morally, this is a bug. Please reconsider this design decision before it sticks.

March 09, 2022
On 3/9/22 11:04, Quirin Schroll wrote:
> In the changelog it says:
>> When used inside a foreach using an overloaded `opApply`, the trait yields the parameters to the delegate and not the function the foreach appears within.
> 
> Why? This will rarely be wanted or be used intentionally. When a programmer uses `__traits(parameters)` in a `foreach` loop, it will for certain happen to someone not aware of this. The iteration implemention (`opApply` or range functions) is a detail one should not really have to care about on the usage side. This complicates the language. Morally, this is a bug. Please reconsider this design decision before it sticks.

It's a consequence of how such a foreach loop is lowered. Looks like one of those cases where a bug was resolved by changing the specification.
March 10, 2022
On Wednesday, 9 March 2022 at 19:02:27 UTC, Timon Gehr wrote:
> On 3/9/22 11:04, Quirin Schroll wrote:
>> In the changelog it says:
>>> When used inside a foreach using an overloaded `opApply`, the trait yields the parameters to the delegate and not the function the foreach appears within.
>> 
>> Why? This will rarely be wanted or be used intentionally. When a programmer uses `__traits(parameters)` in a `foreach` loop, it will for certain happen to someone not aware of this. The iteration implemention (`opApply` or range functions) is a detail one should not really have to care about on the usage side. This complicates the language. Morally, this is a bug. Please reconsider this design decision before it sticks.
>
> It's a consequence of how such a foreach loop is lowered. Looks like one of those cases where a bug was resolved by changing the specification.

Surely we could work around it?
March 10, 2022

On Thursday, 10 March 2022 at 07:21:23 UTC, bauss wrote:

>

On Wednesday, 9 March 2022 at 19:02:27 UTC, Timon Gehr wrote:

>

On 3/9/22 11:04, Quirin Schroll wrote:

>

In the changelog it says:

>

When used inside a foreach using an overloaded opApply, the trait yields the parameters to the delegate and not the function the foreach appears within.

Why? This will rarely be wanted or be used intentionally. When a programmer uses __traits(parameters) in a foreach loop, it will for certain happen to someone not aware of this. The iteration implemention (opApply or range functions) is a detail one should not really have to care about on the usage side. This complicates the language. Morally, this is a bug. Please reconsider this design decision before it sticks.

It's a consequence of how such a foreach loop is lowered. Looks like one of those cases where a bug was resolved by changing the specification.

Surely we could work around it?

The issue is not the complexity of the workaround (example below), but that it is in practice hard to know when it applies and it is hard to teach (cf. Scott Meyers: Language is good == Features are easy to explain). You have to remember to be careful in foreach loops when you use __traits(parameters) . You have to remember not because the case is truly an odd-one-out, but because … Well, I don’t know how to finish that sentence without being rude. A justified special case is: “If we used the simple rule, reasonable expectations would break. Therefore we have a complicated rule.” — Here, we have it backwards: “If we used the simple rule, we’d have a complicated compiler implementation. Therefore we break expectations.” The authors did not even have the mercy to make it an error so that it is guaranteed that programmers work around it. (The error message could claim it is amibiguous what you mean. It morally is not, but technically it is.)

The compiler does a bunch of stuff to properly lower return, break, continue, and goto statments in the foreach body when given to opApply. Why the authors of this feature decided not require it in this case is beyond me.

Workaround

Say you wanted to access the function's parameters in a foreach loop. Say you are in a meta-programming context where you don't know the type of the range.

void f(R, Ts...)(R range, int param, Ts args)
{
    foreach (auto ref x; range)
    {
        // some amount of code
        g!(Ts[1..$])(x, __traits(parameters)[1..$]);
    }
}

This is the idiomatic way to write the call to g, but it might not work correctly depending on the details of the iteration of range. Worse, it might compile and silently do unexpected stuff. The always-correct way:

void f(R, Ts...)(R range, int param, Ts args)
{
    alias relevantParams = __traits(parameters)[1..$]; // Why?
    foreach (auto ref x; range)
    {
        // some amount of code
        g!(Ts[1..$])(x, relevantParams);
    }
}
March 10, 2022
On Thursday, 10 March 2022 at 10:18:09 UTC, Quirin Schroll wrote:
> The issue is not the complexity of the workaround (example below), but that it is in practice hard to know when it applies and it is *hard to teach*


__traits(parameters) always gives the parameters of the function in which it is used *even if it is used inside a nested function*.

> ### Workaround
> Say you wanted to access the function's parameters in a `foreach` loop. Say you are in a meta-programming context where you don't know the type of the range.
> ```D
> void f(R, Ts...)(R range, int param, Ts args)
> {
>     foreach (auto ref x; range)
>     {
>         // some amount of code
>         g!(Ts[1..$])(x, __traits(parameters)[1..$]);

Why wouldn't you just use `args` here? The __traits(parameters) actually doesn't give any real value when you already have a variadic.
March 10, 2022

On Wednesday, 9 March 2022 at 10:04:57 UTC, Quirin Schroll wrote:

>

In the changelog it says:

>

When used inside a foreach using an overloaded opApply, the trait yields the parameters to the delegate and not the function the foreach appears within.

Why? This will rarely be wanted or be used intentionally. When a programmer uses __traits(parameters) in a foreach loop, it will for certain happen to someone not aware of this. The iteration implemention (opApply or range functions) is a detail one should not really have to care about on the usage side. This complicates the language. Morally, this is a bug. Please reconsider this design decision before it sticks.

From the PR discussion, the rationale given for the current behavior was:

>

I'm not sure whether this was desirable or not, but it's not really along the lines of how this feature is intended to be used so I don't see any utility any different behaviour.

So there is no real justification for it; the interaction with foreach was just not considered important enough to be worth the effort of handling correctly.

March 10, 2022
On Thursday, 10 March 2022 at 12:49:57 UTC, Paul Backus wrote:
> handling correctly.

objection, prejudiced language

March 10, 2022

On 3/9/22 5:04 AM, Quirin Schroll wrote:

>

In the changelog it says:

>

When used inside a foreach using an overloaded opApply, the trait yields the parameters to the delegate and not the function the foreach appears within.

Why? This will rarely be wanted or be used intentionally. When a programmer uses __traits(parameters) in a foreach loop, it will for certain happen to someone not aware of this. The iteration implemention (opApply or range functions) is a detail one should not really have to care about on the usage side. This complicates the language. Morally, this is a bug. Please reconsider this design decision before it sticks.

Yes, I agree.

string s = "WTF";

alias p = typeof(__traits(parameters));
foreach(x; s) {
   static assert(__traits(isSame, p, typeof(__traits(parameters))));
}

foreach(dchar x; s) {
   static assert(!__traits(isSame, p, typeof(__traits(parameters))));
}

Lowering should be invisible to the plain language. This is similar to issues where lowered code has errors and you get error messages on code that you never wrote.

Imagine if inside an opApply delegate, a return statement just returned the delegate. This is equivalent to what you are saying here.

-Steve

March 10, 2022
On Thursday, 10 March 2022 at 15:08:11 UTC, Steven Schveighoffer wrote:
> *snip*

I was a bit on the fence regarding the issue, but this is what convinced me.

The issue is primarily one of abstraction and modularity.  It should be an implementation detail whether a given object uses opApply or range APIs.  It should be possible to switch from one to the other without breaking client code.  I should not have to care how the object I'm iterating over arranges to be iterated over.
March 10, 2022
On 10.03.22 13:27, Adam D Ruppe wrote:
> On Thursday, 10 March 2022 at 10:18:09 UTC, Quirin Schroll wrote:
>> The issue is not the complexity of the workaround (example below), but that it is in practice hard to know when it applies and it is *hard to teach*
> 
> 
> __traits(parameters) always gives the parameters of the function in which it is used *even if it is used inside a nested function*.
> 

It is not used inside a nested function. It is used within a foreach loop. Different rules apply. Or are you arguing that (like Steve pointed out already), return statements in opApply foreach loop bodies should return from the delegate because "return always returns from the function in which it is used *even if it is used inside a nested function*?
« First   ‹ Prev
1 2