Some thoughts on templates and compile-time evaluation.

Reiner Pope reiner.pope at REMOVE.THIS.gmail.com
Fri Aug 25 05:34:18 PDT 2006


There seems to be a problem with the template system in D, which is that 
it requires much code duplication to use it for compile-time evaluation.

It seems that template metaprogramming in D must be done by exploiting 
the constant folding capabilities of the compiler. However, this means 
that basically any non-trivial function in D must be rewritten to be 
accessed at compile-time (ie from a template). For instance, finding the 
index of a substring within a string is generally done by calling the 
std.string.find functions, but this doesn't work with templates. The 
following code:

import std.string;
import std.stdio : writefln;

template test(char[] c)
{
	static assert(find(c, "al") != -1);
	const int test = 5;
}

int main()
{
	writefln("%d", test!("alpha"));
	return 0;
}

produces

template.d(6): static assert  ((find)("alpha","al") != -1) is not 
evaluatable at
  compile time
template.d(12): template instance template.test!("alpha") error 
instantiating


I can understand the reason behind this limitation, and I know that 
there are projects which aim to work around this, such as meta in DDL, 
but recreating the libraries as compile-time functions seems to be the 
wrong approach to me. I think a worthier goal would be making some 
functions accessible at compile-time.

The question: what is required for something to be determinable at 
compile time?

The answer: it can't rely on any user input or any other types of 
external info. This means it can't use IO or clocks. But basically 
everything else can feasibly be computed at compile-time.

So how can we apply this to D, to determine whether a function can 
safely be evaluated at compile-time (or, in other words, whether it is 
free of side effects)?

Such a function
1. Can't set or get any global or static variables, because this could 
lead to a different result each time it is evaluated.
2. Can't call any IO, etc. I presume this is done by assembly 
interrupts, and other side effects can also be found using them.
3. Can't call any functions which do any of these three things.

If one imagined annotating functions which had these properties, the 
compiler could check the annotations, and then do any compile-time 
evaluation it would see fit simply by *running the code*.

Pros of such a system:
1. It would simplify the writing of compile-time-evaluated things, 
avoiding code duplication.
2. It would blur the line between compile-time and runtime evaluation, 
perhaps allowing more optimizations/inlining on the compiler's part.
3. If evaluation was done by running the code instead of constant 
folding, it would be much faster (as I have demonstrated in my tests 
where the compile-time evaluation of a function took much longer than 
the runtime evaluation of the same logic).
4. If code were run at compile-time, perhaps compile-time evaluation of 
functions for which the source code was not accessible would even be 
possible, just by calling the already-compiled libraries which are 
accessible.

Problems:
1. I have absolutely no idea about implementing this in a compiler. 
Because of problems which may occur due to dependencies between 
templates, and other tricky situations, this could require arbitrarily 
many cycles of
     compile a little
     run some code, and fold in the results, so we can
     compile a little more, and repeat.
I am yet to come up with such an example, but I am sure it could exist. 
This could make compiler implementation very difficult if it needed to 
be able to do this.
2. This annotation system is very much like a const annotation system 
and it therefore suffers from the same problems: virus-like spreading 
through code, and code bloat.
3. Although compile-time evaluation may be possible, serialization may 
cause difficulties, especially with references, which may stuff up an 
automatic serialization. Specifically, if a function generated a class 
at compile-time, how could this be stored in the executable and accessed 
at runtime? Perhaps the easiest approach is to disallow classes and 
perhaps even structs for this type of compile-time evaluation, but such 
a limitation would also be quite disappointing.

If you're still with me here, I admire your patience with me, someone 
who is only just discovering the power of D and templates. If you have 
any thoughts on what I say, please comment.

Cheers,

Reiner



More information about the Digitalmars-d mailing list