www.digitalmars.com         C & C++   DMDScript  

D - Templates and matrices

reply "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
Hi,

    When reading the D documentation for arrays, we see that every dynamic
array is ragged and every static array is rectangular. For a efficient
matrix library we should be able to provide either:

template Matrix(T) {
    T[][] reversed(T[][] matrix) { ... }
}
Matrix(int) matrix
int[5][5] aMatrix = ...;
matrix.reversed(aMatrix);


and expect automatic conversion when using rectangular arrays, or

template Matrix(T) {
    T reversed(T matrix) { ... }
}
Matrix(int[5][5]) matrix
int[5][5] aMatrix = ...;
matrix.reversed(aMatrix);


    And always instantiate a more specific template instance for every array
dimension used. This last solution is similar to C++ usage of templates with
int parameters. If this possibility was available in D we could write:

template Matrix(T, int rows, int columns) {
    T[rows][columns] reversed(T[rows][columns] matrix) { ... }
}
Matrix(int, 5, 5) matrix
int[5][5] aMatrix = ...;
matrix.reversed(aMatrix);

    And use it anyway we want. The first solution is cleaner and more
intuitive, but the compiler should be able to correctly create additional
function definitions for different array dimensions (which are different
types). The last solution opens the door to non-type parameters in
templates, which are very good to write generic modules:

template TBinaryTree(T, int fun(T, T) comparator) { ... }


    With this approach we have templates capable of generic programming like
in Ada, ML or Haskell. But currently is not possible to use the efficiency
of D's rectangular arrays and the genericity of D's templates without some
tricks.
    Just some thoughts...

    Best regards,
    Daniel Yokomiso.

"I will bend your mind with my spoon."
Nov 25 2002
parent reply "Walter" <walter digitalmars.com> writes:
You have some good ideas. I've been looking at some improvements to the
array conversions between dynamic & static arrays, but it'll be a bit yet
before such is implemented. -Walter

"Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> wrote in message
news:arumno$1i2g$1 digitaldaemon.com...
 Hi,

     When reading the D documentation for arrays, we see that every dynamic
 array is ragged and every static array is rectangular. For a efficient
 matrix library we should be able to provide either:

 template Matrix(T) {
     T[][] reversed(T[][] matrix) { ... }
 }
 Matrix(int) matrix
 int[5][5] aMatrix = ...;
 matrix.reversed(aMatrix);


 and expect automatic conversion when using rectangular arrays, or

 template Matrix(T) {
     T reversed(T matrix) { ... }
 }
 Matrix(int[5][5]) matrix
 int[5][5] aMatrix = ...;
 matrix.reversed(aMatrix);


     And always instantiate a more specific template instance for every
array
 dimension used. This last solution is similar to C++ usage of templates
with
 int parameters. If this possibility was available in D we could write:

 template Matrix(T, int rows, int columns) {
     T[rows][columns] reversed(T[rows][columns] matrix) { ... }
 }
 Matrix(int, 5, 5) matrix
 int[5][5] aMatrix = ...;
 matrix.reversed(aMatrix);

     And use it anyway we want. The first solution is cleaner and more
 intuitive, but the compiler should be able to correctly create additional
 function definitions for different array dimensions (which are different
 types). The last solution opens the door to non-type parameters in
 templates, which are very good to write generic modules:

 template TBinaryTree(T, int fun(T, T) comparator) { ... }


     With this approach we have templates capable of generic programming
like
 in Ada, ML or Haskell. But currently is not possible to use the efficiency
 of D's rectangular arrays and the genericity of D's templates without some
 tricks.
     Just some thoughts...

     Best regards,
     Daniel Yokomiso.

 "I will bend your mind with my spoon."
Nov 26 2002
parent reply "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
"Walter" <walter digitalmars.com> escreveu na mensagem
news:as1108$15fo$3 digitaldaemon.com...
 You have some good ideas. I've been looking at some improvements to the
 array conversions between dynamic & static arrays, but it'll be a bit yet
 before such is implemented. -Walter
