D - Serious deficiencies in template mechanism
- Norbert Nemec <nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> Jan 15 2003
- Norbert Nemec <nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> Jan 15 2003
- Daniel Yokomiso <Daniel_member pathlink.com> Jan 16 2003
- Theodore Reed <rizen surreality.us> Jan 15 2003
- "Walter" <walter digitalmars.com> Jan 15 2003
- Ilya Minkov <midiclub 8ung.at> Jan 15 2003
- Daniel Yokomiso <Daniel_member pathlink.com> Jan 16 2003
- "Walter" <walter digitalmars.com> Jan 18 2003
- Daniel Yokomiso <Daniel_member pathlink.com> Jan 16 2003
- Norbert Nemec <nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> Jan 17 2003
- "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> Jan 17 2003
- Norbert Nemec <Nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> Jan 18 2003
- "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> Jan 18 2003
- "Walter" <walter digitalmars.com> Feb 15 2003
- Marcelo Fontenele S Santos <msantos pobox.com> Feb 04 2003
- Burton Radons <loth users.sourceforge.net> Feb 04 2003
- Marcelo Fontenele S Santos <msantos pobox.com> Feb 05 2003
- "Walter" <walter digitalmars.com> Feb 15 2003
Hi there,
I've just recently come upon the D language and was captured right away by
it. Of all those programming languages I've found over the years, this
definitely is one of the most promising!
Anyway, the template mechanism has a number of deficiencies that makes it
drop far behind that of C++. To those, who have never worked with stuff
like the "expression template" method, this will look like minor details,
but after implementing my own matrix library in C++ using that method, I
definitely have come to realize the real power that lies behind the
template concept in C++.
Two examples of what is possible in C++, which I could not find in D:
-------------------------
* Templated functions - allowing stuff like:
template <typename T>
T special_addition(T a,T b) { ... }
i.e. a function that takes two arguments of an arbitrary -- but identical --
type. Of course, the concept goes far beyond that and is absolutely
indispensible in many cases.
-------------------------
* Integers as template arguments:
template <int N>
class Vector {
double data[N];
};
making Vector<2> a different type than Vector<3>, allowing stuff like direct
vector or matrix manipulations via operators with complete type checking.
(Going far beyond the simple possibilities of direct array manipulations in
D.)
-------------------------
The one thing that I dislike most about C++ templates, and which has been
carried over to D as well, is, that template parameters are not typed. I.e.
if I have:
template <typename T>
class Test {
static T add(T a,T b) { return a + b; } /* X */
};
bool foo() {
return Test<bool>::add(true,false); /* Y */
};
the compiler actually complains about line /*X*/ which is absolutely
correct, instead of telling the user that Test simply should not be
instatiated with bool. Now if Test is somewhere deep within a library, the
application programmer may get a huge bunch of error messages looking like
a bug somewhere in the library, even though, the actual error was simply
the wrong instatiation of a template.
Solution to this would be something like interfaces in D, specifying a set
of capabilities of a type (be it a class or a predefined type), and the
possibility to restrict parameters in a template definition to such an
interface.
-------------------------
Now, this was just a rough sketch of what I found to be missing in D making
it seriously fall back behind C++. It is nothing about philosophy or
far-fetched theory, but just plain experience after many hours of digging
into the depths of C++ templates.
Really bringing anything of this power into D would probably mean a redesign
of large parts of the template and interface mechanism. It would also mean
quite a bit of research and genious in language design. The mechanisms of
expression templates are still quite new and much work is yet to be done to
really understand the basic principles behind it.
Anyhow: unless this power is somehow carried over from C++, the language D
can not seriously aim at measuring up with its ancestor.
Anyone interested in that whole topic, best start reading at
http://www.oonumerics.org
perhaps starting out on blitz++ for the moment.
(I've done my own, smaller and simpler library, but it's hardly documented
at all, so it is not yet a good starting point...)
Ciao,
Nobbi
Jan 15 2003
I guess, my remark directly collides with the message "C and/or C++ in D" by
Ben Woodhead from yesterday, therefore an additional remark:
Everything I mentioned, is, of course somehow possible to be done in D. The
one crucial point simply is PERFORMANCE. What makes templates in C++ such a
powerful tool in C++ is not, that they allow stuff impossible before. They
do not even make life any more comfortable (quite in the contrary, they
make life awfully complicated!!) Anyhow:
They allow much of the work to be done at compile time!
I you don't want to worry about performance, simply ignore this whole topic.
Anyhow, in numerical computation, where performance often is one of the most
crucial goals, expression templates seem to be the only way so far to allow
the full expressiveness of object orientated languages without a huge
performance penalty. Thousands of programmers in the numerical field still
use Fortran 77 simply because of its unbeaten performance and it was not
until the discovery of expression templates, that C++ could close up with
that speed.
Anyhow: the suggestion of allowing to restrict template parameter types is
probably independant of the other extensions, and I would seriously advise
the language designers to consider this unless they want to make using
template libraries a pain to anyone.
Ciao,
Nobbi
Jan 15 2003
In article <b03ehf$2muu$1 digitaldaemon.com>, Norbert Nemec says...I guess, my remark directly collides with the message "C and/or C++ in D" by Ben Woodhead from yesterday, therefore an additional remark: Everything I mentioned, is, of course somehow possible to be done in D. The one crucial point simply is PERFORMANCE. What makes templates in C++ such a powerful tool in C++ is not, that they allow stuff impossible before. They do not even make life any more comfortable (quite in the contrary, they make life awfully complicated!!) Anyhow: They allow much of the work to be done at compile time! I you don't want to worry about performance, simply ignore this whole topic. Anyhow, in numerical computation, where performance often is one of the most crucial goals, expression templates seem to be the only way so far to allow the full expressiveness of object orientated languages without a huge performance penalty. Thousands of programmers in the numerical field still use Fortran 77 simply because of its unbeaten performance and it was not until the discovery of expression templates, that C++ could close up with that speed. Anyhow: the suggestion of allowing to restrict template parameter types is probably independant of the other extensions, and I would seriously advise the language designers to consider this unless they want to make using template libraries a pain to anyone. Ciao, Nobbi
Hi, Expression templates aren't the only way of achieving performance in "correct" OO systems. In a good type system we can do more agressive inlining of operations, reduce the need for dynamic dispatch, do strictness analysis to ignore partial unnecessary results, do loop unrolling, etc.. All of these are good in every piece of code, not just in some particular mechanism. The problem with expression templates is that they require someone (the library writer) to know how to write code with optimization hints. In C++ template syntax is almost a language of its own. In Common Lisp macro expansion (which can do anything expression templates do and more) gives you similar performance, but the syntax is much more similar to Common Lisp. Scheme also provides a expressive and simple to use macro system. Best regards, Daniel Yokomiso.
Jan 16 2003
Daniel Yokomiso wrote:Hi, Expression templates aren't the only way of achieving performance in "correct" OO systems. In a good type system we can do more agressive inlining of operations, reduce the need for dynamic dispatch, do strictness analysis to ignore partial unnecessary results, do loop unrolling, etc.. All of these are good in every piece of code, not just in some particular mechanism. The problem with expression templates is that they require someone (the library writer) to know how to write code with optimization hints. In C++ template syntax is almost a language of its own. In Common Lisp macro expansion (which can do anything expression templates do and more) gives you similar performance, but the syntax is much more similar to Common Lisp. Scheme also provides a expressive and simple to use macro system.
Very true. Al these parts have to play together. As far as I can see it, the main advantage of expression templates is, just as you say, that C++ templates are a language on their own, allowing a library author to do all kinds of optimizations and complex decisions at compile time. The only use for them is in heavily optimized libraries - otherwise the effort for writing them will never be justified. Anyhow, in the quest for performance one always has to consider all the bottlenecks, and in the main field of usage for expression templates of C++ - i.e. array calculations - one of the main bottlenecks is the need for temporary copies. And this can only be solved by either implementing far more advanced array manipulations in D directly or give the library authors the tools necessary to do their best. Ciao, Nobbi
Jan 17 2003
"Norbert Nemec" <nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> escreveu na mensagem news:b08ums$2p3a$1 digitaldaemon.com...Daniel Yokomiso wrote:Hi, Expression templates aren't the only way of achieving performance in "correct" OO systems. In a good type system we can do more agressive inlining of operations, reduce the need for dynamic dispatch, do strictness analysis to ignore partial unnecessary results, do loop unrolling, etc.. All of these are good in every piece of code, not just
some particular mechanism. The problem with expression templates is that they require someone (the library writer) to know how to write code with optimization hints. In C++ template syntax is almost a language of its own. In Common Lisp macro expansion (which can do anything expression templates do and more) gives you similar performance, but the syntax is much more similar to Common Lisp. Scheme also provides a expressive and simple to use macro system.
Very true. Al these parts have to play together. As far as I can see it,
main advantage of expression templates is, just as you say, that C++ templates are a language on their own, allowing a library author to do all kinds of optimizations and complex decisions at compile time. The only use for them is in heavily optimized libraries - otherwise the effort for writing them will never be justified. Anyhow, in the quest for performance one always has to consider all the bottlenecks, and in the main field of usage for expression templates of
- i.e. array calculations - one of the main bottlenecks is the need for temporary copies. And this can only be solved by either implementing far more advanced array manipulations in D directly or give the library
the tools necessary to do their best. Ciao, Nobbi
We can add more powerful array operations if they became safer: http://www.sys.uea.ac.uk/~jrwg/Sisal/ This may solve the problems Expression Templates try to solve... --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.443 / Virus Database: 248 - Release Date: 11/1/2003
Jan 17 2003
Daniel Yokomiso wrote:We can add more powerful array operations if they became safer: http://www.sys.uea.ac.uk/~jrwg/Sisal/ This may solve the problems Expression Templates try to solve...
You may also take a look at SAC (Single Assignment C) at http://www.informatik.uni-kiel.de/~sacbase/ which claims to the the official successor of Sisal. Anyway: Both languages are tailored towards high-performance numerical computing. No way to even consider taking D, which is explicitely designed to be a general purpose language in that direction. And just trying to pick bits from them will not buy us much.
Jan 20 2003
"Norbert Nemec" <nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> escreveu na mensagem news:b0h0gs$159b$1 digitaldaemon.com...Daniel Yokomiso wrote:We can add more powerful array operations if they became safer: http://www.sys.uea.ac.uk/~jrwg/Sisal/ This may solve the problems Expression Templates try to solve...
You may also take a look at SAC (Single Assignment C) at http://www.informatik.uni-kiel.de/~sacbase/ which claims to the the official successor of Sisal. Anyway: Both languages are tailored towards high-performance numerical computing. No way to even consider taking D, which is explicitely designed to be a general purpose language in that direction. And just trying to pick bits from them will not buy us much.
SAC is also very nice too. IMO their array semantics could be extended to D without changing much of D's nature as general purpose programming language. --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.443 / Virus Database: 248 - Release Date: 10/1/2003
Jan 20 2003
On Wed, 15 Jan 2003 11:34:14 +0100 Norbert Nemec <nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> wrote:* Templated functions - allowing stuff like: template <typename T> T special_addition(T a,T b) { ... } i.e. a function that takes two arguments of an arbitrary -- but identical -- type. Of course, the concept goes far beyond that and is absolutely indispensible in many cases.
I thought this was possible: template special (T) { T addition(T a, T b) { ... } } From what I under stand, there isn't anything you can't templatize, although your int example is interesting. I didn't even know C++ templates could do that. -- Theodore Reed (rizen/bancus) -==- http://www.surreality.us/ ~OpenPGP Signed/Encrypted Mail Preferred; Finger me for my public key!~ "Those who hammer their guns into plowshares will plow for those who do not." -- Thomas Jefferson
Jan 15 2003
Theodore Reed wrote:On Wed, 15 Jan 2003 11:34:14 +0100 Norbert Nemec <nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> wrote:* Templated functions - allowing stuff like: template <typename T> T special_addition(T a,T b) { ... } i.e. a function that takes two arguments of an arbitrary -- but identical -- type. Of course, the concept goes far beyond that and is absolutely indispensible in many cases.
I thought this was possible: template special (T) { T addition(T a, T b) { ... } } From what I under stand, there isn't anything you can't templatize, although your int example is interesting. I didn't even know C++ templates could do that.
OK, but as I understand it, this needs to be called like: x = special(int).addition(3,5); whereas in C++, a call to addition(3,5) does implicit instatiation. Don't know, though, whether that would be a fundamental problem.
Jan 17 2003
Comments embedded... "Norbert Nemec" <nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> wrote in message news:b03d7n$2m9r$1 digitaldaemon.com...Hi there, I've just recently come upon the D language and was captured right away by it. Of all those programming languages I've found over the years, this definitely is one of the most promising! Anyway, the template mechanism has a number of deficiencies that makes it drop far behind that of C++. To those, who have never worked with stuff like the "expression template" method, this will look like minor details, but after implementing my own matrix library in C++ using that method, I definitely have come to realize the real power that lies behind the template concept in C++. Two examples of what is possible in C++, which I could not find in D: ------------------------- * Templated functions - allowing stuff like: template <typename T> T special_addition(T a,T b) { ... } i.e. a function that takes two arguments of an arbitrary -- but
type. Of course, the concept goes far beyond that and is absolutely indispensible in many cases.
This does work in D, as Theodore's example shows.------------------------- * Integers as template arguments: template <int N> class Vector { double data[N]; }; making Vector<2> a different type than Vector<3>, allowing stuff like
vector or matrix manipulations via operators with complete type checking. (Going far beyond the simple possibilities of direct array manipulations
D.)
This is probably going to have to be added to D. Nobody seems to like the alternative mechanism I proposed <g>.------------------------- The one thing that I dislike most about C++ templates, and which has been carried over to D as well, is, that template parameters are not typed.
if I have: template <typename T> class Test { static T add(T a,T b) { return a + b; } /* X */ }; bool foo() { return Test<bool>::add(true,false); /* Y */ }; the compiler actually complains about line /*X*/ which is absolutely correct, instead of telling the user that Test simply should not be instatiated with bool. Now if Test is somewhere deep within a library, the application programmer may get a huge bunch of error messages looking like a bug somewhere in the library, even though, the actual error was simply the wrong instatiation of a template. Solution to this would be something like interfaces in D, specifying a set of capabilities of a type (be it a class or a predefined type), and the possibility to restrict parameters in a template definition to such an interface. -------------------------
A good point. Error messages are always a pain with templates.Now, this was just a rough sketch of what I found to be missing in D
it seriously fall back behind C++. It is nothing about philosophy or far-fetched theory, but just plain experience after many hours of digging into the depths of C++ templates. Really bringing anything of this power into D would probably mean a
of large parts of the template and interface mechanism. It would also mean quite a bit of research and genious in language design. The mechanisms of expression templates are still quite new and much work is yet to be done
really understand the basic principles behind it. Anyhow: unless this power is somehow carried over from C++, the language D can not seriously aim at measuring up with its ancestor. Anyone interested in that whole topic, best start reading at http://www.oonumerics.org perhaps starting out on blitz++ for the moment. (I've done my own, smaller and simpler library, but it's hardly documented at all, so it is not yet a good starting point...) Ciao, Nobbi
Jan 15 2003
This is probably going to have to be added to D. Nobody seems to like the alternative mechanism I proposed <g>.
Jan 15 2003
In article <b04fq8$8t8$1 digitaldaemon.com>, Walter says...
[snip]------------------------- * Integers as template arguments: template <int N> class Vector { double data[N]; }; making Vector<2> a different type than Vector<3>, allowing stuff like
vector or matrix manipulations via operators with complete type checking. (Going far beyond the simple possibilities of direct array manipulations
D.)
This is probably going to have to be added to D. Nobody seems to like the alternative mechanism I proposed <g>.
[snip] Hi, I'm glad you're considering adding this to the language. Which kind of value parameters will be possible: just integers or any kind of value? Also there's another problem with current template syntax. We can't define templates which define templatized methods. E.g. a List(T) class with a method List(U) map(U (*operation)(T)). This is a very good thing to have. C# generics proposal allows definition of generic classes with generic methods, and you have to instantiate the generic method when you use it, not when you instantiate the generic class. This, together with integer template parameters will allow D to have type-safe unit classes for scientific programming: // Using CGS unit system public template class Unit(int C, int G, int S) { private double _value; public this(double value) { this._value = value; } public double value() { return this._value; } public Unit(C, G, S) mul(int other) { return new Unit(C, G, S)(value() * other); } public template Unit(C - X, G - Y, S - Z) div(Unit(X, Y, Z) other) { return new Unit(C - X, G - Y, S - Z)(value() / other.value()); } } alias Unit(1, 0, 0) Length; alias Unit(0, 1, 0) Mass; alias Unit(0, 0, 1) Time; alias Unit(1, 0, -1) Speed; alias Unit(2, 1, 2) Energy; const Length metre = new Length(100); const Length second = new Time(1); const Length kilogram = new Mass(1000); const Speed c = 299792458 * metre / second; Mass m = 100 * kilogram; Energy e; e = m * c * c; Nice isn't it? Some issues about invalid instantiation can happen if the code uses variables to define dimensions, but I think limiting dimensions to be constant isn't a big problem. I don't think the compiler complexity is a burden when we compare it to the benefits it brings. Best regards, Daniel Yokomiso.
Jan 16 2003
"Daniel Yokomiso" <Daniel_member pathlink.com> wrote in message news:b06sg3$1m3l$1 digitaldaemon.com...I'm glad you're considering adding this to the language. Which kind of
parameters will be possible: just integers or any kind of value?
Probably just integer constants. I like to be conservative on adding these kinds of things until a real need is clear.Also there's another problem with current template syntax. We can't define templates which define templatized methods. E.g. a List(T) class with a
List(U) map(U (*operation)(T)). This is a very good thing to have.
I don't understand.C# generics proposal allows definition of generic classes with generic methods, and
to instantiate the generic method when you use it, not when you
generic class. This, together with integer template parameters will allow
have type-safe unit classes for scientific programming: // Using CGS unit system public template class Unit(int C, int G, int S) { private double _value; public this(double value) { this._value = value; } public double value() { return this._value; } public Unit(C, G, S) mul(int other) { return new Unit(C, G, S)(value() * other); } public template Unit(C - X, G - Y, S - Z) div(Unit(X, Y, Z) other) { return new Unit(C - X, G - Y, S - Z)(value() / other.value()); } } alias Unit(1, 0, 0) Length; alias Unit(0, 1, 0) Mass; alias Unit(0, 0, 1) Time; alias Unit(1, 0, -1) Speed; alias Unit(2, 1, 2) Energy; const Length metre = new Length(100); const Length second = new Time(1); const Length kilogram = new Mass(1000); const Speed c = 299792458 * metre / second; Mass m = 100 * kilogram; Energy e; e = m * c * c; Nice isn't it? Some issues about invalid instantiation can happen if the
uses variables to define dimensions, but I think limiting dimensions to be constant isn't a big problem. I don't think the compiler complexity is a
when we compare it to the benefits it brings. Best regards, Daniel Yokomiso.
Jan 18 2003
Walter wrote:"Daniel Yokomiso" <Daniel_member pathlink.com> wrote:I'm glad you're considering adding this to the language. Which kind of value parameters will be possible: just integers or any kind of value?
Probably just integer constants. I like to be conservative on adding these kinds of things until a real need is clear.
Hopefully bits and enums as well?
Jan 18 2003
"Walter" <walter digitalmars.com> escreveu na mensagem news:b0b9dm$165s$1 digitaldaemon.com..."Daniel Yokomiso" <Daniel_member pathlink.com> wrote in message news:b06sg3$1m3l$1 digitaldaemon.com...I'm glad you're considering adding this to the language. Which kind of
parameters will be possible: just integers or any kind of value?
Probably just integer constants. I like to be conservative on adding these kinds of things until a real need is clear.Also there's another problem with current template syntax. We can't
templates which define templatized methods. E.g. a List(T) class with a
List(U) map(U (*operation)(T)). This is a very good thing to have.
I don't understand.
We define a template for a generic list class: template TList(T) { class List { T first(); List rest(); T get(int index); void set(int index, T value); int length(); List slice(int start, int afterEnd); List filter(boolean (*predicate)(T)); } } With all needed operations. One idiom from functional programming is using a map operation to transverse data-structures using higher-order functions: Address getAddress(Employee emp) { return emp.address; } instance TList(Employee).List employees = ...; instance TList(Address).List addresses = employees.map(&getAddress); and map has a body that looks like (types ommited): map(*operation) { List result = new List(this.length()); for (int i = 0; i < this.length(); i++) { result.set(i, operation(get(i))); } return result; } If *operation is function that maps values of one type into another type. It's a higher-order function, that means a function that has other functions as parameters. But map must be generic beyond the scope of initial template instantiation, because we can instantiate a list template of ints and use mapping functions that gives lists of doubles (e.g. applying sqrt to all ints), lists of char[] (e.g. string forms of numbers) or even lists of ints (e.g. negated values). This application of functions that are templatized and introduce a second level of parametrization is useful in lots of different areas, a map function in a list type is one of the simplest form of using it. Today we have to write another template: template TMap(T, U) { instance TList(U).List ListU; instance TList(T).List ListT; ListU map(ListT list, U (*operation)(T)) { ListU result = new ListU(this.length()); for (int i = 0; i < list.length(); i++) { result.set(i, operation(list.get(i))); } return result; } } And users must always explicitly instantiate the templates and use tmap.map(list, op) instead of a simpler form list.map(op). There are functions that may be parametrized with more than one extra type parameter, so we end with lots of explicit templates. Also the additional templates must have a way to efficiently access the data from the list type, either using an external iterator, or something like that, where a generic method can access the internals of the class without penalty. As I said, C# and Java's generics proposal allows this kind of specialization. Any statically typed functional language has it too. In Java generic methods look like this: // snippet from last proposal class Seq<A> { <B> Seq<Pair<A,B>> zip(Seq<B> that) { if (this.isEmpty() || that.isEmpty()) { return new Seq<Pair<A,B>>(); } else { return new Seq<Pair<A,B>>( new Pair<A,B>(this.head, that.head), this.tail.<B>zip(that.tail)); } } } where Seq is a sequence type, and zip takes two sequences and return a sequence of pairs taken from each. I think we need this kind of functionality, or else we will be forced to written additional templates for each possible combination of parametrized methods. Also the trick I used in the last a email of making type-safe unit types (combining integer parameters and generic methods) won't be available :-( Best regards, Daniel Yokomiso. "Sometimes I think the surest sign that intelligent life exists elsewhere in the universe is that none of it has tried to contact us." - Bill Watterson --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.443 / Virus Database: 248 - Release Date: 10/1/2003
Jan 18 2003
Hi, Answers embedded. In article <b03d7n$2m9r$1 digitaldaemon.com>, Norbert Nemec says...Hi there, I've just recently come upon the D language and was captured right away by it. Of all those programming languages I've found over the years, this definitely is one of the most promising! Anyway, the template mechanism has a number of deficiencies that makes it drop far behind that of C++. To those, who have never worked with stuff like the "expression template" method, this will look like minor details, but after implementing my own matrix library in C++ using that method, I definitely have come to realize the real power that lies behind the template concept in C++. Two examples of what is possible in C++, which I could not find in D: ------------------------- * Templated functions - allowing stuff like: template <typename T> T special_addition(T a,T b) { ... } i.e. a function that takes two arguments of an arbitrary -- but identical -- type. Of course, the concept goes far beyond that and is absolutely indispensible in many cases.
As others pointed out this is possible in D: template TAddition(T) { T special_addition(T a,T b) { ... } } But we need to name the template.So usually D templates pack a set of related functions.------------------------- * Integers as template arguments: template <int N> class Vector { double data[N]; }; making Vector<2> a different type than Vector<3>, allowing stuff like direct vector or matrix manipulations via operators with complete type checking. (Going far beyond the simple possibilities of direct array manipulations in D.)
As Walter pointed out this will get in D. That is a Good Thing :-)------------------------- The one thing that I dislike most about C++ templates, and which has been carried over to D as well, is, that template parameters are not typed. I.e. if I have: template <typename T> class Test { static T add(T a,T b) { return a + b; } /* X */ }; bool foo() { return Test<bool>::add(true,false); /* Y */ }; the compiler actually complains about line /*X*/ which is absolutely correct, instead of telling the user that Test simply should not be instatiated with bool. Now if Test is somewhere deep within a library, the application programmer may get a huge bunch of error messages looking like a bug somewhere in the library, even though, the actual error was simply the wrong instatiation of a template. Solution to this would be something like interfaces in D, specifying a set of capabilities of a type (be it a class or a predefined type), and the possibility to restrict parameters in a template definition to such an interface. -------------------------
If you restrict yourself to classes, you can restrict the type parameters: template TSort(T : Comparable) { } where Comparable is a type or class. AFAIK it's valid D code, if not is should ;-) . There's some info at http://www.digitalmars.com/d/template.html under Argument Deduction section. I don't think there's any reason for the compiler to always complain about line /*X*/, instead of saying: Template instantiation at line /*Y*/ is invalid because of /*X*/ requirement in template body. Systems with type inference algorithms usually provide some intelligent error messages. There's also some papers about giving intelligent error messages in type inference algorithms.Now, this was just a rough sketch of what I found to be missing in D making it seriously fall back behind C++. It is nothing about philosophy or far-fetched theory, but just plain experience after many hours of digging into the depths of C++ templates. Really bringing anything of this power into D would probably mean a redesign of large parts of the template and interface mechanism. It would also mean quite a bit of research and genious in language design. The mechanisms of expression templates are still quite new and much work is yet to be done to really understand the basic principles behind it. Anyhow: unless this power is somehow carried over from C++, the language D can not seriously aim at measuring up with its ancestor.
IIRC D's ancestor is C. C++ is just an older cousin ;-)Anyone interested in that whole topic, best start reading at http://www.oonumerics.org perhaps starting out on blitz++ for the moment. (I've done my own, smaller and simpler library, but it's hardly documented at all, so it is not yet a good starting point...) Ciao, Nobbi
C++ template mechanism is very powerful, but almost impossible to get right. Walter writes C++ compiler writes since the beginning of C++, so he knows about the pitfalls of template mechanism. All these issues about template are causes of several threads in this newsgroup (check the archives where lots of people, including me, argue for a better template mechanism). Blitz++ does a very good usageof templates, I like particularly the tensor notation. But it carries a 750 page language standard behind it, defining the mechanism it uses. Walter wants something better, we just have to keep throwing him good ideas and critics. Best regards, Daniel Yokomiso.
Jan 16 2003
Daniel Yokomiso wrote:In article <b03d7n$2m9r$1 digitaldaemon.com>, Norbert Nemec says...------------------------- The one thing that I dislike most about C++ templates, and which has been carried over to D as well, is, that template parameters are not typed. I.e. if I have: template <typename T> class Test { static T add(T a,T b) { return a + b; } /* X */ }; bool foo() { return Test<bool>::add(true,false); /* Y */ }; the compiler actually complains about line /*X*/ which is absolutely correct, instead of telling the user that Test simply should not be instatiated with bool. Now if Test is somewhere deep within a library, the application programmer may get a huge bunch of error messages looking like a bug somewhere in the library, even though, the actual error was simply the wrong instatiation of a template. Solution to this would be something like interfaces in D, specifying a set of capabilities of a type (be it a class or a predefined type), and the possibility to restrict parameters in a template definition to such an interface.
If you restrict yourself to classes, you can restrict the type parameters: template TSort(T : Comparable) { } where Comparable is a type or class. AFAIK it's valid D code, if not is should ;-) .
I'm not sure whether that should really be the correct way to say it. Otherwise template X(T:T[]) {} would mean that T has to be a subtype of T[], which is complete nonsense. It is hard to name the actual meaning of the ":", but it definitely is not "subtype of". Actually, I don't have the slightest idea what would be better. Perhaps: template X(T:T < Comparable) {} as in Sather?I don't think there's any reason for the compiler to always complain about line /*X*/, instead of saying: Template instantiation at line /*Y*/ is invalid because of /*X*/ requirement in template body. Systems with type inference algorithms usually provide some intelligent error messages. There's also some papers about giving intelligent error messages in type inference algorithms.
Problem is, that in C++ code (and in D code as well), one cannot even say where the error lies. Did the library author do something illegal with the argument, or did the application programmer do a wrong instatiation? As long as there is no way to specify what capabilities the parameter types are supposed to have, even the most intelligent compiler cannot say more than: /*Y*/ seems to be wrong because of /*X*/, or even more nested error messages. In a case of 10 nested instantiations, the actual error might be located at any level. And what it worse: there is no way to completely test the compilability of a template without doing all possible instantiations.C++ template mechanism is very powerful, but almost impossible to get right. Walter writes C++ compiler writes since the beginning of C++, so he knows about the pitfalls of template mechanism. All these issues about template are causes of several threads in this newsgroup (check the archives where lots of people, including me, argue for a better template mechanism). Blitz++ does a very good usageof templates, I like particularly the tensor notation. But it carries a 750 page language standard behind it, defining the mechanism it uses. Walter wants something better, we just have to keep throwing him good ideas and critics.
Nothing to say against that! Probably, the whole power of the C++ template mechanism was never actually planned. Actually, the fact that it hides a complete language on its own was even found by accident. And up to now, I guess nobody has really pinned down what the details are that make it so powerful. Guess it will take a good portion of genious to extract that part without carrying over all the drawbacks... Ciao, Nobbi
Jan 17 2003
"Norbert Nemec" <nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> escreveu na mensagem news:b08u26$2orh$1 digitaldaemon.com...Daniel Yokomiso wrote:In article <b03d7n$2m9r$1 digitaldaemon.com>, Norbert Nemec says...------------------------- The one thing that I dislike most about C++ templates, and which has
carried over to D as well, is, that template parameters are not typed. I.e. if I have: template <typename T> class Test { static T add(T a,T b) { return a + b; } /* X */ }; bool foo() { return Test<bool>::add(true,false); /* Y */ }; the compiler actually complains about line /*X*/ which is absolutely correct, instead of telling the user that Test simply should not be instatiated with bool. Now if Test is somewhere deep within a library,
application programmer may get a huge bunch of error messages looking
a bug somewhere in the library, even though, the actual error was simply the wrong instatiation of a template. Solution to this would be something like interfaces in D, specifying a
of capabilities of a type (be it a class or a predefined type), and the possibility to restrict parameters in a template definition to such an interface.
If you restrict yourself to classes, you can restrict the type
template TSort(T : Comparable) { } where Comparable is a type or class. AFAIK it's valid D code, if not is should ;-) .
I'm not sure whether that should really be the correct way to say it. Otherwise template X(T:T[]) {} would mean that T has to be a subtype of T[], which is complete nonsense.
is hard to name the actual meaning of the ":", but it definitely is not "subtype of". Actually, I don't have the slightest idea what would be better. Perhaps: template X(T:T < Comparable) {} as in Sather?
Maybe if we use the subtype operator <: from theoretical OO work. template X(T <: Comparable; U; V <: Somethingable, Otherthingable) {} with multiple constraints possible. Are multiple constraints possible in Sather? I don't remember reading about this in the docs, but I'm sure in Eiffel they aren't.I don't think there's any reason for the compiler to always complain about line /*X*/, instead of saying: Template instantiation at line /*Y*/ is invalid because of /*X*/ requirement in template body. Systems with type inference algorithms usually provide some intelligent error messages. There's also some papers about giving intelligent error messages in type inference algorithms.
Problem is, that in C++ code (and in D code as well), one cannot even say where the error lies. Did the library author do something illegal with the argument, or did the application programmer do a wrong instatiation? As long as there is no way to specify what capabilities the parameter types are supposed to have, even the most intelligent compiler cannot say more than: /*Y*/ seems to be wrong because of /*X*/, or even more nested error messages. In a case of 10 nested instantiations, the actual error might be located at any level. And what it worse: there is no way to completely test the compilability of
template without doing all possible instantiations.
The way I see it, a template is always correct. The complete set of requirements that a type parameter must meet is in all the possible usages inside the template (all execution paths). So if the template requires your type to provide + & [] .connectToSQL() operations, it's your problem meeting this. At least until we can add operation interfaces. In current mechanisms the template determine a structural typing constraint a la OCaml methods on their parameters.C++ template mechanism is very powerful, but almost impossible to get right. Walter writes C++ compiler writes since the beginning of C++, so
knows about the pitfalls of template mechanism. All these issues about template are causes of several threads in this newsgroup (check the archives where lots of people, including me, argue for a better template mechanism). Blitz++ does a very good usageof templates, I like particularly the tensor notation. But it carries a 750 page language standard behind it, defining the mechanism it uses. Walter wants
better, we just have to keep throwing him good ideas and critics.
Nothing to say against that! Probably, the whole power of the C++ template mechanism was never actually planned. Actually, the fact that it hides a complete language on its own was even found by accident. And up to now, I guess nobody has really pinned down what the details are that make it so powerful. Guess it will take a good portion of genious to extract that
without carrying over all the drawbacks... Ciao, Nobbi
Maybe if we ordinary folks think hard enough for sufficient time we can mimic geniality? ;-) Best regards, Daniel Yokomiso. "It is a bit embarrassing to have been concerned with the human problem all one's life and find at the end that one has no more to offer by way of advice than 'Try to be a little kinder'." - Aldous Huxley --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.443 / Virus Database: 248 - Release Date: 11/1/2003
Jan 17 2003
Daniel Yokomiso wrote:Maybe if we use the subtype operator <: from theoretical OO work. template X(T <: Comparable; U; V <: Somethingable, Otherthingable) {} with multiple constraints possible.
Are multiple constraints possible in Sather?
No, but you have multiple inheritance (with ingeniously simple mechanisms to avoid all the hassle of it) so you can just pull two interfaces together to a combined one.The way I see it, a template is always correct. The complete set of requirements that a type parameter must meet is in all the possible usages inside the template (all execution paths). So if the template requires your type to provide + & [] .connectToSQL() operations, it's your problem meeting this. At least until we can add operation interfaces. In current mechanisms the template determine a structural typing constraint a la OCaml methods on their parameters.
True, but the core of the problem really is, that you cannot specify those requirements in the interface of the template. That way, a template cannot really be separated into interface and implementation, but you (and the compiler as well) have to read the whole implementation to know the interface.Maybe if we ordinary folks think hard enough for sufficient time we can mimic geniality? ;-)
Dream on! Ingenious ideas have always suddenly sprung up from somewhere, and the hardest part always was to recognize them as such. There are probably tons of ingenious ideas hidden in the discussions of this newsgroup, but in the end it will be the responsibility of Walter to pick out the good ones. And that will always need some geniality on its own... :-)
Jan 18 2003
"Norbert Nemec" <Nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> escreveu na mensagem news:b0bej0$18bu$1 digitaldaemon.com...Daniel Yokomiso wrote:Maybe if we use the subtype operator <: from theoretical OO work. template X(T <: Comparable; U; V <: Somethingable, Otherthingable) {} with multiple constraints possible.
Are multiple constraints possible in Sather?
No, but you have multiple inheritance (with ingeniously simple mechanisms
avoid all the hassle of it) so you can just pull two interfaces together
a combined one.
Sather provide sub and super typing operator IIRC, but it can be used to redefine the inheritance hierarchy of two different set of classes? E.g. in library A there's a method that requires something of type NumericComparable that is both Numeric and Comparable. In library B exists class C that inherits from Numeric and Comparable, but not from NumericComparable. The user of these two libraries may say that C inherits from NumericComparable too?The way I see it, a template is always correct. The complete set of requirements that a type parameter must meet is in all the possible
inside the template (all execution paths). So if the template requires your type to provide + & [] .connectToSQL() operations, it's your
meeting this. At least until we can add operation interfaces. In current mechanisms the template determine a structural typing constraint a la OCaml methods on their parameters.
True, but the core of the problem really is, that you cannot specify those requirements in the interface of the template. That way, a template cannot really be separated into interface and implementation, but you (and the compiler as well) have to read the whole implementation to know the interface.
Or rely on the intelligent and informative error messages from the compiler ;-)Maybe if we ordinary folks think hard enough for sufficient time we can mimic geniality? ;-)
Dream on! Ingenious ideas have always suddenly sprung up from somewhere,
the hardest part always was to recognize them as such. There are probably tons of ingenious ideas hidden in the discussions of this newsgroup, but
the end it will be the responsibility of Walter to pick out the good ones. And that will always need some geniality on its own... :-)
That's why I keep reading posts from several different sources (e.g.: comp.lang.functional, comp.compilers) and download more papers than I can read. To improve my geniality skills ;-) "Norbert Nemec" <Nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> escreveu na mensagem news:b0bej0$18bu$1 digitaldaemon.com...Daniel Yokomiso wrote:Maybe if we use the subtype operator <: from theoretical OO work. template X(T <: Comparable; U; V <: Somethingable, Otherthingable) {} with multiple constraints possible.
Are multiple constraints possible in Sather?
No, but you have multiple inheritance (with ingeniously simple mechanisms
avoid all the hassle of it) so you can just pull two interfaces together
a combined one.
Sather provide sub and super typing operator IIRC, but it can be used to redefine the inheritance hierarchy of two different set of classes? E.g. in library A there's a method that requires something of type NumericComparable that is both Numeric and Comparable. In library B exists class C that inherits from Numeric and Comparable, but not from NumericComparable. The user of these two libraries may say that C inherits from NumericComparable too?The way I see it, a template is always correct. The complete set of requirements that a type parameter must meet is in all the possible
inside the template (all execution paths). So if the template requires your type to provide + & [] .connectToSQL() operations, it's your
meeting this. At least until we can add operation interfaces. In current mechanisms the template determine a structural typing constraint a la OCaml methods on their parameters.
True, but the core of the problem really is, that you cannot specify those requirements in the interface of the template. That way, a template cannot really be separated into interface and implementation, but you (and the compiler as well) have to read the whole implementation to know the interface.
Or rely on the intelligent and informative error messages from the compiler ;-)Maybe if we ordinary folks think hard enough for sufficient time we can mimic geniality? ;-)
Dream on! Ingenious ideas have always suddenly sprung up from somewhere,
the hardest part always was to recognize them as such. There are probably tons of ingenious ideas hidden in the discussions of this newsgroup, but
the end it will be the responsibility of Walter to pick out the good ones. And that will always need some geniality on its own... :-)
That's why I keep reading posts from several different sources (e.g.: comp.lang.functional, comp.compilers) and download more papers than I can read. To improve my geniality skills ;-)
Jan 18 2003
Daniel Yokomiso wrote:"Norbert Nemec" <Nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM>Daniel Yokomiso wrote:Maybe if we use the subtype operator <: from theoretical OO work. template X(T <: Comparable; U; V <: Somethingable, Otherthingable) {} with multiple constraints possible.
Are multiple constraints possible in Sather?
No, but you have multiple inheritance (with ingeniously simple mechanisms to avoid all the hassle of it) so you can just pull two interfaces together to a combined one.
Sather provide sub and super typing operator IIRC, but it can be used to redefine the inheritance hierarchy of two different set of classes? E.g. in library A there's a method that requires something of type NumericComparable that is both Numeric and Comparable. In library B exists class C that inherits from Numeric and Comparable, but not from NumericComparable. The user of these two libraries may say that C inherits from NumericComparable too?
No, but through post-hoc supertyping, he can define MyNumericComparable as a subtype of Numeric and Comparable and a Supertype of C. Anyhow, at that point, the whole class system gets too different from D to be discussed on this list fruitfully. Ciao, Nobbi
Jan 18 2003
"Norbert Nemec" <Nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> wrote in message news:b0bej0$18bu$1 digitaldaemon.com...Dream on! Ingenious ideas have always suddenly sprung up from somewhere,
the hardest part always was to recognize them as such. There are probably tons of ingenious ideas hidden in the discussions of this newsgroup,
You're right about that, and I've adopted more than a few into D.but in the end it will be the responsibility of Walter to pick out the good ones.
My worry is that some adverse combinations of features of D will box it into a corner.And that will always need some geniality on its own... :-)
Saying no to someone's proposed feature they expended a lot of thought and effort into presenting is really the hardest part of my task. I know how they feel, as not one of my proposed changes ever was adopted by the C++ standards people <g>. (And hence, the genesis of D.)
Feb 15 2003
Daniel Yokomiso wrote:The way I see it, a template is always correct. The complete set of requirements that a type parameter must meet is in all the possible usages inside the template (all execution paths). So if the template requires your type to provide + & [] .connectToSQL() operations, it's your problem meeting this. At least until we can add operation interfaces. In current mechanisms the template determine a structural typing constraint a la OCaml methods on their parameters.
Hi, In C++ templates there are concepts like iterator, forward_iterator, allocator, etc. They exist, but are not enforced by the language. Maybe in D we could use interfaces to enforce the concepts needed by the templates. And explicitly tell the compiler the concepts/interfaces a certain template type expects. I think it would make error detection much simpler. OT: Just to put my 2 cents, I prefer the proposed template notation using the $ to specify the generic part. -- Marcelo Fontenele S Santos <msantos pobox.com>
Feb 04 2003
Marcelo Fontenele S Santos wrote:Daniel Yokomiso wrote:The way I see it, a template is always correct. The complete set of requirements that a type parameter must meet is in all the possible usages inside the template (all execution paths). So if the template requires your type to provide + & [] .connectToSQL() operations, it's your problem meeting this. At least until we can add operation interfaces. In current mechanisms the template determine a structural typing constraint a la OCaml methods on their parameters.
Hi, In C++ templates there are concepts like iterator, forward_iterator, allocator, etc. They exist, but are not enforced by the language. Maybe in D we could use interfaces to enforce the concepts needed by the templates. And explicitly tell the compiler the concepts/interfaces a certain template type expects. I think it would make error detection much simpler.
Predicate types - or type constraints - are supported by most generic systems that I've seen. One way they could be in D is like this: predicate TComparable ($T) { $T a, b; a < b; } A type is TComparable if it can be inserted in this block statement and pass the semantic phase. In use it would be, say: $T min ($T first, $T [] rest...) predicate TComparable ($T) body { foreach ( ; i in rest; ) { if (i < first) first = i; } return first; } This is more powerful than Eiffel's syntax because you can include multiple arguments to the predicate and fail a set of types because of an incompatible interaction. It could even include type arguments to act as specialisations. So for example: predicate TMatrix4x4 (int $Rows, int $Cols) { assert ($Rows == 4 && $Cols == 4); } struct Matrix ($T, int $Rows, int $Cols) predicate TMatrix4x4 ($Rows, $Cols) body { /* Various SIMD functionality. */ }
Feb 04 2003
Burton Radons wrote:Predicate types - or type constraints - are supported by most generic systems that I've seen. One way they could be in D is like this: predicate TComparable ($T) { $T a, b; a < b; } A type is TComparable if it can be inserted in this block statement and pass the semantic phase. In use it would be, say: $T min ($T first, $T [] rest...) predicate TComparable ($T) body { foreach ( ; i in rest; ) { if (i < first) first = i; } return first; }
The problem I see with this approach is that you can add something to the body that is not tested in the predicate. So it would pass the test but could fail to instantiate for a specific type. Before I give my sugestion I must say that I don't know anything about compilers. So anything I say can be impossible or unnecessary. My sugestion is to use template interfaces: interface TComparable($T) // $T is the self type { int cmp( $T rhs ); } And maybe a syntax like: $T min ($T first, $T [] rest...) use($T) TComparable { ... } This means $T must implement that interface and in the body it would only allow the use of $T vars through interface defined calls. $T search( $T first1, $T last1, $U first2, $U last2 ) use($T) TForwardIterator, use($U) TForwardIterator { ... } Extending the above example to be more restrictive we could do: interface TComparable( $T, $U ) { // $T is self type int cmp( $U rhs ); // compare itself with another type } $T search( $T first1, $T last1, $U first2, $U last2 ) use($T) TForwardIterator, use($U) TForwardIterator, use(*$T, *$U) TComparable // TForwardIterator must provide a * op { ... } The problem I see is with built-in types. I don't know how the operators relate to the overloadable methods. Should this work? int i = 0; if ( i.cmp( 0 ) == 0 ) { printf("ok\n"); } It doesn't compile in dli-0.1.2. I also don't know how to check if they implement an interface. For it to work, interfaces should not need to be defined in the class (they could be to detect errors). They would have to be checked during compilation. class X : TComparable { // ok X must implement cmp int cmp( X x ); } class Y { int cmp( Y y ); } TComparable c = (TComparable) Y; // ok Y implements cmp So it would work for built-ins. int i = 0; TComparable c = (TComparable) i; // ok int implements cmp Sugestions? Opinions? -- Marcelo Fontenele S Santos <msantos pobox.com>
Feb 05 2003
"Norbert Nemec" <nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> wrote in message news:b08u26$2orh$1 digitaldaemon.com...Probably, the whole power of the C++ template mechanism was never actually planned.
It wasn't. I remember Bjarne's original template paper, and conversations with him about it. The original scheme was not much more than a glorified macro substitution scheme, and few thought it would be hard to implement <g>. There was no hint it would evolve into what it is today.Actually, the fact that it hides a complete language on its own was even found by accident.
Yes.And up to now, I guess nobody has really pinned down what the details are that make it so powerful. Guess it will take a good portion of genious to extract that
without carrying over all the drawbacks...
I think the partial specialization is the real source of the power of templates.
Feb 15 2003









"Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> 