www.digitalmars.com         C & C++   DMDScript  

D - Generics in D

reply "Craig Black" <cblack ara.com> writes:
Walter,

First of all, I would like to say that I like the direction that D is going
so far, and appreciate your innovative work on this new language.

I know support for generics in D is probably still on the horizin, but have
you given it any serious thought?  We can definitely improve on C++
templates, both in ease of implementation and flexibility.  Even before you
start on templates it would be good to have in mind at least a vague concept
of what you plan to do.  I use C++ templates aggressively, so I have some
ideas about features to add and drop.

First, if you haven't read Modern C++ Design: Generic Programming and Design
Patterns Applied by Andrei Alexandrescu, you should get a copy.  A great
book!  I have already applied many of the concepts he covers.  It should be
required reading for anyone who wants in depth understand of Generic
Programming.

I have so many ideas and could write forever about generics, here's just
some food for thought.  If you're interested, I would be glad to share more
ideas.

I will just give you a few insights of where you can improve on C++.  First,
we generic programmers need a compile-time typeof operator.  I have run into
so many cases where I said to myself, "I sure wish I had a typeof operator."
It would make generic programming so much more elegant.  Then you could have
compile-time conditionals to handle different types in different ways, like
this:

cswitch(typeof(variable))
{
   case int:  // handle an integer
   case string: // hangle a string
   ...
}

Here "cswitch" is a compile-time conditional.  This means the conditional is
resolved at compile time, and optimized appropriately so that no performance
is lost and dead code is eliminated.  You could also have cif, celse, etc.

Another idea I have to improve on C++ is type specialization syntax.  We
could implement type specialization more explicitly using typelists.  This
would provide a more readable type specialization that (I think) would be
easier to implement.

Here's a simple example:

template <typename T : {int, uint, long, ulong, float, double}>
T Add(T a, T b)
{
   return a + b;
}

This specializes the Add function to need apply only to int, float, double,
and extended.  You could then write another Add template that would handle
concatenation of container types.

This leads me to another feature that would be nice to have: type lists (see
chapter 3 of Modern C++ design.)  It would be nice to categorize types
something like this:

typelist NumericTypes { int, uint, long, ulong, float, double };
typelist ContainerTypes { list, set, vector, queue, stack };

Then you could rewrite the Add function:

template <typename T : NumericTypes>
T Add(T a, T b)
{
   return a + b;
}

template <typename T : ContainerTypes>
T Add(T a, T b)
{
   T result;
   result.append(a);
   result.append(b);
   return result;
}

Like I said, I have many other ideas, including making generic programming
even more generic to reduce code duplication and improve maintainabiliity.

Let me know what you think.

Craig
Jun 24 2002
next sibling parent "Matthew Wilson" <matthew thedjournal.com> writes:
I like your typelist idea, but am dubious about the compile-time switch. Can
you expound on this idea and give a non-trivial example?

Matthew

"Craig Black" <cblack ara.com> wrote in message
news:af7k2g$1f31$1 digitaldaemon.com...
 Walter,

 First of all, I would like to say that I like the direction that D is
going
 so far, and appreciate your innovative work on this new language.

 I know support for generics in D is probably still on the horizin, but
have
 you given it any serious thought?  We can definitely improve on C++
 templates, both in ease of implementation and flexibility.  Even before
you
 start on templates it would be good to have in mind at least a vague
concept
 of what you plan to do.  I use C++ templates aggressively, so I have some
 ideas about features to add and drop.

 First, if you haven't read Modern C++ Design: Generic Programming and
Design
 Patterns Applied by Andrei Alexandrescu, you should get a copy.  A great
 book!  I have already applied many of the concepts he covers.  It should
be
 required reading for anyone who wants in depth understand of Generic
 Programming.

 I have so many ideas and could write forever about generics, here's just
 some food for thought.  If you're interested, I would be glad to share
more
 ideas.

 I will just give you a few insights of where you can improve on C++.
First,
 we generic programmers need a compile-time typeof operator.  I have run
into
 so many cases where I said to myself, "I sure wish I had a typeof
operator."
 It would make generic programming so much more elegant.  Then you could
have
 compile-time conditionals to handle different types in different ways,
like
 this:

 cswitch(typeof(variable))
 {
    case int:  // handle an integer
    case string: // hangle a string
    ...
 }

 Here "cswitch" is a compile-time conditional.  This means the conditional
is
 resolved at compile time, and optimized appropriately so that no
performance
 is lost and dead code is eliminated.  You could also have cif, celse, etc.

 Another idea I have to improve on C++ is type specialization syntax.  We
 could implement type specialization more explicitly using typelists.  This
 would provide a more readable type specialization that (I think) would be
 easier to implement.

 Here's a simple example:

 template <typename T : {int, uint, long, ulong, float, double}>
 T Add(T a, T b)
 {
    return a + b;
 }

 This specializes the Add function to need apply only to int, float,
double,
 and extended.  You could then write another Add template that would handle
 concatenation of container types.

 This leads me to another feature that would be nice to have: type lists
(see
 chapter 3 of Modern C++ design.)  It would be nice to categorize types
 something like this:

 typelist NumericTypes { int, uint, long, ulong, float, double };
 typelist ContainerTypes { list, set, vector, queue, stack };

 Then you could rewrite the Add function:

 template <typename T : NumericTypes>
 T Add(T a, T b)
 {
    return a + b;
 }

 template <typename T : ContainerTypes>
 T Add(T a, T b)
 {
    T result;
    result.append(a);
    result.append(b);
    return result;
 }

 Like I said, I have many other ideas, including making generic programming
 even more generic to reduce code duplication and improve maintainabiliity.

 Let me know what you think.

 Craig
Jun 24 2002
prev sibling next sibling parent reply Burton Radons <loth users.sourceforge.net> writes:
Craig Black wrote:
 I will just give you a few insights of where you can improve on C++.  First,
 we generic programmers need a compile-time typeof operator.  I have run into
 so many cases where I said to myself, "I sure wish I had a typeof operator."
 It would make generic programming so much more elegant.  Then you could have
 compile-time conditionals to handle different types in different ways, like
 this:
 
 cswitch(typeof(variable))
 {
    case int:  // handle an integer
    case string: // hangle a string
    ...
 }
Good idea, but the new statements aren't necessary. Just having the clauses in the language that: a) Dead code and blocks made inaccessible through constant folding must not be compiled. b) Trivial constant folding must be performed. Is enough to do the same. Then the above code becomes: switch(variable.typeinfo) { case int: case char[]: } These additions aren't difficult to comply with and have value elsewhere.
 Then you could rewrite the Add function:
 
 template <typename T : NumericTypes>
 T Add(T a, T b)
 {
    return a + b;
 }
 
 template <typename T : ContainerTypes>
 T Add(T a, T b)
 {
    T result;
    result.append(a);
    result.append(b);
    return result;
 }
That could easily become, using my generics methodology: $T Add ($T a, $T b) { if ($T.has_field ("length")) return a ~ b; else return a + b; } So I think you introduced a good idea - cswitch/cif/celse - and then didn't use it right when it would work great. Can you produce an example of code where typelist is necessary? LX/XL has an interesting solution to the problem that automatically handles new types. The generic type for a min and max function is: generic type ordered if with ordered A, B with boolean Test := A < B So a type is ordered if it can handle (A < B) and return a boolean. Whether this is just a mind-blower or of actual value is hard to say at this point. Smalltalk has also been tossed around here on occasion, but I don't think anyone's described it and I can't google any information about it.
Jun 24 2002
next sibling parent reply "cblack01" <cblack01 cox.net> writes:
"Burton Radons" <loth users.sourceforge.net> wrote in message
news:3D17D5CE.3040608 users.sourceforge.net...
 Craig Black wrote:
 I will just give you a few insights of where you can improve on C++.
First,
 we generic programmers need a compile-time typeof operator.  I have run
into
 so many cases where I said to myself, "I sure wish I had a typeof
operator."
 It would make generic programming so much more elegant.  Then you could
have
 compile-time conditionals to handle different types in different ways,
like
 this:

 cswitch(typeof(variable))
 {
    case int:  // handle an integer
    case string: // hangle a string
    ...
 }
Good idea, but the new statements aren't necessary. Just having the clauses in the language that: a) Dead code and blocks made inaccessible through constant folding must not be compiled. b) Trivial constant folding must be performed.
Good points. It would just be nice to force the compiler to resolve the conditional at compile time, and give you an error message if it can't so that you know for sure whether the optimization is performed. Perhaps the compile-time conditionals are unnecessary, but if it a construct that is easily added to a language, it would ease my conscience to use them in performance-critical situations. Just a thought: the concept of compile-time conditionals could be extended to looping constructs (cfor, cwhile, etc) to force the unrolling of loops. This idea almost causes me to drool on myself.
 Is enough to do the same.  Then the above code becomes:

      switch(variable.typeinfo)
      {
          case int:
          case char[]:
      }

 These additions aren't difficult to comply with and have value elsewhere.

 Then you could rewrite the Add function:

 template <typename T : NumericTypes>
 T Add(T a, T b)
 {
    return a + b;
 }

 template <typename T : ContainerTypes>
 T Add(T a, T b)
 {
    T result;
    result.append(a);
    result.append(b);
    return result;
 }
That could easily become, using my generics methodology: $T Add ($T a, $T b) { if ($T.has_field ("length")) return a ~ b; else return a + b; }
I like this syntax. A quick question, as I am paranoid about performance, is the $T.has_field("length") resolved at compile time? I am assuming that "length" here is a static member variable. After all, we do not want to have to add a member variable every time we want to classify an attribute of a class. Another question, this looks to me like reflection, introspection, whatever you want to call it. In D, will this information be available at both run-time and compile-time? I can see applications for compile-time reflection and run-time reflection. Is there also an API to get a container of member methods, or a container member variables? An is_class() method? is_basic_type() method? If this API is in existence, and can be resolved at compile-time, then perhaps this is just the solution I have been looking for to solve all my generic programming problems.
 So I think you introduced a good idea - cswitch/cif/celse - and then
 didn't use it right when it would work great.  Can you produce an
 example of code where typelist is necessary?
All my examples (that I can think of right now) would be a result of the problems introduced with C++ generics. As mentioned previously, type lists would provide a more flexible and better solution for the ugly C++ specialization syntax, and C++ type traits. But as you have proposed above, perhaps compile-time reflection would be an even better solution.
 LX/XL has an interesting solution to the problem that automatically
 handles new types.  The generic type for a min and max function is:

 generic type ordered if
     with ordered A, B
     with boolean Test := A < B

 So a type is ordered if it can handle (A < B) and return a boolean.
 Whether this is just a mind-blower or of actual value is hard to say at
 this point.
I remember reading about LX one time. This guy is trying to unify every programming paradigm in existence into one language. An immense task. He certainly has some creative and original ideas. Unfortunately, even if he is successful, his language probably won't be widely adopted because the syntax is not close enough to C++, Java style syntax.
 Smalltalk has also been tossed around here on occasion, but
 I don't think anyone's described it and I can't google any information
 about it.
Sorry, I don't know anything about Smalltalk, so I can't comment. Sounds to me like you are totally on the right track here. Perhaps compile-time reflection will more elegantly resolve many current issues in regard to generics. I guess my main concern is how well the compiler is able to resolve things at compile-time. This is what makes C++ templates so powerful. More concise, maintainable source code with absolutely no run-time overhead, because the compiler does all the work. Are you involved with the D compiler development? If you are, this gives me more confidence in D. Do you have specs for your implementation of generics? I think tommorrow I may post another idea and if you want you can critique it for me. Regards, Craig
Jun 24 2002
parent reply Burton Radons <loth users.sourceforge.net> writes:
cblack01 wrote:

 Good points.  It would just be nice to force the compiler to resolve the
 conditional at compile time, and give you an error message if it can't so
 that you know for sure whether the optimization is performed.  Perhaps the
 compile-time conditionals are unnecessary, but if it a construct that is
 easily added to a language, it would ease my conscience to use them in
 performance-critical situations.