Hi, Ok, I'll implement a dumb Matrix class (using hand written packing of data to mimic rectangular behaviour). Speaking about matrices, why the following code fails to link? int main() { const int[][] matrix = [[0,1,2,3], [4,5,6,7], [8,9,10,11], [12,13,14,15]]; const int[][] sub0 = [[0,1,2,3]]; const int[][] sub1 = [[0,1,2,3], [4,5,6,7]]; const int[][] sub2 = [[4,5,6,7], [8,9,10,11]]; const int[][] sub3 = [[5,6], [9,10]]; assert(matrix[0] == sub0[0]); assert(matrix == matrix); assert(matrix[0..1] == sub0); assert(matrix[0..2] == sub1); assert(matrix[1..3] == sub2); assert(matrix[1..3][1..3] == sub3); return 0; } It gives a message 'Error 42: Symbol Undefined __init_TypeInfo_Ai' And are multidimensional slices valid? Id would be great to matrix programming, so we could write: A[1..3][4..10] += B[5..7][8..14]; Or something like that. Also overloading of the [] operator for subscript and slicing would be a good idea, together with the in operator. And the last thing, I'm stuck with a problem of them current template syntax. Right now I have a template Matrix class defined for any kind of numeric type. But for some operations like the trace of the matrix (sum of the main diagonal elements) I need to have a zero value like: T trace() { T sum = 0; // code to sum the main diagonal elements return sum; } For TMatrix(int) or TMatrix(float) instances this code is ok, but when I start using it for user-defined classes that are not addable to 0, like Vector, Quaternion, Equation, etc. the code stops working. Right now I can solve this by using a template variable to hold the value of zero and code verifications to ensure that the zero value is defined before the programmer uses a Matrix. instance TMatrix(Quatertion) tmatrix; tmatrix.zero = new Quatertion(); tmatrix.Matrix m = new tmatrix.Matrix(10, 10); where new Quatertion() is the zero value for quaternions. Haskell, ML and Ada allow any kind of parameters, besides types, when you instantiate a generic module. I think should follow its example, so our could would be something like: instance TMatrix(Quatertion, new Quatertion()) tmatrix; tmatrix.Matrix m = new tmatrix.Matrix(10, 10); I keep thinking of it as a template constructor. Maybe we could push this template parametrization stuff to module level, and the programmer would use a template module as a regular module: module matrix(T, T zero); // declaration of template module import matrix(int, 0); import matrix(float, 0.0); import matrix(Quaternion, new Quaternion()); And use the module values without any particular need to refer to the instance variable. I think it's still time for a change like that, AFAIK I'm the only one doing serious template work with D, and I can live with any change in the language. It would make the language simpler for template users (if we make matrix programming right we can get lots of people interested in doing scientific programming in D, it's my current goal). Best regards, Daniel Yokomiso. "The best model of the solar system is not an orange for the sun and various fruits at appropriate distances. Even if the orange is a good approximation to the sun in form and color. But the best model is a set of differential equations. Last time I looked they were neither round nor orange, but they are definitely a better model." - Joachim Durchholz
Nov 26 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> wrote in message
news:as1944$1e3u$2 digitaldaemon.com...
     Speaking about matrices, why the following code fails to link?
     It gives a message 'Error 42: Symbol Undefined __init_TypeInfo_Ai'
Because I have failed to write a typeinfo for arrays of ints :-( See the ti*.d files in \dmd\src\phobos.
     And are multidimensional slices valid? Id would be great to matrix
 programming, so we could write:
 A[1..3][4..10] += B[5..7][8..14];
     Or something like that.
I'd have to think about that. I don't think it will work for rectangular arrays.
 Also overloading of the [] operator for
 subscript and slicing would be a good idea, together with the in operator.
Yes, that's a good idea.
     And the last thing, I'm stuck with a problem of them current template
 syntax. Right now I have a template Matrix class defined for any kind of
 numeric type. But for some operations like the trace of the matrix (sum of
 the main diagonal elements) I need to have a zero value like:
 T trace() {
     T sum = 0;
     // code to sum the main diagonal elements
     return sum;
 }
     For TMatrix(int) or TMatrix(float) instances this code is ok, but when
I
 start using it for user-defined classes that are not addable to 0, like
 Vector, Quaternion, Equation, etc. the code stops working. Right now I can
 solve this by using a template variable to hold the value of zero and code
 verifications to ensure that the zero value is defined before the
programmer
 uses a Matrix.
I don't understand.
 instance TMatrix(Quatertion) tmatrix;
 tmatrix.zero = new Quatertion();
 tmatrix.Matrix m = new tmatrix.Matrix(10, 10);

 where new Quatertion() is the zero value for quaternions. Haskell, ML and
 Ada allow any kind of parameters, besides types, when you instantiate a
 generic module. I think should follow its example, so our could would be
 something like:

 instance TMatrix(Quatertion, new Quatertion()) tmatrix;
 tmatrix.Matrix m = new tmatrix.Matrix(10, 10);
That's probably a good idea. I wanted to try it with just types for starters and see how far that got.
     I keep thinking of it as a template constructor. Maybe we could push
 this template parametrization stuff to module level, and the programmer
 would use a template module as a regular module:


 module matrix(T, T zero); // declaration of template module

 import matrix(int, 0);
 import matrix(float, 0.0);
 import matrix(Quaternion, new Quaternion());


     And use the module values without any particular need to refer to the
 instance variable. I think it's still time for a change like that, AFAIK
I'm
 the only one doing serious template work with D, and I can live with any
 change in the language. It would make the language simpler for template
 users (if we make matrix programming right we can get lots of people
 interested in doing scientific programming in D, it's my current goal).
Ah, parameterized modules! Interesting shorthand!
Nov 26 2002
next sibling parent reply Burton Radons <loth users.sourceforge.net> writes:
Walter wrote:
 "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> wrote in message
 news:as1944$1e3u$2 digitaldaemon.com...
 
    Speaking about matrices, why the following code fails to link?
    It gives a message 'Error 42: Symbol Undefined __init_TypeInfo_Ai'
Because I have failed to write a typeinfo for arrays of ints :-( See the ti*.d files in \dmd\src\phobos.
Hooray! The more you have to special-case the easier a proper implementation will be in comparison. ;-)
    And are multidimensional slices valid? Id would be great to matrix
programming, so we could write:
A[1..3][4..10] += B[5..7][8..14];
    Or something like that.
I'd have to think about that. I don't think it will work for rectangular arrays.
I'll make the problem explicit. Given: int [10] [10] a; The resulting type from "a [5] [4]" is int, but the resulting type from "a [5 .. 10] [4 .. 5]" is int[][]. Indices remove a layer, but slicing doesn't, so a double-slice should still result in the same number of dimensions. Here's where they got in the discussion, Daniel. Pavel suggested the syntax: int [r, c] a; So if that's a matrix, I could get a column or a row by "a [0 .. r, c]" or "a [r, 0 .. c]", or get sub-matrices out of that. Powerful stuff, but hard on the compiler and really hard on operator overloading. Needs solid justification.
Also overloading of the [] operator for
subscript and slicing would be a good idea, together with the in operator.
Yes, that's a good idea.
For comparison, Python's method names are (with the bracketing "__" removed): T getitem (U key); T setitem (U key, T value); void delitem (U key); X iter (); /* Return an iterator. */ bit contains (U key); /* For the "a in b" relationship. */ V getslice (U i, U j); V setslice (U i, U j, V sequence); void delslice (U i, U j); Slices were made a part of the language in 2.0; I don't think their reasons for making the switch are applicable to us.
    And the last thing, I'm stuck with a problem of them current template
syntax. Right now I have a template Matrix class defined for any kind of
numeric type. But for some operations like the trace of the matrix (sum of
the main diagonal elements) I need to have a zero value like:
T trace() {
    T sum = 0;
    // code to sum the main diagonal elements
    return sum;
}
    For TMatrix(int) or TMatrix(float) instances this code is ok, but when
I
start using it for user-defined classes that are not addable to 0, like
Vector, Quaternion, Equation, etc. the code stops working. Right now I can
solve this by using a template variable to hold the value of zero and code
verifications to ensure that the zero value is defined before the
programmer
uses a Matrix.
I don't understand.
Take the code: T sum (T [] list) { T result = 0; for (T *c = list, e = c + list.length; c < e; c ++) result += *c; return result; } This is fine if T is int or float, but if it's "struct vec3 { float x, y, z; }" then the initial value of result doesn't make sense. Howsabout the builtin types have an attribute ".zero" that we can put into our user types as static variables/constants. Then that line would be: T result = T.zero; Which I think would work better than C++'s constructor method. zero could even be a static property, and if it's proveably const then the compiler can do lots with it.
Nov 27 2002
next sibling parent "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
Reply embbeded.

"Burton Radons" <loth users.sourceforge.net> escreveu na mensagem
news:as2d4u$2koh$1 digitaldaemon.com...
 Walter wrote:
[snip]
    And are multidimensional slices valid? Id would be great to matrix
programming, so we could write:
A[1..3][4..10] += B[5..7][8..14];
    Or something like that.
I'd have to think about that. I don't think it will work for rectangular arrays.
I'll make the problem explicit. Given: int [10] [10] a; The resulting type from "a [5] [4]" is int, but the resulting type from "a [5 .. 10] [4 .. 5]" is int[][]. Indices remove a layer, but slicing doesn't, so a double-slice should still result in the same number of dimensions. Here's where they got in the discussion, Daniel. Pavel suggested the syntax: int [r, c] a; So if that's a matrix, I could get a column or a row by "a [0 .. r, c]" or "a [r, 0 .. c]", or get sub-matrices out of that. Powerful stuff, but hard on the compiler and really hard on operator overloading. Needs solid justification.
Blitz++ (http://www.oonumerics.org/blitz/manual/blitz02.html#l46) is a solid justification? :-) Jokes aside they have a very powerful slicing mechanism, runtime performance comparable to Fortran and use lots of C++ template magic. I don't mind the syntax for primitive matrices, but this kind of stuff is needed when someone want to write simple code to work with matrices. If you check the Fortran Linpack code (or the Java variant Jama), you'll lot's of explicit written loops for things like: // code snippet from Jama // V is a square n x n matrix for (int k = n-1; k >= 0; k--) { if ((k < nrt) & (e[k] != 0.0)) { for (int j = k+1; j < nu; j++) { double t = 0; for (int i = k+1; i < n; i++) { t += V[i][k]*V[i][j]; } t = -t/V[k+1][k]; for (int i = k+1; i < n; i++) { V[i][j] += t*V[i][k]; } } } for (int i = 0; i < n; i++) { V[i][k] = 0.0; } V[k][k] = 1.0; } that could be written as (or something like that): for (int k = n-1; k >= 0; k--) { if ((k < nrt) & (e[k] != 0.0)) { for (int j = k+1; j < nu; j++) { double t = sum(V[k + 1 .. n][k] * V[k + 1 .. n][j]); t = -t / V[k+1][k]; V[k + 1 .. n][j] += t * V[k + 1 .. n][k]; } } V[0..n][k] = 0.0; V[k][k] = 1.0; } Seven lines smaller and simpler to understand. Every time anyone writes a loop (matrix processing uses several nested loops usually) is because some abstraction was lost. With complete multi-dimentsional slicing we can do almost all loops with less code, and the compiler will know we want to do slice processing, not some generic loop that happens to be a slice. [snip]
    And the last thing, I'm stuck with a problem of them current
template
syntax. Right now I have a template Matrix class defined for any kind of
numeric type. But for some operations like the trace of the matrix (sum
of
the main diagonal elements) I need to have a zero value like:
T trace() {
    T sum = 0;
    // code to sum the main diagonal elements
    return sum;
}
    For TMatrix(int) or TMatrix(float) instances this code is ok, but
when
 I

start using it for user-defined classes that are not addable to 0, like
Vector, Quaternion, Equation, etc. the code stops working. Right now I
can
solve this by using a template variable to hold the value of zero and
code
verifications to ensure that the zero value is defined before the
programmer
uses a Matrix.
I don't understand.
Take the code: T sum (T [] list) { T result = 0; for (T *c = list, e = c + list.length; c < e; c ++) result += *c; return result; } This is fine if T is int or float, but if it's "struct vec3 { float x, y, z; }" then the initial value of result doesn't make sense. Howsabout the builtin types have an attribute ".zero" that we can put into our user types as static variables/constants. Then that line would
be:
         T result = T.zero;

 Which I think would work better than C++'s constructor method.  zero
 could even be a static property, and if it's proveably const then the
 compiler can do lots with it.
This solution is also good, but we need also the .one attribute. But this solution is paliative, because it doesn't address the problem that there is more to generic programming than types. We can simulate this by introducing dummy classes and instances: template TNumerics(T, U) { private U attributes = new U(); T sumSqrt(T[] list) { T result = attributes.zero(); for (int i = 0; i < list.length; i ++) { result += attributes.sqrt(list[i]); } return result; } } This hack, not very ugly but a hack still. There are infinite possible parametrization of templates besides type and primitive values. Best regards, Daniel Yokomiso. "Confucius say... man who run in front of car get tired; man who run behind car get exhausted."
Nov 27 2002
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Burton Radons" <loth users.sourceforge.net> wrote in message
 Howsabout the builtin types have an attribute ".zero" that we can put
 into our user types as static variables/constants.  Then that line would
be: All types have an attribute .init which gives their default initalizer value.
Dec 28 2002
parent reply Burton Radons <loth users.sourceforge.net> writes:
Walter wrote:
 "Burton Radons" <loth users.sourceforge.net> wrote in message
 
Howsabout the builtin types have an attribute ".zero" that we can put
into our user types as static variables/constants.  Then that line would
be: All types have an attribute .init which gives their default initalizer value.
float.init is not zero.
Dec 28 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Burton Radons" <loth users.sourceforge.net> wrote in message
news:aul9s3$1pkf$1 digitaldaemon.com...
 Walter wrote:
 "Burton Radons" <loth users.sourceforge.net> wrote in message

Howsabout the builtin types have an attribute ".zero" that we can put
into our user types as static variables/constants.  Then that line would
be: All types have an attribute .init which gives their default initalizer value.
float.init is not zero.
That's right. I must have misunderstood your point then!
Dec 28 2002
parent reply Burton Radons <loth users.sourceforge.net> writes:
Walter wrote:
 "Burton Radons" <loth users.sourceforge.net> wrote in message
 news:aul9s3$1pkf$1 digitaldaemon.com...
 
Walter wrote:

"Burton Radons" <loth users.sourceforge.net> wrote in message


Howsabout the builtin types have an attribute ".zero" that we can put
into our user types as static variables/constants.  Then that line would
be: All types have an attribute .init which gives their default initalizer value.
float.init is not zero.
That's right. I must have misunderstood your point then!
You read right over it, unless if you're randomly accessing messages. Take a sum template: T sum (T [] list) { T result = 0; for (T *c = list, e = c + list.length; c < e; c ++) result += *c; return c; } The problem is that this depends upon 0 meaning something in the context of T, which is simply not possible in D, and I don't want to depend upon this kind of thing as contextual identification of common forms infects C++ and partly makes it the mess it is with operator overloading. Just "T result;" won't work, as that shouldn't be initialised. "T result = T.zero;", on the other hand, works nicely, clearly means only one thing, and shouldn't mess up anything else. Daniel also noted that we need a .one.
Dec 29 2002
parent reply "Walter" <walter digitalmars.com> writes:
Ok, I'll have to think about that some more. I want to be careful adding in
more . properties, as it could easilly get out of hand.

"Burton Radons" <loth users.sourceforge.net> wrote in message
news:aunkbe$bni$1 digitaldaemon.com...
 Walter wrote:
 "Burton Radons" <loth users.sourceforge.net> wrote in message
 news:aul9s3$1pkf$1 digitaldaemon.com...

Walter wrote:

"Burton Radons" <loth users.sourceforge.net> wrote in message


Howsabout the builtin types have an attribute ".zero" that we can put
into our user types as static variables/constants.  Then that line
would
be:

All types have an attribute .init which gives their default initalizer
value.
float.init is not zero.
That's right. I must have misunderstood your point then!
You read right over it, unless if you're randomly accessing messages. Take a sum template: T sum (T [] list) { T result = 0; for (T *c = list, e = c + list.length; c < e; c ++) result += *c; return c; } The problem is that this depends upon 0 meaning something in the context of T, which is simply not possible in D, and I don't want to depend upon this kind of thing as contextual identification of common forms infects C++ and partly makes it the mess it is with operator overloading. Just "T result;" won't work, as that shouldn't be initialised. "T result = T.zero;", on the other hand, works nicely, clearly means only one thing, and shouldn't mess up anything else. Daniel also noted that we need a
.one.

Jan 07 2003
parent reply Burton Radons <loth users.sourceforge.net> writes:
Walter wrote:
 Ok, I'll have to think about that some more. I want to be careful adding in
 more . properties, as it could easilly get out of hand.
We could entertain extensions: extension FloatNumeric (type) { type sqrt () { return intrinsic.sqrt (*this); } } extension SignedNumeric (type) { static bit signable () { return true; } } extension UnsignedNumeric (type) { static bit signable () { return false; } } extension Numeric (type) { bit signed () { return *this < type.zero; } int sign () { return signed () ? -1 : 1; } } extend FloatNumeric for float, double, extended, complex; extend SignedNumeric for byte, short, int, long, float, double, extended, complex; extend UnsignedNumeric for bit, ubyte, ushort, uint, ulong; extend Numeric for ...; bit.signable; /* Calls UnsignedNumeric.signable */ int x; x.signed; /* Calls SignedNumeric.signed */ Extending a type gives it these properties for the purpose of compilation - all static or final, any overlap is an error (except perhaps that typedef defines a new layer that allows overlapping any previous instantiations? What scopes are used becomes an issue), no fields. This doesn't handle instancing an extension for any type of array, but as it's easy to do so manually it's not so much a problem. Then again, being able to move everything into Phobos, including .sort, indexing, and resizing, would have some advantages. How about: instance ArraySortExtension (* []); The problem is that this becomes unimplementable: extension ArrayIndexExtension (type) { XXX getitem (int index) { assert (index > 0 && index < type.length); return type.data [index]; } } So it could be: extension ArrayIndexExtension (type []) ... instance ArraySortExtension (*); Or: extension ArrayIndexExtension (type) { type.next getitem (int index) } Which would allow user structs to be extended, so I like that best. I feel like I'm dancing around a simpler, more elegant solution, but I can't quite grasp it.
Jan 08 2003
next sibling parent "Sean L. Palmer" <seanpalmer directvinternet.com> writes:
This line of reasoning looks promising.  This has been requested before, as
a way to add custom methods to classes defined elsewhere, such as in the
standard library;  possibly even to basic types.

Seems eminently doable, probably not that difficult to implement aside from
hiding the extensions inside modules so that different modules can provide
similarly named extensions that don't clash with each other.  I think normal
module scoping will handle that.  The extensions have scope of the module
they're defined in, but are otherwise conceptually part of the type that
they extend.  Within said module they appear "embedded" into the extended
type, exactly like a normal member.  Elsewhere they'd have to be imported or
explicitly called maybe like module.type.extendedmember()

Sean

"Burton Radons" <loth users.sourceforge.net> wrote in message
news:avimh0$vks$2 digitaldaemon.com...
 Walter wrote:
 Ok, I'll have to think about that some more. I want to be careful adding
in
 more . properties, as it could easilly get out of hand.
We could entertain extensions: extension FloatNumeric (type) { type sqrt () { return intrinsic.sqrt (*this); } } extension SignedNumeric (type) { static bit signable () { return true; } } extension UnsignedNumeric (type) { static bit signable () { return false; } } extension Numeric (type) { bit signed () { return *this < type.zero; } int sign () { return signed () ? -1 : 1; } } extend FloatNumeric for float, double, extended, complex; extend SignedNumeric for byte, short, int, long, float, double, extended, complex; extend UnsignedNumeric for bit, ubyte, ushort, uint, ulong; extend Numeric for ...; bit.signable; /* Calls UnsignedNumeric.signable */ int x; x.signed; /* Calls SignedNumeric.signed */ Extending a type gives it these properties for the purpose of compilation - all static or final, any overlap is an error (except perhaps that typedef defines a new layer that allows overlapping any previous instantiations? What scopes are used becomes an issue), no fields. This doesn't handle instancing an extension for any type of array, but as it's easy to do so manually it's not so much a problem. Then again, being able to move everything into Phobos, including .sort, indexing, and resizing, would have some advantages. How about: instance ArraySortExtension (* []); The problem is that this becomes unimplementable: extension ArrayIndexExtension (type) { XXX getitem (int index) { assert (index > 0 && index < type.length); return type.data [index]; } } So it could be: extension ArrayIndexExtension (type []) ... instance ArraySortExtension (*); Or: extension ArrayIndexExtension (type) { type.next getitem (int index) } Which would allow user structs to be extended, so I like that best. I feel like I'm dancing around a simpler, more elegant solution, but I can't quite grasp it.
Jan 08 2003
prev sibling parent "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
"Burton Radons" <loth users.sourceforge.net> escreveu na mensagem
news:avimh0$vks$2 digitaldaemon.com...
 Walter wrote:
 Ok, I'll have to think about that some more. I want to be careful adding
in
 more . properties, as it could easilly get out of hand.
We could entertain extensions: extension FloatNumeric (type) { type sqrt () { return intrinsic.sqrt (*this); } } extension SignedNumeric (type) { static bit signable () { return true; } } extension UnsignedNumeric (type) { static bit signable () { return false; } } extension Numeric (type) { bit signed () { return *this < type.zero; } int sign () { return signed () ? -1 : 1; } } extend FloatNumeric for float, double, extended, complex; extend SignedNumeric for byte, short, int, long, float, double, extended, complex; extend UnsignedNumeric for bit, ubyte, ushort, uint, ulong; extend Numeric for ...; bit.signable; /* Calls UnsignedNumeric.signable */ int x; x.signed; /* Calls SignedNumeric.signed */ Extending a type gives it these properties for the purpose of compilation - all static or final, any overlap is an error (except perhaps that typedef defines a new layer that allows overlapping any previous instantiations? What scopes are used becomes an issue), no fields. This doesn't handle instancing an extension for any type of array, but as it's easy to do so manually it's not so much a problem. Then again, being able to move everything into Phobos, including .sort, indexing, and resizing, would have some advantages. How about: instance ArraySortExtension (* []); The problem is that this becomes unimplementable: extension ArrayIndexExtension (type) { XXX getitem (int index) { assert (index > 0 && index < type.length); return type.data [index]; } } So it could be: extension ArrayIndexExtension (type []) ... instance ArraySortExtension (*); Or: extension ArrayIndexExtension (type) { type.next getitem (int index) } Which would allow user structs to be extended, so I like that best. I feel like I'm dancing around a simpler, more elegant solution, but I can't quite grasp it.
Hi, This could be used to add new methods to objects too, so we can define a sqrt method, slices for collection types, etc.. We could even merge extension and template semantics, allowing of safe templates to add methods to objects: template TQuantifiers(T : TIterable(T)) { /*quantifications like find, filter, any, all, etc.*/ } extension ArrayIterable(T) { /* defines a iterator struct for arrays*/ } instance TQuantifiers(int) using ArrayIterable(int); if (array.find(&predicate)) { ... } Leaving out all explicit instantiation verbosity of templates. Would be really nice to extend the templates expressiveness. Current deimos implementation provides classes with Matrix semantics instead of structs, because IMHO classes provide a better semantics for defining true ADTs. But with extensions I could define all operations as extensions of a matrix type provided by the user, so people who prefer structs could use them and provide the correct extension/template instance in their own module. I think this is the correct direction for us to follow. Best regards, Daniel Yokomiso. --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.435 / Virus Database: 244 - Release Date: 30/12/2002
Jan 09 2003
prev sibling parent reply "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
Reply embedded

"Walter" <walter digitalmars.com> escreveu na mensagem
news:as24jh$2b30$1 digitaldaemon.com...
 "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> wrote in message
 news:as1944$1e3u$2 digitaldaemon.com...
     Speaking about matrices, why the following code fails to link?
     It gives a message 'Error 42: Symbol Undefined __init_TypeInfo_Ai'
Because I have failed to write a typeinfo for arrays of ints :-( See the ti*.d files in \dmd\src\phobos.
Ok.
     And are multidimensional slices valid? Id would be great to matrix
 programming, so we could write:
 A[1..3][4..10] += B[5..7][8..14];
     Or something like that.
I'd have to think about that. I don't think it will work for rectangular arrays.
I agree. Maybe some magic is needed :-). If D get a expressive Matrix type (my current work) with this kind of syntax maybe we won't need true rectangular arrays for 95% of its usage (my matrix provides data packed in column order). Blitz++ provides multi-dimensional slices and subarrays (http://www.oonumerics.org/blitz/manual/blitz02.html#l46) so its users will ask for it.
 Also overloading of the [] operator for
 subscript and slicing would be a good idea, together with the in
operator.
 Yes, that's a good idea.
I find myself always writing a contains method, or writing a slice + set/at methods. If D has this capability maybe we can use it instead of STL-like iterators to provide generic programming. After all a slice is a pointer + length, very similar to an iterator (pointer + direction of transversal).
     And the last thing, I'm stuck with a problem of them current
template
 syntax. Right now I have a template Matrix class defined for any kind of
 numeric type. But for some operations like the trace of the matrix (sum
of
 the main diagonal elements) I need to have a zero value like:
 T trace() {
     T sum = 0;
     // code to sum the main diagonal elements
     return sum;
 }
     For TMatrix(int) or TMatrix(float) instances this code is ok, but
when
 I
 start using it for user-defined classes that are not addable to 0, like
 Vector, Quaternion, Equation, etc. the code stops working. Right now I
can
 solve this by using a template variable to hold the value of zero and
code
 verifications to ensure that the zero value is defined before the
programmer
 uses a Matrix.
I don't understand.
In the template code I must declare a variable of type T that is initialized to some value. If T is a user-defined numeric type that isn't addable to number 0 (after all matrix members need only to be addable to their kind). Think of matrices of type-safe quantities, like Mass, Length or Time. Neither of these types are addable to numbers, althought they're addable to themselves. The literal 0 is used to define int zero, not Length 0. So I use this template variables to allow the user to define the interesting numbers (zero and one, all others are defined by those).
 instance TMatrix(Quatertion) tmatrix;
 tmatrix.zero = new Quatertion();
 tmatrix.Matrix m = new tmatrix.Matrix(10, 10);

 where new Quatertion() is the zero value for quaternions. Haskell, ML
and
 Ada allow any kind of parameters, besides types, when you instantiate a
 generic module. I think should follow its example, so our could would be
 something like:

 instance TMatrix(Quatertion, new Quatertion()) tmatrix;
 tmatrix.Matrix m = new tmatrix.Matrix(10, 10);
That's probably a good idea. I wanted to try it with just types for
starters
 and see how far that got.

     I keep thinking of it as a template constructor. Maybe we could push
 this template parametrization stuff to module level, and the programmer
 would use a template module as a regular module:


 module matrix(T, T zero); // declaration of template module

 import matrix(int, 0);
 import matrix(float, 0.0);
 import matrix(Quaternion, new Quaternion());


     And use the module values without any particular need to refer to
the
 instance variable. I think it's still time for a change like that, AFAIK
I'm
 the only one doing serious template work with D, and I can live with any
 change in the language. It would make the language simpler for template
 users (if we make matrix programming right we can get lots of people
 interested in doing scientific programming in D, it's my current goal).
Ah, parameterized modules! Interesting shorthand!
I realized that most of the time I wrote one template per module, for clarity. Before I didn't see D modules as ML modules, capable of parametrization. I think we can drop the template concept and favour a more expressive module system. This is better for allows library writers, because they don't need to write: module collections.set; import collections.collection; template TSet(T) { private instance TCollection(T) tcollection; private alias tcollection.Collection Collection; public class Set : Collection { ... } } Instead they can write: module collections.set(T); import collections.collection(T); public class Set : Collection { ... } and live with less baggage to keep in their heads when trying to define a simple generic set class. Best regards, Daniel Yokomiso. "My opinions may have changed, but not the fact that I am right." - Ashleigh Brilliant
Nov 27 2002
parent reply "Robert W. Cunningham" <FlyPG users.sourceforge.net> writes:
See below.

Daniel Yokomiso wrote:
 
 Reply embedded
 
...
     And the last thing, I'm stuck with a problem of them current
template
 syntax. Right now I have a template Matrix class defined for any kind of
 numeric type. But for some operations like the trace of the matrix (sum
of
 the main diagonal elements) I need to have a zero value like:
 T trace() {
     T sum = 0;
     // code to sum the main diagonal elements
     return sum;
 }
     For TMatrix(int) or TMatrix(float) instances this code is ok, but
when
 I
 start using it for user-defined classes that are not addable to 0, like
 Vector, Quaternion, Equation, etc. the code stops working. Right now I
can
 solve this by using a template variable to hold the value of zero and
code
 verifications to ensure that the zero value is defined before the
programmer
 uses a Matrix.
I don't understand.
In the template code I must declare a variable of type T that is initialized to some value. If T is a user-defined numeric type that isn't addable to number 0 (after all matrix members need only to be addable to their kind). Think of matrices of type-safe quantities, like Mass, Length or Time. Neither of these types are addable to numbers, althought they're addable to themselves. The literal 0 is used to define int zero, not Length 0. So I use this template variables to allow the user to define the interesting numbers (zero and one, all others are defined by those).
... Sorry this reply sounds over-simplified, but for "accumulation" operations (summing, adding to a group, etc.) I generally use assignment for the first element/item, and accumulation for all subsequent iterations. It takes a little extra work to ensure all types passed as template parameters support this use (all native types do), and I find it eliminates the need for many explicit initializations. It can also handle non-existant and soltiary items. -BobC
Nov 27 2002
parent reply "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
"Robert W. Cunningham" <FlyPG users.sourceforge.net> escreveu na mensagem
news:3DE55A59.2C9152BB users.sourceforge.net...
 See below.

 Daniel Yokomiso wrote:
 Reply embedded
...
     And the last thing, I'm stuck with a problem of them current
template
 syntax. Right now I have a template Matrix class defined for any
kind of
 numeric type. But for some operations like the trace of the matrix
(sum
 of
 the main diagonal elements) I need to have a zero value like:
 T trace() {
     T sum = 0;
     // code to sum the main diagonal elements
     return sum;
 }
     For TMatrix(int) or TMatrix(float) instances this code is ok,
but
 when
 I
 start using it for user-defined classes that are not addable to 0,
like
 Vector, Quaternion, Equation, etc. the code stops working. Right now
I
 can
 solve this by using a template variable to hold the value of zero
and
 code
 verifications to ensure that the zero value is defined before the
programmer
 uses a Matrix.
I don't understand.
In the template code I must declare a variable of type T that is initialized to some value. If T is a user-defined numeric type that
isn't
 addable to number 0 (after all matrix members need only to be addable to
 their kind). Think of matrices of type-safe quantities, like Mass,
Length or
 Time. Neither of these types are addable to numbers, althought they're
 addable to themselves. The literal 0 is used to define int zero, not
Length
 0. So I use this template variables to allow the user to define the
 interesting numbers (zero and one, all others are defined by those).
... Sorry this reply sounds over-simplified, but for "accumulation" operations (summing, adding to a group, etc.) I generally use assignment for the first element/item, and accumulation for all subsequent iterations. It takes a little extra work to ensure all types passed as template parameters support this use (all native types do), and I find it eliminates the need for many explicit initializations. It can also handle non-existant and soltiary items. -BobC
How do you handle empty lists with this scheme? Just curious. But this usage isn't useful in other contexts (eg. creating an identity matrix, you need explicit zeros and ones, or comparison to zero value).
Nov 27 2002
parent reply "Robert W. Cunningham" <FlyPG users.sourceforge.net> writes:
Daniel Yokomiso wrote:
 
 "Robert W. Cunningham" <FlyPG users.sourceforge.net> escreveu na mensagem
 news:3DE55A59.2C9152BB users.sourceforge.net...
 Sorry this reply sounds over-simplified, but for "accumulation"
 operations (summing, adding to a group, etc.) I generally use assignment
 for the first element/item, and accumulation for all subsequent
 iterations.

 It takes a little extra work to ensure all types passed as template
 parameters support this use (all native types do), and I find it
 eliminates the need for many explicit initializations.  It can also
 handle non-existant and soltiary items.
How do you handle empty lists with this scheme? Just curious. But this usage isn't useful in other contexts (eg. creating an identity matrix, you need explicit zeros and ones, or comparison to zero value).
In general, I always ensure all new "things" are initialized to null (zero, all bits clear, whatever). Generally, the compiler does this for me. So I get nice empty lists and zero matrices. The vast majority of my matrix applications have been in the realm of image processing, where I'm more interested in initialization to a multiplicative "no-op" (pass-thru) operation than I am in any specific mathematical value (it is a property of the higher level algorithm and/or specific algebra, and not of the lower-level numeric type). So for many pixel-processing operational vectors, this would set all elements to one in the constructor/initializer, and for convolution matrices it would set only the diagonals to one. Hmmm... I suppose I havent yet had the need to perform truly arbitrary algebraic operations using templates, where instantiation with ANY mathematical or numeric-like type should make sense. While arbitrary type instantiation works for things like collections, I can't think of any cases where it would need to work across algebras (such as with non-mathematical types), though your Haskell example of add() is a powerful one I'll be thinking about. However, it seems to me that operations that are general in theory are not all that general in practice. That is to say, algebras based on "simple" types (integers, rationals, reals) are fundamentally different (while having a confusingly similar "feel") to algebras for more complex entities (complex numbers, quaternions, vectors, matrices, etc.). Only the most basic operations would map, and much of the power of the underlying type would not be generally accessible within such a template instantiation. One thing I find I do with annoying frequency is to wrap simple numeric types in "true" object wrappers so they may be handled at a higher level. Languages that give such fundamental types (integers, floats, etc.) FULL (or close to full) object capabilities would, in general, be much more capable of doing what you describe. (Smalltalk, Ruby, etc.) Rather than a problem with templates, aren't we really talking about limitations of the D type system? Shouldn't EVERYTHING be an object, at least from a syntactic perspective? (The compiler can implement the semantics any way it chooses: I doubt integers will need vtables, but I'd like to use object notation to access a set of fundamental methods, ones I can override in my own derived types.) D has already started down this path for IEEE floats, extending the underlying physical representation to encompass "true" NAN support. I argued months ago for "smart integers" that, like D floats, could have explicit "uninitialized" or "Not-An-Integer" states/properties. This would allow integers to more easily participate in higher-level template use, as would any another object (having a mathematical/numeric/algebraic flavor). I suppose I'm uneasy with OO languages have non-OO fundamental types. This seems to be an inherent limitation of all OO languages that try to Adding the syntactic sugar to give ALL fundamental types full object properties may eliminate Daniel's concerns. Or am I missing Daniel's point? Let me back up a bit and tell where I'm coming from: I'm just a language practitioner, not a guru of any kind. I like simple language paradigms that have general (and powerful) application, and I detest special cases. For example, I detest operator precedence rules, so I go out of my way to make them irrelevant in my code, using whatever level of parenthesis nesting is needed to relieve anyone looking at the code of any need to remember language-specific idiosynchracies. Were I to have my way, I'd eliminate nearly all operator precedence rules from D. Another example: I detest implicit type conversion. I always use explicit casts to do what I want done. Again, the reader of the code will have no confusion, even if they are a language newbie. Were I to have my way, I'd eliminate all implicit type conversions from D. Both of these techniques add NOTHING to the compile time, and cause no difference in the generated code (optimizers are usually very smart these days). But I've seen misunderstood precedence rules and unexpected implicit conversions cause "stupid" coding mistakes that can ben needlessly difficult to find. Computer languages are NOT for computers: They are for computer programmers, for people. My code gets reviewed by my peers, by customers and partners, and by our legal department. I want it to be as unambiguous as possible. (This has nothing at all to do with "simple" code: My code is often VERY complex!) (My code is also used to train the people who will maintain it, so I can move on to new and exciting projects. If they can't understand it, I will be the one to maintain it.) Continuing this desire for uniformity: I want all types (fundamental and abstract) to have similar behavior, at least in the syntactical domain. If you have an OO paradigm, then use it everywhere. No special cases or limitations or dependencies! Again, such a paradigm would need to add NOTHING to compile time or code size/complexity. It is all just notation. For "fundamental" types it need be nothing more than syntactic sugar that maps to standard conventions or library calls. Templates are a notation with some peculiar characteristics, in that their "types" are actually meta-types (becoming concrete only upon instantiation). Template instantiation parameters should be as uniform as possible, in that all such types should possess a minimum set of common properties/capabilities (at least from the perspective of their use as template instantiation parameters). This is not the case in D, hence the problems instantiating a template that performs algebraic operations with an int or a matrix or any other mathematical type or entity. From my practitioner's perspective, the template use Daniel describes is intended to be for an algebraic operation that should be valid on all types that are sufficiently "numeric-like", from integers to matrices to quaternions to whatever. While the operation itself may indeed be valid (from a math-theoretical perspective, within a given algebra or set of algebras), the limitations of D's underlying type system makes a truly general implementation impractical. I don't see this as a problem with templates: Isn't the problem Daniel describes really more a problem with types in D? (With "mathematical" types in templates, in this particular case.) -BobC
Nov 28 2002
parent reply "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
"Robert W. Cunningham" <FlyPG users.sourceforge.net> escreveu na mensagem
news:3DE6E65F.E06792D1 users.sourceforge.net...
 Daniel Yokomiso wrote:
 "Robert W. Cunningham" <FlyPG users.sourceforge.net> escreveu na
mensagem
 news:3DE55A59.2C9152BB users.sourceforge.net...
 Sorry this reply sounds over-simplified, but for "accumulation"
 operations (summing, adding to a group, etc.) I generally use
assignment
 for the first element/item, and accumulation for all subsequent
 iterations.

 It takes a little extra work to ensure all types passed as template
 parameters support this use (all native types do), and I find it
 eliminates the need for many explicit initializations.  It can also
 handle non-existant and soltiary items.
How do you handle empty lists with this scheme? Just curious. But this
usage
 isn't useful in other contexts (eg. creating an identity matrix, you
need
 explicit zeros and ones, or comparison to zero value).
In general, I always ensure all new "things" are initialized to null (zero, all bits clear, whatever). Generally, the compiler does this for me. So I get nice empty lists and zero matrices. The vast majority of my matrix applications have been in the realm of image processing, where I'm more interested in initialization to a multiplicative "no-op" (pass-thru) operation than I am in any specific mathematical value (it is a property of the higher level algorithm and/or specific algebra, and not of the lower-level numeric type). So for many pixel-processing operational vectors, this would set all elements to one in the constructor/initializer, and for convolution matrices it would set only the diagonals to one.
Ah, I see. You always seems to use non-empty matrices, so it's not a problem to you. When you use matrix programming for image processing, which kind of operations you normally do? I'll release in the next days the DTL 0.0.2, inclunding a basic matrix class supporting generic operations. I'll be glad to include any particular operations (as functions, additional methods, etc.) to support real world needs.
 Hmmm...  I suppose I havent yet had the need to perform truly arbitrary
 algebraic operations using templates, where instantiation with ANY
 mathematical or numeric-like type should make sense.  While arbitrary
 type instantiation works for things like collections, I can't think of
 any cases where it would need to work across algebras (such as with
 non-mathematical types), though your Haskell example of add() is a
 powerful one I'll be thinking about.

 However, it seems to me that operations that are general in theory are
 not all that general in practice.  That is to say, algebras based on
 "simple" types (integers, rationals, reals) are fundamentally different
 (while having a confusingly similar "feel") to algebras for more complex
 entities (complex numbers, quaternions, vectors, matrices, etc.).  Only
 the most basic operations would map, and much of the power of the
 underlying type would not be generally accessible within such a template
 instantiation.
Yes it is usually a great problem when dealing with different algebras. A template Matrix must provide access only to the common denominator between all algebras, so if you are doing physics simulations with vector fields matrices, you'll be forced to explicit provide any additional operations.
 One thing I find I do with annoying frequency is to wrap simple numeric
 types in "true" object wrappers so they may be handled at a higher
 level.  Languages that give such fundamental types (integers, floats,
 etc.) FULL (or close to full) object capabilities would, in general, be
 much more capable of doing what you describe.  (Smalltalk, Ruby, etc.)

 Rather than a problem with templates, aren't we really talking about
 limitations of the D type system?  Shouldn't EVERYTHING be an object, at
 least from a syntactic perspective?  (The compiler can implement the
 semantics any way it chooses:  I doubt integers will need vtables, but
 I'd like to use object notation to access a set of fundamental methods,
 ones I can override in my own derived types.)
D is not my dream language. It's supposed to be better than C or C++ for systems programming, efficient and safe. I think the current design meets its goals. This template addition to the language is great, but unnecessary for systems programming. So IMHO it's a good compromise to have primitives in the language, instead of everything under the object flag. After all D is not a pure OO language, its base unit is the module, with support to module variables and module functions. After saying this, I must tell you that everything should be an object, not for 'purity' sake but because with full objects for fundamental types we can provide better semantics for our type system. My pet language (Eon) makes everything an object, supports multi-method dispatching and algebraic datatypes. Single dispatched methods are awful for numeric types. My goal is to help D become a better C, very fast and safe, with lots of expressive libraries, so I can write Eon compiler in D, instead of C or Java.
 D has already started down this path for IEEE floats, extending the
 underlying physical representation to encompass "true" NAN support.  I
 argued months ago for "smart integers" that, like D floats, could have
 explicit "uninitialized" or "Not-An-Integer" states/properties.  This
 would allow integers to more easily participate in higher-level template
 use, as would any another object (having a
 mathematical/numeric/algebraic flavor).

 I suppose I'm uneasy with OO languages have non-OO fundamental types.
 This seems to be an inherent limitation of all OO languages that try to

 Adding the syntactic sugar to give ALL fundamental types full object
 properties may eliminate Daniel's concerns.
<Digression size='large' subject='my concerns about type systems'> A proper type system must be included when you give fundamental types object status. Say you have a hierarchy like: Numeric <- Complex <- Real <- Rational <- Integer <- Natural You can observe that other properties aren't really defined in this hierarchy. A view of properties, unrelated to arithmetic, may be: Comparable <- Bounded <--- Enumerable Distinct <--/ ^----- Discrete Ordinal <----/ // Comparable defines a type protocol capable of comparision with itself. template TComparable(T : Comparable) { abstract class Comparable { abstract int cmp(T other); } } // Bounded defines a type that has an upper limit and a lower limit, like ASCII chars or values of sin. template TBounded(T : Bounded) { instance TComparable(T) comp; abstract class Bounded : comp.Comparable{ abstract T min(); // Both max and min should be virtual constructors, but this is not important abstract T max(); invariant { assert(min() <= this); assert(this <= max()); assert(min() <= max()); } } } // Distinct defines a type that has an successor and a predecessor value, like week days or integral numbers. template TDistinct(T : Distinct) { abstract class Distinct { abstract T successor(); abstract T predecessor(); invariant { assert(successor().predecessor() == this); } } } // Ordinal defines a ordinal type, with a first value and successive, ordered values, like prime numbers. template TOrdinal(T : Ordinal) { abstract class Ordinal { abstract T first(); // should be a virtual constructor abstract T successor(); invariant { assert(first() != successor()); assert(this != successor()); } } } // Enumerable defines a type that can be finitely counted. template TEnumerable(T : Enumerable) { instance TBounded(T) bound; instance TDistinct(T) dist; abstract class Enumerable: bound.Bounded, dist.Distinct { invariant { assert(min().predecessor() == max()); assert(max().successor() == min()); if (this != max()) { assert(this < successor()); } if (this != min()) { assert(predecessor() < this); } } } } // Discrete defines a type that can be is infinite but has non-continuous values, like integral types. template TDiscrete(T : Discrete) { instance TOrdinal(T) ord; instance TDistinct(T) dist; abstract class Discrete: ord.Ordinal, dist.Distinct { abstract int sub(T other); // provides the distance between two discrete values out(result) { if (this == other) { assert(result == 0); } else if (this < other) { assert(result < 0); } else { assert(result > 0); } } T neg() { int amount = this - first(); return this - (amount * 2); } T sub(int amount) out(result) { assert((this - result) == amount); } body { if (amount < 0) { return this + (-amount); } else if (amount == 0) { return this; } else { return predecessor() - (amount - 1); } } T add(int amount) out(result) { assert((result - this) == amount); } body { if (amount < 0) { return this - (-amount); } else if (amount == 0) { return this; } else { return successor() + (amount - 1); } } invariant { assert(predecessor() < this); assert(this < successor()); } } } Looking this two type hierarchies we can say that: Comparable(Real) <- Real Discrete(Integer) <- Integer Ordinal(Natural) <- Ordinal So we need some type of variance method types in subtypes (covariance for result and contravariance for parameters). Also we can see that two different concerns produced two different type hierarchies that model properties of one another. My concerns are more fundamental than simple sintatic sugar or make primitives objects. This kind of distinct views on types and properties of types can define several different type hierarchies. More complex, less studied types, like collection types, can produce another type of hierarchies. Furthermore other types can suffer from this concerns. A template Range type is defined for every Comparable type. But if the type is Discrete too, the Range can have a size() operation that return the distance between its lower and upper members. Collections of comparable types can have min, max and bounds methods, but this operations are meaningless to non comparable collections (like Buttons, or Bitmaps). It's very difficult to provide a comprehensive, extensible and statically-safe type system. No language today provides an easy answer to all this questions. </Digression>
 Or am I missing Daniel's point?  Let me back up a bit and tell where I'm
 coming from:

 I'm just a language practitioner, not a guru of any kind.  I like simple
 language paradigms that have general (and powerful) application, and I
 detest special cases.

 For example, I detest operator precedence rules, so I go out of my way
 to make them irrelevant in my code, using whatever level of parenthesis
 nesting is needed to relieve anyone looking at the code of any need to
 remember language-specific idiosynchracies.  Were I to have my way, I'd
 eliminate nearly all operator precedence rules from D.

 Another example:  I detest implicit type conversion.  I always use
 explicit casts to do what I want done.  Again, the reader of the code
 will have no confusion, even if they are a language newbie.  Were I to
 have my way, I'd eliminate all implicit type conversions from D.

 Both of these techniques add NOTHING to the compile time, and cause no
 difference in the generated code (optimizers are usually very smart
 these days).  But I've seen misunderstood precedence rules and
 unexpected implicit conversions cause "stupid" coding mistakes that can
 ben needlessly difficult to find.
I agree with both statements. i'm too a practicioner, not a guru. Operator precedence always puzzles me, so I just puts parenthesis in almost everything.
 Computer languages are NOT for computers:  They are for computer
 programmers, for people.  My code gets reviewed by my peers, by
 customers and partners, and by our legal department.  I want it to be as
 unambiguous as possible.  (This has nothing at all to do with "simple"
 code:  My code is often VERY complex!)  (My code is also used to train
 the people who will maintain it, so I can move on to new and exciting
 projects.  If they can't understand it, I will be the one to maintain
 it.)

 Continuing this desire for uniformity: I want all types (fundamental and
 abstract) to have similar behavior, at least in the syntactical domain.
 If you have an OO paradigm, then use it everywhere.  No special cases or
 limitations or dependencies!  Again, such a paradigm would need to add
 NOTHING to compile time or code size/complexity.  It is all just
 notation.  For "fundamental" types it need be nothing more than
 syntactic sugar that maps to standard conventions or library calls.
This is not true in some senses. If you have a Int class, for int32 values, what should be the expected behaviour when I write a MyInt class that has mutable properties, and try to add an Int to MyInt? Eiffel try to provide answers for this with expanded types, but this has problems of its own. Sather solution is better, but is much more complex for the novice. Functional languages don't have this problem, but they usually provide few (if any) mutable values. Of course you can make all this classes final, and don't bother with this.
 Templates are a notation with some peculiar characteristics, in that
 their "types" are actually meta-types (becoming concrete only upon
 instantiation).  Template instantiation parameters should be as uniform
 as possible, in that all such types should possess a minimum set of
 common properties/capabilities (at least from the perspective of their
 use as template instantiation parameters).  This is not the case in D,
 hence the problems instantiating a template that performs algebraic
 operations with an int or a matrix or any other mathematical type or
 entity.
Yes, numerical templates are very complicated to use, mainly because a numeric class will have its own operations (sqrt, pow, gcd, etc.), but primitives uses the operations from math module. There are some solutions to this problem, but they are all patches to the symptoms not the problems.
 From my practitioner's perspective, the template use Daniel describes is
 intended to be for an algebraic operation that should be valid on all
 types that are sufficiently "numeric-like", from integers to matrices to
 quaternions to whatever.  While the operation itself may indeed be valid
 (from a math-theoretical perspective, within a given algebra or set of
 algebras), the limitations of D's underlying type system makes a truly
 general implementation impractical.
Not so impractical from my experience ;-)
 I don't see this as a problem with templates:  Isn't the problem Daniel
 describes really more a problem with types in D?  (With "mathematical"
 types in templates, in this particular case.)


 -BobC
Well, I think this problem is on both D's type system and templates. But these are hard to solve problem's, and I want solutions, so extending D's template mechanism is better than extending it's type system. AFAIK Walter is an compiler expert for C-like languages, so his experience allows him to write a powerful optimizing compiler for code using primitives. I agree with your concerns about expressiveness of D's type system, but IMO we need both a powerful parametrized module mechanism and a comprehensive type system. Changing templates is easier, because we have lots of different languages implementing great solutions for this problem. Changin the type system is harder, and Walter is an one-man army. But let us keep the discussion level high and the ideas flowing, maybe we can give this problem a simple, fast and safe solution. Best regards, Daniel Yokomiso. "Human beings, who are almost unique in having the ability to learn from the experience of others, are also remarkable for their apparent disinclination to do so." - Douglas Adams, Last Chance to See
Nov 29 2002
next sibling parent reply "Robert W. Cunningham" <FlyPG users.sourceforge.net> writes:
I should have prefaced my prior comments with one item that bears
frequent repeating:  Walter intends D to be a Systems Programming
language first, and a applications language second.  And that's what I
need most:  A language that's safe to use all the way down to the bare
hardware, one that provides maximum benefit and performance for every
feature it acquires.

For example, having GC in a systems programming language is heretical
enough.  But since D provides some control over when and how the GC runs
(and one day may even let me install my own GC), I am very glad to have
it available, since only a relatively small portion of systems
programming code would be negatively impacted by GC, and much of it (the
part that looks more like application programming) would benefit
greatly.  So long as I can handle those special situations within D,
then D is right for me.

While it is true that vastly more applications are written than systems
(such as operating systems), systems programming places special demands
on a language.  Even given omniscient optimizers, I suspect there are
some type systems that won't map to any language that can truly be
called (and used as) a systems programming language.

Practicality and efficiency in implementation and execution will often
need to trump any type system (and template system) concerns, for
example, when such concerns affect the language's ability to still be a
systems programming language (not necessarily the case here).

Of the zillions of languages available, only a handful are suited for
systems programming, which includes application domains such as
Operating Systems (Linux), Embedded Systems (digital cameras), Real-Time
Systems (automotive control), and all the related domains that don't map
neatly into the prior categories.  My professional experience reveals a
dire need for better systems programming languages!

What template and type systems will "fit" within a language intended to
broadly address the needs of systems programming?

Basically, if Walter doesn't know of (and nobody can show him) an
elegant and efficient (OK, even just workable) way to implement a
feature, it won't become part of D.  Many truly great ideas have had to
be set aside when they were shown to be incompatible with any reasonable
implementation of a systems programming language, and/or the fast,
simple and efficient one-pass compilation architecture Walter has
selected for D.

Fitting "smarter" types and templates into D's environment is indeed a
challenge, independent of the merit of any specific recommendation.  I
try to push for the "low-hanging fruit", relatively small type system
changes that would have exceptionally powerful application (for me, at
least).  (More often, my recommendations are limited by my language
knowledge and experience, since I'm no language guru.)

When candidate D features are being discussed, I try to imagine specific
systems programming instances where they might be useful.  For Daniel's
templates, I've been imagining a very high-level file system (rather
than the specific instance of high-level numerics, something that is
seldom a systems programming issue).  Such a file system would have to
perform operations on an unlimited range of content while at the same
time delivering efficient performance.

That's a tough nut to crack!  The slow evolution of practical file
systems attests to the difficulty of the task.  What language features
could make it easier?  This is one area of systems programming where the
power, flexibility and effectiveness of a language's type system could
have a large impact.

So, when I mention that something would have limited applicability, I'm
implicitly limiting the scope of my comments to the systems programming
domain of D, and not to application programming in general.

Now, within that domain, I want there to exist every feature that can
possibly "fit", where "fit" itself has a very specific scope.  It would
appear that any new feature must:

1. "Fit" with the rest of D (not appear to be a wart on the language).
2. "Fit" with Walter's single-pass compiler implementation.
3. "Fit" with (or at least not interfere with) D's use in Systems
Programming.
4. "Fit" with Walter's ability (time, knowledge) and desire to implement
it.

Getting back to the subject of this thread (and if you've read this far,
you must have been wondering if I ever would):

Can D templates and/or type system be improved (particularly for
mathematical/algebraic use) in a way that "fits"?  If so, in what ways,
and how can it be accomplished?

From what I've seen so far, syntactic sugar represents a reasonable
class of implementation strategies that can yield disproportionately
large benefits compared to the cost of implementation.  Going beyond
syntactic sugar could require a complete re-design and/or
re-implementation of the D type system!

While such radical reworking is far from impossible (it has already been
done a time or two), at this point I suspect such a level of effort
would probably keep a feature off of the priority list for some time.

Can we resolve Daniel's concerns with minimal and simple changes to D? 
If yes, then Walter would probably implement it just to let us play with
it.  If no, then greater justification for the effort would be needed.

Would resolving Daniel's concerns make D a significantly better systems
programming language?  If yes, then the additional implementation effort
may be warranted.  So far, I haven't been able to come up with a
scenario where I could make use of such changes within a systems
programming environment (my file system scenario is the closest I've
thought of, and it isn't adequate to provide a need for the feature).

That said, Walter DOES want D to be a powerful numerics language, even
outside the systems programming domain (the two domains share needs for
flexibility and efficiency).  Being able to effectively and efficiently
handle matrices as a native (or near-native) type (instead of a layer
atop fundamental types), especially within templates, would have direct
practical implications if it could, for example, relieve programmers of
having to write myriad low-level loops to step through matrix elements
(something at which languages such as J and APL excel).

A closely related example would be D's inclusion of array slicing.  The
current implementation is generally fast and efficient, but
intentionally limited.  It's lack of complete generality is due not to
any lack of desire for a universal solution, but is instead due to the
lack of an efficient implementation for anything that goes much beyond
what presently exists (or is planned).

I'm still thinking on that one:  There are many systems programing
situations where more powerful array slicing would be useful, especially
if combined with associative arrays.  But such situations are still very
rare and are often unique, which means special-purpose implementations
will probably be needed even if the language feature does exist.  So the
payback for the feature diminishes with its lack of generality,
independent of its power and elegance.

Can Daniel's concerns with templates and matrices be generalized
further?


-BobC
Nov 30 2002
next sibling parent reply "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
"Robert W. Cunningham" <FlyPG users.sourceforge.net> escreveu na mensagem
news:3DE9A868.79E9287B users.sourceforge.net...

[snipped with lots of agreement]

 That said, Walter DOES want D to be a powerful numerics language, even
 outside the systems programming domain (the two domains share needs for
 flexibility and efficiency).  Being able to effectively and efficiently
 handle matrices as a native (or near-native) type (instead of a layer
 atop fundamental types), especially within templates, would have direct
 practical implications if it could, for example, relieve programmers of
 having to write myriad low-level loops to step through matrix elements
 (something at which languages such as J and APL excel).
If we implement matrices as yet another native type we have two problems: first More work to Walter and to any D compiler writers. It's better to have a standard Matrix generic class, with strictly defined semantics, and then when someone wants to implement a numerics specialized compiler, they can do tricks to matrix code compiling that respect the standard semantics; second everyone will want yet another primitive type for their own needs. Some will want a 3D vector, while others will prefer a more generic tensor type. I think it's better to allow people to define their own generic numeric types with a primitive 'look and feel' (i.e.: writing 'Matrix a = b[1..10][5..20];' for a multi-dimensional slice). Blitz++ show us a powerful library using templates to implement a highly-efficient and intuitive multi-dimensional Array type.
 A closely related example would be D's inclusion of array slicing.  The
 current implementation is generally fast and efficient, but
 intentionally limited.  It's lack of complete generality is due not to
 any lack of desire for a universal solution, but is instead due to the
 lack of an efficient implementation for anything that goes much beyond
 what presently exists (or is planned).
I'm not sure I understand you here. Could you give me an example of current limitations (I know some, like multi-dimensional slices, but maybe you're talking about something else here).
 I'm still thinking on that one:  There are many systems programing
 situations where more powerful array slicing would be useful, especially
 if combined with associative arrays.  But such situations are still very
 rare and are often unique, which means special-purpose implementations
 will probably be needed even if the language feature does exist.  So the
 payback for the feature diminishes with its lack of generality,
 independent of its power and elegance.
If a feature is good but not generic enough, let us have a standard function and/or classes to provide this implementation. If time say this feature should be a primitive, revisions of the standard will let us make it so. I prefer to remove all primitives in the D standard, and have a bunch of classes with standardized semantics. This way we can incorporate D's primitive array and associative array in a collections hierarchy. But this is just another idea.
 Can Daniel's concerns with templates and matrices be generalized
 further?
Yes, I think they can, but also I think it's not a concern for D (at least this version). If we put all features in the world in D, we'll discover ourselves creating a new Perl. IMHO D's type system has only one big hole today: function types. D's template system is verbose but highly usable and 10 times better than no genericity system whatsoever. IMO let D be a safe, efficient systems language and use it as intermediate language for other language compilers. This way everyone will be happy. If don't agree with D semantics (or just think there's a better option, like me) you can use D and trust the efficiency and correctness of the generated code.
 -BobC
Best regards, Daniel Yokomiso. "Happiness is when what you think, what you say, and what you do are in harmony." - Mohandas Gandi
Dec 01 2002
next sibling parent reply "Sean L. Palmer" <seanpalmer directvinternet.com> writes:
Matrices and vectors, at least the fixed-size versions, need to be
implemented as structs, not classes, as they are value types.  There are
some important limitations with structs right now that can cause problems.
In an ideal world, in a language which has a uniform type system, I would
have matrix or vector inherit directly from a fixed array.

Sean

"Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> wrote in message
news:ase4eq$4tk$1 digitaldaemon.com...
 "Robert W. Cunningham" <FlyPG users.sourceforge.net> escreveu na mensagem
 news:3DE9A868.79E9287B users.sourceforge.net...

 [snipped with lots of agreement]

 That said, Walter DOES want D to be a powerful numerics language, even
 outside the systems programming domain (the two domains share needs for
 flexibility and efficiency).  Being able to effectively and efficiently
 handle matrices as a native (or near-native) type (instead of a layer
 atop fundamental types), especially within templates, would have direct
 practical implications if it could, for example, relieve programmers of
 having to write myriad low-level loops to step through matrix elements
 (something at which languages such as J and APL excel).
If we implement matrices as yet another native type we have two problems: first More work to Walter and to any D compiler writers. It's better to have a standard Matrix generic class, with strictly defined semantics, and then when someone wants to implement a numerics specialized compiler, they can do tricks to matrix code compiling that respect the standard semantics; second everyone will want yet another primitive type
for
 their own needs. Some will want a 3D vector, while others will prefer a
more
 generic tensor type. I think it's better to allow people to define their
own
 generic numeric types with a primitive 'look and feel' (i.e.: writing
 'Matrix a = b[1..10][5..20];' for a multi-dimensional slice). Blitz++ show
 us a powerful library using templates to implement a highly-efficient and
 intuitive  multi-dimensional Array type.


 A closely related example would be D's inclusion of array slicing.  The
 current implementation is generally fast and efficient, but
 intentionally limited.  It's lack of complete generality is due not to
 any lack of desire for a universal solution, but is instead due to the
 lack of an efficient implementation for anything that goes much beyond
 what presently exists (or is planned).
I'm not sure I understand you here. Could you give me an example of current limitations (I know some, like multi-dimensional slices, but maybe you're talking about something else here).
 I'm still thinking on that one:  There are many systems programing
 situations where more powerful array slicing would be useful, especially
 if combined with associative arrays.  But such situations are still very
 rare and are often unique, which means special-purpose implementations
 will probably be needed even if the language feature does exist.  So the
 payback for the feature diminishes with its lack of generality,
 independent of its power and elegance.
If a feature is good but not generic enough, let us have a standard function and/or classes to provide this implementation. If time say this feature should be a primitive, revisions of the standard will let us make
it
 so. I prefer to remove all primitives in the D standard, and have a bunch
of
 classes with standardized semantics. This way we can incorporate D's
 primitive array and associative array in a collections hierarchy. But this
 is just another idea.

 Can Daniel's concerns with templates and matrices be generalized
 further?
Yes, I think they can, but also I think it's not a concern for D (at least this version). If we put all features in the world in D, we'll discover ourselves creating a new Perl. IMHO D's type system has only one big hole today: function types. D's template system is verbose but highly usable and 10 times better than no genericity system whatsoever. IMO let D be a safe, efficient systems language and use it as intermediate language for other language compilers. This way everyone will be happy. If don't agree with D semantics (or just think there's a better option, like me)
you
 can use D and trust the efficiency and correctness of the generated code.

 -BobC
