D - Templates and matrices
- Daniel Yokomiso (40/40) Nov 25 2002 Hi,
- Walter (8/48) Nov 26 2002 You have some good ideas. I've been looking at some improvements to the
- Daniel Yokomiso (72/75) Nov 26 2002 Hi,
- Walter (14/59) Nov 26 2002 Because I have failed to write a typeinfo for arrays of ints :-( See the
- Burton Radons (44/87) Nov 27 2002 Hooray! The more you have to special-case the easier a proper
- Daniel Yokomiso (76/137) Nov 27 2002 Reply embbeded.
- Walter (4/6) Dec 28 2002 be:
- Burton Radons (2/11) Dec 28 2002 float.init is not zero.
- Walter (3/14) Dec 28 2002 That's right. I must have misunderstood your point then!
- Burton Radons (17/37) Dec 29 2002 You read right over it, unless if you're randomly accessing messages.
- Walter (6/43) Jan 07 2003 Ok, I'll have to think about that some more. I want to be careful adding...
- Burton Radons (57/59) Jan 08 2003 We could entertain extensions:
- Sean L. Palmer (15/74) Jan 08 2003 This line of reasoning looks promising. This has been requested before,...
- Daniel Yokomiso (33/92) Jan 09 2003 in
- Daniel Yokomiso (58/124) Nov 27 2002 Reply embedded
- Robert W. Cunningham (13/49) Nov 27 2002 ...
- Daniel Yokomiso (14/63) Nov 27 2002 kind of
- Robert W. Cunningham (104/120) Nov 28 2002 In general, I always ensure all new "things" are initialized to null
- Daniel Yokomiso (207/327) Nov 29 2002 mensagem
- Robert W. Cunningham (114/114) Nov 30 2002 I should have prefaced my prior comments with one item that bears
-
Daniel Yokomiso
(39/63)
Dec 01 2002
"Robert W. Cunningham"
escreveu na mensage... - Sean L. Palmer (14/77) Dec 01 2002 Matrices and vectors, at least the fixed-size versions, need to be
- Daniel Yokomiso (12/18) Dec 02 2002 What do you mean by 'value types'? Like a fixed size array is a valu...
- Sean L. Palmer (20/37) Dec 03 2002 problems.
- Walter (8/11) Jan 09 2003 inheritance.
- Walter (6/8) Jan 09 2003 One difficulty with that approach is that the compiler will be unable to
- Daniel Yokomiso (12/20) Jan 09 2003 C++
- Walter (6/11) Jan 11 2003 his
- Sean L. Palmer (9/12) Jan 11 2003 That's just because the lazy compilers just spit out the raw type rather
- Daniel Yokomiso (24/36) Jan 12 2003 won't
- Sean L. Palmer (6/18) Jan 12 2003 FWIW, VC .NET actually shows the typedef names in the error messages and...
- Daniel Yokomiso (10/21) Jan 12 2003 basic
- Walter (3/3) Jan 09 2003 Couldn't have said it better myself. Though I'd like to add that many, m...
- Walter (3/6) Dec 29 2002 Yes, I enjoy this discussion quite a bit.
- Daniel Yokomiso (15/21) Dec 29 2002 fast
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
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 everyarraydimension used. This last solution is similar to C++ usage of templateswithint 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 programminglikein 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
"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. -WalterHi, 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
"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 whenIstart 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 theprogrammeruses 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, AFAIKI'mthe 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
Walter wrote:"Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> wrote in message news:as1944$1e3u$2 digitaldaemon.com...Hooray! The more you have to special-case the easier a proper implementation will be in comparison. ;-)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.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.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.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.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.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.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 whenIstart 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 theprogrammeruses a Matrix.I don't understand.
Nov 27 2002
Reply embbeded. "Burton Radons" <loth users.sourceforge.net> escreveu na mensagem news:as2d4u$2koh$1 digitaldaemon.com...Walter wrote:[snip]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]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.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.templateAnd the last thing, I'm stuck with a problem of them currentofsyntax. 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 (sumwhenthe 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, butcanIstart using it for user-defined classes that are not addable to 0, like Vector, Quaternion, Equation, etc. the code stops working. Right now Icodesolve this by using a template variable to hold the value of zero andbe: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 wouldverifications to ensure that the zero value is defined before theprogrammeruses a Matrix.I don't understand.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
"Burton Radons" <loth users.sourceforge.net> wrote in messageHowsabout the builtin types have an attribute ".zero" that we can put into our user types as static variables/constants. Then that line wouldbe: All types have an attribute .init which gives their default initalizer value.
Dec 28 2002
Walter wrote:"Burton Radons" <loth users.sourceforge.net> wrote in messagefloat.init is not zero.Howsabout the builtin types have an attribute ".zero" that we can put into our user types as static variables/constants. Then that line wouldbe: All types have an attribute .init which gives their default initalizer value.
Dec 28 2002
"Burton Radons" <loth users.sourceforge.net> wrote in message news:aul9s3$1pkf$1 digitaldaemon.com...Walter wrote:That's right. I must have misunderstood your point then!"Burton Radons" <loth users.sourceforge.net> wrote in messagefloat.init is not zero.Howsabout the builtin types have an attribute ".zero" that we can put into our user types as static variables/constants. Then that line wouldbe: All types have an attribute .init which gives their default initalizer value.
Dec 28 2002
Walter wrote:"Burton Radons" <loth users.sourceforge.net> wrote in message news:aul9s3$1pkf$1 digitaldaemon.com...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.Walter wrote:That's right. I must have misunderstood your point then!"Burton Radons" <loth users.sourceforge.net> wrote in messagefloat.init is not zero.Howsabout the builtin types have an attribute ".zero" that we can put into our user types as static variables/constants. Then that line wouldbe: All types have an attribute .init which gives their default initalizer value.
Dec 29 2002
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:would"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 messageHowsabout the builtin types have an attribute ".zero" that we can put into our user types as static variables/constants. Then that line.one.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 aThat's right. I must have misunderstood your point then!be: All types have an attribute .init which gives their default initalizer value.float.init is not zero.
Jan 07 2003
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
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:inOk, I'll have to think about that some more. I want to be careful addingmore . 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
"Burton Radons" <loth users.sourceforge.net> escreveu na mensagem news:avimh0$vks$2 digitaldaemon.com...Walter wrote:inOk, I'll have to think about that some more. I want to be careful addingHi, 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/2002more . 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 09 2003
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...Ok.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.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.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.operator.Also overloading of the [] operator for subscript and slicing would be a good idea, together with the inYes, 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).templateAnd the last thing, I'm stuck with a problem of them currentofsyntax. 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 (sumwhenthe 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, butIcanstart using it for user-defined classes that are not addable to 0, like Vector, Quaternion, Equation, etc. the code stops working. Right now Icodesolve this by using a template variable to hold the value of zero andIn 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).verifications to ensure that the zero value is defined before theprogrammeruses a Matrix.I don't understand.andinstance 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, MLstartersAda 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 forand see how far that got.theI 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 toI 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 Brilliantinstance variable. I think it's still time for a change like that, AFAIKI'mthe 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 27 2002
See below. Daniel Yokomiso wrote:Reply embedded...... 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. -BobCtemplateAnd the last thing, I'm stuck with a problem of them currentofsyntax. 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 (sumwhenthe 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, butIcanstart using it for user-defined classes that are not addable to 0, like Vector, Quaternion, Equation, etc. the code stops working. Right now Icodesolve this by using a template variable to hold the value of zero andIn 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).verifications to ensure that the zero value is defined before theprogrammeruses a Matrix.I don't understand.
Nov 27 2002
"Robert W. Cunningham" <FlyPG users.sourceforge.net> escreveu na mensagem news:3DE55A59.2C9152BB users.sourceforge.net...See below. Daniel Yokomiso wrote:kind ofReply embedded...templateAnd the last thing, I'm stuck with a problem of them currentsyntax. Right now I have a template Matrix class defined for any(sumnumeric type. But for some operations like the trace of the matrixbutofthe 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,likewhenIstart using it for user-defined classes that are not addable to 0,IVector, Quaternion, Equation, etc. the code stops working. Right nowandcansolve this by using a template variable to hold the value of zeroisn'tcodeIn 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 thatverifications to ensure that the zero value is defined before theprogrammeruses a Matrix.I don't understand.Length oraddable to number 0 (after all matrix members need only to be addable to their kind). Think of matrices of type-safe quantities, like Mass,LengthTime. Neither of these types are addable to numbers, althought they're addable to themselves. The literal 0 is used to define int zero, notHow 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).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
Daniel Yokomiso wrote:"Robert W. Cunningham" <FlyPG users.sourceforge.net> escreveu na mensagem news:3DE55A59.2C9152BB users.sourceforge.net...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.) -BobCSorry 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).
Nov 28 2002
"Robert W. Cunningham" <FlyPG users.sourceforge.net> escreveu na mensagem news:3DE6E65F.E06792D1 users.sourceforge.net...Daniel Yokomiso wrote:mensagem"Robert W. Cunningham" <FlyPG users.sourceforge.net> escreveu naassignmentnews:3DE55A59.2C9152BB users.sourceforge.net...Sorry this reply sounds over-simplified, but for "accumulation" operations (summing, adding to a group, etc.) I generally useusagefor 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 thisneedisn't useful in other contexts (eg. creating an identity matrix, youAh, 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.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.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.) -BobCWell, 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
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
"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.-BobCBest regards, Daniel Yokomiso. "Happiness is when what you think, what you say, and what you do are in harmony." - Mohandas Gandi
Dec 01 2002
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]forThat 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 typetheir own needs. Some will want a 3D vector, while others will prefer amoregeneric tensor type. I think it's better to allow people to define theirowngeneric 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.itA 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 makeso. I prefer to remove all primitives in the D standard, and have a bunchofclasses 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.youCan 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)can use D and trust the efficiency and correctness of the generated code.-BobCBest regards, Daniel Yokomiso. "Happiness is when what you think, what you say, and what you do are in harmony." - Mohandas Gandi
Dec 01 2002
"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. SeanWhat 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
"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...problems.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 causewouldIn an ideal world, in a language which has a uniform type system, Iwouldhave matrix or vector inherit directly from a fixed array. SeanWhat 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 matricesgive 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
"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 byinheritance.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
"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
"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...C++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 useSTL 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
"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 inhisimplementation.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
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.Someof the mangled names are over a thousand characters long.
Jan 11 2003
"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. Dwon'thave that issue. Sean "Walter" <walter digitalmars.com> wrote in message news:avpnhb$23d1$1 digitaldaemon.com...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/2002I 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.Someof the mangled names are over a thousand characters long.
Jan 12 2003
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. Dwon'thave 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.Someof the mangled names are over a thousand characters long.
Jan 12 2003
"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...basicWhich kinds of incoherent error messages we could get with a value class Int32 instead of a int primitive? Or an array template instead of aSomearray? Standardized classes are better for understanding types and operations, an implementer could assume anything allowed by the spec inhisimplementation.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.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
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
"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
"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...fastBut let us keep the discussion level high and the ideas flowing, maybe we can give this problem a simple,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/2002and safe solution.Yes, I enjoy this discussion quite a bit.
Dec 29 2002