www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - It seems pure ain't so pure after all

reply "Tommi" <tommitissari hotmail.com> writes:
import std.stdio;

int pow2(int val) pure
{
     if (__ctfe)
         return 6;
     else
         return val * val;
}

void main()
{
            assert(pow2(3) == 9);
     static assert(pow2(3) == 6);

     writeln("9 = 6 ... I knew it! '6' was faking it all along");
     readln();
}
Sep 30 2012
next sibling parent reply =?UTF-8?B?QWxleCBSw7hubmUgUGV0ZXJzZW4=?= <alex lycus.org> writes:
On 01-10-2012 07:40, Tommi wrote:
 import std.stdio;

 int pow2(int val) pure
 {
      if (__ctfe)
          return 6;
      else
          return val * val;
 }

 void main()
 {
             assert(pow2(3) == 9);
      static assert(pow2(3) == 6);

      writeln("9 = 6 ... I knew it! '6' was faking it all along");
      readln();
 }
This is a corner case. __ctfe is there to allow special-cased CTFE code when absolutely necessary. By necessity, this separates a function into two worlds: compile time and run time. As far as purity goes, pow2 *is* pure. It just does something different depending on whether you run it at compile time or run time. I don't see this as a problem in practice. -- Alex Rønne Petersen alex lycus.org http://lycus.org
Sep 30 2012
next sibling parent reply "Tommi" <tommitissari hotmail.com> writes:
On Monday, 1 October 2012 at 05:43:39 UTC, Alex Rønne Petersen 
wrote:
 As far as purity goes, pow2 *is* pure. ...
According to http://en.wikipedia.org/wiki/Pure_function it's not: "The [pure] function always evaluates the same result value given the same argument value(s)"
Sep 30 2012
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, October 01, 2012 07:58:39 Tommi wrote:
 On Monday, 1 October 2012 at 05:43:39 UTC, Alex R=C3=B8nne Petersen
=20
 wrote:
 As far as purity goes, pow2 *is* pure. ...
=20 According to http://en.wikipedia.org/wiki/Pure_function it's not: =20 "The [pure] function always evaluates the same result value given the same argument value(s)"
Forget what Wikipedia says about pure. If you focus on that, you're goi= ng to=20 be complaining about D's pure left and right, because what it's talking= about=20 and what D does are related but very different. D takes a very practica= l=20 approach to functional purity. You should read this: http://stackoverflow.com/questions/8572399 - Jonathan M Davis
Sep 30 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 01/10/2012 08:07, Jonathan M Davis a écrit :
 On Monday, October 01, 2012 07:58:39 Tommi wrote:
 On Monday, 1 October 2012 at 05:43:39 UTC, Alex Rønne Petersen

 wrote:
 As far as purity goes, pow2 *is* pure. ...
According to http://en.wikipedia.org/wiki/Pure_function it's not: "The [pure] function always evaluates the same result value given the same argument value(s)"
Forget what Wikipedia says about pure. If you focus on that, you're going to be complaining about D's pure left and right, because what it's talking about and what D does are related but very different. D takes a very practical approach to functional purity. You should read this: http://stackoverflow.com/questions/8572399 - Jonathan M Davis
Or that : http://klickverbot.at/blog/2012/05/purity-in-d/
Oct 01 2012
prev sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, October 01, 2012 07:43:59 Alex R=C3=B8nne Petersen wrote:
 This is a corner case.
=20
 __ctfe is there to allow special-cased CTFE code when absolutely
 necessary. By necessity, this separates a function into two worlds:
 compile time and run time.
=20
 As far as purity goes, pow2 *is* pure. It just does something differe=
nt
 depending on whether you run it at compile time or run time. I don't =
see
 this as a problem in practice.
It would be kind of like complaining that a pure function returns diffe= rent=20 values on Linux and Windows due to a version statement or static if. It= 's just=20 that in the thing that varies is compile time vs runtime not the target= =20 machine. e.g. int func(int val) pure { version(linux) return val + 2; else version(Windows) return val + 3; } - Jonathan M Davis
Sep 30 2012
parent reply "Tommi" <tommitissari hotmail.com> writes:
On Monday, 1 October 2012 at 06:01:24 UTC, Jonathan M Davis wrote:
 It would be kind of like complaining that a pure function 
 returns different
 values on Linux and Windows due to a version statement or 
 static if. It's just
 that in the thing that varies is compile time vs runtime not 
 the target
 machine. e.g.
Actually... let's not even worry about the definition of the word 'pure'. Let's just ask ourselves: do we really want to live in a world where people can write code like that: void main() { auto x = pow2(3); enum y = pow2(3); assert(x == y + 3); writeln("Take that mr. \"math\" professor!"); readln(); }
Sep 30 2012
next sibling parent reply Brad Roberts <braddr puremagic.com> writes:
On 9/30/2012 11:09 PM, Tommi wrote:
 On Monday, 1 October 2012 at 06:01:24 UTC, Jonathan M Davis wrote:
 It would be kind of like complaining that a pure function returns different
 values on Linux and Windows due to a version statement or static if. It's just
 that in the thing that varies is compile time vs runtime not the target
 machine. e.g.
Actually... let's not even worry about the definition of the word 'pure'. Let's just ask ourselves: do we really want to live in a world where people can write code like that: void main() { auto x = pow2(3); enum y = pow2(3); assert(x == y + 3); writeln("Take that mr. \"math\" professor!"); readln(); }
So, Tommi, do you have a suggestion or proposal to make or are you just trying to point and snicker? There's a multitude of ways that bad programmers can write bad code.
Sep 30 2012
parent reply "Tommi" <tommitissari hotmail.com> writes:
On Monday, 1 October 2012 at 06:13:51 UTC, Brad Roberts wrote:
 So, Tommi, do you have a suggestion or proposal to make or are 
 you just trying to point and snicker?  There's a
 multitude of ways that bad programmers can write bad code.
