Hacker News new | ask | show | jobs
by dannymi 1146 days ago
Yes, but this example of matrix multiplication does weird stuff:

    {$MODE DELPHI}
    {$modeswitch advancedrecords}
    type
      TMatrix<T, R, C> = record
        fCoordinates: array[R, C] of Double;
      end;
    function mul<T, R, X, C>(A: TMatrix<T, R, X>; B: TMatrix<T, X, C>): TMatrix<T, R, C>;
    var
      i: R;
      j: X;
      k: C;
    begin
      Writeln('yep');
      Writeln(High(R)); // 3
      Writeln(High(X)); // 4
      Writeln(High(C)); // 3
      for i := Low(R) to High(R) do
        for k := Low(C) to High(C) do begin
          Result.fCoordinates[i, k] := 0;
          for j := Low(X) to High(X) do
            Result.fCoordinates[i, k] += A.fCoordinates[i, j] * B.fCoordinates[j, k]
        end
    end;
    type
      R1 = 1..3;
      R2 = 1..4;
    var
      A: TMatrix<Double, R1, R2>;
      B: TMatrix<Double, R1, R1>;
      C: TMatrix<Double, R2, R1>;
    begin
      mul<Double, R1, R2, R1>(A,B); // should fail because B would have to have as many rows as A has columns--and it doesn't.
      mul(A,C) // does not work even with a right-size C--even though it should
    end.
1 comments

The second bit is because you lack the explicit specialization - you must always tell the compiler how to specialize a generic. Personally i prefer to use generics in objfpc mode as you are more explicit that way. However FPC trunk can allow implicit function specialization if you request it with {$modeswitch implicitfunctionspecialization}. Your code compiles with that.

The first bit is an interesting case and i wonder why it happens. It seems the root cause is that it considers the two type specializations equivalent as even ignoring the generic functions something like "A:=B" is allowed. I trimmed the code down to this:

    {$MODE DELPHI}
    type
      TMatrix<R, C> = record
        X: array[R, C] of Double;
      end;
    type
      R1 = 1..3;
      R2 = 1..4;
    var
      A: TMatrix<R1, R2>;
      B: TMatrix<R1, R1>;
    begin
      A:=B;
    end.
This is clearly a bug and not intentional behavior as doing the specialization by hand shows an error as expected. Also it seems related to multidimensional arrays as with a single element it also shows an error. Note that the other way (B:=A) doesn't work, so my guess is that at some point the type compatibility comparison only checks if assigning one array would fit memory-wise into the other.

I'll check against latest trunk later and if it happens i'll file a bug report.

Thank you!

I also tried

    {$R+}
    type
      R1 = 1..3;
      R2 = 1..4;
    var
      a: R1;
      b: R2;
    begin
      b := 4;
      a := b;
      Writeln(a)
    end.
... and that only gives me a runtime error. So maybe those types count as equal anyway?
The types themselves count as assignable since the values can be compatible and unless the compiler can tell statically a value is out of range it wont complain at compile time (but will at runtime if range checking is enabled).

However using the ranges for arrays is not compatible, if you have a A: array [R1] of Double and a B: array [R2] of Double you can't assign one to the other.