Best regards, Daniel Yokomiso. "Happiness is when what you think, what you say, and what you do are in harmony." - Mohandas Gandi
Dec 01 2002
parent reply "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
"Sean L. Palmer" <seanpalmer directvinternet.com> escreveu na mensagem
news:asec60$hfp$1 digitaldaemon.com...
 Matrices and vectors, at least the fixed-size versions, need to be
 implemented as structs, not classes, as they are value types.  There are
 some important limitations with structs right now that can cause problems.
 In an ideal world, in a language which has a uniform type system, I would
 have matrix or vector inherit directly from a fixed array.

 Sean
What do you mean by 'value types'? Like a fixed size array is a value type? Or in the sense of a integer being a value type? I understand a value type to be a unmodifiable type, generating functional objects without side-effects. So a value type for matrices would give us a side-effect free matrix type. AFAIK D's structs aren't true value types, they just let you use the stack instead of the heap. Best regards, Daniel Yokomiso. "Jesus is coming, everyone look busy."
Dec 02 2002
parent reply "Sean L. Palmer" <seanpalmer directvinternet.com> writes:
"Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> wrote in message
news:asgbf5$2uov$2 digitaldaemon.com...
 "Sean L. Palmer" <seanpalmer directvinternet.com> escreveu na mensagem
 news:asec60$hfp$1 digitaldaemon.com...
 Matrices and vectors, at least the fixed-size versions, need to be
 implemented as structs, not classes, as they are value types.  There are
 some important limitations with structs right now that can cause
