February 16, 2007
Reiner Pope wrote:
> Walter Bright wrote:
>> Michiel wrote:
>>> * the syntax for functions to be executed at compile time isn't the
>>> nice-and-simple D syntax, but the template-syntax. And in another thread
>>> you yourself have mentioned why that's not optimal. I agree.
>>
>> I don't think eval!(expression) is an undue burden. It's hard to imagine how any other syntax would look better.
> But in many situations, you could view compile-time function execution simply as a high-level optimization, an extension on 'inline'. Why, then, does it make sense to require explicit eval!() annotations, when it clearly doesn't make sense to do this for inline?

You can completely ignore and omit the eval!() and your code will run just fine. The eval!() is only for those who wish to do some more advanced tweaking.


> Viewing this as a more complex extension to inlining, I think the correct approach is to allow explicit annotations for 'only at runtime' and 'only at compile time'

I just don't see what is to be gained by this (and consider the extra bloat in the language to provide such annotations). The language already gives complete, absolute control over when a function is executed. Can you give an example where such annotations improve things?

> and have the rest decided according to a compiler switch, eg -pre-eval
February 16, 2007
janderson wrote:
> Walter Bright wrote:
>> Right now, the compiler will fail if the compile time execution results in infinite recursion or an infinite loop. I'll have to figure out some way to eventually deal with this.
> 
> Maybe you could allow the user to specify stack size and maximum iteration per loop/recursion function to the compiler as flags (with some defaults).   This way the user can up the size if they really need it.  This would make it a platform thing.  That way a D compiler could still be made for less powerful systems.

Whether you tell it to fail at a smaller limit, or it fails by itself at a smaller limit, doesn't make any difference as to whether it runs on a less powerful system or not <g>.

The C standard has these "minimum translation limits" for all kinds of things - number of lines, chars in a string, expression nesting level, etc. It's all kind of bogus, hearkening back to primitive compilers that actually used fixed array sizes internally (Brand X, who-shall-not-be-named, was notorious for exceeding internal table limits, much to the delight of Zortech's sales staff). The right way to build a compiler is it either runs out of stack or memory, and that's the only limit.

If your system is too primitive to run the compiler, you use a cross compiler running on a more powerful machine.

I have thought of just putting a timer in the interpreter - if it runs for more than a minute, assume things have gone terribly awry and quit with a message.
February 16, 2007
Derek Parnell wrote:
> I guess its time I came clean and admitted that in spite of this being a
> huge technological advancement in the language, I can't see why I'd ever be
> needing it.
> 
> I mean, when it comes down to it, it's just a fancy way of getting the
> compiler to calculate/generate literals that can be done by myself anyway,
> no? These literals are values that can be determined prior to writing one's
> code, right?
> 
> This is not a troll posting, so can anyone enlighten me on how this ability
> will reduce the cost of maintaining code? I am very open to being educated.

It's a very good question, and I tried to answer it in the follow-on "Motivation for..." thread!
February 16, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> This is by far the least interesting application of this stuff. I don't even count it when I think of the feature. "Oh, yeah, I could compile square root at compile time. How quaint."

I agree. I need a better example. Any ideas?
February 16, 2007
Derek Parnell wrote:
> So this would mean that I could code ...
> 
>     mixin(
>           Conv("moveto 34,56 "
>                "drawto +100,-50 "
>                "drawto +0,+100 "
>                "pencolor red "
>                "drawto -100,-50 "
>               );
>           );
> 
> And expect that the Conv function will, at compile time, create the
> equivalent D code to implement the 2D drawn item for the target platform,
> and have the mixin insert it into the program being compiled. 

Exactamundo!
February 16, 2007
Walter Bright wrote:
>> Viewing this as a more complex extension to inlining, I think the correct approach is to allow explicit annotations for 'only at runtime' and 'only at compile time'
> 
> I just don't see what is to be gained by this (and consider the extra bloat in the language to provide such annotations). The language already gives complete, absolute control over when a function is executed.
It's better for the user only to have complete control when desired. The compiler should infer the rest, but by a better policy than 'leave as much to runtime as possible', since such a policy misses optimization opportunities.

Unless I misunderstand, it seems like compile time function execution opens up a lot of optimization opportunities, yet the programmer is required to explicitly specify every time they should be used.


From what I understood, this function execution was implemented by extending const-folding. Wasn't const-folding initially a form of optimization, to pre-evaluate things, so they didn't need to be evaluated at runtime?

Consider:

int sq(int val)
{
    return val*val;
}

void main()
{
    printf( sq(1024) );
}

Although sq() will probably be inlined, if it gets too big, then it won't. However, there is no reason that it can't be evaluated at compile-time, which would increase the runtime efficiency. Sure, the programmer could enforce compile-time evaluation by using

printf( eval!( sq(1024) ) );

but if the compiler can evaluate it automatically, then there would be no need for programmer annotations here.

I'm just agreeing with Michiel that there should be a compile-time switch tells the compiler to pre-evaluate as much as possible. This is an optimization because it means that some code doesn't need to be run at runtime. I just suggested the 'only at runtime' annotation so that the programmer has a safeguard against the compiler going wild with pre-evaluation, for situations like the compressed-code example you mentioned earlier.