I can't provide a solution until we've agreed that there is a problem.
Sep 30 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/1/12 2:19 AM, Tommi wrote:
 On Monday, 1 October 2012 at 06:13:51 UTC, Brad Roberts wrote:
 So, Tommi, do you have a suggestion or proposal to make or are you
 just trying to point and snicker? There's a
 multitude of ways that bad programmers can write bad code.
I can't provide a solution until we've agreed that there is a problem.
Don't want to sound dismissive, but the short answer is there is no problem. Andrei
Sep 30 2012
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, October 01, 2012 08:09:38 Tommi wrote:
 On Monday, 1 October 2012 at 06:01:24 UTC, Jonathan M Davis wrote:
 It would be kind of like complaining that a pure function
 returns different
 values on Linux and Windows due to a version statement or
 static if. It's just
 that in the thing that varies is compile time vs runtime not
 the target
 machine. e.g.
Actually... let's not even worry about the definition of the word 'pure'. Let's just ask ourselves: do we really want to live in a world where people can write code like that: void main() { auto x = pow2(3); enum y = pow2(3); assert(x == y + 3); writeln("Take that mr. \"math\" professor!"); readln(); }
Then don't write a function which claims to square a value and does something else. That's just bad naming. No language is going to prevent programmers from being idiots. A function which uses __ctfe should probably do essentially the same thing at both runtime and compile time, but it _has_ __ctfe, because the runtime implementation won't work at compile time, and it's up to the programmer to make sure that the function does what it's supposed to at both compile time and runtime. The compiler can't possibly enforce that. - Jonathan M Davis
Sep 30 2012
parent reply "Tommi" <tommitissari hotmail.com> writes:
On Monday, 1 October 2012 at 06:18:48 UTC, Jonathan M Davis wrote:
 A function which uses __ctfe should probably do essentially the 
 same thing at
 both runtime and compile time, but it _has_ __ctfe, because the 
 runtime
 implementation won't work at compile time, and it's up to the 
 programmer to
 make sure that the function does what it's supposed to at both 
 compile time
 and runtime. The compiler can't possibly enforce that.
Thus we're in a situation where pure means pure only by convention, not because it's enforced by the compiler. It's like const in c++ then, it's const only by convention, only because people promise that they're not going to mutate it. I don't like rules that are enforced only by everybody relying on good manners.
Sep 30 2012
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, October 01, 2012 08:25:39 Tommi wrote:
 On Monday, 1 October 2012 at 06:18:48 UTC, Jonathan M Davis wrote:
 A function which uses __ctfe should probably do essentially the
 same thing at
 both runtime and compile time, but it _has_ __ctfe, because the
 runtime
 implementation won't work at compile time, and it's up to the
 programmer to
 make sure that the function does what it's supposed to at both
 compile time
 and runtime. The compiler can't possibly enforce that.
Thus we're in a situation where pure means pure only by convention, not because it's enforced by the compiler. It's like const in c++ then, it's const only by convention, only because people promise that they're not going to mutate it. I don't like rules that are enforced only by everybody relying on good manners.
No. We're not in that situation at all. The function will do the same thing with every call at compile time, and it will do the same thing with every call at runtime. It's like you're complaining about the fact that a function on a big endian machine doesn't do exactly the same thing as when it's compiled for a little endian machine. It's being compiled for a different environment, so its behavior can change. __ctfe really isn't all that different from static if or version blocks which effectively result in different functions depending on how or where the code is compiled. What isn't enforced is that the function does the same thing at compile time as at runtime, and you can't enforce that any more than you can enforce that it does the same thing on one machine as another when they have different architectures or OSes or whatot. By using __ctfe, you are providing an alternate implementation for compile time just like you could provide alternate implementations for different OSes or architectures. - Jonathan M Davis
Sep 30 2012
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, September 30, 2012 23:38:43 Jonathan M Davis wrote:
 On Monday, October 01, 2012 08:25:39 Tommi wrote:
 Thus we're in a situation where pure means pure only by
 convention, not because it's enforced by the compiler. It's like
 const in c++ then, it's const only by convention, only because
 people promise that they're not going to mutate it. I don't like
 rules that are enforced only by everybody relying on good manners.
No. We're not in that situation at all. The function will do the same thing with every call at compile time, and it will do the same thing with every call at runtime. It's like you're complaining about the fact that a function on a big endian machine doesn't do exactly the same thing as when it's compiled for a little endian machine. It's being compiled for a different environment, so its behavior can change. __ctfe really isn't all that different from static if or version blocks which effectively result in different functions depending on how or where the code is compiled. What isn't enforced is that the function does the same thing at compile time as at runtime, and you can't enforce that any more than you can enforce that it does the same thing on one machine as another when they have different architectures or OSes or whatot. By using __ctfe, you are providing an alternate implementation for compile time just like you could provide alternate implementations for different OSes or architectures.
While __ctfe is not used in a static if, that's effectively the behavior that it has. You're basically complaining about how these two functions don't return the same value: static if(__ctfe) { int pow2(int val) pure { return 6; } } else { int pow2(int val) pure { return val * val; } } They're effectively completely different functions that share the same name (and presumably the same intent), but they're implementations are different, and it's obviously up to the programmer to make sure that they do what they're supposed to do. It has _nothing_ to do with pure. - Jonathan M Davis
Sep 30 2012
parent reply "Tommi" <tommitissari hotmail.com> writes:
I'll have to consider all functions potentially schizophrenic 
then. They might do one thing at compile-time and another at 
run-time.