When you read later you'll understand what I mean when I say: $type constant ($type $value) { return $value; } "if (constant (...))" is more cumbersome than "cif (...)", but cute nonetheless. Updated rules about mandatory trivial constant folding: a) Dead code and blocks made inaccessible through constant folding must not be compiled. b) Trivial constant folding must be performed. c) All methods and field accesses of ClassInfo, TypeInfo, and Interface that have constant parameters must be converted into the appropriate constant for further folding. d) If the compiler supports nontrivial constant folding, it must be aware of the distinction. If a block is made redundant due to nontrivial constant folding, but would be compiled with trivial constant folding, it must be compiled, even if it is subsequently thrown away. This is another effort to smooth the differences between compilers and disallow people from abusing their intelligent compiler.
That could easily become, using my generics methodology:

     $T Add ($T a, $T b)
     {
         if ($T.has_field ("length"))
             return a ~ b;
         else
             return a + b;
     }
I like this syntax. A quick question, as I am paranoid about performance, is the $T.has_field("length") resolved at compile time? I am assuming that "length" here is a static member variable. After all, we do not want to have to add a member variable every time we want to classify an attribute of a class.
It's turned into one line of code at maximum at compile time. Should be using interfaces to indicate compliance instead, really.
 Another question, this looks to me like reflection, introspection, whatever
 you want to call it.  In D, will this information be available at both
 run-time and compile-time?  I can see applications for compile-time
 reflection and run-time reflection.  Is there also an API to get a container
 of member methods, or a container member variables?  An is_class() method?
 is_basic_type() method?  If this API is in existence, and can be resolved at
 compile-time, then perhaps this is just the solution I have been looking for
 to solve all my generic programming problems.
It will be in the future, at least for runtime. Nic's brought up the case of ... disjoint typelist genericism. Or something. My opinion is that once those issues kick into gear, not using methods to do this is criminal, particularly as using generic methods with super allows even more flexibility in doing this.
Smalltalk has also been tossed around here on occasion, but
I don't think anyone's described it and I can't google any information
about it.
Sorry, I don't know anything about Smalltalk, so I can't comment. Sounds to me like you are totally on the right track here. Perhaps compile-time reflection will more elegantly resolve many current issues in regard to generics. I guess my main concern is how well the compiler is able to resolve things at compile-time. This is what makes C++ templates so powerful. More concise, maintainable source code with absolutely no run-time overhead, because the compiler does all the work. Are you involved with the D compiler development? If you are, this gives me more confidence in D. Do you have specs for your implementation of generics?
Nope and nope. The generics stuff is just a shorthand at the moment - I haven't thought about how it would behave. But how about this. For generic functions and generic class methods, a "$" followed by an identifier in the place where a type would exist indicates a generic type. The specific type of this generic is determined by the first argument using it; a function cannot return a generic type only. For example: $type foo ($type a, $type b) $type bar (int c); /* Illegal */ complex x = foo (4, 2.3); /* Returns int and casts 2.3 to int */ Generic values are also prefixed with a $ and must be trivially constant. Generic values of type "TypeInfo" or "ClassInfo" are special in that they can be used both as a value and a generic type, and if so must be the first argument to use this generic value: $type cast (TypeInfo $type, $type value) { return value; } $type tsac ($type value, TypeInfo $type) /* Illegal */ cast (int, 4.3); /* Returns 4 */ A generic function that takes only generic types and values and only accesses other similarly compile-time values is considered trivially constant and must be folded. The generic function for a given function name and number of arguments is searched for eligibility after any specific-type functions. There can't be two generic functions with the same name and number of arguments as it's easy to make the overloading rules go crazy, and this restriction can be relaxed in the future. On to struct, class, and union. I've only taken the most obvious choice: struct Vector (TypeInfo $type, int $size) { $type [$size] array; } Vector (float, 4) x; struct Vector2 (TypeInfo $type) : Vector ($type, 2) { float x () { return array [0]; } float y () { return array [1]; } } struct Vector3 (TypeInfo $type) : Vector2 ($type) { float z () { return array [2]; } void z (float v) { array [2] = v; } } Vector3 (float) v; v.z = 16; Perhaps not Vector2 and Vector3, but the possibilities intrigue me. I _think_ the ambiguities that C++ was fighting against are nullified by D, which would be very good, as <> is not. I have also been thinking about making the generic values as a kind of virtual field and specifying their value in the constructor, but a usable syntax has not emerged. So in total it's just C++'s templates without the template keyword.
Jun 27 2002
next sibling parent "Craig Black" <cblack ara.com> writes:
This is good stuff.

--Craig

"Burton Radons" <loth users.sourceforge.net> wrote in message
news:3D1AD1CD.3080308 users.sourceforge.net...
 cblack01 wrote:

 Good points.  It would just be nice to force the compiler to resolve the
 conditional at compile time, and give you an error message if it can't
so
 that you know for sure whether the optimization is performed.  Perhaps
the
 compile-time conditionals are unnecessary, but if it a construct that is
 easily added to a language, it would ease my conscience to use them in
 performance-critical situations.
When you read later you'll understand what I mean when I say: $type constant ($type $value) { return $value; } "if (constant (...))" is more cumbersome than "cif (...)", but cute nonetheless. Updated rules about mandatory trivial constant folding: a) Dead code and blocks made inaccessible through constant folding must not be compiled. b) Trivial constant folding must be performed. c) All methods and field accesses of ClassInfo, TypeInfo, and Interface that have constant parameters must be converted into the appropriate constant for further folding. d) If the compiler supports nontrivial constant folding, it must be aware of the distinction. If a block is made redundant due to nontrivial constant folding, but would be compiled with trivial constant folding, it must be compiled, even if it is subsequently thrown away. This is another effort to smooth the differences between compilers and disallow people from abusing their intelligent compiler.
That could easily become, using my generics methodology:

     $T Add ($T a, $T b)
     {
         if ($T.has_field ("length"))
             return a ~ b;
         else
             return a + b;
     }
I like this syntax. A quick question, as I am paranoid about
performance,
 is the $T.has_field("length") resolved at compile time?  I am assuming
that
 "length" here is a static member variable.  After all, we do not want to
 have to add a member variable every time we want to classify an
attribute of
 a class.
It's turned into one line of code at maximum at compile time. Should be using interfaces to indicate compliance instead, really.
 Another question, this looks to me like reflection, introspection,
whatever
 you want to call it.  In D, will this information be available at both
 run-time and compile-time?  I can see applications for compile-time
 reflection and run-time reflection.  Is there also an API to get a
container
 of member methods, or a container member variables?  An is_class()
method?
 is_basic_type() method?  If this API is in existence, and can be
resolved at
 compile-time, then perhaps this is just the solution I have been looking
for
 to solve all my generic programming problems.
It will be in the future, at least for runtime. Nic's brought up the case of ... disjoint typelist genericism. Or something. My opinion is that once those issues kick into gear, not using methods to do this is criminal, particularly as using generic methods with super allows even more flexibility in doing this.
Smalltalk has also been tossed around here on occasion, but
I don't think anyone's described it and I can't google any information
about it.
Sorry, I don't know anything about Smalltalk, so I can't comment. Sounds to me like you are totally on the right track here. Perhaps compile-time reflection will more elegantly resolve many current issues
in
 regard to generics.  I guess my main concern is how well the compiler is
 able to resolve things at compile-time.  This is what makes C++
templates so
 powerful.  More concise, maintainable source code with absolutely no
 run-time overhead, because the compiler does all the work.

 Are you involved with the D compiler development?  If you are, this
gives me
 more confidence in D.  Do you have specs for your implementation of
 generics?
Nope and nope. The generics stuff is just a shorthand at the moment - I haven't thought about how it would behave. But how about this. For generic functions and generic class methods, a "$" followed by an identifier in the place where a type would exist indicates a generic type. The specific type of this generic is determined by the first argument using it; a function cannot return a generic type only. For example: $type foo ($type a, $type b) $type bar (int c); /* Illegal */ complex x = foo (4, 2.3); /* Returns int and casts 2.3 to int */ Generic values are also prefixed with a $ and must be trivially constant. Generic values of type "TypeInfo" or "ClassInfo" are special in that they can be used both as a value and a generic type, and if so must be the first argument to use this generic value: $type cast (TypeInfo $type, $type value) { return value; } $type tsac ($type value, TypeInfo $type) /* Illegal */ cast (int, 4.3); /* Returns 4 */ A generic function that takes only generic types and values and only accesses other similarly compile-time values is considered trivially constant and must be folded. The generic function for a given function name and number of arguments is searched for eligibility after any specific-type functions. There can't be two generic functions with the same name and number of arguments as it's easy to make the overloading rules go crazy, and this restriction can be relaxed in the future. On to struct, class, and union. I've only taken the most obvious choice: struct Vector (TypeInfo $type, int $size) { $type [$size] array; } Vector (float, 4) x; struct Vector2 (TypeInfo $type) : Vector ($type, 2) { float x () { return array [0]; } float y () { return array [1]; } } struct Vector3 (TypeInfo $type) : Vector2 ($type) { float z () { return array [2]; } void z (float v) { array [2] = v; } } Vector3 (float) v; v.z = 16; Perhaps not Vector2 and Vector3, but the possibilities intrigue me. I _think_ the ambiguities that C++ was fighting against are nullified by D, which would be very good, as <> is not. I have also been thinking about making the generic values as a kind of virtual field and specifying their value in the constructor, but a usable syntax has not emerged. So in total it's just C++'s templates without the template keyword.
Jun 27 2002
prev sibling next sibling parent reply "Craig Black" <cblack ara.com> writes:
Burton,

I've been thinking about your approach for generic functions.  How would
your syntax handle this C++ template:

template <int a, int b>
float[b][a] Inverse(float matrix[a][b]) { ... }

Perhaps you have a radically new idea that can handle this.

Craig
Jun 27 2002
next sibling parent reply "Craig Black" <cblack ara.com> writes:
 Burton,

 I've been thinking about your approach for generic functions.  How would
 your syntax handle this C++ template:

 template <int a, int b>
 float[b][a] Inverse(float matrix[a][b]) { ... }

 Perhaps you have a radically new idea that can handle this.

 Craig
I am going to answer my own question. void Invert(in $M matrix, out &I inverse) in { assert(constant(matrix.rows == inverse.cols)); assert(constant(matrix.cols == inverse.rows)); } body { ... } Given that there are row and column properties for the matrix, or some way of getting that information at compile time.
Jun 27 2002
parent "Juan Carlos Arevalo Baeza" <jcab roningames.com> writes:
"Craig Black" <cblack ara.com> wrote in message
news:affqjc$2kbf$1 digitaldaemon.com...

 Burton,

 I've been thinking about your approach for generic functions.  How would
 your syntax handle this C++ template:

 template <int a, int b>
 float[b][a] Inverse(float matrix[a][b]) { ... }

 Perhaps you have a radically new idea that can handle this.

 Craig
I am going to answer my own question. void Invert(in $M matrix, out &I inverse) in { assert(constant(matrix.rows == inverse.cols)); assert(constant(matrix.cols == inverse.rows)); } body { ... } Given that there are row and column properties for the matrix, or some way of getting that information at compile time.
Still, in order to make this work completely at compile time, the function's contract should be part of the official interface of the function. The "template<>" (or, in LX, "generic[]", or Haskell's class assertions and contexts) construct is more robust in the general case. It's like saying... "assuming we have a and b which are integer values, this is a generic matrix-inverting function". It's clean, it's non-ambiguous. Of course, using angle brackets IS ambiguous for parsing (eek!), but that's besides the point. Salutaciones, JCAB
Jun 27 2002
prev sibling parent reply Burton Radons <loth users.sourceforge.net> writes:
Craig Black wrote:

 I've been thinking about your approach for generic functions.  How would
 your syntax handle this C++ template:
 
 template <int a, int b>
 float[b][a] Inverse(float matrix[a][b]) { ... }
