Jump to page: 1 24  
Page
Thread overview
Templates and matrices
Nov 26, 2002
Daniel Yokomiso
Nov 26, 2002
Walter
Nov 27, 2002
Daniel Yokomiso
Nov 27, 2002
Walter
Nov 27, 2002
Burton Radons
Nov 27, 2002
Daniel Yokomiso
Dec 28, 2002
Walter
Dec 28, 2002
Burton Radons
Dec 29, 2002
Walter
Dec 29, 2002
Burton Radons
Jan 07, 2003
Walter
Jan 09, 2003
Burton Radons
Jan 09, 2003
Sean L. Palmer
Jan 09, 2003
Daniel Yokomiso
Nov 27, 2002
Daniel Yokomiso
Nov 28, 2002
Daniel Yokomiso
Nov 29, 2002
Daniel Yokomiso
Dec 01, 2002
Daniel Yokomiso
Dec 02, 2002
Sean L. Palmer
Dec 02, 2002
Daniel Yokomiso
Dec 03, 2002
Sean L. Palmer
Jan 09, 2003
Walter
Jan 09, 2003
Walter
Jan 09, 2003
Daniel Yokomiso
Jan 11, 2003
Walter
Jan 11, 2003
Sean L. Palmer
Jan 12, 2003
Daniel Yokomiso
Jan 12, 2003
Sean L. Palmer
Jan 12, 2003
Daniel Yokomiso
Jan 09, 2003
Walter
Dec 29, 2002
Walter
Dec 29, 2002
Daniel Yokomiso
November 26, 2002
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."


November 26, 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 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."
>
>


November 27, 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. -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


November 27, 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 when
I
> start using it for user-defined classes that are not addable to 0, like Vector, Quaternion, Equation, etc. the code stops working. Right now I can solve this by using a template variable to hold the value of zero and code verifications to ensure that the zero value is defined before the
programmer
> uses a Matrix.

I don't understand.

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

That's probably a good idea. I wanted to try it with just types for starters and see how far that got.