I was going to make a feature request to add a compiler flag for 
making more functions execute at compile-time than those which 
the compiler *has* to. But this __ctfe thing renders that 
impossible.
Oct 01 2012
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, October 01, 2012 09:46:43 Tommi wrote:
 I'll have to consider all functions potentially schizophrenic
 then. They might do one thing at compile-time and another at
 run-time.
That's only the case if they're buggy, so that's pretty much the same as considering all functions potentially buggy.
 I was going to make a feature request to add a compiler flag for
 making more functions execute at compile-time than those which
 the compiler *has* to. But this __ctfe thing renders that
 impossible.
I have no idea how you could possibly use a compiler flag for that, whether __ctfe or not. It really doesn't make sense to try and make functions in general run at compile time. Most of the time, functions need to be run at runtime, otherwise you wouldn't even need to generate an executable, just a result. And you _can't_ determine ahead of time which functions can be safely executed at compile time either, because that's an instance of the halting problem. So, it really doesn't make sense to have the compiler trying to evaluate functions at compile time when it hasn't been explicitly told to. And it works just fine to assign the result of a function call to enum if you want a specific function call to be executed at compile time, so I really don't think that this is an issue anyway. - Jonathan M Davis
Oct 01 2012
parent reply "Tommi" <tommitissari hotmail.com> writes:
On Monday, 1 October 2012 at 08:04:49 UTC, Jonathan M Davis wrote:
 And you _can't_ determine ahead of time which functions can be 
 safely executed at compile time either, because that's an
 instance of the halting problem.
I don't understand (I did read what "halting problem" means just now, but I still don't understand). If there was no __ctfe variable, and thus a guarantee that all functions do the same thing at compile-time and run-time, couldn't the compiler just try aggressively to execute all function calls at compile-time? Obviously it wouldn't bother trying CTFE for function calls that had arguments which weren't evaluable at compile-time. Nor would it bother with functions that it knows have memory allocations or other limitations of CTFE. If not a compiler flag, then it could be a function attribute. Just like c++ function attribute constexpr, which guarantees that the function executes at compile time given you provide it with compile-time evaluable arguments (and there are limitations to what the function can do). Why wouldn't this attribute be possible with D?
Oct 01 2012
next sibling parent reply "foobar" <foo bar.com> writes:
On Monday, 1 October 2012 at 17:46:00 UTC, Tommi wrote:
 On Monday, 1 October 2012 at 08:04:49 UTC, Jonathan M Davis 
 wrote:
 And you _can't_ determine ahead of time which functions can be 
 safely executed at compile time either, because that's an
 instance of the halting problem.
I don't understand (I did read what "halting problem" means just now, but I still don't understand). If there was no __ctfe variable, and thus a guarantee that all functions do the same thing at compile-time and run-time, couldn't the compiler just try aggressively to execute all function calls at compile-time? Obviously it wouldn't bother trying CTFE for function calls that had arguments which weren't evaluable at compile-time. Nor would it bother with functions that it knows have memory allocations or other limitations of CTFE. If not a compiler flag, then it could be a function attribute. Just like c++ function attribute constexpr, which guarantees that the function executes at compile time given you provide it with compile-time evaluable arguments (and there are limitations to what the function can do). Why wouldn't this attribute be possible with D?
__ctfe is a horrible yet very useful hack to address the underlying issue - the execution model for CTFE, which I personally do not agree with. Adding a compiler flag for the existing model, makes no sense whatsoever. Functions are essentially a run-time abstraction and the compiler generally speaking has no business trying to execute them at compile-time. The compiler is after all *not* an interpreter. Besides, what would be the use case for such a flag anyway? If you already know that all parameters are known at compile-time, you can already "tell" the compiler to execute the function by assigning to a static/enum variable. IMO, this mixing of code for various stages of execution is bad design but this cannot be changed in a backwards compatible way.
Oct 01 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 10/01/2012 08:02 PM, foobar wrote:
 On Monday, 1 October 2012 at 17:46:00 UTC, Tommi wrote:
 On Monday, 1 October 2012 at 08:04:49 UTC, Jonathan M Davis wrote:
 And you _can't_ determine ahead of time which functions can be safely
 executed at compile time either, because that's an
 instance of the halting problem.
I don't understand (I did read what "halting problem" means just now, but I still don't understand). If there was no __ctfe variable, and thus a guarantee that all functions do the same thing at compile-time and run-time, couldn't the compiler just try aggressively to execute all function calls at compile-time? Obviously it wouldn't bother trying CTFE for function calls that had arguments which weren't evaluable at compile-time. Nor would it bother with functions that it knows have memory allocations or other limitations of CTFE. If not a compiler flag, then it could be a function attribute. Just like c++ function attribute constexpr, which guarantees that the function executes at compile time given you provide it with compile-time evaluable arguments (and there are limitations to what the function can do). Why wouldn't this attribute be possible with D?
__ctfe is a horrible yet very useful hack to address the underlying issue - the execution model for CTFE, which I personally do not agree with. Adding a compiler flag for the existing model, makes no sense whatsoever. Functions are essentially a run-time abstraction and the compiler generally speaking has no business trying to execute them at compile-time. The compiler is after all *not* an interpreter.
A D compiler is also a D interpreter. I wouldn't even bother with D if this wasn't the case.
 Besides, what would be the use case for such a flag anyway? If you
 already know that all parameters are known at compile-time, you can
 already "tell" the compiler to execute the function by assigning to a
 static/enum variable.

 IMO, this mixing of code for various stages of execution is bad design
 but this cannot be changed in a backwards compatible way.
