digitalmars.D - DIP proposal: Enum parameters
- Quirin Schroll (3/3) Sep 23 2022 Read the draft here:
- ryuukk_ (9/12) Sep 23 2022 I use ``enum`` a lot in my code base, and i think the name of
- Quirin Schroll (22/35) Sep 23 2022 If you thought about `enum c = val;`, then we simply disagree
- Quirin Schroll (10/20) Sep 26 2022 Nevermind. I just found out that [attributes for function
- Paul Backus (22/25) Sep 23 2022 Some thoughts.
- Timon Gehr (5/10) Sep 23 2022 This is reasoning by analogy, and it is not really appropriate here.
- Quirin Schroll (64/90) Sep 26 2022 Good catch, `__traits(isEnum)` does not exist currently. But it
- Timon Gehr (10/14) Sep 23 2022 - The author's name is spelled wrong.
- Quirin Schroll (19/35) Sep 26 2022 Happens to the best. š
- Nicholas Wilson (22/26) Sep 23 2022 Regarding Prior Art, this reminds me a lot of C++'s `auto`
- Quirin Schroll (83/105) Sep 26 2022 Sorry in advance for the long answer.
- Imperatorn (9/12) Sep 24 2022 I'm a bit torn about static vs enum. I partly agree with prior
- claptrap (9/18) Sep 26 2022 Its not really adding a new meaning, enum already (somtimes)
- Quirin Schroll (16/19) Sep 26 2022 The value of that would be close to zero. At least in Phobos,
- Imperatorn (3/20) Sep 26 2022 Hmm, yes, this is the other sane option. To actually introduce a
- Imperatorn (3/19) Sep 26 2022 Maybe a keyword like fixed, invariant could be used. I don't know
- Quirin Schroll (25/28) Sep 26 2022 One biggie against using `static` is that on member functions it
- Imperatorn (4/12) Sep 26 2022 True, and we're used to enum behaving like that, so I guess it's
- Generic Human (6/9) Sep 25 2022 If it were up to me I'd approve this right now, we need something
- Ogi (3/6) Sep 26 2022 Stop, little pot! We can only eat so much attribute porridge.
- victoroak (4/7) Sep 26 2022 About prior work, you could take a look at comptime from Zig and
- Quirin Schroll (7/15) Sep 27 2022 For anyone else interested, the relevant sections are:
- bauss (13/16) Sep 27 2022 I am really confused about how it differs from what we have now
- Paul Backus (4/13) Sep 27 2022 The only difference is that at the call site, you can write
- bauss (4/21) Sep 27 2022 Yeah that's what I thought.
- Quirin Schroll (5/28) Sep 27 2022 You probably mean adding more language constructs. `enum` is
- Timon Gehr (2/19) Sep 27 2022 This is not true, it is adding new capabilities. (E.g., due to overloadi...
- Quirin Schroll (26/37) Sep 27 2022 Calling syntax: āThe difference between enum parameters `and`
- TheGag96 (9/19) Sep 27 2022 So my first thought seeing this was that `enum` parameters would
- Quirin Schroll (9/29) Sep 27 2022 Yes. If you boil it down to a short and vague sentence, youāre
- TheGag96 (22/28) Sep 27 2022 Hmm okay. The idea of detecting compile-time-ness is really great
- Quirin Schroll (11/16) Sep 28 2022 The template bloat is unavoidable to some degree. If you want to
- jmh530 (43/46) Sep 28 2022 It might be good to start with a simple example. For instance,
- Guillaume Piolat (4/9) Sep 28 2022 Huh? I think this is already possible with
- Guillaume Piolat (4/6) Sep 28 2022 and, may I say, I don't know of a single instance of anyone doing
- Quirin Schroll (19/26) Sep 28 2022 TL;DR: Doesnāt work for non-trivial run-time expressions.
Read the draft here: https://github.com/Bolpat/DIPs/blob/EnumParameters/DIPs/1NNN-QFS.md Feedback is welcome.
Sep 23 2022
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
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: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).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.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
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: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).``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 26 2022
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
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
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: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.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.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
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
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
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 lvaluesThis is not the case, for C++ interop reasons (among others) `const ref` can bind rvalues.
Sep 23 2022
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.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.cf. a ref parameter only binds to lvaluesThis is not the case, for C++ interop reasons (among others) `const ref` can bind rvalues.
Sep 26 2022
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
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: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.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.
Sep 26 2022
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
On Monday, 26 September 2022 at 09:18:54 UTC, claptrap wrote:On Saturday, 24 September 2022 at 07:51:32 UTC, Imperatorn wrote:Hmm, yes, this is the other sane option. To actually introduce a new keyword to differentiateOn Friday, 23 September 2022 at 15:41:21 UTC, Quirin Schroll 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.[...]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.
Sep 26 2022
On Monday, 26 September 2022 at 11:55:00 UTC, Imperatorn wrote:On Monday, 26 September 2022 at 09:18:54 UTC, claptrap wrote:Maybe a keyword like fixed, invariant could be used. I don't know how immutable would work since it's already used.On Saturday, 24 September 2022 at 07:51:32 UTC, Imperatorn wrote:Hmm, yes, this is the other sane option. To actually introduce a new keyword to differentiate[...]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
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
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:True, and we're used to enum behaving like that, so I guess it's preferrable[...]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). [...]
Sep 26 2022
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
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
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
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: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.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 27 2022
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
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
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: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.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
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: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.On Tuesday, 27 September 2022 at 11:52:03 UTC, bauss wrote: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.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
On 27.09.22 14:13, Paul Backus wrote:On Tuesday, 27 September 2022 at 11:52:03 UTC, bauss wrote:This is not true, it is adding new capabilities. (E.g., due to overloading.)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
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
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
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: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.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
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
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
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
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
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
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: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)`.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