|
Archives
D Programming
D
D.gnu
digitalmars.D
digitalmars.D.bugs
digitalmars.D.dtl
digitalmars.D.dwt
digitalmars.D.announce
digitalmars.D.learn
digitalmars.D.debugger
C/C++ Programming
c++
c++.announce
c++.atl
c++.beta
c++.chat
c++.command-line
c++.dos
c++.dos.16-bits
c++.dos.32-bits
c++.idde
c++.mfc
c++.rtl
c++.stl
c++.stl.hp
c++.stl.port
c++.stl.sgi
c++.stlsoft
c++.windows
c++.windows.16-bits
c++.windows.32-bits
c++.wxwindows
digitalmars.empire
digitalmars.DMDScript
|
D - Templates and matrices
↑ ↓ ← → "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."
↑ ↓ ← → "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
dimension used. This last solution is similar to C++ usage of templates
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
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."
↑ ↓ ← → "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
↑ ↓ ← → "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
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
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
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!
↑ ↓ ← → 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.
↑ ↓ ← → "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.
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
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
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
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
solve this by using a template variable to hold the value of zero and
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
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."
↑ ↓ ← → "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
All types have an attribute .init which gives their default initalizer
value.
↑ ↓ ← → 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.
↑ ↓ ← → "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!
↑ ↓ ← → 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.
↑ ↓ ← → "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
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
↑ ↓ ← → 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.
↑ ↓ ← → "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
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.
↑ ↓ ← → "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
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
↑ ↓ ← → "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
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
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
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
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
solve this by using a template variable to hold the value of zero and
verifications to ensure that the zero value is defined before the
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
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
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
instance variable. I think it's still time for a change like that, AFAIK
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
↑ ↓ ← → "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
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
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
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
solve this by using a template variable to hold the value of zero and
verifications to ensure that the zero value is defined before the
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
↑ ↓ ← → "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
syntax. Right now I have a template Matrix class defined for any
numeric type. But for some operations like the trace of the matrix
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,
when
I
start using it for user-defined classes that are not addable to 0,
Vector, Quaternion, Equation, etc. the code stops working. Right now
can
solve this by using a template variable to hold the value of zero
code
verifications to ensure that the zero value is defined before the
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
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,
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
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).
↑ ↓ ← → "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
retain the procedural programming model (C++, Java, C#, D, etc.).
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
↑ ↓ ← → "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
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
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
isn't useful in other contexts (eg. creating an identity matrix, you
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
retain the procedural programming model (C++, Java, C#, D, etc.).
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
↑ ↓ ← → "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
↑ ↓ ← → "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
↑ ↓ ← → "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
their own needs. Some will want a 3D vector, while others will prefer a
generic tensor type. I think it's better to allow people to define their
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
so. I prefer to remove all primitives in the D standard, and have a bunch
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)
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
↑ ↓ ← → "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."
↑ ↓ ← → "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
In an ideal world, in a language which has a uniform type system, I
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
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
↑ ↓ ← → "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
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.
↑ ↓ ← → "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.
↑ ↓ ← → "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
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
↑ ↓ ← → "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
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.
↑ ↓ ← → "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.
of the mangled names are over a thousand characters long.
↑ ↓ ← → "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
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.
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
↑ ↓ ← → "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
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.
of the mangled names are over a thousand characters long.
↑ ↓ ← → "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
array? Standardized classes are better for understanding types and
operations, an implementer could assume anything allowed by the spec in
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.
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
↑ ↓ ← → "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.
↑ ↓ ← → "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.
↑ ↓ ← → "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,
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
|
|