Oct 01 2012
parent "foobar" <foo bar.com> writes:
On Monday, 1 October 2012 at 22:47:48 UTC, Timon Gehr wrote:

 A D compiler is also a D interpreter. I wouldn't even bother 
 with D if this wasn't the case.
A D compiler _contains_ a limited interpreter for constant expression evaluation. This has limitations such as not being able to perform IO at compile-time (there's a special pragma for that). This is merely an outgrowth of a compiler optimization technique. Now, back on topic: I agree with Jonathan - in general the compiler can't evaluate _all_ functions at-compile time due these limitations of CTFE and adding a compiler flag will make compilation times far worse than c++. Adding a function attribute adds marginal benefit over simply assigning to a static/enum variable.
Oct 02 2012
prev sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, October 01, 2012 19:46:16 Tommi wrote:
 On Monday, 1 October 2012 at 08:04:49 UTC, Jonathan M Davis wrote:
 And you _can't_ determine ahead of time which functions can be
 safely executed at compile time either, because that's an
 instance of the halting problem.
I don't understand (I did read what "halting problem" means just now, but I still don't understand). If there was no __ctfe variable, and thus a guarantee that all functions do the same thing at compile-time and run-time,
__ctfe exists purely so that you can provide an alternate implementation which works at compile time when the normal implementation doesn't (since CTFE _is_ more restrictive in what it allows than running a function at runtime is).
 couldn't the compiler just
 try aggressively to execute all function calls at compile-time?
So, the compiler is supposed to run every function at compile time and then back up if it a function doesn't work? It can't even always know for sure when a function doesn't work (e.g. infinite loop). This would absolutely _tank_ compilation times. Most functions _can't_ be run at compile time simply because of how they're called, and the compiler can't necessarily know that unless it does full flow analysis if not outright _runs_ every function, which would be _incredibly_ expensive.
 If not a compiler flag, then it could be a function attribute.
 Just like c++ function attribute constexpr, which guarantees that
 the function executes at compile time given you provide it with
 compile-time evaluable arguments (and there are limitations to
 what the function can do). Why wouldn't this attribute be
 possible with D?
CTFE was specifically designed with the idea that you would not need to mark functions as CTFEable. You can call _any_ function at compile time. Some will fail, because they're doing things that CTFE won't allow, but that's quickly caught, because it happens at compile time. It completely avoids needing to mark functions as CTFEable all over the place (which would generally mean that functions wouldn't be CTFEable, because people would frequently not mark them as CTFEable). constexpr is in direct conflict with that design goal. And if you want a function to be executed at compile time, you assign its result to an enum or static variable. Done. It's easy and straightforward. I really don't understand why this is an issue at all. - Jonathan M Davis
Oct 01 2012
parent reply "Tommi" <tommitissari hotmail.com> writes:
On Monday, 1 October 2012 at 18:36:23 UTC, Jonathan M Davis wrote:
 CTFE was specifically designed with the idea that you would not 
 need to mark functions as CTFEable. You can call _any_ functio
 at compile time. Some will fail, because they're doing things 
 that CTFE won't allow, but that's quickly caught, because it 
 happens at compile time. It completely avoids needing to mark
 functions as CTFEable all over the place (which would generally
 mean that functions wouldn't be CTFEable, because people would
 frequently not mark them as CTFEable). constexpr is in direct
 conflict with that design goal.
That's not what I suggested. I meant that all functions would still be implicitly CTFEable by default, but an attribute like force_ctfe would make it so that the function is guaranteed to execute at compile-time when its arguments are compile-time constants.
 And if you want a function to be executed at compile time, you 
 assign its result to an enum or static variable. Done. It's easy
 and straightforward. I really don't understand why this is an
 issue at all.
The issue to me is complicating the syntax of your code. The problem is *having* to assign the result first to an enum, when I shouldn't have to. I would like to be able to just say fun("times") and be confident that that's going to be evaluated at compile-time. I feel like I've done my part of the deal here, I provided the compile-time argument, and now I'd expect the compiler to do his part and evaluate the function at compile-time (if it has that force_ctfe attribute).
Oct 01 2012
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 01 Oct 2012 16:10:48 -0400, Tommi <tommitissari hotmail.com> wrote:

 On Monday, 1 October 2012 at 18:36:23 UTC, Jonathan M Davis wrote:
 CTFE was specifically designed with the idea that you would not need to  
 mark functions as CTFEable. You can call _any_ functio
 at compile time. Some will fail, because they're doing things that CTFE  
 won't allow, but that's quickly caught, because it happens at compile  
 time. It completely avoids needing to mark
 functions as CTFEable all over the place (which would generally
 mean that functions wouldn't be CTFEable, because people would
 frequently not mark them as CTFEable). constexpr is in direct
 conflict with that design goal.
That's not what I suggested. I meant that all functions would still be implicitly CTFEable by default, but an attribute like force_ctfe would make it so that the function is guaranteed to execute at compile-time when its arguments are compile-time constants.
 And if you want a function to be executed at compile time, you assign  
 its result to an enum or static variable. Done. It's easy
 and straightforward. I really don't understand why this is an
 issue at all.
