www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Using "cast(enum)" for explicit request of ctfe

reply "monarch_dodra" <monarchdodra gmail.com> writes:
I love D's ctfe capabilities. They allow using complex values, 
with no run-time cost, and at a very low "code" cost.

One thing that does kind of get on my nerves is how you *always* 
have to declare an actual enum to do that. You can't do CTFE on 
the fly.

This ranges from mildly annoying, typically:

//Declare a CTFE message
enum message = format("Some message: %s", some_static_args);
//Use the CTFE message
enforce(pointer, message);

Or, also,
enum fib5 = fib(5); //CTFE calculate
writeln(fib5); //use

To, sometimes, downright *impossible*. If you ever need to do 
CTFE inside the body of a static foreach, dmd will block you due 
to "redeclaration":

foreach(T; Types)
{
     enum message = format("This type is %s.", T.stringof); 
//Error! Redaclaration
     writeln(message);
}

Fixing this one requies an external template that will create 
your enum on the fly.

----------------

I'm thinking: While this is all surmountable, I'm pretty sure the 
language could give us a easier time of this. We have the 
possibility to declare and call a lambda both in one line. Why 
not be able to declare a ctfe value as a 1-liner too?

I'm thinking, a simple cast: A cast to the "enum" type, which 
explicitly means "this value needs to be compile time known". 
Usage would look like:

enforce(pointer, cast(enum)format("Some message: %s", 
some_static_args));
writeln(cast(enum)fib(5));
foreach(T; Types)
     writeln(cast(enum)format("This type is %s.", T.stringof));

Here, we have some very simple code, no redundant variables, and 
no run-time overhead.

I'm just throwing this out there to get some quick feedback 
before filling an ER, or maybe a DIP.

Or, if somebody has an idea of how to do this via a library 
solution?

Thoughts?
Dec 04 2013
next sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 4 December 2013 at 11:12:54 UTC, monarch_dodra 
wrote:
 Or, if somebody has an idea of how to do this via a library 
 solution?
alias eval(alias exp) = exp;
Dec 04 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Wednesday, 4 December 2013 at 11:32:40 UTC, Jakob Ovrum wrote:
 On Wednesday, 4 December 2013 at 11:12:54 UTC, monarch_dodra 
 wrote:
 Or, if somebody has an idea of how to do this via a library 
 solution?
alias eval(alias exp) = exp;
Nice :D Very very nice. Though that should be "enum" I think. I think having this somewhere in Phobos would be a great addition. Not sure "eval" would be correct though, as "eval" tends to imply parsing. I'll just file a simple ER then. Where would we put such an addition?
Dec 04 2013
next sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 4 December 2013 at 11:41:53 UTC, monarch_dodra 
wrote:
 On Wednesday, 4 December 2013 at 11:32:40 UTC, Jakob Ovrum 
 wrote:
 On Wednesday, 4 December 2013 at 11:12:54 UTC, monarch_dodra 
 wrote:
 Or, if somebody has an idea of how to do this via a library 
 solution?
alias eval(alias exp) = exp;
Nice :D Very very nice. Though that should be "enum" I think. I think having this somewhere in Phobos would be a great addition. Not sure "eval" would be correct though, as "eval" tends to imply parsing. I'll just file a simple ER then. Where would we put such an addition?
Right, maybe it should be (substitute the name `eval`): --- template eval(alias exp) if(isExpressionTuple!exp) { static immutable eval = exp; } --- Which might make it avoid the pitfalls of using enum. Not sure if the `static` actually does something in this context, IIRC it does not. No idea where to put it. I've experimented with such a template before but I haven't used it in any real code. object.d and std.typecons come to mind, but I don't particularly like the prospect of putting it in either.
Dec 04 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Wednesday, 4 December 2013 at 11:51:11 UTC, Jakob Ovrum wrote:
 Right, maybe it should be (substitute the name `eval`):

 ---
 template eval(alias exp) if(isExpressionTuple!exp)
 {
     static immutable eval = exp;
 }
 ---

 Which might make it avoid the pitfalls of using enum. Not sure 
 if the `static` actually does something in this context, IIRC 
 it does not.
Problem is that doing this returns an immutable type, which isn't quite the same as a ctfe variable (which was the initial goal, as far as I'm concerned)
 No idea where to put it. I've experimented with such a template 
 before but I haven't used it in any real code. object.d and 
 std.typecons come to mind, but I don't particularly like the 
 prospect of putting it in either.
Yeah... I wouldn't want to import all of typecons just for this, but if we split it into packages, it would perfectly fit in its own little package (IMO).
Dec 04 2013
parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 4 December 2013 at 13:16:35 UTC, monarch_dodra 
wrote:
 Problem is that doing this returns an immutable type, which 
 isn't quite the same as a ctfe variable (which was the initial 
 goal, as far as I'm concerned)