problems.
 In an ideal world, in a language which has a uniform type system, I
would
 have matrix or vector inherit directly from a fixed array.

 Sean
What do you mean by 'value types'? Like a fixed size array is a value type? Or in the sense of a integer being a value type? I understand a value type to be a unmodifiable type, generating functional objects without side-effects. So a value type for matrices
would
 give us a side-effect free matrix type.
No a value type is as opposed to a reference type. Any two copies of the same value are as good as each other and are functionally interchangeable. Classes usually have some other wierdnesses inside with references and whatnot so that a copy of a class isn't functionally identical to the original class; oftentimes the copy will share data with the original somehow or be linked to it in some way. Like ints, as long as the bits are the same, who cares if you have the exact same int value or not? If I have two identity matrices, does it matter which one I use? No.
     AFAIK D's structs aren't true value types, they just let you use the
 stack instead of the heap.
And you can embed a struct directly into another struct, not just a pointer to it. Those two characteristics enable you to save lots of memory and needless pointer dereferencing. If you want a struct on the heap you can always do new struct and store a pointer to it. You can't put a class on the stack, or embed a class directly in another class except by inheritance. Seems a needless restriction. Sean
Dec 03 2002
parent "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <seanpalmer directvinternet.com> wrote in message
news:asi0nc$1v8i$1 digitaldaemon.com...
 You can't put a class on
 the stack, or embed a class directly in another class except by