The issue to me is complicating the syntax of your code. The problem is *having* to assign the result first to an enum, when I shouldn't have to. I would like to be able to just say fun("times") and be confident that that's going to be evaluated at compile-time. I feel like I've done my part of the deal here, I provided the compile-time argument, and now I'd expect the compiler to do his part and evaluate the function at compile-time (if it has that force_ctfe attribute).
We already have that, use templates: private auto _funimpl(string s) { /* what was previously in fun(string) */ } template fun(string s) { enum fun = _funimpl(s); } // usage: fun!("times"); // always executes _funimpl at compile time -Steve
Oct 01 2012
next sibling parent reply "Tommi" <tommitissari hotmail.com> writes:
On Monday, 1 October 2012 at 20:16:09 UTC, Steven Schveighoffer 
wrote:
 We already have that, use templates:

 private auto _funimpl(string s) {
 /* what was previously in fun(string) */
 }

 template fun(string s)
 {
    enum fun = _funimpl(s);
 }

 // usage:

 fun!("times"); // always executes _funimpl at compile time
Yes, I know. And this helps even more: template ct(alias expr) { enum ct = expr; } auto fun(string s) { ... } // usage: ct!(fun("times")) But that's still a nuisance.
Oct 01 2012
next sibling parent reply "Tommi" <tommitissari hotmail.com> writes:
It's a lot less work to add one attribute to your function once, 
than to write something extra every time you call that function.
Oct 01 2012
parent reply "Graham Fawcett" <fawcett uwindsor.ca> writes:
On Monday, 1 October 2012 at 20:30:26 UTC, Tommi wrote:
 It's a lot less work to add one attribute to your function 
 once, than to write something extra every time you call that 
 function.
string exclaim(string v)() { return v ~ "!"; } void main() { writeln(exclaim!("howdy")); } Graham
Oct 01 2012
parent reply "Tommi" <tommitissari hotmail.com> writes:
On Monday, 1 October 2012 at 20:33:29 UTC, Graham Fawcett wrote:
 string exclaim(string v)() {
   return v ~ "!";
 }

 void main() {
   writeln(exclaim!("howdy"));
 }
It doesn't scale well to have to write both compile-time and run-time versions of your functions.
Oct 01 2012
parent reply "Graham Fawcett" <fawcett uwindsor.ca> writes:
On Monday, 1 October 2012 at 20:41:53 UTC, Tommi wrote:
 On Monday, 1 October 2012 at 20:33:29 UTC, Graham Fawcett wrote:
 string exclaim(string v)() {
  return v ~ "!";
 }

 void main() {
  writeln(exclaim!("howdy"));
 }
It doesn't scale well to have to write both compile-time and run-time versions of your functions.
And how is your proposed force_ctfe attribute going to solve this issue? If you're forcing CTFE, by definition you don't have runtime support. As you clearly know, D already supports functions that can run at compile-time and run-time: that's the whole point of of CTFE. Graham
Oct 01 2012
next sibling parent reply "Tommi" <tommitissari hotmail.com> writes:
On Monday, 1 October 2012 at 21:15:40 UTC, Graham Fawcett wrote:
 And how is your proposed  force_ctfe attribute going to solve 
 this issue? If you're forcing CTFE, by definition you don't 
 have runtime support.
Maybe the name force_ctfe is misleading. But, like I said: "the attribute asdf would make it so that the function is guaranteed to execute at compile-time when its arguments are compile-time constants". Never do I say that the attribute would prevent run-time evaluation. The function would behave just like constexpr functions do in c++ (although, in c++ what those functions can actually do is much more limited).
Oct 01 2012
parent "Tommi" <tommitissari hotmail.com> writes:
On Monday, 1 October 2012 at 21:29:33 UTC, Tommi wrote:
 Maybe the name  force_ctfe is misleading. But, like I said: 
 "the attribute  asdf would make it so that the function is 
 guaranteed to execute at compile-time when its arguments are 
 compile-time constants".
Oh, wait. Now I understand why that sentence is misleading. It should be: The function attribute asdf would guarantee compile-time evaluation of all such calls to that function, where all arguments (that you pass into the function), of which the function's output depends on, are compile-time evaluable.
Oct 01 2012
prev sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, October 01, 2012 23:15:55 Graham Fawcett wrote:
 On Monday, 1 October 2012 at 20:41:53 UTC, Tommi wrote:
 On Monday, 1 October 2012 at 20:33:29 UTC, Graham Fawcett wrote:
 string exclaim(string v)() {
 
 return v ~ "!";
 
 }
 
 void main() {
 
 writeln(exclaim!("howdy"));
 
 }
It doesn't scale well to have to write both compile-time and run-time versions of your functions.
And how is your proposed force_ctfe attribute going to solve this issue? If you're forcing CTFE, by definition you don't have runtime support. As you clearly know, D already supports functions that can run at compile-time and run-time: that's the whole point of of CTFE.
If I understand correctly, what he wants is for CTFE to be forced only in cases where all of the arguments are known at compile time (e.g. when you pass a string literal), so it wouldn't prevent anything from running at runtime. This would be a complete disaster if it were done in the general case. For instance, writeln("hello") can't be executed at compile time, and since the only way for the compiler to know that in most cases is to run the function, compile times would tank and probably cause other entertaining issues. So, a compiler flag like was previously suggested in this thread would be a _very_ bad idea. On the other hand, if a function attribute were introduced where the programmer indicated that they wanted that function to execute at compile time as long as all of the arguments were known at compile time, that would work just fine (as long as it really _could_ be executed at compile time - but we already have that issue when assigning the results of functions to enums). The question is whether it's worth adding such an attribute to the language, and clearly, most of us think that explicitly using enum suffices whereas Tommi wants such a function attribute. - Jonathan M Davis
Oct 01 2012
parent reply "Tommi" <tommitissari hotmail.com> writes:
On Monday, 1 October 2012 at 21:35:16 UTC, Jonathan M Davis wrote:
 The question is whether it's worth adding such an attribute to
 the language, and clearly, most of us think that explicitly
 using enum suffices whereas Tommi wants such a function
 attribute.