float[$b][$a] Inverse (float matrix[$a][$b]) { ... } They're generic values of type int. But this is your one and only chance to have a generic function of this name and number of arguments in this scope, so a method is a much better solution. Before it comes up: foo (int [TypeInfo $type] bar) For associated arrays.
 Perhaps you have a radically new idea that can handle this.
No, just an undescribed part of the syntax. I meant to describe it but forgot.
Jun 27 2002
parent reply "Craig Black" <cblack ara.com> writes:
      float[$b][$a] Inverse (float matrix[$a][$b]) { ... }
So in a generic function the $symbol can be a type, or an integer, or anything else? I could see where this ambiguity might be too much for the compiler to handle. That is why C++ uses "template<>" for functions. For classes I think its unnecessary, but for functions the compiler has to infer what the type or value is, so it is best to give the compiler as many hints as possible to reduce ambiguity. It would be different if we assumed that all $symbol's were types. How about preceding each $symbol with a type where the value is to be deduced? Not only will this specify what the type is, but it will tell the compiler which parameter to get the value from. I admit I've never designed a compiler, but I assume these kinds of generic functions are difficult to implement. Let's keep in mind that somebody is going to have to implement this stuff if it's ever going to work. float[$b][$a] Inverse (float matrix[int $a][int $b]) { ... }
Jun 28 2002
next sibling parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:

Array subscripts are always integral (and unsigned!) so one could leave out
the "int" there, it's implicitly uint.

