Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2023-02

Additional generic type constraints

9 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Hairy Pixels

As a follow up to my other question, why doesn’t FPC support more types for generic type constraints, like Pointer, Integer, String etc...? Especially since we have implicit function specialization now allowing the user to constrain the types more precision would help.

For example my other problem could be resolved if I could constrain to pointer types but this is illegal.

generic procedure Bind<T: Pointer>(value: T);
begin
end;

Regards,
Ryan Joseph

Reply by email

Sven Barth
to Hairy Pixels

Hairy Pixels via fpc-devel schrieb am So.,
19. Feb. 2023, 03:54:

Show quoted text 5 lines
As a follow up to my other question, why doesn’t FPC support more types for generic type constraints, like Pointer, Integer, String etc...? Especially since we have implicit function specialization now allowing the user to constrain the types more precision would help.

Because Delphi doesn't have them and when constraints were implemented they were implemented for Delphi compatibility.

Regards,
Sven

Reply by email

Kostas Michalopoulos
to Sven Barth
Because Delphi doesn't have them and when constraints were implemented they were implemented for Delphi compatibility.

Can they be added? The original announcement ~13 years ago mentioned that those could be added at some point. "object" and "operator X" would be quite useful for me.

I tried to use constraints at some point in my code but they proven too limited in functionality - the biggest issue i faced was that i couldn't use a specialization with a forward declared class (which is the entire point of the forward declaration). For example i have a generic collection that only works on TSerializable subclasses so i gave it that constraint, but in another unit i need to define a specialization for it before it was defined (so the class was available with a forward class declaration) since that specialization also needs to be used by the class itself. In general it seems like having constrained generic work with a tree structure of derived types that use the generic itself is impossible.

Wouldn't storing a list of "specializations to confirm later in this unit" work (with later being when the class is actually defined in the unit)? The language already has forward declarations for a bunch of other things.

Kostas

Reply by email

Sven Barth
to Kostas Michalopoulos

Kostas Michalopoulos via fpc-devel schrieb
am Mi., 22. Feb. 2023, 10:37:

Show quoted text 7 lines
> Because Delphi doesn't have them and when constraints were implemented > they were implemented for Delphi compatibility. Can they be added? The original announcement ~13 years ago mentioned that those could be added at some point. "object" and "operator X" would be quite useful for me.

Some additional constraints might be added. However I have none of them planned currently and enough other things to do.

Show quoted text 17 lines
I tried to use constraints at some point in my code but they proven too limited in functionality - the biggest issue i faced was that i couldn't use a specialization with a forward declared class (which is the entire point of the forward declaration). For example i have a generic collection that only works on TSerializable subclasses so i gave it that constraint, but in another unit i need to define a specialization for it before it was defined (so the class was available with a forward class declaration) since that specialization also needs to be used by the class itself. In general it seems like having constrained generic work with a tree structure of derived types that use the generic itself is impossible. Wouldn't storing a list of "specializations to confirm later in this unit" work (with later being when the class is actually defined in the unit)? The language already has forward declarations for a bunch of other things.

Forward declarations can not be used for constraints, because a specialization of the generic might be used before the forward declared type is fully defined and the compiler *must* be able to check whether the parameter in question is compatible at the time the specialization is declared.

Regards,
Sven

Reply by email

Hairy Pixels
to Sven Barth
On Feb 22, 2023, at 8:11 PM, Sven Barth via fpc-devel wrote: Some additional constraints might be added. However I have none of them planned currently and enough other things to do.

I think Delphi basically has the same constraints as C# so FPC is in that same category. Swift has additional constraints for compiler types but I think they can pull that off because they have protocols such as “Numeric” which can be used on integer and floating point types.

It does seem incomplete that you can’t make a generic constrained for string types for example. Why don’t more languages have that? It seems like an obvious feature which should be supported.

Regards,
Ryan Joseph

Reply by email

denisgolovan
to Hairy Pixels
It does seem incomplete that you can’t make a generic constrained for string types for example. Why don’t more languages have that? It seems like an obvious feature which should be supported.

Rust & Haskell do that.
But that's another story :)

-- Regards,
Denis Golovan

Reply by email

