www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - DIP proposal: Enum parameters

reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
Read the draft here: 
https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

Feedback is welcome.
Sep 23 2022
next sibling parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

 Feedback is welcome.
I use ``enum`` a lot in my code base, and i think the name of that keyword for that usecase is very wrong, it conveys the wrong intention ``enum`` is supposed to be an enumeration, not a _ for compile time usecase, in fact, ``comptime`` is a better name, so i personally would love if it was changed to that, if that's possible
Sep 23 2022
parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Friday, 23 September 2022 at 16:17:01 UTC, ryuukk_ wrote:
 On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
 wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

 Feedback is welcome.
I use ``enum`` a lot in my code base, and i think the name of that keyword for that usecase is very wrong, it conveys the wrong intention
If you thought about `enum c = val;`, then we simply disagree about the intention. If you thought about `enum { ... }` or `enum Name { ... }`, then youā€™re right, but isnā€™t the `enum` keyword used quite (if not even more) frequently like this? ```d enum constant = value; enum empty = false; // infinite ranges use this ``` Semantically, `enum c = val;` is not exactly sugar for `enum { c = val }`, because `enum c(T) = val!T;` works, but no equivalent (not even with explicit `template`) exists for `enum {}`. They are quite obviously different conceptually. Single `enum` is exactly what `comptime` would express: A compile-time only value, or (as I call it in my mind) a named literal (to better understand why an `enum` with slice type always allocates).
 ``enum`` is supposed to be an enumeration, not a _ for compile 
 time usecase, in fact, ``comptime`` is a better name, so i 
 personally would love if it was changed to that, if that's 
 possible.