inheritance.
 Seems a needless restriction.
The reason for that is to eliminate the need for copy constructors, assignment operator overloading, and the proposed C++ "move" constructors. Many C++ classes are dominated by such various bookkeeping functions that obscure the point of the class and leave many opportunities for bugs. This is also why structs do not have constructors.
Jan 09 2003
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> wrote in message
news:ase4eq$4tk$1 digitaldaemon.com...
 I prefer to remove all primitives in the D standard, and have a bunch of
 classes with standardized semantics.
One difficulty with that approach is that the compiler will be unable to produce coherent error messages, sort of like what happens when you use C++ STL incorrectly. Build the basics into the compiler, such as strings, and it'll be a lot easier to develop user code.
Jan 09 2003
parent reply "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
"Walter" <walter digitalmars.com> escreveu na mensagem
news:avktc3$28b7$2 digitaldaemon.com...
 "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> wrote in message
 news:ase4eq$4tk$1 digitaldaemon.com...
 I prefer to remove all primitives in the D standard, and have a bunch of
 classes with standardized semantics.
One difficulty with that approach is that the compiler will be unable to produce coherent error messages, sort of like what happens when you use
C++
 STL incorrectly. Build the basics into the compiler, such as strings, and
 it'll be a lot easier to develop user code.
Which kinds of incoherent error messages we could get with a value class Int32 instead of a int primitive? Or an array template instead of a basic array? Standardized classes are better for understanding types and operations, an implementer could assume anything allowed by the spec in his implementation. --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.435 / Virus Database: 244 - Release Date: 30/12/2002
Jan 09 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> wrote in message
news:avkul2$295a$1 digitaldaemon.com...
 Which kinds of incoherent error messages we could get with a value class
 Int32 instead of a int primitive? Or an array template instead of a basic
 array? Standardized classes are better for understanding types and
 operations, an implementer could assume anything allowed by the spec in
