October 01, 2012
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.
October 01, 2012
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.
October 01, 2012
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

October 01, 2012
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
October 01, 2012
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
October 01, 2012
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).

October 01, 2012
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
October 01, 2012
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.


October 01, 2012
> 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;


October 01, 2012
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.