$f[#b][#a] Inverse ($f matrix[#a][#b]) { ... }

Sean

"Craig Black" <cblack ara.com> wrote in message
news:afhrmp$1v65$1 digitaldaemon.com...
      float[$b][$a] Inverse (float matrix[$a][$b]) { ... }
So in a generic function the $symbol can be a type, or an integer, or anything else? I could see where this ambiguity might be too much for the compiler to handle. That is why C++ uses "template<>" for functions. For classes I think its unnecessary, but for functions the compiler has to
infer
 what the type or value is, so it is best to give the compiler as many
hints
 as possible to reduce ambiguity.  It would be different if we assumed that
 all $symbol's were types.  How about preceding each $symbol with a type
 where the value is to be deduced?  Not only will this specify what the
type
 is, but it will tell the compiler which parameter to get the value from.
I
 admit I've never designed a compiler, but I assume these kinds of generic
 functions are difficult to implement.  Let's keep in mind that somebody is
 going to have to implement this stuff if it's ever going to work.

  float[$b][$a] Inverse (float matrix[int $a][int $b]) { ... }
Jun 28 2002
parent reply "cblack01" <cblack01 cox.net> writes:

 Array subscripts are always integral (and unsigned!) so one could leave
out
 the "int" there, it's implicitly uint.

 $f[#b][#a] Inverse ($f matrix[#a][#b]) { ... }

 Sean
This would work if we assumed that all #symbol's were unsigned integers. But what about associative arrays? With an associative array, the subscript can be char[] or potentially any other type. We would need another symbol for every kind of type. Craig
Jun 28 2002
parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:

wrong about its ability to use uint implicitly in the array index situation.

So I'd have to modify my example thusly:

$f[#b][#a] Inverse ($f matrix[uint #a][uint #b]) { ... }

any #value would have to have a type specified for it at least once (and all
such declarations would have to match).  If the compiler can figure out the
type implicitly that's great, if not, it has to be specified.

Sean

"cblack01" <cblack01 cox.net> wrote in message
news:afj697$9eo$1 digitaldaemon.com...

 Array subscripts are always integral (and unsigned!) so one could leave
out
 the "int" there, it's implicitly uint.

 $f[#b][#a] Inverse ($f matrix[#a][#b]) { ... }

 Sean
This would work if we assumed that all #symbol's were unsigned integers. But what about associative arrays? With an associative array, the
subscript
 can be char[] or potentially any other type.  We would need another symbol
 for every kind of type.

 Craig
Jun 29 2002
parent reply Pavel Minayev <evilone omen.ru> writes:
On Sat, 29 Jun 2002 11:49:40 -0700 "Sean L. Palmer" <seanpalmer earthlink.net> 
wrote:

 $f[#b][#a] Inverse ($f matrix[uint #a][uint #b]) { ... }
I hate, by the way, especially its synatx =) Could we come with some more elegant decision? I wonder if even C++-style < > brackets could be used, and teach the compiler to hanle >> properly for templates?
Jun 29 2002
next sibling parent reply Patrick Down <pat codemoon.com> writes:
Pavel Minayev <evilone omen.ru> wrote in
news:CFN374370467174768 news.digitalmars.com: 

 On Sat, 29 Jun 2002 11:49:40 -0700 "Sean L. Palmer"
 <seanpalmer earthlink.net> wrote:
 
 $f[#b][#a] Inverse ($f matrix[uint #a][uint #b]) { ... }
I hate, by the way, especially its synatx =) Could we come with some more elegant decision? I wonder if even C++-style < > brackets could be used, and teach the compiler to hanle >> properly for templates?
How about something like this. template { // Define parametric types type T1; type T2; uint XA; uint YA; uint XB; uint YB; // Define families of parametric fuctions // and structures T1[YA][XA] Inverse(T1[XA][YA] matrix) { } T1[XB][YA] Multipy(T1[XA][YA] m1,T1[XB][YB] m2) {} // struct foo(T1,T2) { T1[T2] bar; T1 func { } } }
Jun 29 2002
next sibling parent Pavel Minayev <evilone omen.ru> writes:
On Sat=2C 29 Jun 2002 22=3A25=3A55 +0000 =28UTC=29 Patrick Down
=3Cpat=40codemoon=2Ecom=3E wrote=3A

=3E How about something like this=2E
=3E 
=3E template
=3E {
=3E   =2F=2F Define parametric types
=3E   type T1=3B 
=3E   type T2=3B 
=3E   uint XA=3B
=3E   uint YA=3B 
=3E   uint XB=3B
=3E   uint YB=3B
=3E 
=3E   =2F=2F Define families of parametric fuctions
=3E   =2F=2F and structures
=3E 
=3E   T1=5BYA=5D=5BXA=5D Inverse=28T1=5BXA=5D=5BYA=5D matrix=29 { }  
=3E 
=3E   T1=5BXB=5D=5BYA=5D Multipy=28T1=5BXA=5D=5BYA=5D m1=2CT1=5BXB=5D=5BYB=5D
m2=29 {}   
=3E 
=3E   =2F=2F   
=3E   struct foo=28T1=2CT2=29
=3E   {
=3E     T1=5BT2=5D bar=3B
=3E 
=3E     T1 func { }
=3E   }
=3E }
 
Then how do you declare an instance of foo=3F Like this=3F

=09foo=28char=5B=5D=2C int=29 x=3B

Looks too much like a function pointer =28or delegate=29=2E I think a distinct
syntax is still needed=2E
Jun 30 2002
prev sibling parent "Sean L. Palmer" <seanpalmer earthlink.net> writes:
Well this at least has a look that fits with the rest of D quite nicely.

I agree with Pavel, I don't like the () syntax ... something else is needed.
In fact the below doesn't even really need the (T1,T2) part in struct
foo(T1,T2) as it's already well known that T1 and T2 are generic types since
the whole thing is within a template{} declaration that defines T1 and T2.

Perhaps you could name the template scope and instantiate the template scope
with some special syntax to access the identifiers of the templated types?

template T1
{
    type T;
    T min(T a, T b)
    {
        return (a<b) ? a : b;
    }
}

void main()
{
    int x,y;
    return T1(int T).min(x,y);
}

Sean

"Patrick Down" <pat codemoon.com> wrote in message
news:Xns923CB3E63DC17patcodemooncom 63.105.9.61...
 Pavel Minayev <evilone omen.ru> wrote in
 news:CFN374370467174768 news.digitalmars.com:

 On Sat, 29 Jun 2002 11:49:40 -0700 "Sean L. Palmer"
 <seanpalmer earthlink.net> wrote:

 $f[#b][#a] Inverse ($f matrix[uint #a][uint #b]) { ... }
I hate, by the way, especially its synatx =) Could we come with some more elegant decision? I wonder if even C++-style < > brackets could be used, and teach the compiler to hanle >> properly for templates?
How about something like this. template { // Define parametric types type T1; type T2; uint XA; uint YA; uint XB; uint YB; // Define families of parametric fuctions // and structures T1[YA][XA] Inverse(T1[XA][YA] matrix) { } T1[XB][YA] Multipy(T1[XA][YA] m1,T1[XB][YB] m2) {} // struct foo(T1,T2) { T1[T2] bar; T1 func { } } }
Jun 30 2002
prev sibling parent "Sean L. Palmer" <seanpalmer earthlink.net> writes:
If D source could use Unicode characters, we could use French quotation
marks << and >> for shifts, and < and > for templates.   ;)



you're dealing with generic types or values.

Sean

"Pavel Minayev" <evilone omen.ru> wrote in message
news:CFN374370467174768 news.digitalmars.com...
 On Sat, 29 Jun 2002 11:49:40 -0700 "Sean L. Palmer"
<seanpalmer earthlink.net>
 wrote:

 $f[#b][#a] Inverse ($f matrix[uint #a][uint #b]) { ... }
I hate, by the way, especially its synatx =) Could we come with some more elegant decision? I wonder if even C++-style < > brackets could be used, and teach the compiler to hanle >> properly for templates?
Jun 30 2002
prev sibling parent reply Burton Radons <loth users.sourceforge.net> writes:
Craig Black wrote:

  >>     float[$b][$a] Inverse (float matrix[$a][$b]) { ... }
  >
  > So in a generic function the $symbol can be a type, or an integer, or
  > anything else?  I could see where this ambiguity might be too much
for the
  > compiler to handle.  That is why C++ uses "template<>" for functions.
   For
  > classes I think its unnecessary, but for functions the compiler has
to infer
  > what the type or value is, so it is best to give the compiler as many
hints
  > as possible to reduce ambiguity.  It would be different if we assumed
that
  > all $symbol's were types.  How about preceding each $symbol with a type
  > where the value is to be deduced?  Not only will this specify what
the type
  > is, but it will tell the compiler which parameter to get the value
from.  I
  > admit I've never designed a compiler, but I assume these kinds of 
generic
  > functions are difficult to implement.  Let's keep in mind that
somebody is
  > going to have to implement this stuff if it's ever going to work.

Compiler-wise this matter is trivial (functions anyway); I wouldn't 
recommend it if I knew otherwise.

For divining matches you'd merely take the call and cycle through the
arguments, filling in as you walk along.  Then create a signature out of
it, and if it matches the arguments and has not been compiled do so.
There's a good set of very well-defined steps for getting there from 
here.  $ defines new mandatory trivial constant folding points to put it 
in its purest term.  Couple hundred lines of code max.

Blarg.  I'm trying to figure out what kind of monster struct, class, and 
union would turn out to be if it could have specific values passed to 
its factory.  This is haunting me:

     struct Matrix (TypeInfo $type, int rows, int cols)
     {
        $type [rows] [cols] array;
     }

     Matrix (float, 4, 4) foo; /* constant size */
     Matrix (Color, h, w) bar; /* run-time sized using alloca */

     struct Moof
     {
         Matrix (int) m;
     }

     Moof moof;

     moof.m = Matrix (int, h, w);

I don't like the last part of the syntax which has some weird internal 
gymnastics and odd syntax.  Maybe:

     struct Moof (int rows, int cols)
     {
         Matrix (int, rows, cols) m;
     }

     Moof (h, w) moof;

But I don't like things propagating like this, even if it is consistent 
with templates.  Hm.

  >  float[$b][$a] Inverse (float matrix[int $a][int $b]) { ... }

This is redundant.  I don't particularly care how it goes - it's just 
that allowing "int $a" implies that you can do "float $a", and it's kind 
of confusing that "[int $a]" and "[int]" mean entirely different things. 
  At least "(int $a)" and "(int)" are within speaking distance.
Jun 29 2002
parent "Craig Black" <cblack ara.com> writes:
 Compiler-wise this matter is trivial (functions anyway); I wouldn't
 recommend it if I knew otherwise.

 For divining matches you'd merely take the call and cycle through the
 arguments, filling in as you walk along.  Then create a signature out of
 it, and if it matches the arguments and has not been compiled do so.
 There's a good set of very well-defined steps for getting there from
 here.  $ defines new mandatory trivial constant folding points to put it
 in its purest term.  Couple hundred lines of code max.
OK. Sounds like you know what you're talking about. Just to clarify in my mind, what is the justification for ditching the "template <>" or "generic []" syntax for generic functions? Is this just unnecessary?
 Blarg.  I'm trying to figure out what kind of monster struct, class, and
 union would turn out to be if it could have specific values passed to
 its factory.  This is haunting me:

      struct Matrix (TypeInfo $type, int rows, int cols)
      {
         $type [rows] [cols] array;
      }

      Matrix (float, 4, 4) foo; /* constant size */
      Matrix (Color, h, w) bar; /* run-time sized using alloca */

      struct Moof
      {
          Matrix (int) m;
      }

      Moof moof;

      moof.m = Matrix (int, h, w);

 I don't like the last part of the syntax which has some weird internal
 gymnastics and odd syntax.  Maybe:

      struct Moof (int rows, int cols)
      {
          Matrix (int, rows, cols) m;
      }

      Moof (h, w) moof;

 But I don't like things propagating like this, even if it is consistent
 with templates.  Hm.
Ouch! This stuff is starting to make my brain hurt. Are you trying to find a syntax that can instantiate a generic at run-time? What would we get if we removed the $ from "$type" in this template. A run-time generic? Is this what you are trying to accomplish? If so, perhaps we could use the same syntax for both run-time and compile-time. The same generic could be instantiated at compile-time or run-time. That way you wouldn't have to duplicate any code in order to have both compile-time and run-time generics.
   >  float[$b][$a] Inverse (float matrix[int $a][int $b]) { ... }

 This is redundant.  I don't particularly care how it goes - it's just
 that allowing "int $a" implies that you can do "float $a", and it's kind
 of confusing that "[int $a]" and "[int]" mean entirely different things.
   At least "(int $a)" and "(int)" are within speaking distance.
float[$b][$a] Inverse (float matrix[$a][$b]) { ... } Hey, if the compiler doesn't mind, neither do I.
Jul 01 2002
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Burton Radons" <loth users.sourceforge.net> wrote in message
news:3D1AD1CD.3080308 users.sourceforge.net...
 I _think_ the ambiguities that C++ was fighting against are nullified by
 D, which would be very good, as <> is not.
That's some of the problem with C++ templates. The rest of the complexities come about because of implicit instantiation. If, instead, things are explicitly instantiated, most of the complexity goes away. None of the rules about partial ordering, etc., would be necessary.
Jul 03 2002
parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
That could also suck alot of the ease-of-use out of them.  Would mean
writing every declaration of a generic type in at least two places.  Ok,
most of the time in C++ we do this anyway using a typedef, but when splicing
together expressions etc it's convenient to not have to manually instantiate
them.

Sean

"Walter" <walter digitalmars.com> wrote in message
news:afvs5u$2lce$1 digitaldaemon.com...
 "Burton Radons" <loth users.sourceforge.net> wrote in message
 news:3D1AD1CD.3080308 users.sourceforge.net...
 I _think_ the ambiguities that C++ was fighting against are nullified by
 D, which would be very good, as <> is not.
That's some of the problem with C++ templates. The rest of the
complexities
 come about because of implicit instantiation. If, instead, things are
 explicitly instantiated, most of the complexity goes away. None of the
rules
 about partial ordering, etc., would be necessary.
Jul 05 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
news:ag40dh$mc2$1 digitaldaemon.com...
 That could also suck alot of the ease-of-use out of them.  Would mean
 writing every declaration of a generic type in at least two places.  Ok,
 most of the time in C++ we do this anyway using a typedef, but when
splicing
 together expressions etc it's convenient to not have to manually
instantiate
 them.
I know it's convenient when it works, but I don't believe it's worth it. When there are a bunch of partially specialized templates spread all over the header files, don't you find yourself wondering sometimes which is getting picked, and then trying to figure out an easy way to verify it? If your code is working, there's no problem, but if it isn't or it needs enhancement, I just don't think the obfuscation is saving any programming effort. What I see for D is what you say, basically enshrining the typedef approach as the way to instantiate a template.
Jul 05 2002
parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
That will work.  And it'll be simple and elegant.

It just might be a teeny bit inconvenient as it will prevent
expression-style programming without having to put typedefs beforehand.

in STL C++:

    #include <list>
    #include <algorithm>

    list<int> intlist;
    sort(intlist.begin(), intlist.end(), less<int>());

in D:  something like

    import list;
    import algorithm;

    typedef list{int} list_of_int;
    list_of_int intlist;

    typedef less{int} less_for_int;
    typedef sort{int, list_of_int.iterator_type, less_for_int} sort_routine;
    sort_routine(intlist.begin(), intlist.end(), less_for_int());

As you can see, it's not nearly as elegant when you have to typedef
everything.  This is just one teensy example;  I've seen really cool stuff
done this way:  Check out http://spirit.sourceforge.net for a cool example
of the power of C++ templates when combined with operator overloading.

Maybe you could make it work so that if you fully spell out the type it can
be instantiated in an expression.  I suppose this can be added later without
much problem, so if it turns out to be impractical it can be fixed later.
Something like:

    import list;
    import algorithm;

    list{int} intlist;
    sort{int, list{int}.iterator_type, less{int}}(intlist.begin(),
intlist.end());

But in reality I'd rather the standard templated containers be safer than
STL iterator-based containers, perhaps something that functions more like a
dynamic array?  It'll be tough to make a linked list behave like a dynamic
array though.  The algorithmic performance characteristics and the preferred
usage patterns are completely different.

Perhaps the dynamic array reallocation problem can be solved by making a
dynamic array actually a container of pointers to subranges of the array.
So if it needs expanded and the memory can't be resized in place, it just
allocates a new block and doesn't copy the old one.  That would make dynamic
array access have to go through one more level of indirection and you
couldn't typecast it to a pointer to a flat array anymore (maybe a function
to "flatten" a dynamic array would be good) but it would solve most of the
"for (ever) myarray ~= asingleentry;" problem.

Sean

"Walter" <walter digitalmars.com> wrote in message
news:ag4kct$1bdq$3 digitaldaemon.com...
 "Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
 news:ag40dh$mc2$1 digitaldaemon.com...
 That could also suck alot of the ease-of-use out of them.  Would mean
 writing every declaration of a generic type in at least two places.  Ok,
 most of the time in C++ we do this anyway using a typedef, but when
splicing
 together expressions etc it's convenient to not have to manually
instantiate
 them.
I know it's convenient when it works, but I don't believe it's worth it. When there are a bunch of partially specialized templates spread all over the header files, don't you find yourself wondering sometimes which is getting picked, and then trying to figure out an easy way to verify it? If your code is working, there's no problem, but if it isn't or it needs enhancement, I just don't think the obfuscation is saving any programming effort. What I see for D is what you say, basically enshrining the typedef
approach
 as the way to instantiate a template.
Jul 05 2002
parent reply "Walter" <walter digitalmars.com> writes:
Yes I know it'll be more typing. Perhaps I can find a way to reduce that
without doing the implicit instantiation two-step. There's another maddening
example of the implict instantiation resulting in bizarre rules - the way
dependent and non-dependent names are looked up. The non-dependent names are
looked up in an earlier version of the global symbol table than the
dependent names are. Perhaps this makes sense to someone, but to me it's
just a way to torture compiler writers <g>.

"Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
news:ag4q05$1grg$1 digitaldaemon.com...
 That will work.  And it'll be simple and elegant.

 It just might be a teeny bit inconvenient as it will prevent
 expression-style programming without having to put typedefs beforehand.

 in STL C++:

     #include <list>
     #include <algorithm>

     list<int> intlist;
     sort(intlist.begin(), intlist.end(), less<int>());

 in D:  something like

     import list;
     import algorithm;

     typedef list{int} list_of_int;
     list_of_int intlist;

     typedef less{int} less_for_int;
     typedef sort{int, list_of_int.iterator_type, less_for_int}
sort_routine;
     sort_routine(intlist.begin(), intlist.end(), less_for_int());

 As you can see, it's not nearly as elegant when you have to typedef
 everything.  This is just one teensy example;  I've seen really cool stuff
 done this way:  Check out http://spirit.sourceforge.net for a cool example
 of the power of C++ templates when combined with operator overloading.

 Maybe you could make it work so that if you fully spell out the type it
can
 be instantiated in an expression.  I suppose this can be added later
without
 much problem, so if it turns out to be impractical it can be fixed later.
 Something like:

     import list;
     import algorithm;

     list{int} intlist;
     sort{int, list{int}.iterator_type, less{int}}(intlist.begin(),
 intlist.end());

 But in reality I'd rather the standard templated containers be safer than
 STL iterator-based containers, perhaps something that functions more like
a
 dynamic array?  It'll be tough to make a linked list behave like a dynamic
 array though.  The algorithmic performance characteristics and the
preferred
 usage patterns are completely different.

 Perhaps the dynamic array reallocation problem can be solved by making a
 dynamic array actually a container of pointers to subranges of the array.
 So if it needs expanded and the memory can't be resized in place, it just
 allocates a new block and doesn't copy the old one.  That would make
dynamic
 array access have to go through one more level of indirection and you
 couldn't typecast it to a pointer to a flat array anymore (maybe a
function
 to "flatten" a dynamic array would be good) but it would solve most of the
 "for (ever) myarray ~= asingleentry;" problem.
Jul 05 2002
next sibling parent reply "Juan Carlos Arevalo Baeza" <jcab roningames.com> writes:
"Walter" <walter digitalmars.com> wrote in message
news:ag5bvm$227j$1 digitaldaemon.com...

 As you can see, it's not nearly as elegant when you have to typedef
 everything.
Yes I know it'll be more typing. Perhaps I can find a way to reduce that without doing the implicit instantiation two-step. There's another
maddening
 example of the implict instantiation resulting in bizarre rules - the way
 dependent and non-dependent names are looked up. The non-dependent names
are
 looked up in an earlier version of the global symbol table than the
 dependent names are.
Yes, the non-dependent names are looked up at the point where the template is defined, and the dependent names are looked up at the point the template is instantiated. But how could this be different? Unless you enforce the explicit enumeration of all allowed instances at the point where the template is defined, or somehow store a snapshot of the symbol tables at the point the template was defined, you still need to match names at the point of instantiation. And those alternatives do not sound reasonable to me.
 Perhaps this makes sense to someone, but to me it's
 just a way to torture compiler writers <g>.
What alternative could there be? Looking up _all_ names at the point of instantiation? I'm really curious. Or am I missing something here? Salutaciones, JCAB
Jul 05 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Juan Carlos Arevalo Baeza" <jcab roningames.com> wrote in message
news:ag5j9q$29lv$1 digitaldaemon.com...
    Yes, the non-dependent names are looked up at the point where the
 template is defined, and the dependent names are looked up at the point
the
 template is instantiated. But how could this be different? Unless you
 enforce the explicit enumeration of all allowed instances at the point
where
 the template is defined, or somehow store a snapshot of the symbol tables
at
 the point the template was defined, you still need to match names at the
 point of instantiation. And those alternatives do not sound reasonable to
 me.
 Perhaps this makes sense to someone, but to me it's
 just a way to torture compiler writers <g>.
What alternative could there be? Looking up _all_ names at the point of instantiation? I'm really curious. Or am I missing something here?
The DMC++ compiler collects the template definition as a list of tokens, and then inserts it into the source at the point of instantiation. So, all symbol table lookups are done at the point of instantiation. I believe that is a much more understandable rule (easy to explain) than the confusing and subtle difference in behavior between dependent and non-dependent names.
Jul 07 2002
next sibling parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
I don't believe the scoping rules would work with that scheme, would it?
Like treating an inline function as a pure macro expansion, it just doesn't
have the right semantics.

Sean

"Walter" <walter digitalmars.com> wrote in message
news:agajj6$1h6g$1 digitaldaemon.com...
 "Juan Carlos Arevalo Baeza" <jcab roningames.com> wrote in message
 news:ag5j9q$29lv$1 digitaldaemon.com...
    Yes, the non-dependent names are looked up at the point where the
 template is defined, and the dependent names are looked up at the point
the
 template is instantiated. But how could this be different? Unless you
 enforce the explicit enumeration of all allowed instances at the point
where
 the template is defined, or somehow store a snapshot of the symbol
tables
 at
 the point the template was defined, you still need to match names at the
 point of instantiation. And those alternatives do not sound reasonable
to
 me.
 Perhaps this makes sense to someone, but to me it's
 just a way to torture compiler writers <g>.
What alternative could there be? Looking up _all_ names at the point
of
 instantiation? I'm really curious.
    Or am I missing something here?
The DMC++ compiler collects the template definition as a list of tokens,
and
 then inserts it into the source at the point of instantiation. So, all
 symbol table lookups are done at the point of instantiation. I believe
that
 is a much more understandable rule (easy to explain) than the confusing
and
 subtle difference in behavior between dependent and non-dependent names.
Jul 08 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
news:agbegl$2bll$1 digitaldaemon.com...
 I don't believe the scoping rules would work with that scheme, would it?
 Like treating an inline function as a pure macro expansion, it just
doesn't
 have the right semantics.
I neglected to mention that only the global namespace and the current namespace(s) are included in the instantiation symbol lookups.
Jul 08 2002
parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
Seems like it should only have access to:

1) The symbols available at the point of template declaration
2) The symbols injected into the template as template parameters
3) Symbols local to the template