> You can completely ignore and omit the eval!() and your code will run just fine. The eval!() is only for those who wish to do some more advanced tweaking.
For sure. However, what I referred to in my post was the similarity between requiring 'inline' in C++ to tell the compiler to optimize the function call and requiring eval!() in D to tell the compiler to pre-evaluate the function.


Cheers,

Reiner
February 16, 2007
Walter Bright wrote:
> janderson wrote:
>> Walter Bright wrote:
>>> Right now, the compiler will fail if the compile time execution results in infinite recursion or an infinite loop. I'll have to figure out some way to eventually deal with this.
>>
>> Maybe you could allow the user to specify stack size and maximum iteration per loop/recursion function to the compiler as flags (with some defaults).   This way the user can up the size if they really need it.  This would make it a platform thing.  That way a D compiler could still be made for less powerful systems.
> 
> Whether you tell it to fail at a smaller limit, or it fails by itself at a smaller limit, doesn't make any difference as to whether it runs on a less powerful system or not <g>.
> 
> The C standard has these "minimum translation limits" for all kinds of things - number of lines, chars in a string, expression nesting level, etc. It's all kind of bogus, hearkening back to primitive compilers that actually used fixed array sizes internally (Brand X, who-shall-not-be-named, was notorious for exceeding internal table limits, much to the delight of Zortech's sales staff). The right way to build a compiler is it either runs out of stack or memory, and that's the only limit.
> 
> If your system is too primitive to run the compiler, you use a cross compiler running on a more powerful machine.
> 
> I have thought of just putting a timer in the interpreter - if it runs for more than a minute, assume things have gone terribly awry and quit with a message.

That could be achieved with a watchdog process without changing the compiler, and it's more flexible.

I think you just let the compiler go and crunch at it. Since you esentially have partial evaluation anyway, the execution process can be seen as extended to compile time. If you have a non-terminating program, that non-termination can be naturally manifest itself during compilation=partial evaluation.


Andrei
February 16, 2007
Walter Bright wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>> This is by far the least interesting application of this stuff. I don't even count it when I think of the feature. "Oh, yeah, I could compile square root at compile time. How quaint."
> 
> I agree. I need a better example. Any ideas?

Well we talked about:

int a = foo();
char[] b = bar();
print("a is $a and b is $b, dammit\n");

The interesting part is that this will also require you to screw in a couple of extra nuts & bolts (that were needed anyway).

Smart enums (that know printing & parsing) are another example. But the print() example is simple, of immediate clear benefit, and suggestive of more powerful stuff.


Andrei
February 16, 2007
Walter Bright wrote:
> janderson wrote:
>> Walter Bright wrote:
>>> Right now, the compiler will fail if the compile time execution results in infinite recursion or an infinite loop. I'll have to figure out some way to eventually deal with this.
>>
>> Maybe you could allow the user to specify stack size and maximum iteration per loop/recursion function to the compiler as flags (with some defaults).   This way the user can up the size if they really need it.  This would make it a platform thing.  That way a D compiler could still be made for less powerful systems.
> 
> Whether you tell it to fail at a smaller limit, or it fails by itself at a smaller limit, doesn't make any difference as to whether it runs on a less powerful system or not <g>.
> 
> The C standard has these "minimum translation limits" for all kinds of things - number of lines, chars in a string, expression nesting level, etc. It's all kind of bogus, hearkening back to primitive compilers that actually used fixed array sizes internally (Brand X, who-shall-not-be-named, was notorious for exceeding internal table limits, much to the delight of Zortech's sales staff). The right way to build a compiler is it either runs out of stack or memory, and that's the only limit.
> 
> If your system is too primitive to run the compiler, you use a cross compiler running on a more powerful machine.
> 
> I have thought of just putting a timer in the interpreter - if it runs for more than a minute, assume things have gone terribly awry and quit with a message.

I remember one of the first "facts of life" of computer science in college being that some problems can't be solved, and the example given was the 'halting problem'.

If it runs until done or bust, that's okay with me.  But for the purpose of distributing software, it would be a lot easier if either it was unbounded, or the bound was something that depended mostly or completely on the source code.  That way I can compile on my super-duper development machine and if it doesn't "time out", I know whether it will fail on some user's second rate hardware or not.

If the number used doesn't mean anything concrete, that's okay.  I guess the simplest thing would be to bump a counter in some inner loop and cut out when it gets to a jillion.

Kevin
February 16, 2007
Walter Bright wrote:
> ... is now in DMD 1.006. For example:
> 
>> -------------------------------------------
>> import std.stdio;
>>
>> real sqrt(real x)
>> {
>>    real root = x / 2;
>>    for (int ntries = 0; ntries < 5; ntries++)
>>    {
>>        if (root * root - x == 0)
>>            break;
>>        root = (root + x / root) / 2;
>>    }
>>    return root;
>> }
>>
>> void main()
>> {
>>    static x = sqrt(10);   // woo-hoo! set to 3.16228 at compile time!
>>    writefln("%s, %s", x, sqrt(10));  // this sqrt(10) runs at run time
>> }
>> ------------------------------------------
> 
> This should obsolete using templates to compute values at compile time.

Can I use the results of compile-time evaluatable functions in "static if", "pragma(msg)" ? This makes D a scripting language :)


// This does not (yet) work though:
bool func() { return true; }

static if (func()) {
pragma(msg, "true" );
} else {
pragma(msg, "false" );
}


L.