Warning: this would break existing code.

I have an idea for a completely new template syntax that is easier to use and 
implement. I'll explain it in therms of a hypothetical language called Dscript. 
Let's start by imagining that Dscript works identically to D 2.0, but it is 
interpreted and dynamically typed, with eval's (this is not intended to be 
implemented, and not the main point of this post). Obviously there isn't much 
of a point to creating two languages with the same syntax, especially since 
Dscript would be useless for systems programming, contradicting one of the 
design goals of D. However, there are some things that you can't do if you have 
static type enforcement. For example, in D this is impossible:

template foo(string Type) {
    invariant foo = eval(Type ~ ".init");
}

but it would be legal in Dscript. The point of templates in D is compile-time 
evaluation of functions and compile-time creation of types, but if Dscript is 
going to be interpreted, templates will obviously not get instantiated until 
they are used since I could write:

string type = readLine();
writeln(foo!(type));

If they are not going to be evaluated until run-time, they become identical to 
functions. For syntactic clarity, Dscript could remove templates altogether and 
replace them with functions:

eval(Type) foo(string Type) {
    return eval(Type ~ ".init");
}

If you don't like the idea of the return type being an eval, remember that 
Dscript is dynamically typed and is not supposed to be implemented. I'm just 
writing the type to make the next step easier: bringing this back to D. First, 
we have to eliminate eval. Well, I have only been using it to convert strings 
into types, so what if we made a type type?

type myint = int;                                    //we don't need alias 
anymore
type myOtherInt : int;                             //or typedef, note the colon
type[3] threeIntegers = [short, int, long]; //and tuples are now built-ins

// ! syntax is obsolete too, because templates are functions
to(myint, 13.7);

//and is-expressions are simpler
if(int <= float) {
    writeln("int is cast-able or identical to float.");
}

if(myint == int) {
    writeln("myint and int are the same type.");
}

import std.socket;

if(UdpSocket <: Socket) {
    writeln("UdpSocket extends Socket.");
}

Everything is so much simpler!

This is easier for both programmers and compilers, but we still have one 
problem: dynamic typing. This isn't too hard to eliminate, though; we just have 
to make sure that whenever we use a type, it's known at compile time. This 
checking is already in place, for example integer template parameters are known 
at compile-time. If necessary, it can also be easily accomplished by making 
types immutable. What about templates with non-type parameters, such int's? 
Simple:

int bar(string str, static int abc, type T)

abc must be known at compile-time, since it is static. T can be regarded as 
implicitly static, just as str, being declared string instead of char[], is 
implicitly invariant in D 2.0. A template is instantiated for each unique set 
of static parameters, giving either a function (pointer), a new type 
(struct/class), or whatever else could be templated. Now we have our static 
typing back.

The last use of templates is compile-time function evaluation. There is already 
a better way to do this in D. instead of writing a function twice, once 
normally, and once confusingly using recursive templates, just say:

static int a = baz('a', 5, "asdf");

The static keyword tells it to evaluate at compile time, similar to my proposed 
use of static. This is recommended, instead of the template approach, at 
http://www.digitalmars.com/d/2.0/templates-revisited.html, just before the 
section on SFINAE.

We've created a language just like D, except it simplifies alias's, typedef's, 
tuples, is expressions, and especially templates, all the confusing and 
hard-to-learn aspects. Instead, we have the much more elegant syntax of types.

Reply via email to