Sean

"Walter" <walter digitalmars.com> wrote in message
news:agcei2$aln$2 digitaldaemon.com...
 "Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
 news:agbegl$2bll$1 digitaldaemon.com...
 I don't believe the scoping rules would work with that scheme, would it?
 Like treating an inline function as a pure macro expansion, it just
doesn't
 have the right semantics.
I neglected to mention that only the global namespace and the current namespace(s) are included in the instantiation symbol lookups.
Jul 08 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
news:agclqa$iaq$1 digitaldaemon.com...
 Seems like it should only have access to:

 1) The symbols available at the point of template declaration
 2) The symbols injected into the template as template parameters
 3) Symbols local to the template

 Sean
To me either the symbols from the point of declaration or the point of instantiation, not a mish-mash of both.
Jul 09 2002
next sibling parent reply Patrick Down <pat codemoon.com> writes:
"Walter" <walter digitalmars.com> wrote in news:age1p7$1vei$2
 digitaldaemon.com:

 
 "Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
 news:agclqa$iaq$1 digitaldaemon.com...
 Seems like it should only have access to:

 1) The symbols available at the point of template declaration
 2) The symbols injected into the template as template parameters
 3) Symbols local to the template

 Sean
To me either the symbols from the point of declaration or the point of instantiation, not a mish-mash of both.
Symbols from the point of declaration whould fit the function model best. In a function I can use values passed in as parameters, values in my current global namespace or object namespace at the point of definition. However taking symbols from the point of instantiation could be quite interesting. Could a function declared outside of an object be used as a member function? I don't know what our template syntax will be so I'll just make it up. :-) template<> char[] getName() { return name; } class foo { char[] name; } class bar { char[] name; } a = new foo; b = new bar; a.getName(); b.getName(); Or, better yet, if you require explicit instantiation. class foo { char[] name; templatedef getName(); // foo now has GetName member; }
Jul 09 2002
parent reply "Walter" <walter digitalmars.com> writes:
Hmm. I don't think that should work!

"Patrick Down" <pat codemoon.com> wrote in message
news:Xns9246524F06ABDpatcodemooncom 63.105.9.61...
 "Walter" <walter digitalmars.com> wrote in news:age1p7$1vei$2
  digitaldaemon.com:

 "Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
 news:agclqa$iaq$1 digitaldaemon.com...
 Seems like it should only have access to:

 1) The symbols available at the point of template declaration
 2) The symbols injected into the template as template parameters
 3) Symbols local to the template

 Sean
To me either the symbols from the point of declaration or the point of instantiation, not a mish-mash of both.
Symbols from the point of declaration whould fit the function model best. In a function I can use values passed in as parameters, values in my current global namespace or object namespace at the point of definition. However taking symbols from the point of instantiation could be quite interesting. Could a function declared outside of an object be used as a member function? I don't know what our template syntax will be so I'll just make it up. :-) template<> char[] getName() { return name; } class foo { char[] name; } class bar { char[] name; } a = new foo; b = new bar; a.getName(); b.getName(); Or, better yet, if you require explicit instantiation. class foo { char[] name; templatedef getName(); // foo now has GetName member; }
Jul 09 2002
next sibling parent Patrick Down <pat codemoon.com> writes:
"Walter" <walter digitalmars.com> wrote in
news:agf4b6$ho7$2 digitaldaemon.com: 

 Hmm. I don't think that should work!
 
 "Patrick Down" <pat codemoon.com> wrote in message
 news:Xns9246524F06ABDpatcodemooncom 63.105.9.61...
 "Walter" <walter digitalmars.com> wrote in news:age1p7$1vei$2
  digitaldaemon.com:


 However taking symbols from the point of instantiation could be quite
 interesting.  Could a function declared outside of an object be used
 as a member function?

 I don't know what our template syntax will be so I'll just
 make it up. :-)

 template<>
 char[] getName()
 {
   return name;
 }

 class foo { char[] name; }
 class bar { char[] name; }

 a = new foo;
 b = new bar;

 a.getName();
 b.getName();
Well even I will admit that the attaching a template function like above is pretty silly.
 Or, better yet, if you require explicit instantiation.

 class foo
 {
   char[] name;

   templatedef getName();  // foo now has GetName member;
 }
However the explicit template instantiation above has some merit I think. It's a way to implement mixins.
Jul 09 2002
prev sibling parent reply "Juan Carlos Arevalo Baeza" <jcab roningames.com> writes:
"Walter" <walter digitalmars.com> wrote in message
news:agf4b6$ho7$2 digitaldaemon.com...

 "Patrick Down" <pat codemoon.com> wrote in message
 news:Xns9246524F06ABDpatcodemooncom 63.105.9.61...

 I don't know what our template syntax will be so I'll just
 make it up. :-)

 template<>
 char[] getName()
 {
   return name;
 }

 ...
Hmm. I don't think that should work!
template< class T > char[] T::getName() { return name; } As far as expressivity is concerned, this type of thing should be doable. It's definitely no more ambiguous than operator overloading in C++. Salutaciones, JCAB
Jul 09 2002
parent "Sean L. Palmer" <seanpalmer earthlink.net> writes:
And if T didn't have a name member, it would be an error to try to
instantiate getName().  No harm done though.  That's probably exactly the
desired behavior.

Sean

"Juan Carlos Arevalo Baeza" <jcab roningames.com> wrote in message
news:agfebn$r98$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:agf4b6$ho7$2 digitaldaemon.com...

 "Patrick Down" <pat codemoon.com> wrote in message
 news:Xns9246524F06ABDpatcodemooncom 63.105.9.61...

 I don't know what our template syntax will be so I'll just
 make it up. :-)

 template<>
 char[] getName()
 {
   return name;
 }

 ...
Hmm. I don't think that should work!
template< class T > char[] T::getName() { return name; } As far as expressivity is concerned, this type of thing should be
doable.
 It's definitely no more ambiguous than operator overloading in C++.

 Salutaciones,
                          JCAB
Jul 09 2002
prev sibling parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
Then I would pick the point of declaration;  however you still need to be
able to "inject" symbols via template parameters that aren't otherwise
available at the point of template declaration.

The template writer has no idea what symbols will be available at point of
instantiation and cannot rely on having all its needed symbols present.  So
point of instantiation seems like a bad choice.

Sean

"Walter" <walter digitalmars.com> wrote in message
news:age1p7$1vei$2 digitaldaemon.com...
 "Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
 news:agclqa$iaq$1 digitaldaemon.com...
 Seems like it should only have access to:

 1) The symbols available at the point of template declaration
 2) The symbols injected into the template as template parameters
 3) Symbols local to the template

 Sean
To me either the symbols from the point of declaration or the point of instantiation, not a mish-mash of both.
Jul 09 2002
parent reply "Juan Carlos Arevalo Baeza" <jcab roningames.com> writes:
"Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
news:agf4m8$i7u$1 digitaldaemon.com...

 "Walter" <walter digitalmars.com> wrote in message
 news:age1p7$1vei$2 digitaldaemon.com...
 To me either the symbols from the point of declaration or the point of
 instantiation, not a mish-mash of both.
Then I would pick the point of declaration; however you still need to be able to "inject" symbols via template parameters that aren't otherwise available at the point of template declaration. The template writer has no idea what symbols will be available at point of instantiation and cannot rely on having all its needed symbols present.
So
 point of instantiation seems like a bad choice.
Hmmm... I'm thinking... This would not be a problem is the following applies: - The language is structurally unambiguous. So you know what is a function call, a declaration, etc... without doing symbol lookup. - The overloading resolution is done at link time. This would mean the syntax would have to differ from C/C++ more significantly, and a big portion of the code generation would move to the linker. Not for D, I suppose, but interesting nonetheless. Salutaciones, JCAB
Jul 09 2002
parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
"Juan Carlos Arevalo Baeza" <jcab roningames.com> wrote in message
news:agfek2$roc$1 digitaldaemon.com...
    Hmmm... I'm thinking...

    This would not be a problem is the following applies:

 - The language is structurally unambiguous. So you know what is a function
 call, a declaration, etc... without doing symbol lookup.
 - The overloading resolution is done at link time.

    This would mean the syntax would have to differ from C/C++ more
 significantly, and a big portion of the code generation would move to the
 linker. Not for D, I suppose, but interesting nonetheless.

 Salutaciones,
                          JCAB
Exactly. Move most of the actual code generation to *after* linking. Some optimization could be done on the parse tree ahead of time, but the brunt should be done after link, when everything aside from dynamic linkage is execution time. That's not a bad idea either but it requires more runtime support, and could take a little time, which is bad for many classes of apps. At least it should only have to be done once per app per machine. I wonder if anyone has really taken a step back from the lex/parse/semantic/codegen/link design we've had for several decades and tried to design a better way? Surely some improvements could be made. Sean
Jul 09 2002
parent "Juan Carlos Arevalo Baeza" <jcab roningames.com> writes:
"Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
news:agghgk$1uis$1 digitaldaemon.com...

 "Juan Carlos Arevalo Baeza" <jcab roningames.com> wrote in message
 news:agfek2$roc$1 digitaldaemon.com...

    This would mean the syntax would have to differ from C/C++ more
 significantly, and a big portion of the code generation would move to
the
 linker. Not for D, I suppose, but interesting nonetheless.
Exactly. Move most of the actual code generation to *after* linking.
Some
 optimization could be done on the parse tree ahead of time, but the brunt
 should be done after link, when everything aside from dynamic linkage is
 known.
