www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The "type" type dream

reply Michel Fortin <michel.fortin michelf.com> writes:
In the "Unification and extension of compile-time reflection" thread, 
Jarrett Billingsley wrote:

 static if(is(T : V[K], K, V))
 {
    // V and K are defined here
 }
Since we're talking about unifying things... [enter dream mode] Imagine types as first-class values. static if (type V[type K] = T) { // V and K assigned from type T. } Basically, we're declaring variables V and K of type "type". The "type" type simply holds a type. "type V[type K]" is a combined declaration/assignment containing the name and relative disposition of the two type variables to which we try to assign T. An impossible type assignment would assign void to both, a correct assigment would assign the corresponding non-void type for each variable in the specified position. In the if statement, void types converts to false, non-void types converts to true. Now you can do all the above as separate statements if you wish (should make things easier to understand): type V; // V is void type K; // K is void V[K] = T; // compiler assign T to V and K according to the V[K] disposition. if (V && K) ... Even better, type as function return type: type valueTypeOf(type T) { type V[type K] = T; return V; } static if (type V = valueTypeOf(T)) { // V assigned from function valueTypeOf(T) (which returns a type). } valueTypeOf(T) a; // variable of the type returned by valueTypeOf(T). Here, valueTypeOf is a function taking a type as argument and returning another type. Obviously, it's a function that can only be evaluated at compile-time, but with it we can just forget about using templates and declarative programming when we need to create variables of related types. [exit dream mode] Not sure what all this would entail, but if we could return types from functions, would we still need templates at all? Couldn't we just create any struct or class inside the function and return that? Unifying templates and functions... hum, I'm probably still dreaming. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Nov 25 2008
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Michel Fortin:
 Unifying templates and functions... hum, I'm probably still dreaming.
I suggest you to take a good look at Haskell :-) Its type systems is Turing-complete, you can really use it to do many kinds of things, including the ones you dream about now, and much more complex ones too you currently aren't able to dream about yet :-) While we are at this: from this post and the last posts about 'Unification and extension of compile-time reflection' I can see that probably D may also enjoy a bit more refined pattern matching capabilities (on values and types too). Scala and OcaML languages show few examples of this, but again you can look at Haskell for something better. From what I've seen so far if you want a very flexible language there are two solutions: 1) you can attach types to values, and manage types at runtime, ending with dynamic languages like Ruby, Python, Scheme, CLips, tcl, and so on. 2) Otherwise you probably need a very flexible static type system, with strong type-inferencing capabilities, and capable to manage types is a refined way. A Hindley–Milner type inference algorithm may suffice: http://en.wikipedia.org/wiki/Type_inference#Hindley.E2.80.93Milner_type_inference_algorithm http://web.archive.org/web/20050911123640/http://www.cs.berkeley.edu/~nikitab/courses/cs263/hm.html Bye, bearophile
Nov 25 2008
next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Tue, 25 Nov 2008 16:51:43 -0500, bearophile wrote:

 Michel Fortin:
 Unifying templates and functions... hum, I'm probably still dreaming.
I suggest you to take a good look at Haskell :-) Its type systems is Turing-complete, you can really use it to do many kinds of things, including the ones you dream about now, and much more complex ones too you currently aren't able to dream about yet :-)
Haskell is a different language built around different design principles. Your comment seems to me like this: Michel: "I'd like a cookie..." bearophile: "Look at the bananas! They're tasty, too! And Sweet!" Well, who argues. But bananas aren't cookies! You can make a banana cookie, but it has a lot to do with cookies and little with bananas. Haskell is nice, solid language. But the point is to work with types like you work with any other data in D. If you adopt Haskell for type manipulations then the point is lost because you end up with two languages for run-time and compile-time manipulations again.
Nov 25 2008
parent bearophile <bearophileHUGS lycos.com> writes:
Sergey Gromov:
 Haskell is a different language built around different design
 principles.  Your comment seems to me like this:
 Well, who argues.  But bananas aren't cookies!  You can make a banana
 cookie, but it has a lot to do with cookies and little with bananas.
