On Wednesday, 28 October 2020 at 10:57:37 UTC, Jacob Carlborg
wrote:
> On Tuesday, 27 October 2020 at 10:54:07 UTC, Mike Parker wrote:
>> This is the discussion thread for the first round of Community
>> Review of DIP 1037, "Add Unary Operator ...":
>>
>> https://github.com/dlang/DIPs/blob/ba81eec84ddf0aeeb2cb652743b292455ec8c62a/DIPs/DIP1037.md
>
> Is there a risk of causing some form of ambiguity with variadic
> functions?
No, because the DIP does not propose anything with types. In the
DIP, it says "expression" everywhere; it's for use in
expressions, not types. It doesn't forbid types to occur in the
tuple for good reasons, simply because types can very well occur
in expression contexts, but the result will always be an
expression. If you want a drastic example how far reaching
expression vs type context is: given a type T, T[] can mean:
slice of T or T.opIndex. If T defines static opIndex, in an
expression context, T[] will mean T.opIndex, and in a type
context, it will mean slice of T (kinda obvious). Let Ts := (T1,
T2); if you'd do (Ts[]).sizeof... --- because ... is only valid
in an expression context --- it *must* lower to
(T1.opIndex.sizeof, T2.opIndex.sizeof). By the grammar given in
the DIP, the ... do not have anything to do with the type
expression stuff.
The example given in the DIP with `cast(Types)Values...` might
work by accident because types are also expressions. By the
grammar provided, `cast(Types[])Values...` could never work as
intended! The example
alias staticMap(alias F, T...) = F!T...;
cannot work. By the grammar provided, F!T... must be an
expression, not a "type expression", but alias requires a type
context as its target. (I.e. alias x = 5; doesn't work; alias x =
y + 1; doesn't work either because the right-hand sides aren't
types.)
In the Feedback Thread, I commented that it would be great to
have the proposed operator apply to type contexts as well [1].
Didn't get an answer, but do hope I get one eventually.
Problem is, as you started digging: For a class C (and classes
only, not structs or anything else), the parameter definition
C... is already defined [2, see "4. For class objects"]. For
example,
class C { this(int, string) { } }
void foo(C...) { }
void main() { foo(1, "abc"); }
compiles [3]. So if C is the last entry in a type-only AliasSeq
that is used in a parameter list, notations clash. Currently
valid [4] code like
import std.meta : AliasSeq;
class C { this(int, string) { } }
alias Types = AliasSeq!(int, string, C);
void foo(Types args...) { }
void main() { foo(1, "abc", 2, "xyz"); }
would no longer compile, since `Types...` would be the literal
same as using `Types` without dots.
[1]
https://forum.dlang.org/post/fogbdhcdeukiucbxxxns@forum.dlang.org
[2]
https://dlang.org/spec/function.html#typesafe_variadic_functions
[3] https://run.dlang.io/is/GmgAaG
[4] https://run.dlang.io/is/DeGZF7
Thank you for this analysis. This is useful. I'll confirm the DIP's spec and implementation against your points.
It might be that the implementation works by happen-stance, or perhaps my stated grammar change wasn't accurate.
It's very hard to express a grammar change in a DIP like this, since D doesn't have a grammar as such; it's just an implementation, and reverse-engineering a grammar from the implementation is imprecise. Perhaps my implementation implies additional grammar changes that I didn't notice.