You'd get simpler syntax at the cost of just one lousy new keyword. It's a once in a life time deal! Any takers?
Oct 01 2012
next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, October 01, 2012 23:55:11 Tommi wrote:
 On Monday, 1 October 2012 at 21:35:16 UTC, Jonathan M Davis wrote:
 The question is whether it's worth adding such an attribute to
 the language, and clearly, most of us think that explicitly
 using enum suffices whereas Tommi wants such a function
 attribute.
You'd get simpler syntax at the cost of just one lousy new keyword. It's a once in a life time deal! Any takers?
Keywords are considered to be incredibly expensive. Being able to use a name starting with rather than a keyword does reduce the cost, but you're still going to have to drive a very hard bargain to talk Walter into adding anything like that to the language at this point. Adding much of _anything_ to the language is generally considered expensive. - Jonathan M Davis
Oct 01 2012
next sibling parent reply "Tommi" <tommitissari hotmail.com> writes:
On Monday, 1 October 2012 at 22:23:51 UTC, Jonathan M Davis wrote:
 Keywords are considered to be incredibly expensive. Being able 
 to use a name
 starting with   rather than a keyword does reduce the cost, but 
 you're still
 going to have to drive a very hard bargain to talk Walter into 
 adding anything
 like that to the language at this point. Adding much of 
 _anything_ to the
 language is generally considered expensive.
I'm not sure if you meant the same, but I meant the word "cost" of a feature as "the amount the feature complicates the language". Also, I'm only asking people to "buy" this feature in the sense of: "what would you say about this feature if the language was still back on the drawing board and nothing had been written down yet".
Oct 01 2012
next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Tuesday, October 02, 2012 01:19:51 Tommi wrote:
 I'm not sure if you meant the same, but I meant the word "cost"
 of a feature as "the amount the feature complicates the
 language". Also, I'm only asking people to "buy" this feature in
 the sense of: "what would you say about this feature if the
 language was still back on the drawing board and nothing had been
 written down yet".
No offense, but a feature proposal is pretty pointless if it's a "this would have been nice had we had thought of it when we could have easily added it." A feature proposal is only relevant if it's really an attempt to change the language now. If we were really back at the drawing board, there are plenty of other things which would change, some of which could completely change how some things work and possibly invalidate a feature which might seem reasonable now but wouldn't work with the other changes (which probably isn't the case here, but still, discussing what could have been doesn't really buy us anything IMHO). - Jonathan M Davis
Oct 01 2012
parent reply "Tommi" <tommitissari hotmail.com> writes:
On Monday, 1 October 2012 at 23:31:21 UTC, Jonathan M Davis wrote:
 No offense, but a feature proposal is pretty pointless if it's 
 a "this would
 have been nice had we had thought of it when we could have 
 easily added it." A
 feature proposal is only relevant if it's really an attempt to 
 change the
 language now.

 If we were really back at the drawing board, there are plenty 
 of other things
 which would change, some of which could completely change how 
 some things work
 and possibly invalidate a feature which might seem reasonable 
 now but wouldn't
 work with the other changes (which probably isn't the case 
 here, but still,
 discussing what could have been doesn't really buy us anything 
 IMHO).

 - Jonathan M Davis
I don't think you should stop trying to write the best language specification you can just because you're past the point of it being implementable by this language. Call the specification D++ and maybe some day someone will pick it up and try to implement it.
Oct 01 2012
parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Tuesday, October 02, 2012 02:19:21 Tommi wrote:
 I don't think you should stop trying to write the best language
 specification you can just because you're past the point of it
 being implementable by this language.
Discussing changes to D2 is of relevance, because that affects what we're doing now. Discussing features for a possible D3 is of some relevance, because it could eventually affect the language (but is also kind of pointless in that it won't matter for years yet). Discussing features simply because they might have been nice but we don't intend to ever add them is utterly pointless IMHO. Aside from D, discussing your dream language is all well and good, but it's not really going to help D if the discussion isn't aimed at improving D as it is now.
 Call the specification D++
 and maybe some day someone will pick it up and try to implement
 it.
Um. That would actually be bad from our point of view. We don't want to fragment the community. D will evolve, and we may even eventually end up with D3, and someone may very well create a D++ someday, but it's not really in our best interest to promote the creation of such a language (especially not now). We want D to succeed, and it's going to have to do that pretty much as it is now. At some point, you have to make what you have work and get it to gain traction in the programming community as it is, or what you have is of little practical value. Forever tweaking it won't get you there even if the tweaks create what is theoretically a better language. So, feel free to air your ideas, but if they're not of practical value towards improving D right now, many of us aren't going to pay much attention to them. - Jonathan M Davis
Oct 01 2012
prev sibling parent =?ISO-8859-1?Q?Jos=E9_Armando_Garc=EDa_Sancio?= <jsancio gmail.com> writes:
On Mon, Oct 1, 2012 at 4:19 PM, Tommi <tommitissari hotmail.com> wrote:

 On Monday, 1 October 2012 at 22:23:51 UTC, Jonathan M Davis wrote:

 Keywords are considered to be incredibly expensive. Being able to use a
 name
 starting with   rather than a keyword does reduce the cost, but you're
 still
 going to have to drive a very hard bargain to talk Walter into adding
 anything
 like that to the language at this point. Adding much of _anything_ to the
 language is generally considered expensive.