I think that learning Haskell, or taking a good look at it, may teach me/you some computer science, especially things related to the type system and operations with types. Such knowledge may be useful if I/you want to invent new ways to manage types in another language, like D. Bye, bearophile
Nov 25 2008
prev sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
bearophile wrote:
 From what I've seen so far if you want a very flexible language there are two
solutions:
 1) you can attach types to values, and manage types at runtime, ending with
dynamic languages like Ruby, Python, Scheme, CLips, tcl, and so on.
 2) Otherwise you probably need a very flexible static type system, with strong
type-inferencing capabilities, and capable to manage types is a refined way. A
Hindley–Milner type inference algorithm may suffice:
 http://en.wikipedia.org/wiki/Type_inference#Hindley.E2.80.93Milner_type_inference_algorithm
 http://web.archive.org/web/20050911123640/http://www.cs.berkeley.edu/~nikitab/courses/cs263/hm.html
Static type inference + OO = disaster. Say a module consists of this: class A { void add(int o); } class B { void add(string o); } f(x, y) { x.add(y); } What are the types of x and y in f? The only correct answer is that there are two versions of f: void f(A x, int y) { x.add(y); } void f(B x, string y) { x.add(y); } This may not have been intended by the programmer (if there are 30 classes with an "add" method, many which are totally unrelated, it's very likely the programmer didn't intend to create overloads for every single one). If the programmer did intend a sort of "compile-time duck typing", this will propogate to every caller of f. The big problem, however is efficiency. Every method name is bound to many possible classes, each of which must be considered. These possibilities explode up the type graph, a Cartesian product of all possible arguments being created at each function declaration. This means that literally thousands of versions of a function might need to be created for any non-trivial function (some of this can be turned into branches or broken down into multiple functions). It's simply not a scalable solution. For an example, take a look at ShedSkin (which does a combination of this method and flow analysis), which works great for up to 200 LOC, but in its current form will likely never work for enterprise-size applications. The last problem: every class must be known at the time of compiling one module, which is a killer for complete TI in D, anyways. TI + OO works very well in the forward case (flow analysis, i.e. this type is this here, so these are the possible types it can be here...). This has been put to good use in optimizing Java/.NET. However, if a function is going to be exported for use outside a module, flow analysis won't work, since another module has to be able to call it with unexpected types. It also can prove to be very slow unless localized to a small region (see how long it takes to run Coverity on a sufficiently large project... and that's just tracking nulls through code-path analysis, imagine it was tracking type sets). Static type inference is best left to a language like Haskell - the type of a function (it's return, arguments, etc.) can be determined by looking only at the function itself because every possible function it can call is within a known namespace. (if you're still reading this sorry -- I just started doing some research on a similar topic, so I'm pretty excited about it).
Nov 25 2008
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Robert Fraser:
 For an example, take a look at ShedSkin
I have given a hand developing ShedSkin with Mark, and I am helping its development now too :-) I agree it may not be able to scale to medium or large programs, currently it can handle programs of about 3000-5000 lines. But note that: - it's written in Python, so using a faster language (like Java or D) it can become significantly faster (but writing it in Java or D may require lot of work, despite it's only few thousands or lines of code). - Mark has not used a lot of tricks that if used can speed up the code more. The code was not even profiled. - ShedSkin used a pure approach: it uses no type signatures at all, given by the programmer. But I was not suggesting to use a full type inferencing in D. This means that in a more realistic system you add several type signatures, and this will speed the algorithm a lot. - There's also the trick of progressive compilation. If integrated into the editor such type inferencing can digest the program in smaller bites, making the actual compilation faster. This is of course not easy to do, but it's possible. Note that something similar also allows you to create an IDE for dynamic languages that has capabilities similar to the IDEs for static languages. Scala is an example of OO language that also has some type inferencing.
 (if you're still reading this sorry
Sorry for what? Bye, bearophile
Nov 25 2008
parent reply Robert Fraser <fraserofthenight gmail.com> writes:
bearophile wrote:
 Robert Fraser:
 For an example, take a look at ShedSkin
I have given a hand developing ShedSkin with Mark, and I am helping its development now too :-)
Awesome! That's one of the coolest OSS projects out there!
 I agree it may not be able to scale to medium or large programs, currently it
