Defining an infinite family of classes "MatrixMxN" in C#

50 views Asked by At

Suppose I was writing a linear algebra library in C#, and I wanted to implement matrix multiplication. Of course, two matrices A and B can only be multiplied to form AB if A has as many columns as B has rows. Hence one would typically include an argument validation step in the definition of multiplication, throwing an error in case of a mismatch. This is how it is done in the MathNet.Numerics library, which I have been using recently. However, wouldn't it be better if the matrix dimensionality was part of the type definition?

If the matrix dimensions were part of the type definition, then such dimensional mismatch could be caught at compile time instead of run-time. This would also be useful for constraining the shape of matrices in other contexts, such as when implementing mathematical functions that are only defined for square matrices. In particular, it would not be possible to introduce bugs by forgetting to validate matrix dimensions.

However, to implement this we would need some language mechanism that lets us define an infinite (at least in theory) family of classes, all of the form MatrixMxN with M and N being natural numbers describing the matrix dimensions. From what I know, this is not possible in C#, but maybe there is some way? We can define a generic class Matrix<T1,T2>, but unless we can create one type T for each natural number I don't see how this would get us anywhere. I notice that some functional languages has something called "type-level naturals" which seems to correspond to this latter idea.

So, is there a way to implement something like what I have described above? If not in C#, then how about in other languages?

1

There are 1 answers

1
Logarr On

As far as C# goes, no there is no such mechanism. If there was, Microsft themselves wouldn't have to do this for defining the varied Type Parameter counts of Tuple<T>

public static Tuple<T1> Create<T1>(T1 item1) {
    return new Tuple<T1>(item1);
}

public static Tuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2) {
    return new Tuple<T1, T2>(item1, item2);
}

public static Tuple<T1, T2, T3> Create<T1, T2, T3>(T1 item1, T2 item2, T3 item3) {
    return new Tuple<T1, T2, T3>(item1, item2, item3);
}

public static Tuple<T1, T2, T3, T4> Create<T1, T2, T3, T4>(T1 item1, T2 item2, T3 item3, T4 item4) {
    return new Tuple<T1, T2, T3, T4>(item1, item2, item3, item4);
}

public static Tuple<T1, T2, T3, T4, T5> Create<T1, T2, T3, T4, T5>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) {
    return new Tuple<T1, T2, T3, T4, T5>(item1, item2, item3, item4, item5);
}

public static Tuple<T1, T2, T3, T4, T5, T6> Create<T1, T2, T3, T4, T5, T6>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) {
    return new Tuple<T1, T2, T3, T4, T5, T6>(item1, item2, item3, item4, item5, item6);
}

public static Tuple<T1, T2, T3, T4, T5, T6, T7> Create<T1, T2, T3, T4, T5, T6, T7>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) {
    return new Tuple<T1, T2, T3, T4, T5, T6, T7>(item1, item2, item3, item4, item5, item6, item7);
}

public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8>> Create<T1, T2, T3, T4, T5, T6, T7, T8>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) {
    return new Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8>>(item1, item2, item3, item4, item5, item6, item7, new Tuple<T8>(item8));
}

For further fun, check out the entirety of the Tuple.cs source file.