I'm not sure if you meant the same, but I meant the word "cost" of a feature as "the amount the feature complicates the language". Also, I'm only asking people to "buy" this feature in the sense of: "what would you say about this feature if the language was still back on the drawing board and nothing had been written down yet".
That is a close to useless exercise (or domain problem). Problems to solve are only interested in a specific domain. In other words the problem as you have defined it will have a different solution in D, C++, Java, vaporware language, etc. To some degree it reminds me of the slew of TCP replacement protocols that academia has designed and implemented that will probably never see the day of light. Don't get me wrong it is educational to read them and implementable in private networks but it is highly probably that the Internet will never see them. Thanks, -Jose
Oct 01 2012
prev sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 Adding much of _anything_ to the
 language is generally considered expensive.
Sometimes you add from a language removing something, like allowing handy code like this, removing such limitations and making the language more regular/uniform: class Foo { int x, y; } struct Bar { int x, y; } void main() { auto f1 = new Foo(1, 2); auto f2 = new Foo(1); auto b1 = new Bar(10, 20); auto b2 = new Bar(10); } Bye, bearophile
Oct 01 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/1/12 5:55 PM, Tommi wrote:
 On Monday, 1 October 2012 at 21:35:16 UTC, Jonathan M Davis wrote:
 The question is whether it's worth adding such an attribute to
 the language, and clearly, most of us think that explicitly
 using enum suffices whereas Tommi wants such a function
 attribute.
You'd get simpler syntax at the cost of just one lousy new keyword. It's a once in a life time deal! Any takers?
I understand the motivation. We will not consider such a feature for the time being. Andrei
Oct 01 2012
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 01 Oct 2012 16:23:17 -0400, Tommi <tommitissari hotmail.com> wrote:

 On Monday, 1 October 2012 at 20:16:09 UTC, Steven Schveighoffer wrote:
 We already have that, use templates:

 private auto _funimpl(string s) {
 /* what was previously in fun(string) */
 }

 template fun(string s)
 {
    enum fun = _funimpl(s);
 }

 // usage:

 fun!("times"); // always executes _funimpl at compile time
Yes, I know. And this helps even more: template ct(alias expr) { enum ct = expr; } auto fun(string s) { ... } // usage: ct!(fun("times")) But that's still a nuisance.
Hm... I was focusing on changing fun("times") into fun!("times") in order to make it always execute at compile time. I think that is not so bad. The idea is that _funimpl is implementation details for fun, and doesn't ever expect to be called at runtime. In fact, you could do if(!_ctfe) throw Error(); -Steve
Oct 01 2012
prev sibling parent "jerro" <a a.com> writes:
 private auto _funimpl(string s) {
 /* what was previously in fun(string) */
 }

 template fun(string s)
 {
    enum fun = _funimpl(s);
 }

 // usage:

 fun!("times"); // always executes _funimpl at compile time

 -Steve