can handle programs of about 3000-5000 lines. But note that:
 - it's written in Python, so using a faster language (like Java or D) it can
become significantly faster (but writing it in Java or D may require lot of
work, despite it's only few thousands or lines of code).
 - Mark has not used a lot of tricks that if used can speed up the code more.
The code was not even profiled.
 - ShedSkin used a pure approach: it uses no type signatures at all, given by
the programmer.
It would still only work for private (non-exported) function in D. If given the earlier example of "f(x, y) { x.add(y); }", the compiler would know about A and B, so could generate overloads for those two. But the compiler would have to know about every other class in the program with an "add" method to be able to codegen for the function. This just wouldn't be possible with D's current compilation model, and if it were, it would lead to crazy code bloat anyway.
 - There's also the trick of progressive compilation. If integrated into the
editor such type inferencing can digest the program in smaller bites, making
the actual compilation faster. This is of course not easy to do, but it's
possible. Note that something similar also allows you to create an IDE for
dynamic languages that has capabilities similar to the IDEs for static
languages.
IDEs only need to consider the forward case (flow analysis), so it's much simpler. If everywhere in the current program only cals a function foo with Bars, it's OK to suggest methods only from Bar. However when exporting a function to be linked/executed in an unknown environment, the function needs to be ready for anything. Also, IDEs don't need 100% accuracy in 100% of cases. If the IDE is missing some methods or can't make a suggestion, that sucks, but the IDE is still usable. Native-code compilers, however, need 100%.
 But I was not suggesting to use a full type inferencing in D. This 
means that in a more realistic system you add several type signatures, and this will speed the algorithm a lot.
 Scala is an example of OO language that also has some type inferencing.
Wish I had read this before starting on the rest of this post ;-P. Sure, I guess limited type inference can't be bad, though it's not on my "must have" list for D. "auto" is good enough, IMO.
Nov 25 2008
parent bearophile <bearophileHUGS lycos.com> writes:
Robert Fraser:
 Awesome! That's one of the coolest OSS projects out there!
Thank you ^_^
 It would still only work for private (non-exported) function in D.
ShedSkin (SS) can also be used to create modules that you can import from Python. I have solved this problem in the following way: in a Python module there can be a part that runs only when you run the module by itself, and that doesn't run when you import the module (under if __name__ == "__main__":). In such part you can put module tests. SS digests the code present into such tests even when you compile the module to create a python extension, giving the module the missing types, the ones that come from the calling Python code :-) You need to re-compile the module if you want to add new tests with new types, but for the current purpose of SS this is not a problem. Bye, bearophile
Nov 25 2008
prev sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2008-11-25 17:31:02 -0500, Robert Fraser <fraserofthenight gmail.com> said:

 Static type inference + OO = disaster.
 
 Say a module consists of this:
 class A { void add(int    o); }
 class B { void add(string o); }
 f(x, y) { x.add(y); }
 
 What are the types of x and y in f? The only correct answer is that 
 there are two versions of f:
 void f(A x, int    y) { x.add(y); }
 void f(B x, string y) { x.add(y); }
 
 This may not have been intended by the programmer (if there are 30 
 classes with an "add" method, many which are totally unrelated, it's 
 very likely the programmer didn't intend to create overloads for every 
 single one). If the programmer did intend a sort of "compile-time duck 
 typing", this will propogate to every caller of f.
 
 The big problem, however is efficiency. Every method name is bound to 
 many possible classes, each of which must be considered. These 
 possibilities explode up the type graph, a Cartesian product of all 
 possible arguments being created at each function declaration. This 
 means that literally thousands of versions of a function might need to 
 be created for any non-trivial function (some of this can be turned 
 into branches or broken down into multiple functions). It's simply not 
 a scalable solution.
Hum, isn't static type inference is already possible in D using templates? And yes, templates can lead to code bloat. I think that with static type inference we would have a good starting ground to obsolete template functions. Take this function (invented syntax): void f(auto x, auto y) { x.add(y); } which would be the same as this template (current D syntax): void f(X, Y)(X x, Y y) { x.add(y); } Surprisingly, it's the same number of character. The first is easier to read and understand though. Obviously, that's a function you couldn't export for the same reasons you can't export templates.
 The last problem: every class must be known at the time of compiling 
 one module, which is a killer for complete TI in D, anyways.
The way it works in D is that a template is converted to code when compiling the module that use and instanciate it. All types used need to be accessible when compiling the module that uses and instanciate the template. It's mostly the same with C++, and it seems to scale well enough. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Nov 26 2008
prev sibling next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Michel Fortin" <michel.fortin michelf.com> wrote in message 
news:gghpq4$2nc6$1 digitalmars.com...
 In the "Unification and extension of compile-time reflection" thread, 
 Jarrett Billingsley wrote:

 static if(is(T : V[K], K, V))
 {
    // V and K are defined here
 }
Since we're talking about unifying things... [enter dream mode] Imagine types as first-class values. static if (type V[type K] = T) { // V and K assigned from type T. } Basically, we're declaring variables V and K of type "type". The "type" type simply holds a type. "type V[type K]" is a combined declaration/assignment containing the name and relative disposition of the two type variables to which we try to assign T. An impossible type assignment would assign void to both, a correct assigment would assign the corresponding non-void type for each variable in the specified position. In the if statement, void types converts to false, non-void types converts to true. Now you can do all the above as separate statements if you wish (should make things easier to understand): type V; // V is void type K; // K is void V[K] = T; // compiler assign T to V and K according to the V[K] disposition. if (V && K) ... Even better, type as function return type: type valueTypeOf(type T) { type V[type K] = T; return V; } static if (type V = valueTypeOf(T)) { // V assigned from function valueTypeOf(T) (which returns a type). } valueTypeOf(T) a; // variable of the type returned by valueTypeOf(T). Here, valueTypeOf is a function taking a type as argument and returning another type. Obviously, it's a function that can only be evaluated at compile-time, but with it we can just forget about using templates and declarative programming when we need to create variables of related types. [exit dream mode] Not sure what all this would entail, but if we could return types from functions, would we still need templates at all? Couldn't we just create any struct or class inside the function and return that? Unifying templates and functions... hum, I'm probably still dreaming. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
I love it!
Nov 25 2008
parent reply Eric Poggel <fake example.com> writes:
Nick Sabalausky wrote:
 "Michel Fortin" <michel.fortin michelf.com> wrote in message 
 news:gghpq4$2nc6$1 digitalmars.com...
 In the "Unification and extension of compile-time reflection" thread, 
 Jarrett Billingsley wrote:

 static if(is(T : V[K], K, V))
 {
    // V and K are defined here
 }
Since we're talking about unifying things... [enter dream mode] Imagine types as first-class values. static if (type V[type K] = T) { // V and K assigned from type T. } Basically, we're declaring variables V and K of type "type". The "type" type simply holds a type. "type V[type K]" is a combined declaration/assignment containing the name and relative disposition of the two type variables to which we try to assign T. An impossible type assignment would assign void to both, a correct assigment would assign the corresponding non-void type for each variable in the specified position. In the if statement, void types converts to false, non-void types converts to true. Now you can do all the above as separate statements if you wish (should make things easier to understand): type V; // V is void type K; // K is void V[K] = T; // compiler assign T to V and K according to the V[K] disposition. if (V && K) ... Even better, type as function return type: type valueTypeOf(type T) { type V[type K] = T; return V; } static if (type V = valueTypeOf(T)) { // V assigned from function valueTypeOf(T) (which returns a type). } valueTypeOf(T) a; // variable of the type returned by valueTypeOf(T). Here, valueTypeOf is a function taking a type as argument and returning another type. Obviously, it's a function that can only be evaluated at compile-time, but with it we can just forget about using templates and declarative programming when we need to create variables of related types. [exit dream mode] Not sure what all this would entail, but if we could return types from functions, would we still need templates at all? Couldn't we just create any struct or class inside the function and return that? Unifying templates and functions... hum, I'm probably still dreaming. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
I love it!
Seconded. What if we treat Type like a class and store all of the type specific meta-data as properties of it? int a; Type T = a.type; // or valueTypeOf(a), whatever syntax you prefer. writefln(T.sizeof); // writes 4 Type could even have it's own set of subclasses for dealing with specific types of data. We could have type info classes for Primitive, Struct, Class, Union, etc. that all inherit from Type. class A { void foo(){} void bar(){} }; A a; Class C = a.type; // Class inherits from Type foreach (M; C.publicMethods) if (M.type == A.foo.type) writefln(M.name); // writes "foo") // We could also do things like this, if the stack would allow it. a.type b = new a.type; It's probably quite a pipe dream to be able to do this outside a scripting language: void foo() { writefln("bar")}; Class C = new Class(); C.addMethod(&foo); auto c = new C(); c.foo(); // writes "bar" Finally, being able to work with types in this manner could eliminate most/all of the other competing syntaxes. Of course, I haven't spent much thought on the actual implementation, which could be quite hard/impossible.
Nov 26 2008
next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Wed, 26 Nov 2008 20:47:01 +0300, Eric Poggel <fake example.com> wrote:

 Nick Sabalausky wrote:
 "Michel Fortin" <michel.fortin michelf.com> wrote in message  
 news:gghpq4$2nc6$1 digitalmars.com...
 In the "Unification and extension of compile-time reflection" thread,  
 Jarrett Billingsley wrote:

 static if(is(T : V[K], K, V))
 {
    // V and K are defined here
 }
Since we're talking about unifying things... [enter dream mode] Imagine types as first-class values. static if (type V[type K] = T) { // V and K assigned from type T. } Basically, we're declaring variables V and K of type "type". The "type" type simply holds a type. "type V[type K]" is a combined declaration/assignment containing the name and relative disposition of the two type variables to which we try to assign T. An impossible type assignment would assign void to both, a correct assigment would assign the corresponding non-void type for each variable in the specified position. In the if statement, void types converts to false, non-void types converts to true. Now you can do all the above as separate statements if you wish (should make things easier to understand): type V; // V is void type K; // K is void V[K] = T; // compiler assign T to V and K according to the V[K] disposition. if (V && K) ... Even better, type as function return type: type valueTypeOf(type T) { type V[type K] = T; return V; } static if (type V = valueTypeOf(T)) { // V assigned from function valueTypeOf(T) (which returns a type). } valueTypeOf(T) a; // variable of the type returned by valueTypeOf(T). Here, valueTypeOf is a function taking a type as argument and returning another type. Obviously, it's a function that can only be evaluated at compile-time, but with it we can just forget about using templates and declarative programming when we need to create variables of related types. [exit dream mode] Not sure what all this would entail, but if we could return types from functions, would we still need templates at all? Couldn't we just create any struct or class inside the function and return that? Unifying templates and functions... hum, I'm probably still dreaming. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
I love it!
Seconded. What if we treat Type like a class and store all of the type specific meta-data as properties of it? int a; Type T = a.type; // or valueTypeOf(a), whatever syntax you prefer. writefln(T.sizeof); // writes 4 Type could even have it's own set of subclasses for dealing with specific types of data. We could have type info classes for Primitive, Struct, Class, Union, etc. that all inherit from Type. class A { void foo(){} void bar(){} }; A a; Class C = a.type; // Class inherits from Type foreach (M; C.publicMethods) if (M.type == A.foo.type) writefln(M.name); // writes "foo") // We could also do things like this, if the stack would allow it. a.type b = new a.type;
int i = 42; Type type = i.GetType(); Console.WriteLine(type); Type MyTypeObject = Type.GetType(TestClass); MemberInfo[] MyMemberArray = MyTypeObject.GetMembers();
 It's probably quite a pipe dream to be able to do this outside a  
 scripting language:

 void foo() { writefln("bar")};
 Class C = new Class();
 C.addMethod(&foo);
 auto c = new C();
 c.foo(); // writes "bar"

 Finally, being able to work with types in this manner could eliminate  
 most/all of the other competing syntaxes.  Of course, I haven't spent  
 much thought on the actual implementation, which could be quite  
 hard/impossible.
Nov 26 2008
prev sibling parent "Nick Sabalausky" <a a.a> writes:
"Eric Poggel" <fake example.com> wrote in message 
news:492D8B95.9020100 example.com...
 Seconded.

 What if we treat Type like a class and store all of the type specific 
 meta-data as properties of it?

 int a;
 Type T = a.type; // or valueTypeOf(a), whatever syntax you prefer.
 writefln(T.sizeof); // writes 4

 Type could even have it's own set of subclasses for dealing with specific 
 types of data.  We could have type info classes for Primitive, Struct, 
 Class, Union, etc. that all inherit from Type.
I agree with all of this.
 class A {
 void foo(){}
 void bar(){}
 };
 A a;
 Class C = a.type; // Class inherits from Type
 foreach (M; C.publicMethods)
 if (M.type == A.foo.type)
 writefln(M.name); // writes "foo")

 // We could also do things like this, if the stack would allow it.
 a.type b = new a.type;

 It's probably quite a pipe dream to be able to do this outside a scripting 
 language:

 void foo() { writefln("bar")};
 Class C = new Class();
 C.addMethod(&foo);
 auto c = new C();
 c.foo(); // writes "bar"

 Finally, being able to work with types in this manner could eliminate 
 most/all of the other competing syntaxes.  Of course, I haven't spent much 
 thought on the actual implementation, which could be quite 
 hard/impossible.
I'm not convinced I would want to be able to do that outside a scripting language (or even in one, for that matter). Making type definitions mutable at run-time breaks the concept of static typing. And I like static typing. I effect.
Nov 26 2008
prev sibling parent reply "Stewart Gordon" <smjg_1998 yahoo.com> writes:
"Michel Fortin" <michel.fortin michelf.com> wrote in message 
news:gghpq4$2nc6$1 digitalmars.com...
<snip>
 Here, valueTypeOf is a function taking a type as argument and returning 
 another type. Obviously, it's a function that can only be evaluated at 
 compile-time, but with it we can just forget about using templates and 
 declarative programming when we need to create variables of related types.
If it can only be evaluated at compile-time, ISTM it isn't really types being first-class values. Your idea is an attempt to capture the power of templates without using templates, whereas a real system of types as first-class values would be more powerful than this.
 [exit dream mode]

 Not sure what all this would entail, but if we could return types from 
 functions, would we still need templates at all? Couldn't we just create 
 any struct or class inside the function and return that?
<snip> We might no longer need templates for functions or type aliases, but I think that we would still need class/struct templates. You could invent a notation to construct a class or struct within a function, but I'm not sure that you can make it look as nice as the template notation we already have. Whether you want types to be full first-class values manipulable at runtime or merely entities that you can manipulate using CTFE, one implication is that type syntax and expression syntax would merge into one common syntax. This might make parsing simpler, but you would have to work out how to deal with such funny things as that * is postfix for types and prefix for expressions, but [] is postfix for both. I thought a while back about the possibility of giving types first-class citizenship. I suppose that what it really means is that a type used as an expression would become the TypeInfo for that type. But the TypeInfo classes probably ought to have more human-readable names - Type, ArrayType, ClassType, AAType, etc. And some properties specific to each of these, e.g. valueType for ArrayType and AAType, eliminating the need for the valueTypeOf function you proposed. Hmm.... Stewart. -- My e-mail address is valid but not my primary mailbox. Please keep replies on the 'group where everybody may benefit.
Nov 26 2008
parent Michel Fortin <michel.fortin michelf.com> writes:
On 2008-11-26 17:49:45 -0500, "Stewart Gordon" <smjg_1998 yahoo.com> said:

 "Michel Fortin" <michel.fortin michelf.com> wrote in message 
 news:gghpq4$2nc6$1 digitalmars.com...
 <snip>
 Here, valueTypeOf is a function taking a type as argument and returning 
 another type. Obviously, it's a function that can only be evaluated at 
 compile-time, but with it we can just forget about using templates and 
 declarative programming when we need to create variables of related 
 types.
If it can only be evaluated at compile-time, ISTM it isn't really types being first-class values. Your idea is an attempt to capture the power of templates without using templates, whereas a real system of types as first-class values would be more powerful than this.
Well, being able to work with types at runtime implies many things, and I didn't want to make it seem like any runtime support is needed to implement what I'm proposing. Also, I haven't thought about everything that is needed to fully replace templates with functions. I'll bring back something that was proposed a while ago: static arguments for functions. Static arguments were supposed to instanciate code for the function for each value you'd pass to the given argument, much like a template argument would do. Here's how you can use this to create a function that return a type parametrized to a given argument: type foo(static int a) { class C { int opCall() { return a; } } return C; } Which could replace this: template bar(int a) { class bar { int opCall() { return a; } } } There's also a difference in the instanciation syntax: foo(1) a; bar!(1) b; Now you can implement "headconst(Object)" in a library. :-)
 [exit dream mode]
 
 Not sure what all this would entail, but if we could return types from 
 functions, would we still need templates at all? Couldn't we just 
 create any struct or class inside the function and return that?
<snip> We might no longer need templates for functions or type aliases, but I think that we would still need class/struct templates. You could invent a notation to construct a class or struct within a function, but I'm not sure that you can make it look as nice as the template notation we already have.
Yes, I agree. The "class A(arguments) {}" and "struct B(arguments) {}" syntax should be kept. But then it should also be harmonised to use the same parameter naming convention as functions. Basically: class A(type T) { ... } could become a shortcut for function: type A(type T) { class A { ... }; return A; } instead of: template A(T) { class A { ... } } And it could be instanciated like this: A(int) a;
 Whether you want types to be full first-class values manipulable at 
 runtime or merely entities that you can manipulate using CTFE, one 
 implication is that type syntax and expression syntax would merge into 
 one common syntax. This might make parsing simpler, but you would have 
 to work out how to deal with such funny things as that * is postfix for 
 types and prefix for expressions, but [] is postfix for both.
I'm mostly interested in CTFE, at least for now. It's true that parsing postfix * may be an interesting problem. "foo(x) * a" has a totally different meaning depending on foo(x) giving you a type or a value. If I'm not mistaken, fixing this would imply breaking separation of syntaxic and semantic analysis in the compiler, probably not something Walter will be interested in.
 I thought a while back about the possibility of giving types 
 first-class citizenship.  I suppose that what it really means is that a 
 type used as an expression would become the TypeInfo for that type.  
 But the TypeInfo classes probably ought to have more human-readable 
 names - Type, ArrayType, ClassType, AAType, etc.  And some properties 
 specific to each of these, e.g. valueType for ArrayType and AAType, 
 eliminating the need for the valueTypeOf function you proposed.
Seems you want runtime reflection. :-) -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Nov 26 2008