Hacker News new | ask | show | jobs
by stefanos82 1146 days ago
> No metaprogramming in the language meant we had some parts of the code that were written in pascal to generate pascal code before compilation of the main project.

Weird...if you are talking about Generics, FreePascal has them and work just fine, plus it compiles instantly!

Here's a demo:

    program UseGenerics;

    {$mode objfpc}{$H+}

    type
        generic TFakeClass<_GT> = class
            class function gmax(a,b: _GT): _GT;
        end;

        TFakeClassInt = specialize TFakeClass<integer>;
        TFakeClassDouble = specialize TFakeClass<double>;

        class function TFakeClass.gmax(a,b: _GT): _GT;
        begin
            if a > b then
                result := a
            else
                result := b;
        end;

    begin
        { show max of two integers }
        writeln('Integer GMax: ', TFakeClassInt.gmax(23, 56));
        { show max of two doubles }
        writeln('Double GMax: ', TFakeClassDouble.gmax(23.89, 56.5));
    end.
3 comments

> > No metaprogramming in the language meant we had some parts of the code that were written in pascal to generate pascal code before compilation of the main project.

> Weird…if you are talking about Generics

But aren’t they very clearly talking about metaprogramming, which is a completely different thing than generics?

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.
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.

Generics are a very recent addition, though.
Free Pascal had support for generics since 2006, predating even Delphi's. While that isn't as long as Wirth's Pascal, it still is almost two decades old which i wouldn't call that recent.