It probably must be an existing keyword. `comptime` would be possible as a contextual keyword, something Walter opposes. `static` is the only other candidate. Iā€™ve looked through the whole [list of keywords](https://dlang.org/spec/lex.html#keywords).
Sep 23 2022
parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Friday, 23 September 2022 at 16:44:05 UTC, Quirin Schroll 
wrote:
 On Friday, 23 September 2022 at 16:17:01 UTC, ryuukk_ wrote:
 ``enum`` is supposed to be an enumeration, not a _ for compile 
 time usecase, in fact, ``comptime`` is a better name, so i 
 personally would love if it was changed to that, if that's 
 possible.
It probably must be an existing keyword. `comptime` would be possible as a contextual keyword, something Walter opposes. `static` is the only other candidate. Iā€™ve looked through the whole [list of keywords](https://dlang.org/spec/lex.html#keywords).
Nevermind. I just found out that [attributes for function parameters](https://dlang.org/spec/function.html#Parameters) are a thing as of late. As with ` safe`, ` nogc` and ` property`, ` comptime` could be another compiler-recognized attribute. As a member function attribute, ` comptime` intuitively looks more like it means that the function can only be used for CTFE and never at run-time (cf. [`consteval`](https://en.cppreference.com/w/cpp/language/consteval) in C++; something like this was suggested for D before).
Sep 26 2022
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

 Feedback is welcome.
Some thoughts. * Does this need `__traits(isEnum)` (like `__traits(isRef)`) for working with `auto enum` parameters? * Sometimes library authors prefer to have a parameter passed at runtime even when it *could* be passed at compile time, to avoid template bloat. So for use cases like `format` and `regex`, this proposal is not a clear win. * A significant chunk of the proposed use-cases could also be addressed by adding `opStaticIndex`. Probably worth mentioning in the "Alternatives" section. * Incompatibility with other parameter storage classes is a design smell. Ideally, we would like our language features to be orthogonal and work together with each other. * Similarity to `ref` is also a design smell. `ref` is badly designed, and things like `auto ref` and `core.lifetime.forward` exist entirely to work around its deficiencies. We should avoid making the same mistake twice. Overall, my impression is slightly negative. I can see situations where this would be useful, but I'm not convinced it's useful *enough* to justify the language-complexity and maintenance costs.
Sep 23 2022
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 23.09.22 19:54, Paul Backus wrote:
 
 * Similarity to `ref` is also a design smell. `ref` is badly designed, 
 and things like `auto ref` and `core.lifetime.forward` exist entirely to 
 work around its deficiencies. We should avoid making the same mistake 
 twice.
This is reasoning by analogy, and it is not really appropriate here. It's not the same: A non-ref parameter is always an lvalue, but a non-enum parameter is never a compile-time constant. You just don't have the same problems.
Sep 23 2022
prev sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Friday, 23 September 2022 at 17:54:49 UTC, Paul Backus wrote:
 On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
 wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

 Feedback is welcome.
Some thoughts. * Does this need `__traits(isEnum)` (like `__traits(isRef)`) for working with `auto enum` parameters?
Good catch, `__traits(isEnum)` does not exist currently. But it makes sense to add it, not only for this proposal, but generally. Iā€™ll add that.
 * Sometimes library authors prefer to have a parameter passed 
 at runtime even when it *could* be passed at compile time, to 
 avoid template bloat. So for use cases like `format` and 
 `regex`, this proposal is not a clear win.
Is that the case? Of course, you get *one* additional template instantiation for regex because it normally is not a template at all. `format` is already a template; with a template argument format, you ideally only get a different instantiation. A high number of template instances is usually only a problem if the templates are recursive. If it happens to be a problem, one can always put the format or regex in a local variable and use that. This workaround is harder if you also want the value to be an rvalue; youā€™ll then need to define a function that returns the value *and* takes a run-time value as a parameter (that it wonā€™t actually use) so that CTFE is out of the picture. This is a niche case; I guess itā€™s okay for this to be a little work.
 * A significant chunk of the proposed use-cases could also be 
 addressed by adding `opStaticIndex`. Probably worth mentioning 
 in the "Alternatives" section.
I wrote a draft for static indexing. There, I mentioned that something like this, i.e. a feature to pass parameters at compile-time were added, it would supersede the proposal. I figured that it was actually easier to specify and explain compile-time parameter passing than static indexing. Static indexing with a completely separate set of operators almost doubles the jungle of indexing operators and is very specific.
 * Incompatibility with other parameter storage classes is a 
 design smell. Ideally, we would like our language features to 
 be orthogonal and work together with each other.
Maybe you got this wrong. It is not that by virtue of `enum` any thinkable parameter storage class would be incompatible. It is just that all of the existing ones are. `enum` is a stronger form of `in` and thus is in the same group with `in`, `out`, and `ref`, which specify parameter passing. Two of them together would have a very specific meaning and not simply the rules for both because that would be contradictory. Letā€™s go through [the list](https://dlang.org/spec/function.html#ParameterStorageClass): * `auto` can only be used in conjunction with `ref` to form `auto ref`. Alone, it is invalid. * `TypeCtor` are allowed. It says so in the DIP. * `final` as far as I know is only there to generate a specific error; it is never valid. * `in` (see below)Ā² * `lazy` makes no sense; the value is already produced at compile-time. * `out` is incompatible with `enum` because `enum` values are never lvalues and cannot be written to. * `ref` is incompatible with `enum` because `enum` values are never lvalues. * `return` is really only something for run-time values.Ā¹ * `scope` is really only something for run-time values.Ā¹ Ā¹ To be honest, I donā€™t know how `scope` and by extension `return` would apply to compile-time constants. If Iā€™m not mistaken, life-time of those objects is infinite. Ā² The `in` storage class means `const scope` and maybe `ref`. `enum` is incompatible with `ref`, `scope` does not apply.
 * Similarity to `ref` is also a design smell. `ref` is badly 
 designed, and things like `auto ref` and 
 `core.lifetime.forward` exist entirely to work around its 
 deficiencies. We should avoid making the same mistake twice.
I see how `auto ref` has flaws because the value category is not preserved, i.e. the parameter is an lvalue regardless whether it was initialized by an rvalue or lvalue. This is where the analogy parts. An `auto enum` parameter initialized by a compile-time constant is a compile-time constant and an `auto enum` parameter initialized by a run-time value is a run-time value. Nothing has to be done to forward. The analogy is limited to the way overloading and inference on the category (value category for `ref` and compile-time category for `enum`) works.
 [ā€¦] I can see situations where this would be useful, but I'm 
 not convinced it's useful *enough* to justify the 
 language-complexity and maintenance costs.
Thatā€™s what Iā€™m trying to find out.
Sep 26 2022
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 23.09.22 17:41, Quirin Schroll wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md
 ...
Looks useful.
 Feedback is welcome.
- The author's name is spelled wrong. - The description of `auto enum` makes it not fully clear if the compiler attempts to interpret the argument with CTFE or not to check whether it is a compile-time constant. Same question about overloads. (Though for overloads I would expect CTFE to be attempted, as in the first step it has to be determined which overloads even match.) - The special-case qualifier and storage class behavior is not necessary, just make it behave like a normal `enum` variable.
Sep 23 2022
parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Saturday, 24 September 2022 at 00:12:35 UTC, Timon Gehr wrote:
 Looks useful.
Thanks.
 - The author's name is spelled wrong.
Happens to the best. šŸ˜…
 - The description of `auto enum` makes it not fully clear if 
 the compiler attempts to interpret the argument with CTFE or 
 not to check whether it is a compile-time constant. Same 
 question about overloads. (Though for overloads I would expect 
 CTFE to be attempted, as in the first step it has to be 
 determined which overloads even match.)
Yes, I have to make this clear. CTFE *must* be attempted when binding to `auto enum`. It only becomes to non-`enum` if `enum` (without `auto`) would not be able to bind the argument. (Similarly, `auto ref` is required to bind as `ref` even if it could bind via copy.)
 - The special-case qualifier and storage class behavior is not 
 necessary, just make it behave like a normal `enum` variable.
I donā€™t really know what you mean exactly. Maybe this section:
 The `enum` storage class is incompatible with any other storage 
 classes except type constructors. Using them together is a 
 compile error. Type constructors are allowed, but have no 
 effect because compile-time values are `immutable` and for any 
 type constructor `qual` and every type `T`, we have 
 `qual(immutable T)` ā‰” `immutable T`).
By the way, Iā€™ve figured the explanation in the DIP is wrong. `enum` values are not immutable or effectively immutable. It appears that the semantics of `enum` currently make the value a literal for the first and second level of indirection. The other parts are mutable unless qualified, and do not count as mutable global state as far as `pure` is concerned. [Issue 23375](https://issues.dlang.org/show_bug.cgi?id=23375) I realized when answering to Paul Backus about the incompatibility with storage classes, in particular `scope`. I created some examples with indirections and found bugs.
Sep 26 2022
prev sibling next sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

 Feedback is welcome.
Regarding Prior Art, this reminds me a lot of C++'s `auto` function parameters that make the function a template function without the need of the syntax overhead that comes with C++. It also reminds me of SPIR-V specialisation constants, which serve a different purpose of late binding values for the optimiser. Those are two incompatible meanings, and it is not entirely clear which one the DIP wants to be. I think the best option would be to have a static opSlice be something else and have enum parameters be values that must resolve at compile time. This DIP makes no mention of what one can do with an enum parameter. In particular it would be very useful to be able to use CTFE with it, and do static assertions (e.g. check signatures of format strings). and (not that I'm sure there is any easy way to do it) being able to differentiate (and let the compiler know, probably more of a problem for DMD) if the intention is to reduce template bloat (in the case of format) or use the value as a specialisation constant.
 cf. a ref parameter only binds to lvalues
This is not the case, for C++ interop reasons (among others) `const ref` can bind rvalues.
Sep 23 2022
parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
Sorry in advance for the long answer.

On Saturday, 24 September 2022 at 01:25:17 UTC, Nicholas Wilson 
wrote:
 Regarding Prior Art, this reminds me a lot of C++'s `auto` 
 function parameters that make the function a template function 
 without the need of the syntax overhead that comes with C++.
The key difference here is that C++ template syntax is verbose and `auto` is a syntax sugar. I thought about having functions with `enum` parameters be templates implicitly. I decided against that. For one, it could be allowed later if deemed useful, for second, making a function a function template in D requires adding `()` between the name and the parameter list. I.e. ```d void f(enum int i) { } // error, f is not a template void g()(enum int i) { } // this is how itā€™s done ```
 It also reminds me of SPIR-V specialisation constants, which 
 serve a different purpose of late binding values for the 
 optimiser.
I know nothing about SPIR-V. If you donā€™t mind, you can explain it to me or send me a link to a description.
 Those are two incompatible meanings, and it is not entirely 
 clear which one the DIP wants to be. I think the best option 
 would be to have a static opSlice be something else and have 
 enum parameters be values that must resolve at compile time.
Do you mean that that they must resolve at compile-time and then be run-time values? That would throw away valuable information with virtually no gain.
 This DIP makes no mention of what one can do with an enum 
 parameter.
It does, three examples are provided: format, regex, static indexing. You can do with them whatever you can do with template value parameters.
 In particular it would be very useful to be able to use CTFE 
 with it, and do static assertions (e.g. check signatures of 
 format strings). and (not that I'm sure there is any easy way 
 to do it) being able to differentiate (and let the compiler 
 know, probably more of a problem for DMD) if the intention is 
 to reduce template bloat (in the case of format) or use the 
 value as a specialisation constant.
That is exactly the specified semantics. I thought about defining the feature in terms or rewrites. Those would make the proposal more understandable, but also constrain the implementation. Also, it would define things to be a certain way that could be depended upon. Potential rewrite implementation examples for ```d void f(T)(enum T x, T y) { /+impl+/ } ``` (A) Append the `enum` parameters to the template argument list (cf. C++ `auto` parameters). ```d void f(T, T x)(T y) { /+impl+/ } // which in turn is actually template f(T, T x) { void f(T y) { /+impl+/ } } ``` ```d f(10, 20) // equivalently, 10.f(20) // is actually f!(int, 10)(20) ``` (B) Nested template ```d template f(T) { template f(T x) { void f(T y) { /+impl+/ } } } ``` ```d f(10, 20) // is actually (f!int)!10(20) // note: syntactically invalid, // i.e. in code, it must be: Instantiate!(f!int, 10)(20) ``` I guess both have their upsides and downsides. I did not investigate this further. Again, it need not be a rewrite at all, and I donā€™t think it will be, unless someone has a great idea that has no weird semantics.
 cf. a ref parameter only binds to lvalues
This is not the case, for C++ interop reasons (among others) `const ref` can bind rvalues.
I tried on run.dlang.io/nightly, this statement is wrong; `const ref` parameters cannot bind rvalues. You can, however, use `in` instead of `const ref`. With [`-preview=in`](https://dlang.org/changelog/2.094.0.html#preview-in), [`in` on parameters of an `extern(C++)` function]((https://dlang.org/changelog/pending.html#previewInLink)) means what `const&` means in C++. Example: ```d extern(C++) void f(const ref int x) { } extern(C++) void g(in int x) { } void main() { f(1); // error g(1); // good } ``` [DIP 1016](https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.md) wanted `ref` to be able to bind rvalues. It was rejected.
Sep 26 2022
prev sibling next sibling parent reply Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

 Feedback is welcome.
I'm a bit torn about static vs enum. I partly agree with prior speakers about enum getting too many meanings, but on the other hand, it's not really a problem imo. Would it otherwise be called static parameters? Otherwise const of course, but that's already a used attribute. I think the rationale behind it is solid and would simplify things in the long run (less to think about = better).
Sep 24 2022
next sibling parent reply claptrap <clap trap.com> writes:
On Saturday, 24 September 2022 at 07:51:32 UTC, Imperatorn wrote:
 On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
 wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

 Feedback is welcome.
I'm a bit torn about static vs enum. I partly agree with prior speakers about enum getting too many meanings, but on the other hand, it's not really a problem imo.
Its not really adding a new meaning, enum already (somtimes) means "do this at compile time", that's really the problem, that usage of enum has literally nothing to do with enumeration. It's like we've run out of English words so we have to use "count" any time we want to say "now". Using enum to mean "compile time" or "manifest constant" should be depreciated in favour of a keyword that actually at least vaguely relates to those meanings.
Sep 26 2022
next sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Monday, 26 September 2022 at 09:18:54 UTC, claptrap wrote:
 Using enum to mean "compile time" or "manifest constant" should 
 be depreciated in favour of a keyword that actually at least 
 vaguely relates to those meanings.
The value of that would be close to zero. At least in Phobos, `enum` is more often used to define a single value than an enumeration type. With a new keyword, weā€™d need to deprecate `enum` or have both, also weā€™d have make sure the new keyword is dissimilar to identifiers currently used not to break code. On parameters, a compiler-recognized UDA, say ` comptime`, would be an alternative, but that cannot be trivially used instead of `enum` without special-casing it in the grammar. Inconsistency is worse than bad naming. Also, I donā€™t think that `enum` is actually a bad name. It has a specific meaning in D, but apart from that, itā€™s not that bad. [Half Joking] Another option would be no keyword at all, but some kind of other token. But I guess no one prefers ``f()(^int x)`` or similar over ``f(enum int x)``.
Sep 26 2022
prev sibling parent reply Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Monday, 26 September 2022 at 09:18:54 UTC, claptrap wrote:
 On Saturday, 24 September 2022 at 07:51:32 UTC, Imperatorn 
 wrote:
 On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
 wrote:
 [...]
I'm a bit torn about static vs enum. I partly agree with prior speakers about enum getting too many meanings, but on the other hand, it's not really a problem imo.
Its not really adding a new meaning, enum already (somtimes) means "do this at compile time", that's really the problem, that usage of enum has literally nothing to do with enumeration. It's like we've run out of English words so we have to use "count" any time we want to say "now". Using enum to mean "compile time" or "manifest constant" should be depreciated in favour of a keyword that actually at least vaguely relates to those meanings.
Hmm, yes, this is the other sane option. To actually introduce a new keyword to differentiate
Sep 26 2022
parent Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Monday, 26 September 2022 at 11:55:00 UTC, Imperatorn wrote:
 On Monday, 26 September 2022 at 09:18:54 UTC, claptrap wrote:
 On Saturday, 24 September 2022 at 07:51:32 UTC, Imperatorn 
 wrote:
 [...]
Its not really adding a new meaning, enum already (somtimes) means "do this at compile time", that's really the problem, that usage of enum has literally nothing to do with enumeration. It's like we've run out of English words so we have to use "count" any time we want to say "now". Using enum to mean "compile time" or "manifest constant" should be depreciated in favour of a keyword that actually at least vaguely relates to those meanings.
Hmm, yes, this is the other sane option. To actually introduce a new keyword to differentiate
Maybe a keyword like fixed, invariant could be used. I don't know how immutable would work since it's already used.
Sep 26 2022
prev sibling parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Saturday, 24 September 2022 at 07:51:32 UTC, Imperatorn wrote:
 I'm a bit torn about `static` vs `enum`. I partly agree with 
 prior speakers about `enum` getting too many meanings, but on 
 the other hand, it's not really a problem imo.
One biggie against using `static` is that on member functions it would have to go to the back (where it is currently invalid) with a totally different meaning than in front (although it would make the function a static). Non-member: ```d void f()(static int ctValue) { } // this is fine ``` Member: ```d struct S { void f()() static { } // different from: static void g()() { } void p()() enum { } // same as enum void q()() { } } ``` This would be no problem if D did not allow putting some stuff before and after the function declaration with the same meaning. I really dislike `void f()() static { }`. Currently, `enum` is valid for member functions like `q`, but does nothing. That would change, but Iā€™d bet that almost no one has this in their code base anyway, so breakage would be minimal.
Sep 26 2022
parent Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Monday, 26 September 2022 at 11:23:58 UTC, Quirin Schroll 
wrote:
 On Saturday, 24 September 2022 at 07:51:32 UTC, Imperatorn 
 wrote:
 [...]
One biggie against using `static` is that on member functions it would have to go to the back (where it is currently invalid) with a totally different meaning than in front (although it would make the function a static). [...]
True, and we're used to enum behaving like that, so I guess it's preferrable
Sep 26 2022
prev sibling next sibling parent Generic Human <generic human.com> writes:
On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

 Feedback is welcome.
If it were up to me I'd approve this right now, we need something like `comptime`. The exact keyword is not important, `enum` is okay, `static` is okay, `comptime` is okay, whatever.
Sep 25 2022
prev sibling next sibling parent Ogi <ogion.art gmail.com> writes:
On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

 Feedback is welcome.
Stop, little pot! We can only eat so much attribute porridge.
Sep 26 2022
prev sibling next sibling parent reply victoroak <jackpboy gmail.com> writes:
On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

 Feedback is welcome.
About prior work, you could take a look at comptime from Zig and static from Nim.
Sep 26 2022
parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Tuesday, 27 September 2022 at 00:47:31 UTC, victoroak wrote:
 On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
 wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

 Feedback is welcome.
About prior work, you could take a look at comptime from Zig and static from Nim.
For anyone else interested, the relevant sections are: * Zig: [Comptime](https://ziglang.org/documentation/0.9.1/#comptime) * Nim: [static[T]](https://nim-lang.org/docs/manual.html#special-types-static-t) Thank you.
Sep 27 2022
prev sibling next sibling parent reply bauss <jacobbauss gmail.com> writes:
On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

 Feedback is welcome.
I am really confused about how it differs from what we have now with templates. Like how is this: ``` auto opSlice()(enum size_t l, enum size_t u) => slice!(l, u); ``` An improvement over: ``` auto opSlice(size_t l, size_t u)() => slice!(l, u); ``` Maybe I am not entirely grasping this concept or something.
Sep 27 2022
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 27 September 2022 at 11:52:03 UTC, bauss wrote:
 Like how is this:

 ```
 auto opSlice()(enum size_t l, enum size_t u) => slice!(l, u);
 ```

 An improvement over:

 ```
 auto opSlice(size_t l, size_t u)() => slice!(l, u);
 ```

 Maybe I am not entirely grasping this concept or something.
The only difference is that at the call site, you can write `opSlice(i, j)` instead of `opSlice!(i, j)`. It's pure syntax sugar.
Sep 27 2022
next sibling parent reply bauss <jacobbauss gmail.com> writes:
On Tuesday, 27 September 2022 at 12:13:53 UTC, Paul Backus wrote:
 On Tuesday, 27 September 2022 at 11:52:03 UTC, bauss wrote:
 Like how is this:

 ```
 auto opSlice()(enum size_t l, enum size_t u) => slice!(l, u);
 ```

 An improvement over:

 ```
 auto opSlice(size_t l, size_t u)() => slice!(l, u);
 ```

 Maybe I am not entirely grasping this concept or something.
The only difference is that at the call site, you can write `opSlice(i, j)` instead of `opSlice!(i, j)`. It's pure syntax sugar.
Yeah that's what I thought. I'm not for this particular DIP then. I think adding more keywords is going to make the language much more messy.
Sep 27 2022
parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Tuesday, 27 September 2022 at 12:23:18 UTC, bauss wrote:
 On Tuesday, 27 September 2022 at 12:13:53 UTC, Paul Backus 
 wrote:
 On Tuesday, 27 September 2022 at 11:52:03 UTC, bauss wrote:
 Like how is this:

 ```
 auto opSlice()(enum size_t l, enum size_t u) => slice!(l, u);
 ```

 An improvement over:

 ```
 auto opSlice(size_t l, size_t u)() => slice!(l, u);
 ```

 Maybe I am not entirely grasping this concept or something.
The only difference is that at the call site, you can write `opSlice(i, j)` instead of `opSlice!(i, j)`. It's pure syntax sugar.
Yeah that's what I thought. I'm not for this particular DIP then. I think adding more keywords is going to make the language much more messy.
You probably mean adding more language constructs. `enum` is already a keyword. Compile-time values are already a thing in D. It would be valuable if you provide examples or some further explanations that demonstrate the mess introduced.
Sep 27 2022
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 27.09.22 14:13, Paul Backus wrote:
 On Tuesday, 27 September 2022 at 11:52:03 UTC, bauss wrote:
 Like how is this:

 ```
 auto opSlice()(enum size_t l, enum size_t u) => slice!(l, u);
 ```

 An improvement over:

 ```
 auto opSlice(size_t l, size_t u)() => slice!(l, u);
 ```

 Maybe I am not entirely grasping this concept or something.
The only difference is that at the call site, you can write `opSlice(i, j)` instead of `opSlice!(i, j)`. It's pure syntax sugar.
This is not true, it is adding new capabilities. (E.g., due to overloading.)
Sep 27 2022
prev sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Tuesday, 27 September 2022 at 11:52:03 UTC, bauss wrote:
 I am really confused about how it differs from what we have now 
 with templates.
Calling syntax: ā€œThe difference between enum parameters `and` template value parameters is only in calling syntax: `f!ct_value(args)` versus `f(ct_value, args)`ā€ And that difference matters for meta-programming and operator rewrites.
 Like how is this:

 ```d
 auto opSlice()(enum size_t l, enum size_t u) => slice!(l, u);
 ```

 An improvement over:

 ```d
 auto opSlice(size_t l, size_t u)() => slice!(l, u);
 ```

 Maybe I am not entirely grasping this concept or something.
**TL;DR:** Because the former one works for an operator rewrite while the latter does not, and changing that in a consistent way is very likely a breaking change. ā€• *end TL;DR* You can call both explicitly, i.e. `tup.opSlice!(0, 2)` will perfectly compile, but thatā€™s not why we defined `opSlice`. The syntax `tup[l .. u]` rewrites to the first one of those that compiles: 1. `tup.opIndex(tup.opSlice!0(l, u))` 2. `tup.opSlice(l, u)` What is not in the list is `tup.opSlice!(l, u)()` which currently would have to be for it to work, but probably cannot without breaking code. Itā€™s a bit long, but if you take a look at [Operator Overloading Ā§ā€ÆArray Indexing and Slicing Operators Overloading](https://dlang.org/spec/operatoroverloading.html#array-ops), youā€™ll notice that the only template parameters involved are supplying `opDollar` and `opSlice` (the one that [rewrites the `l..u` pseudo-subexpression](https://dlang.org/spec/operatoroverloading.html#slice) when indexing) with the position in the argument list they appeared in. ----- In 2020, I drafted a DIP for [Compile-time Indexing Operators](https://github.com/Bolpat/DIPs/blob/StaticIndexingOperators/DI s/DIP-1NN1-QFS.md), which is a different feature with a wild range of consequences if thought to its conclusion, among which is spreading the indexable sequence and overload spreading in and of itself. In its *Alternative* section, [Ā§ā€ÆCompile-time Function Parameters](https://github.com/Bolpat/DIPs/blob/StaticIndexingOperators/DIPs/DIP-1NN1-QFS.md#compile-time-f nction-parameters), Iā€™ve already outlined the `enum` parameters DIP and that a large portion of static indexing would be superseded by `enum` parameters. Note that the draft is older and when writing something, I rather put it in than leave it out as after review, stuff can always be deleted, but a thought never put down may be gone forever.
Sep 27 2022
prev sibling next sibling parent reply TheGag96 <thegag96 gmail.com> writes:
On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

 Feedback is welcome.
So my first thought seeing this was that `enum` parameters would bind to compile-time constants but don't incur extra codegen for each value passed in, and `auto enum`, when used once at least, would implicitly generate exactly two copies. But given this, I don't think that's what you're saying:
 In the function body (including contracts and constraints), an 
 `enum` parameterā€™s value is a compile-time constant as if it 
 were template value parameter. The same is true for `this` in 
 an `enum` non-`static` member function body. The difference 
 between `enum` parameters and template value parameters is only 
 in calling syntax: `f!ct_value(args)` versus `f(ct_value, 
 args)`.
So the DIP exists purely to introduce a more unified calling syntax?
Sep 27 2022
parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Tuesday, 27 September 2022 at 14:39:58 UTC, TheGag96 wrote:
 On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
 wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

 Feedback is welcome.
So my first thought seeing this was that `enum` parameters would bind to compile-time constants but don't incur extra codegen for each value passed in, and `auto enum`, when used once at least, would implicitly generate exactly two copies. But given this, I don't think that's what you're saying:
 In the function body (including contracts and constraints), an 
 `enum` parameterā€™s value is a compile-time constant as if it 
 were template value parameter. The same is true for `this` in 
 an `enum` non-`static` member function body. The difference 
 between `enum` parameters and template value parameters is 
 only in calling syntax: `f!ct_value(args)` versus `f(ct_value, 
 args)`.
So the DIP exists purely to introduce a more unified calling syntax?
Yes. If you boil it down to a short and vague sentence, youā€™re right. Itā€™s another way to get compile-time information into a function that is ā€“ notably ā€“ syntactically identical to passing run-time information into a function. This is on purpose so the function can potentially react to information passed this way or the other and in the compile-time case, on the information itself, and the user need not care at all.
Sep 27 2022
parent reply TheGag96 <thegag96 gmail.com> writes:
On Tuesday, 27 September 2022 at 15:33:35 UTC, Quirin Schroll 
wrote:
 Itā€™s another way to get compile-time information into a 
 function that is ā€“ notably ā€“ syntactically identical to passing 
 run-time information into a function. This is on purpose so the 
 function can potentially react to information passed this way 
 or the other and in the compile-time case, on the information 
 itself, and the user need not care at all.
Hmm okay. The idea of detecting compile-time-ness is really great I think, but I have always figured it's an issue that there's no way currently to have a compile-time parameter and do all the nice checking that can come with that without introducing some level of template bloat. I dunno, maybe it's manageable with what exists today - I'm just now seeing that the checked version of `writefln` is implemented like this: ```d void writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt))) { import std.format : checkFormatException; alias e = checkFormatException!(fmt, A); static assert(!e, e); return this.writefln(fmt, args); } ``` So, you'll get another instance of the function per call, but they would hopefully be inlined away anyway. (Maybe this should be marked with `pragma(inline, true)`...)
Sep 27 2022
parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Tuesday, 27 September 2022 at 21:02:39 UTC, TheGag96 wrote:
 Hmm okay. The idea of detecting compile-time-ness is really 
 great I think, but I have always figured it's an issue that 
 there's no way currently to have a compile-time parameter and 
 do all the nice checking that can come with that without 
 introducing some level of template bloat.
The template bloat is unavoidable to some degree. If you want to do Design by Introspection, itā€™s hard to imagine how to do without. However, CTFE alone does not instantiate templates. Types do not exist at run-time, but they do exist at half of the compile-time. By that I mean that thereā€™s two compile-times, CTFE and the rest (including e.g. template instantiation), and at the latter, types do exist, but at CTFE, they donā€™t. Stefan Koch tried to change that when implementing type functions ([one thread about it](https://forum.dlang.org/thread/jyweryaicaumoaozyfdc forum.dlang.org)). With those, symbols (including types) would exist and could be inspected and manipulated with CTFE.
Sep 28 2022
prev sibling next sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

 Feedback is welcome.
It might be good to start with a simple example. For instance, suppose we have two functions: ```d void foo(int x) { //do something } void foo(int x)() { //do something } ``` We can call `foo` like `foo(x)` or `foo!x` currently. In the first call, `x` can be a runtime variable or an enum. If `x` is an enum, then CTFE and compiler optimizations might simplify the resulting code significantly (though it's not always clear how much it will do that). In the second version, each `x` would generate a new function, resulting in template bloat, but at least as fast as the enum version and potentially simpler code. You propose to add ```d void bar(enum int x) { //do something } ``` and ```d void bar(auto enum int x) { //do something } ``` My reading of the DIP is that the first `bar` is equivalent to the second `foo` (with template value parameters). The second `bar` would be like automatically including an overload for `void bar(int x) {}` along with the first one. So in this sense, you can call `bar(x)` where `x` is a runtime variable or a template value parameter. However, as others have pointed out, what if `x` is an enum and you don't intend to use it like a template value parameter. This results in unnecessary template bloat. So I'm sympathetic to the argument that this should only work for `enums`. It limits the usefulness of this feature, but might be a bit more consistent with the language. The question would be whether that will work for your use case with respect to `opIndex` etc. In order to better unify template value parameters with normal function calls, it might make more sense to borrow from zig's approach of first class types (though I wouldn't use that as an excuse to get rid of the D template syntax, just as an addition to). There are a number of typos as well (beyond what is just mentioned so far in the thread) that would need fixing.
Sep 28 2022
prev sibling parent reply Guillaume Piolat <first.last spam.org> writes:
On Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 
wrote:
 Read the draft here: 
 https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md

 Feedback is welcome.
 With auto enum, ā€œcompile-time-nessā€ is determined from argument 
 (cf. auto ref) and queried via a trait.
Huh? I think this is already possible with http://p0nce.github.io/d-idioms/#Is-this-available-at-compile-time-or-runtime?
Sep 28 2022
parent reply Guillaume Piolat <first.last spam.org> writes:
On Wednesday, 28 September 2022 at 15:21:54 UTC, Guillaume Piolat 
wrote:
 Huh? I think this is already possible with 
 http://p0nce.github.io/d-idioms/#Is-this-available-at-compile-time-or-runtime?
and, may I say, I don't know of a single instance of anyone doing that.
Sep 28 2022
parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Wednesday, 28 September 2022 at 15:36:27 UTC, Guillaume Piolat 
wrote:
 On Wednesday, 28 September 2022 at 15:21:54 UTC, Guillaume 
 Piolat wrote:
 Huh? I think this is already possible with 
 http://p0nce.github.io/d-idioms/#Is-this-available-at-compile-time-or-runtime?
and, may I say, I don't know of a single instance of anyone doing that.
TL;DR: Doesnā€™t work for non-trivial run-time expressions. You can do this on a *type* rather easily. Handling values is annoying. The DIP explains it in the Alternatives section: You cannot take run-time values as a template value parameter. You can bind run-time variables with template alias parameters, but then you have to ensure itā€™s indeed a value (with `if (is(typeof(x)))` usually) and not a type or some other symbol. Still, you cannot put `n+1` into it (unless it can be constant-folded). Bonus: In D, you *have* to use the `alias` parameter even if you want to bind a value as a template value parameter and just infer its type. You have to make sure itā€™s a value and itā€™s a compile-time constant. (C++20 has template auto parameters for this.) ``void f(auto x)()`` does not parse; ``void f(T x, T)()`` doesnā€™t work, but ``void f(T)(enum T x)`` is intended to infer `T` given `f(value)`. ``void f(T, T x)()`` works, but it must be called `f!(int, 2)`.
Sep 28 2022