You could even use this template: template ct(alias f) { template ct(params...) { enum ct = f(params); } } to define templates like this: alias ct!((string s) { // function body... }) fun; or with this syntax: alias ct!(a => a * a) pow2;
Oct 01 2012
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
On 10/1/2012 1:10 PM, Tommi wrote:
 I meant that all functions would still be
 implicitly CTFEable by default, but an attribute like force_ctfe would
 make it so that the function is guaranteed to execute at compile-time
 when its arguments are compile-time constants.
Since all you need to do to guarantee compile time evaluation is use it in a context that requires CTFE, which are exactly the cases where you'd care that it was CTFE'd, I just don't see much utility here. Note that it is also impossible in the general case for the compiler to guarantee that a specific function is CTFE'able for all arguments that are also CTFE'able.
Oct 01 2012
parent "Tommi" <tommitissari hotmail.com> writes:
On Tuesday, 2 October 2012 at 01:00:25 UTC, Walter Bright wrote:
 Since all you need to do to guarantee compile time evaluation 
 is use it in a context that requires CTFE, which are exactly 
 the cases where you'd care that it was CTFE'd, I just don't see 
 much utility here.
I suppose the most common use case would be efficient struct literals which are essentially value types but have non-trivial constructors. struct Law { ulong _encodedId; this(string state, int year) aggressive_ctfe { // non-trivial constructor sets _encodedId // ... } } Policy policy = getPolicy(); if( policy.isLegalAccordingTo(Law("Kentucky", 1898)) ) { // ... } I think the function attribute would be the most convenient solution.
 Note that it is also impossible in the general case for the 
 compiler to guarantee that a specific function is CTFE'able for 
 all arguments that are also CTFE'able.
I'll have to take your word for it for not knowing enough (anything) about the subject.
Oct 04 2012
prev sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Monday, 1 October 2012 at 06:25:19 UTC, Tommi wrote:
 On Monday, 1 October 2012 at 06:18:48 UTC, Jonathan M Davis 
 wrote:
 A function which uses __ctfe should probably do essentially 
 the same thing at
 both runtime and compile time, but it _has_ __ctfe, because 
 the runtime
 implementation won't work at compile time, and it's up to the 
 programmer to
 make sure that the function does what it's supposed to at both 
 compile time
 and runtime. The compiler can't possibly enforce that.
Thus we're in a situation where pure means pure only by convention, not because it's enforced by the compiler. It's like const in c++ then, it's const only by convention, only because people promise that they're not going to mutate it. I don't like rules that are enforced only by everybody relying on good manners.
The only real problem here is that you wrote a function called pow2 that effectively returns 6 unconditionally. I doubt your math professor would be particularly impressed. If you had used __ctfe properly, it would return the same value both at compile-time and runtime. At present, __ctfe is a necessary evil as CTFE can be severely crippled without it.
Oct 01 2012
parent reply "Tommi" <tommitissari hotmail.com> writes:
On Monday, 1 October 2012 at 08:00:47 UTC, Jakob Ovrum wrote:
 The only real problem here is that you wrote a function called 
 pow2 that effectively returns 6 unconditionally. I doubt your 
 math professor would be particularly impressed. If you had used 
 __ctfe properly, it would return the same value both at 
 compile-time and runtime. At present, __ctfe is a necessary 
 evil as CTFE can be severely crippled without it.
I solemnly swear not to use __ctfe improperly. But my problem with it is, that there *exists* the possibility of improper use of __ctfe. Plus, I just realized, I've never actually met any math professor. I never went to a university (nor study programming for that matter).
Oct 01 2012
next sibling parent "Graham Fawcett" <fawcett uwindsor.ca> writes:
On Monday, 1 October 2012 at 17:53:35 UTC, Tommi wrote:
 On Monday, 1 October 2012 at 08:00:47 UTC, Jakob Ovrum wrote:
 The only real problem here is that you wrote a function called 
 pow2 that effectively returns 6 unconditionally. I doubt your 
 math professor would be particularly impressed. If you had 
 used __ctfe properly, it would return the same value both at 
 compile-time and runtime. At present, __ctfe is a necessary 
 evil as CTFE can be severely crippled without it.
I solemnly swear not to use __ctfe improperly. But my problem with it is, that there *exists* the possibility of improper use of __ctfe.
Can you name a programming language that supports CTFE, but doesn't allow the possibility of misuse? Actually, can you name any language with a safety or purity feature that cannot be thwarted by a motivated programmer? To echo Jonathan's sentiment, no programming language can stop a programmer who is determined to write stupid code from doing so. For the sake of discussion, here's an example of a pure function in Haskell (arguably the "purest" language in wide use today) that happily returns a random integer every time it is called. import System.IO.Unsafe import System.Random pureFunction :: () -> Int pureFunction x = unsafePerformIO impureInside where impureInside = getStdRandom (randomR (1,6)) main = do print (pureFunction ()) print (pureFunction ()) print (pureFunction ()) This prints three random integers between 1 and 6 when executed. -- Graham
Oct 01 2012
prev sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, October 01, 2012 19:53:53 Tommi wrote:
 I solemnly swear not to use __ctfe improperly. But my problem
 with it is, that there *exists* the possibility of improper use
 of __ctfe.
I could just as easily name a function pow2 and make it return the square root. I could overload operator + to do operator -. I could make it so that a function does one thing on Linux and does something completely different and unreleated on Windows. There are _tons_ of places in programming where the programmer has to not be an idiot or their fellow programmers are going to end up with tons of pain. __ctfe is no different from static if or version or any feature which makes it so that the block of code is different on different machines, and it's generally a whale of a lot less error-prone than stuff like endianness issues. It's even trivial to use the same unit tests to test that your function does the same thing in CTFE as at runtime, which you can test on the same machine, unlike with architecture and OS differences. I really think that you're blowing this way out of proportion. Certainly, you think that there's a problem here when the rest of us don't. - Jonathan M Davis
Oct 01 2012
prev sibling parent "Jesse Phillips" <Jessekphillips+D gmail.com> writes:
On Monday, 1 October 2012 at 06:09:18 UTC, Tommi wrote:

 Actually... let's not even worry about the definition of the 
 word 'pure'. Let's just ask ourselves: do we really want to 
 live in a world where people can write code like that:
Yes, yes we do, because if we didn't there wouldn't be a _ctfe to make this possible.
Oct 01 2012
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 01 Oct 2012 01:40:37 -0400, Tommi <tommitissari hotmail.com> wrote:

 import std.stdio;

 int pow2(int val) pure
 {
      if (__ctfe)
          return 6;
      else
          return val * val;
 }

 void main()
 {
             assert(pow2(3) == 9);
      static assert(pow2(3) == 6);

      writeln("9 = 6 ... I knew it! '6' was faking it all along");
      readln();
 }
You have a bug in your code, here let me fix that for you:
 int pow2(int val) pure
 {
     return val * val;
 }
OK, on to the next thread... -Steve
Oct 01 2012
prev sibling parent reply Don Clugston <dac nospam.com> writes:
On 01/10/12 07:40, Tommi wrote:
 import std.stdio;

 int pow2(int val) pure
 {
      if (__ctfe)
          return 6;
      else
          return val * val;
 }

 void main()
 {
             assert(pow2(3) == 9);
      static assert(pow2(3) == 6);

      writeln("9 = 6 ... I knew it! '6' was faking it all along");
      readln();
 }
You don't need the if (__ctfe) to show this behaviour. Nor do you even need CTFE at all (though it would be a bit less obvious). You could demonstrate it in C++ too. Any code that behaves differently when compiled with -O, will do this as well. Constant folding of floating point numbers does the same thing, if the numbers are represented in the compiler in a different precision to how the machine calculates them. I believe that GCC, for example, uses very much higher precision (hundreds of bits) at compile time.
Oct 02 2012
parent Marco Leise <Marco.Leise gmx.de> writes:
Am Tue, 02 Oct 2012 09:38:56 +0200
schrieb Don Clugston <dac nospam.com>:

 Any code that behaves differently when compiled with -O, will do this as 
 well. Constant folding of floating point numbers does the same thing, if 
 the numbers are represented in the compiler in a different precision to 
 how the machine calculates them. I believe that GCC, for example, uses 
 very much higher precision (hundreds of bits) at compile time.
I'm not an expert, but I would have thought compilers strive to be IEEE compliant - whatever that means in detail. I've seen a compression algorithm that relies on exact floating-point semantics and accuracy. It would just fail, if compilers were creative or lax at certain optimization levels. (excluding the "I know what I am doing -ffast-math.) -- Marco
Oct 07 2012