his
 implementation.
I don't have any examples offhand. But every time I get an error message from STL, it's "here we go again" trying to figure out what went wrong. Some of the mangled names are over a thousand characters long.
Jan 11 2003
next sibling parent reply "Sean L. Palmer" <seanpalmer directvinternet.com> writes:
That's just because the lazy compilers just spit out the raw type rather
than searching for or remembering a suitable typedef name.  It can be
handled better.

The only reason they're mangled is because of the legacy C linker.  D won't
have that issue.

Sean

"Walter" <walter digitalmars.com> wrote in message
news:avpnhb$23d1$1 digitaldaemon.com...
 I don't have any examples offhand. But every time I get an error message
 from STL, it's "here we go again" trying to figure out what went wrong.
Some
 of the mangled names are over a thousand characters long.
Jan 11 2003
next sibling parent "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
"Sean L. Palmer" <seanpalmer directvinternet.com> escreveu na mensagem
news:avpo2h$23fi$1 digitaldaemon.com...
 That's just because the lazy compilers just spit out the raw type rather
 than searching for or remembering a suitable typedef name.  It can be
 handled better.

 The only reason they're mangled is because of the legacy C linker.  D
won't
 have that issue.

 Sean

 "Walter" <walter digitalmars.com> wrote in message
 news:avpnhb$23d1$1 digitaldaemon.com...
 I don't have any examples offhand. But every time I get an error message
 from STL, it's "here we go again" trying to figure out what went wrong.
