View mode: basic / threaded / horizontal-split · Log in · Help
October 01, 2012
Re: It seems pure ain't so pure after all
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
Re: It seems pure ain't so pure after all
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
Re: It seems pure ain't so pure after all
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
Re: It seems pure ain't so pure after all
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
Re: It seems pure ain't so pure after all
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
Re: It seems pure ain't so pure after all
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
Re: It seems pure ain't so pure after all
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
Re: It seems pure ain't so pure after all
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
Re: It seems pure ain't so pure after all
> 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
Re: It seems pure ain't so pure after all
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.
1 2 3 4 5 6
Top | Discussion index | About this forum | D home