Interestingly, this would make the "export" support in C++ trivial...

 execution time.  That's not a bad idea either but it requires more runtime
 support, and could take a little time, which is bad for many classes of
 apps.  At least it should only have to be done once per app per machine.
Yes, their JIT system is actually very impressive, but a bit overkill for some cases. You'd never want to use something like that on a Playstation 2...
 I wonder if anyone has really taken a step back from the
 lex/parse/semantic/codegen/link design we've had for several decades and
 tried to design a better way?  Surely some improvements could be made.
I'm experimenting along those lines... :) Salutaciones, JCAB
Jul 10 2002
prev sibling parent reply "Juan Carlos Arevalo Baeza" <jcab roningames.com> writes:
"Walter" <walter digitalmars.com> wrote in message
news:agajj6$1h6g$1 digitaldaemon.com...

 "Juan Carlos Arevalo Baeza" <jcab roningames.com> wrote in message
 news:ag5j9q$29lv$1 digitaldaemon.com...

    What alternative could there be? Looking up _all_ names at the point
of
 instantiation? I'm really curious.
    Or am I missing something here?
The DMC++ compiler collects the template definition as a list of tokens,
and
 then inserts it into the source at the point of instantiation. So, all
 symbol table lookups are done at the point of instantiation. I believe
that
 is a much more understandable rule (easy to explain) than the confusing
and
 subtle difference in behavior between dependent and non-dependent names.
It's simpler to explain, but it differs from the normal behavior of lookups. As in: --- void func(int) {} template < class T > void tfunc() { func('a'); } void func(char) {} int main() { tfunc<float>(); // calls func(char) return 0; } --- where if tfunc() is made non-template, it'll call func(int) instead. The distinction between dependent and non-dependent names is meant to lessen that difference. That makes it quite obviously good, IMHO. It does make the compiler harder to make, though, I'll agree to that :) Your solution is elegantly simple. I assume you'll make it "the law" for D's generics when it comes to that. Salutaciones, JCAB
Jul 08 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Juan Carlos Arevalo Baeza" <jcab roningames.com> wrote in message
news:agcmvq$jjf$1 digitaldaemon.com...
    It does make the compiler harder to make, though, I'll agree to that :)
 Your solution is elegantly simple. I assume you'll make it "the law" for
D's
 generics when it comes to that.
D does it one better. The symbol table is generated before the templates are processed, so all the symbols in the file are available, not just the ones lexically preceding it.
Jul 09 2002
next sibling parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
sweet

but hopefully there's some rule about what scope the template instantiation
uses

Perhaps it can first try to use the scope of its declaration

and if that fails, try the scope of the point of instantiation

Sean

"Walter" <walter digitalmars.com> wrote in message
news:age66h$24qt$1 digitaldaemon.com...
 "Juan Carlos Arevalo Baeza" <jcab roningames.com> wrote in message
 news:agcmvq$jjf$1 digitaldaemon.com...
    It does make the compiler harder to make, though, I'll agree to that
:)
 Your solution is elegantly simple. I assume you'll make it "the law" for
D's
 generics when it comes to that.
D does it one better. The symbol table is generated before the templates
are
 processed, so all the symbols in the file are available, not just the ones
 lexically preceding it.
Jul 09 2002
parent "Walter" <walter digitalmars.com> writes:
Yeah, that will require some careful thought. -Walter

"Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
news:agf4ib$i2h$1 digitaldaemon.com...
 sweet

 but hopefully there's some rule about what scope the template
instantiation
 uses

 Perhaps it can first try to use the scope of its declaration

 and if that fails, try the scope of the point of instantiation

 Sean
Jul 09 2002
prev sibling parent "Juan Carlos Arevalo Baeza" <jcab roningames.com> writes:
"Walter" <walter digitalmars.com> wrote in message
news:age66h$24qt$1 digitaldaemon.com...

 D does it one better. The symbol table is generated before the templates
are
 processed, so all the symbols in the file are available, not just the ones
 lexically preceding it.
Right! That makes it substantially better. Still see a problem with templates exported from a module and used in several other modules. Ironically, C/C++'s system of using textual include files for module handling makes this point pretty much moot. Personally, I don't think there's a perfect solution, no matter how you do it. Salutaciones, JCAB
Jul 09 2002
prev sibling parent "Sean L. Palmer" <seanpalmer earthlink.net> writes:
Yeah, it seems difficult.  I never implemented generics before.  You're like
15 steps ahead of me there.  ;)

Sean

"Walter" <walter digitalmars.com> wrote in message
news:ag5bvm$227j$1 digitaldaemon.com...
 Yes I know it'll be more typing. Perhaps I can find a way to reduce that
 without doing the implicit instantiation two-step. There's another
maddening
 example of the implict instantiation resulting in bizarre rules - the way
 dependent and non-dependent names are looked up. The non-dependent names
are
 looked up in an earlier version of the global symbol table than the
 dependent names are. Perhaps this makes sense to someone, but to me it's
 just a way to torture compiler writers <g>.
Jul 07 2002
prev sibling next sibling parent "Nic Tiger" <nictiger pt.comcor.ru> writes:
"Burton Radons" <loth users.sourceforge.net> wrote in message
news:3D17D5CE.3040608 users.sourceforge.net...
 Craig Black wrote:
 Then you could rewrite the Add function:

 template <typename T : NumericTypes>
 T Add(T a, T b)
 {
    return a + b;
 }

 template <typename T : ContainerTypes>
 T Add(T a, T b)
 {
    T result;
    result.append(a);
    result.append(b);
    return result;
 }
That could easily become, using my generics methodology: $T Add ($T a, $T b) { if ($T.has_field ("length")) return a ~ b; else return a + b; } So I think you introduced a good idea - cswitch/cif/celse - and then didn't use it right when it would work great. Can you produce an example of code where typelist is necessary?
Imagine that Add for numerical and Add for containers are placed in different libraries and written by different authors. Then there is no way to unite then into one function you showed. Another example for using typelists is to restrict using template with some types. Of course, without using typelists you should get plenty of compiler errors when attempting to instantiate template for wrong type/class. Sometimes you can hardly understand what happenings. But I would like to have only one error: "type '...' is not suitable for template '..'". Nic Tiger.
Jun 25 2002
prev sibling parent reply Patrick Down <pat codemoon.com> writes:
Burton Radons <loth users.sourceforge.net> wrote in
news:3D17D5CE.3040608 users.sourceforge.net: 

 That could easily become, using my generics methodology:
 
      $T Add ($T a, $T b)
      {
          if ($T.has_field ("length"))
              return a ~ b;
          else
              return a + b;
      }