Some
 of the mangled names are over a thousand characters long.
Hi, I think it's also have something to do with C++ templates not being type safe: you can use any type parameter to instantiate a template, the compiler isn't expecting a particular type hierarchy. So all assumptions about operations needed by the template parameter must be figured out by the template writer and it's users. It gets messy when a template instantiate another template that instantiates another template that... until you have some strange combination of type requirements and your parameter fails with something (or worse silently does something semantically wrong, but sintatically correct). Sometimes I try to follow STL or Blitz++ template instantiations trying to figure out a particular algorithm. Most of the times I just quit. Best regards, Daniel Yokomiso. "Sometimes I lie awake at night, and I ask, 'Where have I gone wrong?' Then a voice says to me, 'This is going to take more than one night." - Charles Schulz --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.435 / Virus Database: 244 - Release Date: 30/12/2002
Jan 12 2003
prev sibling parent "Sean L. Palmer" <seanpalmer directvinternet.com> writes:
FWIW, VC .NET actually shows the typedef names in the error messages and in
the debugger.  That helps *alot*.

Sean

"Sean L. Palmer" <seanpalmer directvinternet.com> wrote in message
news:avpo2h$23fi$1 digitaldaemon.com...
 That's just because the lazy compilers just spit out the raw type rather
 than searching for or remembering a suitable typedef name.  It can be
 handled better.

 The only reason they're mangled is because of the legacy C linker.  D