Stefan Glienke
to Hairy Pixels: I think Delphi basically has the same constraints as C# so FPC is in that same category.…
I think Delphi basically has the same constraints as C#

Not quite (well maybe back then when they implemented them but C# improved since)- also they are subtly implemented differently.

In C# the constraint *class* means "a reference type" - in Delphi it actually means it has to be a class.

C# for a long time had similar issues with performing any kind of math in generics as Delphi or FPC have - the IComparer and IEqualityComparer interfaces where a huge kludge to be able to do generic collections.

Only C# 11 finally fixed this issue via static virtual members on interfaces (see https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/static-virtual-interface-members) so interfaces can be used as constraints and those static members are then being used when using operators and alike which enables all kinds of operations inside of generic code. A bit similar to C++ concepts (see https://en.cppreference.com/w/cpp/language/constraints)

Reply by email

Kostas Michalopoulos
to Sven Barth: Kostas Michalopoulos via fpc-devel schrieb am Mi., 22. Feb. 2023, 10:37: Some additional…
Sven Barth wrote 48 lines
Kostas Michalopoulos via fpc-devel < > schrieb am Mi., 22. Feb. 2023, 10:37: > Because Delphi doesn't have them and when constraints were implemented > they were implemented for Delphi compatibility. Can they be added? The original announcement ~13 years ago mentioned that those could be added at some point. "object" and "operator X" would be quite useful for me. Some additional constraints might be added. However I have none of them planned currently and enough other things to do. I tried to use constraints at some point in my code but they proven too limited in functionality - the biggest issue i faced was that i couldn't use a specialization with a forward declared class (which is the entire point of the forward declaration). For example i have a generic collection that only works on TSerializable subclasses so i gave it that constraint, but in another unit i need to define a specialization for it before it was defined (so the class was available with a forward class declaration) since that specialization also needs to be used by the class itself. In general it seems like having constrained generic work with a tree structure of derived types that use the generic itself is impossible. Wouldn't storing a list of "specializations to confirm later in this unit" work (with later being when the class is actually defined in the unit)? The language already has forward declarations for a bunch of other things. Forward declarations can not be used for constraints, because a specialization of the generic might be used before the forward declared type is fully defined and the compiler *must* be able to check whether the parameter in question is compatible at the time the specialization is declared. Regards, Sven

That is what the compiler does now, but can't it be changed to treat constraints that use forward declarations as unconstrained (at least as far as these forward declarations are concerned) until the moment they are declared and validate those constraints at that point?

After all the code below does work if there is no constraint despite the specialization being used before the forward declaration:

----
program Test;
{$MODE OBJFPC}{$H+}
type
    TProvidesFoo = class
      function Foo: Integer; virtual; abstract;
    end;
    // The following with a constraint could be instead:
    // generic TGetFoo<T: TProvidesFoo> = class
    generic TGetFoo<T> = class
      Wrap: T;
      function GetFoo: Integer;
    end;
type
    TForwardClass = class;
    // Instead of making the test here, add it to a list to check later...
    TFCHasFoo = specialize TGetFoo<TForwardClass>;
procedure DumpFoo(FCHasFoo: TFCHasFoo);
begin
    Writeln(FCHasFoo.GetFoo);
end;
function TGetFoo.GetFoo: Integer;
begin
    Result:=Wrap.Foo;
end;
type
    // ...and when this is declared check the if any of the remaining
    // checks in the list will be valid or not and remove it from the list
    TForwardClass = class(TProvidesFoo)
      function Foo: Integer; override;
    end;
    function TForwardClass.Foo: Integer;
    begin
      Result:=42;
    end;
var
    FC: TForwardClass;
    HF: TFCHasFoo;
begin
    HF:=TFCHasFoo.Create;
    HF.Wrap:=TForwardClass.Create;
    DumpFoo(HF);
    HF.Wrap.Free;
    HF.Free;
end.
----

At the end of the day this isn't a major flaw, after all if the expected functionality wasn't there (e.g. the generic code tried to access something the type used in the specialization didn't provide) you'd still get a compile error anyway, though it does feel a bit like there a hole in Free Pascal's type safety here (e.g. imagine passing a type to a specialization that happens to have the same function call so the code compiles but the call does something different than what you'd expect).

Kostas

Reply by email