Frist Draft (in this forum): Enum Parameters
Quirin Schroll
qs.il.paperinik at gmail.com
Tue May 7 11:23:10 UTC 2024
On Monday, 29 April 2024 at 19:17:47 UTC, Paul Backus wrote:
> On Thursday, 25 April 2024 at 17:56:59 UTC, Quirin Schroll
> wrote:
>> https://github.com/Bolpat/DIPs/blob/2bfef0b05e99c5448b416078da2e743482e3193d/DIPs/1NNN-QFS.md
>
> Yet another non-orthogonal language feature that all generic
> code will have to go out of its way to account for until the
> end of time. `ref` is already bad enough; the last thing we
> need is another feature that repeats the exact same design
> mistakes.
TL;DR: While `auto enum` is similar to `auto ref` in that it
infers a storage class based on the argument passed, forwarding
is a non-issue for `auto enum` and even `auto enum auto ref`.
Analysis is below.
> For example: currently, in order to forward an argument
> correctly in generic code, we must write the following:
>
> ```d
> void forwardTo(alias fun, T)(auto ref T arg)
> {
> import core.lifetime: forward;
> fun(forward!arg);
> }
> ```
>
> Notice how much overhead is required *just* to deal with the
> `ref` storage class.
I agree that forwarding in its current form is indeed a nuisance.
> If this proposal were accepted, we would have to replace all
> instances of the above with the following:
>
> ```d
> void forwardTo(alias fun, T)(auto enum auto ref T arg)
> {
> import core.lifetime: forward;
> static if (__traits(isEnum, arg))
> fun(arg);
> else
> fun(forward!arg);
> }
> ```
>
> Needless to say, the vast majority of D programmers are not
> going to bother with this, which means that we will forever be
> plagued by buggy handling of `enum` parameters in our library
> code (just as we are already plagued by buggy handling of `ref`
> parameters).
>
> I do not think it is a good idea to introduce language features
> that set future D programmers up for failure like this.
You’re right in that `forward` better be mentioned in the DIP.
Writing an answer to this post, I found out that not only could
`forward` easily be adapted to recognize `enum` parameters and
handle them properly by leaving them alone, `forward` actually
would handle them correctly today.
First things first, contrary to `auto ref`, an `auto enum`
parameter bound to a compile-time constant (i.e. one that becomes
`enum`) would stay a compile-time constant (and a run-time value
would stay a run-time value), so forwarding `auto enum` is
automatic (it requires nothing in terms of written code to
happen). That is contrary to `auto ref` where no matter how the
argument was passed (i.e. no matter whether the parameter becomes
`ref`), the parameter itself is always an lvalue and binds
to/prefers `ref` when passed to another function. This is why
forwarding `auto ref` must be explicit.
Another aspect is that there is no `enum ref` parameters, so
`auto enum auto ref` is non-orthogonal by design: It can become
`enum` (pass as compile-time constant), `ref` (pass by reference)
or none (pass by value).
Assuming the following code (which apart from additional `auto
enum` is identical to the example of currently existing code):
```d
void forwardTo(alias fun, T)(auto enum auto ref T arg)
{
import core.lifetime: forward;
fun(forward!arg);
}
```
How does `forward` handle the three cases? The cases `ref` and
pass-by-value are clear, they are as they are today. As far as
the function template implementation is concerned, the `enum`
case is equivalent to:
```d
void forwardTo(alias fun, T, T arg)()
{
import core.lifetime: forward;
fun(forward!arg);
}
```
That is valid code today!
```d
void main()
{
immutable myInt = 10;
forwardTo!(
(auto ref x) { pragma(msg, __traits(isRef, x)); },
int,
myInt
);
}
```
It prints `false`. This means, `forward` passed the compile-time
constant to the lambda unchanged and a compile-time constant is
seen as an rvalue. Note that `myInt` could absolutely be bound as
an lvalue.
The reason for this is that `forward` not only recognizes some
storage classes (`ref` among them), it also checks if `move`
would work and if it doesn’t, it does not use it. That is because
template value parameters are rvalues and `enum` parameters would
be rvalues as well, and `move` only works on lvalues.
More information about the dip.development
mailing list