won't
 have that issue.

 Sean

 "Walter" <walter digitalmars.com> wrote in message
 news:avpnhb$23d1$1 digitaldaemon.com...
 I don't have any examples offhand. But every time I get an error message
 from STL, it's "here we go again" trying to figure out what went wrong.
Some
 of the mangled names are over a thousand characters long.
Jan 12 2003
prev sibling parent "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
"Walter" <walter digitalmars.com> escreveu na mensagem
news:avpnhb$23d1$1 digitaldaemon.com...
 "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> wrote in message
 news:avkul2$295a$1 digitaldaemon.com...
 Which kinds of incoherent error messages we could get with a value class
 Int32 instead of a int primitive? Or an array template instead of a
basic
 array? Standardized classes are better for understanding types and
 operations, an implementer could assume anything allowed by the spec in
his
 implementation.
I don't have any examples offhand. But every time I get an error message from STL, it's "here we go again" trying to figure out what went wrong.
Some
 of the mangled names are over a thousand characters long.
hey, you should do what the C++ professionals do: write a perl script that will munge the error message and provide a simpler version ;-) --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.435 / Virus Database: 244 - Release Date: 30/12/2002
Jan 12 2003
prev sibling parent "Walter" <walter digitalmars.com> writes:
Couldn't have said it better myself. Though I'd like to add that many, many
applications have portions of them that require system programming features.
The need to dip into assembler now and then is one of them.
Jan 09 2003
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> wrote in message
news:as8274$hck$1 digitaldaemon.com...
 But let us keep the discussion level
 high and the ideas flowing, maybe we can give this problem a simple, fast
 and safe solution.
Yes, I enjoy this discussion quite a bit.
Dec 29 2002
parent "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
"Walter" <walter digitalmars.com> escreveu na mensagem
news:aunk3v$be4$1 digitaldaemon.com...
 "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> wrote in message
 news:as8274$hck$1 digitaldaemon.com...
 But let us keep the discussion level
 high and the ideas flowing, maybe we can give this problem a simple,
fast
 and safe solution.
Yes, I enjoy this discussion quite a bit.
I'm waiting for the 0.51 compiler before continuing with this discussion. Maybe new features in it will drive this conversation to other areas. Just a wish: join delegates and function pointers under a single syntax for function types, so we can write a single template for both kinds of functions. Also a convenient way to express function types is needed, because writing: instance myTemplate(int (*fp)(int, int)) int_int_int; is very strange. --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.434 / Virus Database: 243 - Release Date: 25/12/2002
Dec 29 2002