>     I keep thinking of it as a template constructor. Maybe we could push
> this template parametrization stuff to module level, and the programmer
> would use a template module as a regular module:
>
>
> module matrix(T, T zero); // declaration of template module
>
> import matrix(int, 0);
> import matrix(float, 0.0);
> import matrix(Quaternion, new Quaternion());
>
>
>     And use the module values without any particular need to refer to the
> instance variable. I think it's still time for a change like that, AFAIK
I'm
> the only one doing serious template work with D, and I can live with any change in the language. It would make the language simpler for template users (if we make matrix programming right we can get lots of people interested in doing scientific programming in D, it's my current goal).

Ah, parameterized modules! Interesting shorthand!


November 27, 2002
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.

November 27, 2002
Reply embedded

"Walter" <walter@digitalmars.com> escreveu na mensagem news:as24jh$2b30$1@digitaldaemon.com...
>
> "Daniel Yokomiso" <daniel_yokomiso@yahoo.com.br> wrote in message news:as1944$1e3u$2@digitaldaemon.com...
> >     Speaking about matrices, why the following code fails to link?
> >     It gives a message 'Error 42: Symbol Undefined __init_TypeInfo_Ai'
>
> Because I have failed to write a typeinfo for arrays of ints :-( See the ti*.d files in \dmd\src\phobos.
>

Ok.

> >     And are multidimensional slices valid? Id would be great to matrix
> > programming, so we could write:
> > A[1..3][4..10] += B[5..7][8..14];
> >     Or something like that.
>
> I'd have to think about that. I don't think it will work for rectangular arrays.
>

I agree. Maybe some magic is needed :-). If D get a expressive Matrix type (my current work) with this kind of syntax maybe we won't need true rectangular arrays for 95% of its usage (my matrix provides data packed in column order). Blitz++ provides multi-dimensional slices and subarrays (http://www.oonumerics.org/blitz/manual/blitz02.html#l46) so its users will ask for it.

> > Also overloading of the [] operator for
> > subscript and slicing would be a good idea, together with the in
operator.
>
> Yes, that's a good idea.
>

    I find myself always writing a contains method, or writing a slice +
set/at methods. If D has this capability maybe we can use it instead of
STL-like iterators to provide generic programming. After all a slice is a
pointer + length, very similar to an iterator (pointer + direction of
transversal).

> >     And the last thing, I'm stuck with a problem of them current
template
> > syntax. Right now I have a template Matrix class defined for any kind of numeric type. But for some operations like the trace of the matrix (sum
of
> > the main diagonal elements) I need to have a zero value like:
> > T trace() {
> >     T sum = 0;
> >     // code to sum the main diagonal elements
> >     return sum;
> > }
> >     For TMatrix(int) or TMatrix(float) instances this code is ok, but
when
> I
> > start using it for user-defined classes that are not addable to 0, like Vector, Quaternion, Equation, etc. the code stops working. Right now I
can
> > solve this by using a template variable to hold the value of zero and
code
> > verifications to ensure that the zero value is defined before the
> programmer
> > uses a Matrix.
>
> I don't understand.

    In the template code I must declare a variable of type T that is
initialized to some value. If T is a user-defined numeric type that isn't
addable to number 0 (after all matrix members need only to be addable to
their kind). Think of matrices of type-safe quantities, like Mass, Length or
Time. Neither of these types are addable to numbers, althought they're
addable to themselves. The literal 0 is used to define int zero, not Length
0. So I use this template variables to allow the user to define the
interesting numbers (zero and one, all others are defined by those).

>
> > instance TMatrix(Quatertion) tmatrix;
> > tmatrix.zero = new Quatertion();
> > tmatrix.Matrix m = new tmatrix.Matrix(10, 10);
> >
> > where new Quatertion() is the zero value for quaternions. Haskell, ML
and
> > Ada allow any kind of parameters, besides types, when you instantiate a generic module. I think should follow its example, so our could would be something like:
> >
> > instance TMatrix(Quatertion, new Quatertion()) tmatrix;
> > tmatrix.Matrix m = new tmatrix.Matrix(10, 10);
>
> That's probably a good idea. I wanted to try it with just types for
starters
> and see how far that got.
>
> >     I keep thinking of it as a template constructor. Maybe we could push
> > this template parametrization stuff to module level, and the programmer
> > would use a template module as a regular module:
> >
> >
> > module matrix(T, T zero); // declaration of template module
> >
> > import matrix(int, 0);
> > import matrix(float, 0.0);
> > import matrix(Quaternion, new Quaternion());
> >
> >
> >     And use the module values without any particular need to refer to
the
> > instance variable. I think it's still time for a change like that, AFAIK
> I'm
> > the only one doing serious template work with D, and I can live with any change in the language. It would make the language simpler for template users (if we make matrix programming right we can get lots of people interested in doing scientific programming in D, it's my current goal).
>
> Ah, parameterized modules! Interesting shorthand!
>

    I realized that most of the time I wrote one template per module, for
clarity. Before I didn't see D modules as ML modules, capable of
parametrization. I think we can drop the template concept and favour a more
expressive module system. This is better for allows library writers, because
they don't need to write:


module collections.set;
import collections.collection;
template TSet(T) {
    private instance TCollection(T) tcollection;
    private alias tcollection.Collection Collection;
    public class Set : Collection {
        ...
    }
}


    Instead they can write:


module collections.set(T);
import collections.collection(T);
public class Set : Collection {
    ...
}


and live with less baggage to keep in their heads when trying to define a simple generic set class.

    Best regards,
    Daniel Yokomiso.


"My opinions may have changed, but not the fact that I am right." - Ashleigh Brilliant


November 27, 2002
Reply embbeded.

"Burton Radons" <loth@users.sourceforge.net> escreveu na mensagem news:as2d4u$2koh$1@digitaldaemon.com...
> Walter wrote:

[snip]

> >>    And are multidimensional slices valid? Id would be great to matrix
> >>programming, so we could write:
> >>A[1..3][4..10] += B[5..7][8..14];
> >>    Or something like that.
> >
> > I'd have to think about that. I don't think it will work for rectangular arrays.
>
> I'll make the problem explicit.  Given:
>
>      int [10] [10] a;
>
> The resulting type from "a [5] [4]" is int, but the resulting type from "a [5 .. 10] [4 .. 5]" is int[][].  Indices remove a layer, but slicing doesn't, so a double-slice should still result in the same number of dimensions.
>
> Here's where they got in the discussion, Daniel.  Pavel suggested the syntax:
>
>      int [r, c] a;
>
> So if that's a matrix, I could get a column or a row by "a [0 .. r, c]" or "a [r, 0 .. c]", or get sub-matrices out of that.  Powerful stuff, but hard on the compiler and really hard on operator overloading.  Needs solid justification.
>
    Blitz++ (http://www.oonumerics.org/blitz/manual/blitz02.html#l46) is a
solid justification? :-)
    Jokes aside they have a very powerful slicing mechanism, runtime
performance comparable to Fortran and use lots of C++ template magic. I
don't mind the syntax for primitive matrices, but this kind of stuff is
needed when someone want to write simple code to work with matrices. If you
check the Fortran Linpack code (or the Java variant Jama), you'll lot's of
explicit written loops for things like:

// code snippet from Jama
// V is a square n x n matrix

for (int k = n-1; k >= 0; k--) {
   if ((k < nrt) & (e[k] != 0.0)) {
      for (int j = k+1; j < nu; j++) {
         double t = 0;
         for (int i = k+1; i < n; i++) {
            t += V[i][k]*V[i][j];
         }
         t = -t/V[k+1][k];
         for (int i = k+1; i < n; i++) {
            V[i][j] += t*V[i][k];
         }
      }
   }
   for (int i = 0; i < n; i++) {
      V[i][k] = 0.0;
   }
   V[k][k] = 1.0;
}


that could be written as (or something like that):

for (int k = n-1; k >= 0; k--) {
   if ((k < nrt) & (e[k] != 0.0)) {
      for (int j = k+1; j < nu; j++) {
         double t = sum(V[k + 1 .. n][k] * V[k + 1 .. n][j]);
         t = -t / V[k+1][k];
         V[k + 1 .. n][j] += t * V[k + 1 .. n][k];
      }
   }
   V[0..n][k] = 0.0;
   V[k][k] = 1.0;
}

    Seven lines smaller and simpler to understand. Every time anyone writes
a loop (matrix processing uses several nested loops usually) is because some
abstraction was lost. With complete multi-dimentsional slicing we can do
almost all loops with less code, and the compiler will know we want to do
slice processing, not some generic loop that happens to be a slice.


[snip]

> >>    And the last thing, I'm stuck with a problem of them current
template
> >>syntax. Right now I have a template Matrix class defined for any kind of numeric type. But for some operations like the trace of the matrix (sum
of
> >>the main diagonal elements) I need to have a zero value like:
> >>T trace() {
> >>    T sum = 0;
> >>    // code to sum the main diagonal elements
> >>    return sum;
> >>}
> >>    For TMatrix(int) or TMatrix(float) instances this code is ok, but
when
> >
> > I
> >
> >>start using it for user-defined classes that are not addable to 0, like Vector, Quaternion, Equation, etc. the code stops working. Right now I
can
> >>solve this by using a template variable to hold the value of zero and
code
> >>verifications to ensure that the zero value is defined before the
> >
> > programmer
> >
> >>uses a Matrix.
> >
> >
> > I don't understand.
>
> Take the code:
>
>     T sum (T [] list)
>     {
>         T result = 0;
>
>         for (T *c = list, e = c + list.length; c < e; c ++)
>             result += *c;
>
>         return result;
>     }
>
> This is fine if T is int or float, but if it's "struct vec3 { float x, y, z; }" then the initial value of result doesn't make sense.
>
> Howsabout the builtin types have an attribute ".zero" that we can put into our user types as static variables/constants.  Then that line would
be:
>
>         T result = T.zero;
>
> Which I think would work better than C++'s constructor method.  zero could even be a static property, and if it's proveably const then the compiler can do lots with it.
>

    This solution is also good, but we need also the .one attribute. But
this solution is paliative, because it doesn't address the problem that
there is more to generic programming than types. We can simulate this by
introducing dummy classes and instances:


template TNumerics(T, U) {
    private U attributes = new U();
    T sumSqrt(T[] list) {
        T result = attributes.zero();
        for (int i = 0; i < list.length; i ++) {
            result += attributes.sqrt(list[i]);
        }
        return result;
   }
}


    This hack, not very ugly but a hack still. There are infinite possible
parametrization of templates besides type and primitive values.

    Best regards,
    Daniel Yokomiso.

"Confucius say... man who run in front of car get tired; man who run behind car get exhausted."


November 27, 2002
See below.

Daniel Yokomiso wrote:
> 
> Reply embedded
> 
...
> > >     And the last thing, I'm stuck with a problem of them current
> template
> > > syntax. Right now I have a template Matrix class defined for any kind of numeric type. But for some operations like the trace of the matrix (sum
> of
> > > the main diagonal elements) I need to have a zero value like:
> > > T trace() {
> > >     T sum = 0;
> > >     // code to sum the main diagonal elements
> > >     return sum;
> > > }
> > >     For TMatrix(int) or TMatrix(float) instances this code is ok, but
> when
> > I
> > > start using it for user-defined classes that are not addable to 0, like Vector, Quaternion, Equation, etc. the code stops working. Right now I
> can
> > > solve this by using a template variable to hold the value of zero and
> code
> > > verifications to ensure that the zero value is defined before the
> > programmer
> > > uses a Matrix.
> >
> > I don't understand.
> 
>     In the template code I must declare a variable of type T that is
> initialized to some value. If T is a user-defined numeric type that isn't
> addable to number 0 (after all matrix members need only to be addable to
> their kind). Think of matrices of type-safe quantities, like Mass, Length or
> Time. Neither of these types are addable to numbers, althought they're
> addable to themselves. The literal 0 is used to define int zero, not Length
> 0. So I use this template variables to allow the user to define the
> interesting numbers (zero and one, all others are defined by those).
...

Sorry this reply sounds over-simplified, but for "accumulation" operations (summing, adding to a group, etc.) I generally use assignment for the first element/item, and accumulation for all subsequent iterations.

It takes a little extra work to ensure all types passed as template parameters support this use (all native types do), and I find it eliminates the need for many explicit initializations.  It can also handle non-existant and soltiary items.


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

How do you handle empty lists with this scheme? Just curious. But this usage isn't useful in other contexts (eg. creating an identity matrix, you need explicit zeros and ones, or comparison to zero value).


November 29, 2002
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
« First   ‹ Prev
1 2 3 4