Immutable "global" variables with initializers are readable at compile-time (as the initializers are required to be readable at compile-time). I don't know what you mean by "CTFE variable". It's probably better to use const over immutable though, so that it can accept values of a type with mutable indirection. Using enum has issues, as elaborated upon by Don in this enhancement request[1] (for reference; I'm sure you remember them). [1] http://d.puremagic.com/issues/show_bug.cgi?id=10950
Dec 04 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Wednesday, 4 December 2013 at 13:45:35 UTC, Jakob Ovrum wrote:
 On Wednesday, 4 December 2013 at 13:16:35 UTC, monarch_dodra 
 wrote:
 Problem is that doing this returns an immutable type, which 
 isn't quite the same as a ctfe variable (which was the initial 
 goal, as far as I'm concerned)
Immutable "global" variables with initializers are readable at compile-time (as the initializers are required to be readable at compile-time). I don't know what you mean by "CTFE variable".
I mean that: auto a = eval!(1 + 2); a += 1; // <= cannot modify immutable expression 3 Should work. Or that: auto arr = eval!(iota(0, 10).array()); //arr is expected to be int[] //NOT immutable(int[]) arr[0] = 10; //Error: cannot modify immutable expression arr[0] Long story short, just because something is pre-calculated with CTFE doesn't mean it can't be mutable. For example: enum a10 = iota(0, 10).array(); auto arr1 = a10; auto arr2 = a10; assert(arr1 !is arr2); assert(arr1 == arr2); arr1[0] = 10; assert(arr1 != arr2);
 Using enum has issues, as elaborated upon by Don in this 
 enhancement request[1] (for reference; I'm sure you remember 
 them).

 [1] http://d.puremagic.com/issues/show_bug.cgi?id=10950
I absolutely remember that. But I still believe that is a *bug*, and not an enhancement request. Buggy behavior should not dictate our design.
Dec 04 2013
parent reply luka8088 <luka8088 owave.net> writes:
On 4.12.2013. 16:28, monarch_dodra wrote:
 On Wednesday, 4 December 2013 at 13:45:35 UTC, Jakob Ovrum wrote:
 On Wednesday, 4 December 2013 at 13:16:35 UTC, monarch_dodra wrote:
 Problem is that doing this returns an immutable type, which isn't
 quite the same as a ctfe variable (which was the initial goal, as far
 as I'm concerned)
Immutable "global" variables with initializers are readable at compile-time (as the initializers are required to be readable at compile-time). I don't know what you mean by "CTFE variable".
I mean that: auto a = eval!(1 + 2); a += 1; // <= cannot modify immutable expression 3 Should work.
I don't think so. With type inference a is immutable. int a = eval!(1 + 2); Works.
 
 Or that:
 
 auto arr = eval!(iota(0, 10).array());
 //arr is expected to be int[]
 //NOT immutable(int[])
 arr[0] = 10; //Error: cannot modify immutable expression arr[0]
 
 Long story short, just because something is pre-calculated with CTFE
 doesn't mean it can't be mutable.
 
 For example:
 enum a10 = iota(0, 10).array();
 auto arr1 = a10;
 auto arr2 = a10;
 assert(arr1 !is arr2);
 assert(arr1 == arr2);
 arr1[0] = 10;
 assert(arr1 != arr2);
 
 
 Using enum has issues, as elaborated upon by Don in this enhancement
 request[1] (for reference; I'm sure you remember them).

 [1] http://d.puremagic.com/issues/show_bug.cgi?id=10950
I absolutely remember that. But I still believe that is a *bug*, and not an enhancement request. Buggy behavior should not dictate our design.
Dec 04 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 5 December 2013 at 07:36:30 UTC, luka8088 wrote:
 On 4.12.2013. 16:28, monarch_dodra wrote:
 
 I mean that:
 
 auto a = eval!(1 + 2);
 a += 1; // <= cannot modify immutable expression 3
 
 Should work.
I don't think so. With type inference a is immutable. int a = eval!(1 + 2); Works.
Right, but that's my point. If you use an enum, as opposed to a global immutable, it *does* work. Also, your rebuke works for the trivial int type, but not for a more complicated type with indirection, such as a dynamic array, and even less so a struct with a complex structure. -------- Having a template that stores a single global-static-immutable value also has its uses (which I have *also* used in phobos, more than once. But: a) It's for a different use case. b) The use cases where specific enough that not having "on-the-fly" template to do it.
Dec 04 2013
prev sibling parent reply luka8088 <luka8088 owave.net> writes:
On 4.12.2013. 12:41, monarch_dodra wrote:
 On Wednesday, 4 December 2013 at 11:32:40 UTC, Jakob Ovrum wrote:
 On Wednesday, 4 December 2013 at 11:12:54 UTC, monarch_dodra wrote:
 Or, if somebody has an idea of how to do this via a library solution?
alias eval(alias exp) = exp;
Nice :D Very very nice. Though that should be "enum" I think. I think having this somewhere in Phobos would be a great addition. Not sure "eval" would be correct though, as "eval" tends to imply parsing. I'll just file a simple ER then. Where would we put such an addition?
Eval comes from examples of http://dlang.org/function.html#interpretation
Dec 04 2013
next sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 4 December 2013 at 11:54:08 UTC, luka8088 wrote:
 Eval comes from examples of 
 http://dlang.org/function.html#interpretation
A couple of notes: The use of a variadic template parameter instead of an alias parameter is misleading because the template does not need to support types in the first place (and the proposed implementation would fail when given a type). The name used on dlang.org is correctly using a lowercase `e`, according to the naming convention for templates that always evaluate to values/variables as opposed to types.
Dec 04 2013
parent luka8088 <luka8088 owave.net> writes:
On 4.12.2013. 13:08, Jakob Ovrum wrote:
 On Wednesday, 4 December 2013 at 11:54:08 UTC, luka8088 wrote:
 Eval comes from examples of http://dlang.org/function.html#interpretation
A couple of notes: The use of a variadic template parameter instead of an alias parameter is misleading because the template does not need to support types in the first place (and the proposed implementation would fail when given a type).
Yeah. I guess it is a documentation (example) issue.
 
 The name used on dlang.org is correctly using a lowercase `e`, according
 to the naming convention for templates that always evaluate to
 values/variables as opposed to types.
Oh, I didn't know that. Thanks!
Dec 04 2013
prev sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Wednesday, 4 December 2013 at 11:54:08 UTC, luka8088 wrote:
 On 4.12.2013. 12:41, monarch_dodra wrote:
 On Wednesday, 4 December 2013 at 11:32:40 UTC, Jakob Ovrum 
 wrote:
 On Wednesday, 4 December 2013 at 11:12:54 UTC, monarch_dodra 
 wrote:
 Or, if somebody has an idea of how to do this via a library 
 solution?
alias eval(alias exp) = exp;
Nice :D Very very nice. Though that should be "enum" I think. I think having this somewhere in Phobos would be a great addition. Not sure "eval" would be correct though, as "eval" tends to imply parsing. I'll just file a simple ER then. Where would we put such an addition?
Eval comes from examples of http://dlang.org/function.html#interpretation
Alright, then eval is fine.
Dec 04 2013
prev sibling parent luka8088 <luka8088 owave.net> writes:
On 4.12.2013. 12:12, monarch_dodra wrote:
 I love D's ctfe capabilities. They allow using complex values, with no
 run-time cost, and at a very low "code" cost.
 
 One thing that does kind of get on my nerves is how you *always* have to
 declare an actual enum to do that. You can't do CTFE on the fly.
 
 This ranges from mildly annoying, typically:
 
 //Declare a CTFE message
 enum message = format("Some message: %s", some_static_args);
 //Use the CTFE message
 enforce(pointer, message);
 
 Or, also,
 enum fib5 = fib(5); //CTFE calculate
 writeln(fib5); //use
 
 To, sometimes, downright *impossible*. If you ever need to do CTFE
 inside the body of a static foreach, dmd will block you due to
 "redeclaration":
 
 foreach(T; Types)
 {
     enum message = format("This type is %s.", T.stringof); //Error!
 Redaclaration
     writeln(message);
 }
 
 Fixing this one requies an external template that will create your enum
 on the fly.
 
 ----------------
 
 I'm thinking: While this is all surmountable, I'm pretty sure the
 language could give us a easier time of this. We have the possibility to
 declare and call a lambda both in one line. Why not be able to declare a
 ctfe value as a 1-liner too?
 
 I'm thinking, a simple cast: A cast to the "enum" type, which explicitly
 means "this value needs to be compile time known". Usage would look like:
 
 enforce(pointer, cast(enum)format("Some message: %s", some_static_args));
 writeln(cast(enum)fib(5));
 foreach(T; Types)
     writeln(cast(enum)format("This type is %s.", T.stringof));
 
 Here, we have some very simple code, no redundant variables, and no
 run-time overhead.
 
 I'm just throwing this out there to get some quick feedback before
 filling an ER, or maybe a DIP.
 
 Or, if somebody has an idea of how to do this via a library solution?
 
 Thoughts?
You can use Eval template: template Eval (alias value) { alias value Eval; } writeln(Eval!(1 + 1)); enforce(pointer, Eval!(format("Some message: %s", some_static_args))); Does this answer your question?
Dec 04 2013