I generally like this form of generics. It works well for generic fuctions but what about generic types? A tree of int or char[]? I have a rather crazy idea. What if we treated "type" as a type? type IntType = int; IntType b = 3; One of the things you could do with this is create functions that dealt with types. Here is a function that builds a binary tree type. type BinaryTree(type T) { struct Tree { T data; Tree left; Tree right; }; return Tree; } Another idea I have is that design by contract can be extended to deal type in generics. void Func($T a) in { // Make sure $T has a cmp function with the // correct signature assert($T.cmp && $T.cmp.type == (int (*)($T,$T))); } body { //... } This could help with some of the really bad error messages you see with C++ templates.
 
 Smalltalk has also been tossed around here on
 occasion, but I don't think anyone's described it and I can't google
 any information about it.
There are more that one type of generic. Generic types. Generic functions. Generic algorthms. Smalltalk and Ruby are good languages to look at for how they do generic algorthms. Here is what I think people need to look at when doing generics. Take a look at the C++ STL. It is a good idea with a poor implementaion due to the the way C++ does templates. How can you do the same sorts of things in a way that is more understandable and easier to deal with?
Jun 25 2002
next sibling parent reply "Craig Black" <cblack ara.com> writes:
"Patrick Down" <pat codemoon.com> wrote in message
news:Xns92385BBDDAE20patcodemooncom 63.105.9.61...
 Burton Radons <loth users.sourceforge.net> wrote in
 news:3D17D5CE.3040608 users.sourceforge.net:

 That could easily become, using my generics methodology:

      $T Add ($T a, $T b)
      {
          if ($T.has_field ("length"))
              return a ~ b;
          else
              return a + b;
      }
I generally like this form of generics. It works well for generic fuctions but what about generic types? A tree of int or char[]? I have a rather crazy idea. What if we treated "type" as a type? type IntType = int;
Isn't this very similar to typedef or alias?
 IntType b = 3;

 One of the things you could do with this is create functions
 that dealt with types.  Here is a function that builds a
 binary tree type.

 type BinaryTree(type T)
 {
   struct Tree
   {
     T data;
     Tree left;
     Tree right;
   };

   return Tree;
 }
Things that make you go "Hmmm.....". Fundamentally type generics use "parametric types". You propose to introduce type syntax just as if it were a fundamental type. I'm not trying to shoot down this idea, but is there any reason do this? What then is the difference between the above and a template like this: struct Tree<$T> { $T data; Tree left; Tree right; };
 Another idea I have is that design by contract can be extended to
 deal type in generics.

 void Func($T a)
 in
 {
   // Make sure $T has a cmp function with the
   // correct signature
   assert($T.cmp && $T.cmp.type == (int (*)($T,$T)));
 }
 body
 {
   //...
 }
That's a good idea. This would be a compile-time assertion. Perhaps we could have a "cassert" to denote this.
 This could help with some of the really bad error messages
 you see with C++ templates.

 Smalltalk has also been tossed around here on
 occasion, but I don't think anyone's described it and I can't google
 any information about it.
There are more that one type of generic. Generic types. Generic functions. Generic algorthms. Smalltalk and Ruby are good languages to look at for how they do generic algorthms. Here is what I think people need to look at when doing generics. Take a look at the C++ STL. It is a good idea with a poor implementaion due to the the way C++ does templates. How can you do the same sorts of things in a way that is more understandable and easier to deal with?
Yes STL is good. How can we achieve the same functionality and performance with more understandable syntax and less code? More readability and greater genericity.
Jun 25 2002
parent reply Patrick Down <pat codemoon.com> writes:
"Craig Black" <cblack ara.com> wrote in
news:afa0ae$pm0$1 digitaldaemon.com: 

 "Patrick Down" <pat codemoon.com> wrote in message
 news:Xns92385BBDDAE20patcodemooncom 63.105.9.61...
 Burton Radons <loth users.sourceforge.net> wrote in
 news:3D17D5CE.3040608 users.sourceforge.net:

 That could easily become, using my generics methodology:

      $T Add ($T a, $T b)
      {
          if ($T.has_field ("length"))
              return a ~ b;
          else
              return a + b;
      }
I generally like this form of generics. It works well for generic fuctions but what about generic types? A tree of int or char[]? I have a rather crazy idea. What if we treated "type" as a type? type IntType = int;
Isn't this very similar to typedef or alias?
Yes it is. But it's not all that weard C++ was forced to create a similar concept. template<class T> class X { typename T::Y; // treat Y as a type Y m_y; };
 
 Fundamentally type generics use "parametric types".  You propose to
 introduce type syntax just as if it were a fundamental type.  I'm not
 trying to shoot down this idea, but is there any reason do this?  
Conditional data structure building, although I can't think of a good example right now. So I'll give a really bogas one. type BinaryTree(type T) { if(T == int) { struct Tree { T data; Tree left; Tree right; } return Tree; } else { struct Tree { T data; int key; Tree left; Tree right; } return Tree; } }
 Yes STL is good.  How can we achieve the same functionality and
 performance with more understandable syntax and less code?  More
 readability and greater genericity.
Yes, with some care. One of the problems I think C++ templates has is that they use the same syntax to try to solve a a bunch of unrelated problems.
Jun 25 2002
next sibling parent Patrick Down <pat codemoon.com> writes:
 Yes, with some care.  One of the problems I think C++ templates has
 is that they use the same syntax to try to solve a a bunch of unrelated
 problems. 
Sorry that was't very clear. I mean that C++ uses the template syntax where other solutions might be more appropriate.
Jun 25 2002
prev sibling parent reply "Craig Black" <cblack ara.com> writes:
 Conditional data structure building, although I can't think
 of a good example right now.  So I'll give a really bogas one.

 type BinaryTree(type T)
 {
   if(T == int)
   {
     struct Tree
     {
       T data;
       Tree left;
       Tree right;
     }

     return Tree;
   }
   else
   {
     struct Tree
     {
       T data;
       int  key;
       Tree left;
       Tree right;
     }

     return Tree;
   }
 }
How about allowing compile-time conditionals outside of function context: struct BinaryTree<$T> { T data; cif ($T != int) { int key; } Tree left; Tree right; };
Jun 25 2002
next sibling parent "Sean L. Palmer" <seanpalmer earthlink.net> writes:
That's *very* close to the functionality of the current 'version' construct.

Sean

"Craig Black" <cblack ara.com> wrote in message
news:afaljg$2t64$1 digitaldaemon.com...
 Conditional data structure building, although I can't think
 of a good example right now.  So I'll give a really bogas one.

 type BinaryTree(type T)
 {
   if(T == int)
   {
     struct Tree
     {
       T data;
       Tree left;
       Tree right;
     }

     return Tree;
   }
   else
   {
     struct Tree
     {
       T data;
       int  key;
       Tree left;
       Tree right;
     }

     return Tree;
   }
 }
How about allowing compile-time conditionals outside of function context: struct BinaryTree<$T> { T data; cif ($T != int) { int key; } Tree left; Tree right; };
Jun 26 2002
prev sibling parent reply C.R.Chafer <blackmarlin nospam.asean-mail.com> writes:
Craig Black wrote:

 How about allowing compile-time conditionals outside of function context:
 
 struct BinaryTree<$T>
 {
       T data;
       cif ($T != int) { int  key; }
       Tree left;
       Tree right;
 };
Ignoring whether or not this is a good idea for the moment, I believe the syntax could be improved. Instead of using many new keywords (cif, cswitch) we could use a single keyword to inform the compiler the statement should be resolved at compile time (it being an error if the statement is not resolved). So your example would become: struct BinaryTree<$T> { T data; compileTime if ($T != int) { int key; } Tree left; Tree right; }; struct BinaryTree<$T> { T data; if( resolve( $T != int ) ) { int key; } Tree left; Tree right; }; (actual keywords used are debateable) as mandating constant folding. C 2002/5/26
Jun 26 2002
parent reply "cblack01" <cblack01 cox.net> writes:
Whichever is easiest to implement and give us the functionality we need.
I'm not stuck on any particular keyword here.  In this case, we could just
use the if keyword.  Since its outside function context, we can assume it
will be resolved at compile time.  In this case there's really no way for
the compiler NOT to resolve this conditional at compile time, unless we are
dealing with an interpreted or JITed language.  This kind of conditional
would sure help me with the C++ templates that I am maintaining right now.

struct BinaryTree<$T>
{
      T data;
      if ($T != int) { int  key; }
      Tree left;
      Tree right;
};
Jun 26 2002
parent C.R.Chafer <blackmarlin nospam.asean-mail.com> writes:
cblack01 wrote:

 Whichever is easiest to implement and give us the functionality we need.
And we want something which is simple to type, easy to figure out the meaning of, consistant with the rest of the language and an effective solution - C++ seams to just go for easy to implement and results in impossible to read syntax (though this may be a by product of the committe design process), we know D can do better.
 I'm not stuck on any particular keyword here.  In this case, we could just
 use the if keyword.  Since its outside function context, we can assume it
 will be resolved at compile time.
Probably, and that is what D does implicitly - adding a keyword would make resolving explicit. In your example it would need to be resolved however if we had a similar construct within a function then some keyword would be needed to ensure, for example... int aFunc($T t) { if( $T == MyClass ) { ... do someting ... } else { ... do something else ... } }
 In this case there's really no way for
 the compiler NOT to resolve this conditional at compile time, unless we
 are dealing with an interpreted or JITed language.
Or if the types compared are classes - then RTTI could come into play, which is not what we want in the above example.
  This kind of conditional
 would sure help me with the C++ templates that I am maintaining right now.
Yes, though D version statements may be used to achieve a similar effect. C 2002/6/26
Jun 26 2002
prev sibling parent reply "OddesE" <OddesE_XYZ hotmail.com> writes:
"Patrick Down" <pat codemoon.com> wrote in message
news:Xns92385BBDDAE20patcodemooncom 63.105.9.61...
<SNIP>
 I have a rather crazy idea.  What if we treated "type" as a
 type?

 type IntType = int;

 IntType b = 3;

 One of the things you could do with this is create functions
 that dealt with types.  Here is a function that builds a
 binary tree type.

 type BinaryTree(type T)
 {
   struct Tree
   {
     T data;
     Tree left;
     Tree right;
   };

   return Tree;
 }
<SNIP>

I like this idea!
And it is not as crazy as you may think. In fact
I am pretty sure something like this is already
in Delphi. I think there it only works for class
types, however...Maybe Pavel can give us some
more insight to this, I think that he knows
Delphi pretty well.


--
Stijn
OddesE_XYZ hotmail.com
http://OddesE.cjb.net
_________________________________________________
Remove _XYZ from my address when replying by mail
Jun 26 2002
next sibling parent Pavel Minayev <evilone omen.ru> writes:
On Wed, 26 Jun 2002 15:27:45 +0200 "OddesE" <OddesE_XYZ hotmail.com> wrote:

 I like this idea!
 And it is not as crazy as you may think. In fact
 I am pretty sure something like this is already
 in Delphi. I think there it only works for class
 types, however...Maybe Pavel can give us some
 more insight to this, I think that he knows
 Delphi pretty well.
Well, I wouldn't say I know it very well, but I don't remember anything like this in Delphi.
Jun 26 2002
prev sibling parent reply "Alix Pexton" <Alix seven-point-star.co.uk> writes:
Ada has a type type, if memory serves, so does ecmsScript 2.0 (not a proper
standard jet, but it's in development).

I think the type type is an elegant solution that fits in with the rest of
D, much better than C++ style templates do...

The other solution I can imagine fitting in with D syntactically, is to
have a type that is a supertype of the primitives, structs etc, and
objects, a bit like a void* but without being a pointer. To my mind
however, it seems overly complicated.

-- 
Alix Pexton...

Webmaster, The D Journal
web: www.thedjournal.com
email:webmaster thedjournal.com
"The D journal, a work in progress..."

OddesE <OddesE_XYZ hotmail.com> wrote in article
<afcenf$1bka$1 digitaldaemon.com>...
 "Patrick Down" <pat codemoon.com> wrote in message
 news:Xns92385BBDDAE20patcodemooncom 63.105.9.61...
 <SNIP>
 I have a rather crazy idea.  What if we treated "type" as a
 type?
<SNIP>

 
 I like this idea!
 And it is not as crazy as you may think. In fact
 I am pretty sure something like this is already
 in Delphi. I think there it only works for class
 types, however...Maybe Pavel can give us some
 more insight to this, I think that he knows
 Delphi pretty well.
Jun 26 2002
next sibling parent reply Patrick Down <pat codemoon.com> writes:
"Alix Pexton" <Alix seven-point-star.co.uk> wrote in
news:01c21d38$e8680360$96497ad5 jpswm: 

 Ada has a type type, if memory serves, so does ecmsScript 2.0 (not a
 proper standard jet, but it's in development).
 
 I think the type type is an elegant solution that fits in with the
 rest of D, much better than C++ style templates do...
 
 The other solution I can imagine fitting in with D syntactically, is
 to have a type that is a supertype of the primitives, structs etc, and
 objects, a bit like a void* but without being a pointer. To my mind
 however, it seems overly complicated.
 
If you look in the D libary right now you will see that there is ( if I remember correctly ) a ClassInfo and a TypeInfo structure. ClassInfo describes user defined classes. TypeInfo is used more internally to hold certain functions related to basic types and to help implement dynamic arrays and such. It would seem that perhaps these two could be unified to make a standard type descriptor for all types, basic and user defined.
Jun 26 2002
next sibling parent reply Pavel Minayev <evilone omen.ru> writes:
On Wed, 26 Jun 2002 18:10:29 +0000 (UTC) Patrick Down <pat codemoon.com> wrote:

 If you look in the D libary right now you will see that there is
 ( if I remember correctly ) a ClassInfo and a TypeInfo structure.  
 ClassInfo describes user defined classes.  TypeInfo is used more 
 internally to hold certain functions related to basic types and 
 to help implement dynamic arrays and such.  It would seem that 
 perhaps these two could be unified to make a standard type descriptor 
 for all types, basic and user defined.
However, it's a run-time solution, as opposed to compile-time (and thus fast and easy to optimize) templates. Personally, I'd prefer to have both, but if I had to choose, I'd better have templates, because they allow to create a set of _fast_, mostly inline classes, encapsulating common algorithms (like STL).
Jun 27 2002
parent reply Patrick Down <pat codemoon.com> writes:
Pavel Minayev <evilone omen.ru> wrote in
news:CFN374344668740509 news.digitalmars.com: 

 On Wed, 26 Jun 2002 18:10:29 +0000 (UTC) Patrick Down
 <pat codemoon.com> wrote: 
 
 If you look in the D libary right now you will see that there is
 ( if I remember correctly ) a ClassInfo and a TypeInfo structure.  
 ClassInfo describes user defined classes.  TypeInfo is used more 
 internally to hold certain functions related to basic types and 
 to help implement dynamic arrays and such.  It would seem that 
 perhaps these two could be unified to make a standard type descriptor
 for all types, basic and user defined.
However, it's a run-time solution, as opposed to compile-time (and thus fast and easy to optimize) templates. Personally, I'd prefer to have both, but if I had to choose, I'd better have templates, because they allow to create a set of _fast_, mostly inline classes, encapsulating common algorithms (like STL).
I agree. I think what I've suggested in earlier posts could be a compile time solution. The suggestion I made above about ClassInfo and TypeInfo is a runtime solution but I think the runtime use of a "type" type is for reflection and RTTI. Most "type" expressions would be constant expressions. They could be folded at compile time to concrete types. All variable, parameter, and structure declarations must be reduced to a concrete type by the compiler or it's an error. The main problem with my syntax is that type functions have a similar syntax to normal functions. This makes it hard for the user and the compiler to tell the difference between the two. As suggested elsewhere this is probably a better syntax although I think we should find some other delimiters than < and >. Why not ()? struct SquareMatrix(type T,int size) { T[size][size] data; Matrix(T,size) Multipy(Matrix(T,size)) { } } Design by contract would be nice for this too. struct SquareMatrix(type T,int size) in { assert(isNumericType(T)); } body { T[size][size] data; Matrix(T,size) Multipy(Matrix(T,size) m) { } } However we want isNumericType to be a compile time function. Seems like this is getting back to having a macro language again.
Jun 27 2002
next sibling parent Pavel Minayev <evilone omen.ru> writes:
On Thu, 27 Jun 2002 12:21:19 +0000 (UTC) Patrick Down <pat codemoon.com> wrote:

 However we want isNumericType to be a compile
 time function.  Seems like this is getting back
 to having a macro language again.
It could be a property .isnumeric
Jun 27 2002
prev sibling parent reply "Craig Black" <cblack ara.com> writes:
 As suggested elsewhere this is probably a better syntax although
 I think we should find some other delimiters than < and >.
 Why not ()?
I think the purpose of not using parens for templates in C++ was so that you (and the compiler) would not confuse template parameters with constructor parameters. Parens are used for a lot of things and if we use them for everything, the syntax can get confusing.
Jun 27 2002
next sibling parent reply "Craig Black" <cblack ara.com> writes:
"Craig Black" <cblack ara.com> wrote in message
news:aff948$1joc$1 digitaldaemon.com...
 As suggested elsewhere this is probably a better syntax although
 I think we should find some other delimiters than < and >.
 Why not ()?
I think the purpose of not using parens for templates in C++ was so that
you
 (and the compiler) would not confuse template parameters with constructor
 parameters.  Parens are used for a lot of things and if we use them for
 everything, the syntax can get confusing.
Just to expound on this, let's say we have a class A, that has a constructor that takes an integer. Class A is also a template class which takes an integral template parameter. I don't know if D supports the same, but in C++ you can create an temporary instance of this class using the syntax A(n). Do you see the problem? I suppose the compiler could assume that the first set of parens refers to the template, and the second to the constructor. I guess this is just a matter of personal preference. A(n)(n) or A<n>(n)? Then again, D might not even allow the creation of temporaries this way. I dunno.
Jun 27 2002
next sibling parent Pavel Minayev <evilone omen.ru> writes:
On Thu, 27 Jun 2002 10:21:12 -0500 "Craig Black" <cblack ara.com> wrote:

 matter of personal preference.  A(n)(n) or A<n>(n)?  Then again, D might not
 even allow the creation of temporaries this way.  I dunno.
It doesn't. You always have to use operator new to create an object. There is no such thing as "temporary" in D.
Jun 27 2002
prev sibling parent "Juan Carlos Arevalo Baeza" <jcab roningames.com> writes:
"Craig Black" <cblack ara.com> wrote in message
news:affa1j$1qdt$1 digitaldaemon.com...

 I suppose the compiler could assume that the first set of parens refers to
 the template, and the second to the constructor.
Actually, if the name references a template class, using the first set of parens as constructor parameters wouldn't be an option, anyway. The problem would still exist with template functions, though. I see no harm in using something unambiguous and visible, like [[...]]: struct Array[[type T, int size]] { T theArray[size]; }; generic[[type T]] T Min(T v0, T v1); Salutaciones, JCAB
Jun 27 2002
prev sibling parent reply Patrick Down <pat codemoon.com> writes:
"Craig Black" <cblack ara.com> wrote in
news:aff948$1joc$1 digitaldaemon.com: 

 As suggested elsewhere this is probably a better syntax although
 I think we should find some other delimiters than < and >.
 Why not ()?
I think the purpose of not using parens for templates in C++ was so that you (and the compiler) would not confuse template parameters with constructor parameters. Parens are used for a lot of things and if we use them for everything, the syntax can get confusing.
Yes but they caused an equall number of problems in C++ with the >> operator.
Jun 27 2002
next sibling parent Pavel Minayev <evilone omen.ru> writes:
On Thu=2C 27 Jun 2002 16=3A15=3A56 +0000 =28UTC=29 Patrick Down
=3Cpat=40codemoon=2Ecom=3E wrote=3A

=3E Yes but they caused an equall number of problems in C++ with the =3E=3E
=3E operator=2E  

Well=2C then=2C use something else =3D=29
Maybe backticks=3F

=09template `type T` class tree { =2E=2E=2E }
=09template `type T` class deque { =2E=2E=2E }

=09tree`deque`int`` foo=3B
Jun 27 2002
prev sibling parent "Craig Black" <cblack ara.com> writes:
Hello,


way to represet variable parameters using the params keyword.  It works
something like this.

void print(params string [] strings) { ... }

print("Any", "number", "of", "parameters");

Brace yourselves.  This is a very advanced idea.  But I encourage you to
stretch your mind and think outside of the box.

We could extend this variable parameter concept to templates.  Observe.

class Tuple<params type $types[]>
{
   // We would have to provide some syntax to extract each type
   //  outside of function syntax.  The following is very sketchy.
  foreach (type T in $types)
  {

  }
};

I know it looks bad.  It's kinda like a C preprocessor macro combined with a

suggestions as to how to best express this.  Perhaps the "type" type
approach suggested earlier.  What is required is a syntax for conditional
structure building.

With the above generic you could have multiple return values:

Tuple<int, float> func();

In C++ you would have to define a template for each number of parameters
supported e.g. Tuple2, Tuple3, Tuple4, Tuple5, etc.  But shouldn't we be
able to define one template that can handle any number of parameters?

--Craig
Jul 02 2002
prev sibling parent "Walter" <walter digitalmars.com> writes:
"Patrick Down" <pat codemoon.com> wrote in message
news:Xns923986BCD9ED7patcodemooncom 63.105.9.61...
 If you look in the D libary right now you will see that there is
 ( if I remember correctly ) a ClassInfo and a TypeInfo structure.
 ClassInfo describes user defined classes.  TypeInfo is used more
 internally to hold certain functions related to basic types and
 to help implement dynamic arrays and such.  It would seem that
 perhaps these two could be unified to make a standard type descriptor
 for all types, basic and user defined.
TypeInfo certainly has promise for a more general type descriptor functionality.
Jul 10 2002
prev sibling parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
void  // only property is that it's a type, although you can't do *anything*
with it!
|
+---scalar
|   |
|   +---integral
|       symbolic
|       real
|       enumeration
+---aggregate
|   |
|   +---struct
|       class
|       union
|
+---pointer
|
+---array
    |
    +---fixedarray
        dynarray

Something like that.

I could really go for a "unknown type" keyword, but when there's more than
one you have to say which unknown type you mean.  And all usages have to
agree.

Sean

"Alix Pexton" <Alix seven-point-star.co.uk> wrote in message
news:01c21d38$e8680360$96497ad5 jpswm...
 Ada has a type type, if memory serves, so does ecmsScript 2.0 (not a
proper
 standard jet, but it's in development).

 I think the type type is an elegant solution that fits in with the rest of
 D, much better than C++ style templates do...

 The other solution I can imagine fitting in with D syntactically, is to
 have a type that is a supertype of the primitives, structs etc, and
 objects, a bit like a void* but without being a pointer. To my mind
 however, it seems overly complicated.

 --
 Alix Pexton...

 Webmaster, The D Journal
 web: www.thedjournal.com
 email:webmaster thedjournal.com
 "The D journal, a work in progress..."

 OddesE <OddesE_XYZ hotmail.com> wrote in article
 <afcenf$1bka$1 digitaldaemon.com>...
 "Patrick Down" <pat codemoon.com> wrote in message
 news:Xns92385BBDDAE20patcodemooncom 63.105.9.61...
 <SNIP>
 I have a rather crazy idea.  What if we treated "type" as a
 type?
<SNIP>

 I like this idea!
 And it is not as crazy as you may think. In fact
 I am pretty sure something like this is already
 in Delphi. I think there it only works for class
 types, however...Maybe Pavel can give us some
 more insight to this, I think that he knows
 Delphi pretty well.
Jun 27 2002
parent "OddesE" <OddesE_XYZ hotmail.com> writes:
I suggested something similar to this in the
thread 'Type categories and inheritance'
ab925u$20bq$1 digitaldaemon.com
It looked like this:

+-- Variable
     +-- void
     +-- Boolean
     |    +-- bit
     |
     +-- Character
     |    +-- char
     |    +-- wchar
     |
     +-- Number
     |    +-- imaginary
     |    +-- complex
     |    +-- Real
     |    |    +-- float
     |    |    +-- double
     |    |    +-- extended
     |    |
     |    +-- Integral
     |         +-- Signed
     |         |    +-- byte
     |         |    +-- short
     |         |    +-- int
     |         |    +-- long
     |         |    +-- cent
     |         |
     |         +-- Unsigned
     |              +-- ubyte
     |              +-- ushort
     |              +-- uint
     |              +-- ulong
     |              +-- ucent
     |
     +-- Pointer
     |    +-- void *
     |    +-- char *
     |    +-- etc, etc...
     |
     +-- Delegate
     |    +-- FunctionPointer
     |
     +-- Array
     |
     +-- Struct
     |    +-- Structs...
     |
     +-- Class
     |    +-- Object
     |         +-- Loads
     |         +-- Of
     |         +-- Classes...
     |
     +-- Interface
          +-- IUnknown
          |    +-- COM Interfaces...
          |
          +-- Other Interfaces...



it can convert any basic type to an Object,
so you can call methods on it:

int i = 10;
print (i.toString());

It even works with literals!:

print (10.toString());


To me this seems like a really awesome
feature, allowing you to easily write
functions that accept parameters of all types.


--
Stijn
OddesE_XYZ hotmail.com
http://OddesE.cjb.net
_________________________________________________
Remove _XYZ from my address when replying by mail
Jun 27 2002
prev sibling next sibling parent "anderson" <anderson firestar.com.au> writes:
You'll have to wait a few days for Walters return.

He informs us that he is working on templates at this very moment.  Well
probably only in his subconscious at present because he's on holidays.
We'll probably have to wait and see what he comes up with.  I'd be a good
idea to get any good ideas into the design though.
Jun 25 2002
prev sibling parent "Walter" <walter digitalmars.com> writes:
"Craig Black" <cblack ara.com> wrote in message
news:af7k2g$1f31$1 digitaldaemon.com...
 First of all, I would like to say that I like the direction that D is
going
 so far, and appreciate your innovative work on this new language.
Thanks!
 I know support for generics in D is probably still on the horizin, but
have
 you given it any serious thought?  We can definitely improve on C++
 templates, both in ease of implementation and flexibility.  Even before
you
 start on templates it would be good to have in mind at least a vague
concept
 of what you plan to do.  I use C++ templates aggressively, so I have some
 ideas about features to add and drop.
What I'm doing right now is finishing template support in the C++ compiler. That gives me a credible foundation to do a better job in D.
 First, if you haven't read Modern C++ Design: Generic Programming and
Design
 Patterns Applied by Andrei Alexandrescu, you should get a copy.  A great
 book!  I have already applied many of the concepts he covers.  It should
be
 required reading for anyone who wants in depth understand of Generic
 Programming.
Ok, I'll check it out. Though if it advocates the STL design, I'm afraid I'm skeptical.
 I have so many ideas and could write forever about generics, here's just
 some food for thought.  If you're interested, I would be glad to share
more
 ideas.
Fire away!
 I will just give you a few insights of where you can improve on C++.
First,
 we generic programmers need a compile-time typeof operator.  I have run
into
 so many cases where I said to myself, "I sure wish I had a typeof
operator."
 It would make generic programming so much more elegant.  Then you could
have
 compile-time conditionals to handle different types in different ways,
like
 this:

 cswitch(typeof(variable))
 {
    case int:  // handle an integer
    case string: // hangle a string
    ...
 }

 Here "cswitch" is a compile-time conditional.  This means the conditional
is
 resolved at compile time, and optimized appropriately so that no
performance
 is lost and dead code is eliminated.  You could also have cif, celse, etc.
Ok, that's a good idea.
 Another idea I have to improve on C++ is type specialization syntax.  We
 could implement type specialization more explicitly using typelists.  This
 would provide a more readable type specialization that (I think) would be
 easier to implement.

 Here's a simple example:

 template <typename T : {int, uint, long, ulong, float, double}>
 T Add(T a, T b)
 {
    return a + b;
 }

 This specializes the Add function to need apply only to int, float,
double,
 and extended.  You could then write another Add template that would handle
 concatenation of container types.
I have some ideas on type specialization, the syntax for C++ is terrible (and trust me, it's really hard to write a compiler for!).
 This leads me to another feature that would be nice to have: type lists
(see
 chapter 3 of Modern C++ design.)  It would be nice to categorize types
 something like this:

 typelist NumericTypes { int, uint, long, ulong, float, double };
 typelist ContainerTypes { list, set, vector, queue, stack };

 Then you could rewrite the Add function:

 template <typename T : NumericTypes>
 T Add(T a, T b)
 {
    return a + b;
 }

 template <typename T : ContainerTypes>
 T Add(T a, T b)
 {
    T result;
    result.append(a);
    result.append(b);
    return result;
 }
Ok, that's another good idea.
Jul 03 2002