Constants in generics

classic Classic list List threaded Threaded
96 messages Options
12345
Reply | Threaded
Open this post in threaded view
|

Constants in generics

Ryan Joseph
I implemented a first draft of constants (integers) in generics. My reason was specifically that I wanted a way to add methods to static arrays and generic records is the only way to accomplish this AFAIK.

If I fix this up will it be considered as a patch? I wanted to present the idea first before I spent any more time. Here’s what I has so far (on GitHub).

https://github.com/genericptr/freepascal/commit/ec518542b2da7d7f016702a82b2d05349a01a6fb

{$mode objfpc}
{$modeswitch advancedrecords}

program generic_constants;

type
        generic TList<T, U> = record
                list: array[0..U-1] of T;
                function capacity: integer;
        end;

function TList.capacity: integer;
begin
        result := U;
end;

var
        nums: specialize TList<integer,10>;
        strs: specialize TList<integer,4>;
begin
        writeln('sizeof:',sizeof(nums), ' capacity:',nums.capacity);
        writeln('sizeof:',sizeof(strs), ' capacity:',strs.capacity);
end.



Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Free Pascal - General mailing list
Am Di., 6. Nov. 2018, 08:44 hat Ryan Joseph <[hidden email]> geschrieben:
I implemented a first draft of constants (integers) in generics. My reason was specifically that I wanted a way to add methods to static arrays and generic records is the only way to accomplish this AFAIK.

If I fix this up will it be considered as a patch? I wanted to present the idea first before I spent any more time. Here’s what I has so far (on GitHub).

https://github.com/genericptr/freepascal/commit/ec518542b2da7d7f016702a82b2d05349a01a6fb

{$mode objfpc}
{$modeswitch advancedrecords}

program generic_constants;

type
        generic TList<T, U> = record
                list: array[0..U-1] of T;
                function capacity: integer;
        end;

function TList.capacity: integer;
begin
        result := U;   
end;   

var
        nums: specialize TList<integer,10>;
        strs: specialize TList<integer,4>;
begin
        writeln('sizeof:',sizeof(nums), ' capacity:',nums.capacity);
        writeln('sizeof:',sizeof(strs), ' capacity:',strs.capacity);
end.

First of I'm not a fan of adding support for constants, mainly because it will definitely not help parsing of inline specializations in mode Delphi which are going to be annoying enough already. 
That said: even if we do add it, then only if a generic constant parameter is designated as such with "const N" in the generic declaration instead of merely "N", so that the compiler does not assume it's a type. 

Regards, 
Sven 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph


> On Nov 6, 2018, at 8:21 PM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> First of I'm not a fan of adding support for constants, mainly because it will definitely not help parsing of inline specializations in mode Delphi which are going to be annoying enough already.

Can you give an example? There’s lots of ways generics can be used and I tested in only a few.

This is a very important improvement because it allows us to extend static arrays in ways we haven’t been able to do before. Unless I missed something huge it seems like a simple thing to implement (see the minor changes I made in a single unit).

>  
> That said: even if we do add it, then only if a generic constant parameter is designated as such with "const N" in the generic declaration instead of merely "N", so that the compiler does not assume it's a type.

Why is this preferable? If you tried to pass a type for a constant in a specialization then you’d probably just get a type error which is easy to catch. Generics in FPC are already very verbose (“specialize" keyword is exhaustingly long) but adding “const” is at least a small cosmetic change in a single location.


Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Free Pascal - General mailing list
Am Di., 6. Nov. 2018, 14:35 hat Ryan Joseph <[hidden email]> geschrieben:


> On Nov 6, 2018, at 8:21 PM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> First of I'm not a fan of adding support for constants, mainly because it will definitely not help parsing of inline specializations in mode Delphi which are going to be annoying enough already.

Can you give an example? There’s lots of ways generics can be used and I tested in only a few.

Complex inline specializations containing, for example a multiplication with specializations on the left and the right side are currently not possible in mode Delphi. In addition to that Delphi allows overloads of generic types with variables and constants, so when the parser encounters "Bla<N" and has not yet encountered the ">" it does not know whether it needs to start a specialization or an expression. This is already annoying enough to solve with merely types, but if the right side of the "<" can also take constants for generics that gets downright proplematic. 



> That said: even if we do add it, then only if a generic constant parameter is designated as such with "const N" in the generic declaration instead of merely "N", so that the compiler does not assume it's a type.

Why is this preferable? If you tried to pass a type for a constant in a specialization then you’d probably just get a type error which is easy to catch. Generics in FPC are already very verbose (“specialize" keyword is exhaustingly long) but adding “const” is at least a small cosmetic change in a single location.

Specialization is an expensive operation. When the compiler can already decide that "Something<SomeType, SomeOtherType>" can never be valid, because it's declared as "Something<T, const N>", then this is preferable. Otherwise the user might get some cryptic error message during the specialization, because "SomeOtherType" is used where only a constant can be used. 
Pascal is considered a type safe language and without forcing users to decorate such parameters as "const" you essentially allow users to mix types and constants. 
Not to mention that parsing the generic can already be done more correctly then it could be if the parameter is a typesym (which it would be right now). 
You can't do e.g. "const SomeOtherConst = ConstParam * Pi" that way as it would mean to ease up the compiler's restrictions for type checking even more. 

Regards, 
Sven 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph


> On Nov 6, 2018, at 11:19 PM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> Complex inline specializations containing, for example a multiplication with specializations on the left and the right side are currently not possible in mode Delphi. In addition to that Delphi allows overloads of generic types with variables and constants, so when the parser encounters "Bla<N" and has not yet encountered the ">" it does not know whether it needs to start a specialization or an expression. This is already annoying enough to solve with merely types, but if the right side of the "<" can also take constants for generics that gets downright proplematic.

Can you show full a code example? If it really is so problematic because of something Delphi does then maybe it’s best left to objfpc mode until it can be resolved.

> Specialization is an expensive operation. When the compiler can already decide that "Something<SomeType, SomeOtherType>" can never be valid, because it's declared as "Something<T, const N>", then this is preferable. Otherwise the user might get some cryptic error message during the specialization, because "SomeOtherType" is used where only a constant can be used.
> Pascal is considered a type safe language and without forcing users to decorate such parameters as "const" you essentially allow users to mix types and constants.
> Not to mention that parsing the generic can already be done more correctly then it could be if the parameter is a typesym (which it would be right now).
> You can't do e.g. "const SomeOtherConst = ConstParam * Pi" that way as it would mean to ease up the compiler's restrictions for type checking even more.

I guess if you include const it could save some compile time if you tried to specialize with the wrong type? It’s a pretty minor savings but that’s ok.

type
        generic TList<T,const U> = record
                list: array[0..U-1] of T;
                function capacity: integer;
        end;

Btw I only implemented this with integers (no strings or floats) because afaik this feature is only useful for arrays. I guess you could use non-ints for default parameters but this is probably a stupid idea because of how much compile time you’ll add to specialize a large amount of types.

Is there any reason to support strings and floats?

type
        generic TCaller<T,const U> = record
                procedure Say(message:T = U);
        end;

var
        caller: specialize TCaller<string,’hello’>;
begin
        caller.Say;

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Martin Friebe
In reply to this post by Ryan Joseph
On 06/11/2018 08:13, Ryan Joseph wrote:
> I implemented a first draft of constants (integers) in generics. My reason was specifically that I wanted a way to add methods to static arrays and generic records is the only way to accomplish this AFAIK.
>
> If I fix this up will it be considered as a patch? I wanted to present the idea first before I spent any more time. Here’s what I has so far (on GitHub).
>

Just for info, there already is a way to do this, though being able to
do it direct would be nice. You can wrap any constants into an object
(or advanced record).

program Project1;
{$mode objfpc}{$H+}
uses  Classes;

type
   generic Foo<A> = object
     class procedure test;
   end;

   Data = object
     const val = 1;
   end;

var
   bar: specialize Foo<data>;

class procedure Foo.test;
begin
   writeln(A.val);
end;

begin
   bar.test;
   readln;
end.

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

denisgolovan
Does this trick also work for declaring arrays using "val"?

BR,
Denis
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph
I added const in declarations so you get syntax error. I also disabled the const’s in delphi mode because it does indeed break inline specializations. Not sure how difficult that will be to fix or if it’s even worth it since this feature isn’t in delphi afaik.

https://github.com/genericptr/freepascal/tree/generic_constants

Pointless test to show the syntax:

{$mode objfpc}
{$modeswitch advancedrecords}
{$interfaces CORBA}

program generic_constants_interfaces;

type
        generic IMyInterface<T, const U> = interface
                procedure DoThis (value: T);
        end;

type
        generic THelper<T, const U> = record
                list: array[0..U-1] of T;
        end;

type
        generic TMyClass<T, const U> = class (specialize IMyInterface<T, U>)
                type
                        // todo: second param in THelper must be const also!
                        THelperType = specialize THelper<T, U>;
                public
                        helper: THelperType;
                        procedure DoThis (value: T);
        end;
        TListClass = specialize TMyClass<integer,10>;

procedure TMyClass.DoThis (value: T);
begin
        writeln('DoThis:',value, ' default: ', U);
end;

var
        c: TListClass;
begin
        c := TListClass.Create;
        c.helper.list[5] := 100;
        writeln(c.helper.list[5]);
        c.DoThis(100);
end.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Florian Klämpfl
In reply to this post by Ryan Joseph
Am 06.11.2018 um 08:13 schrieb Ryan Joseph:
> I implemented a first draft of constants (integers) in generics. My reason was specifically that I wanted a way to add methods to static arrays and generic records is the only way to accomplish this AFAIK.
>
> If I fix this up will it be considered as a patch?
> I wanted to present the idea first before I spent any more time. Here’s what I has so far (on GitHub).

I like the idea of const in generics, but it needs serious cleanup when it's working:
- commit messages like "first commit" are useless
- do not commit meta files like .gitignore with a functional commit
- follow the indention style of the surrounding code
- the compiler contains already a type called tgenericdef
- do not change the lazarus project files best practice is, to make a local copy for your own work (like ppcx64_ryan.lpi)
- create tests: succeeding as well as failing one
- do not use c style operators in the compiler, use inc/dec instead of += and -=
- replace comments like // note: ryan by comments like: "check for const generic paramters"
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph


> On Nov 9, 2018, at 4:28 AM, Florian Klämpfl <[hidden email]> wrote:
>
> I like the idea of const in generics, but it needs serious cleanup when it's working:
> - commit messages like "first commit" are useless

Those are for github so I could share but I need to learn SVN (again) eventually.

> - do not commit meta files like .gitignore with a functional commit
> - follow the indention style of the surrounding code

Is the compiler 2 spaces for indent? There’s already enough inconsistency but I’m trying to figure it out.

> - the compiler contains already a type called tgenericdef

I think I changed that name in the last commit. Btw I made all those types because I didn’t want to populate the main types like ttypesym with extra bytes but I don’t know what’s best practice in the codebase.

> - do not change the lazarus project files best practice is, to make a local copy for your own work (like ppcx64_ryan.lpi)

Good idea, thanks.

> - create tests: succeeding as well as failing one

ok.

> - do not use c style operators in the compiler, use inc/dec instead of += and -=

ok.

> - replace comments like // note: ryan by comments like: "check for const generic paramters”

those are temporary for my use. I’ll remove those of course.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph
In reply to this post by Florian Klämpfl


> On Nov 9, 2018, at 4:28 AM, Florian Klämpfl <[hidden email]> wrote:
>
> I like the idea of const in generics, but it needs serious cleanup when it's working:

Question: should other consts besides integers be allowed? I don’t think it makes sense personally to use strings, floats or sets but maybe I’m wrong.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Free Pascal - General mailing list
In reply to this post by Ryan Joseph
Am Fr., 9. Nov. 2018, 03:44 hat Ryan Joseph <[hidden email]> geschrieben:


> On Nov 9, 2018, at 4:28 AM, Florian Klämpfl <[hidden email]> wrote:
>
> I like the idea of const in generics, but it needs serious cleanup when it's working:
> - commit messages like "first commit" are useless

Those are for github so I could share but I need to learn SVN (again) eventually.

You can also use git svn. At least I myself use that for my own work (I think Florian does, too). Of course you need to set up syncing with your GitHub Repo as well, but I haven't done that for a SVN Repo yet... 


> - do not commit meta files like .gitignore with a functional commit
> - follow the indention style of the surrounding code

Is the compiler 2 spaces for indent? There’s already enough inconsistency but I’m trying to figure it out.

Yes, two spaces. And "begin... end" is indented and its content is indented again. 


> - the compiler contains already a type called tgenericdef

I think I changed that name in the last commit. Btw I made all those types because I didn’t want to populate the main types like ttypesym with extra bytes but I don’t know what’s best practice in the codebase.

When something belongs in a main type then it belongs there. But as I already wrote it should not be a ttypesym for constant generic parameters anyway.

Regards, 
Sven 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Florian Klämpfl
In reply to this post by Ryan Joseph
Am 09.11.2018 um 05:20 schrieb Ryan Joseph:
>
>
>> On Nov 9, 2018, at 4:28 AM, Florian Klämpfl <[hidden email]> wrote:
>>
>> I like the idea of const in generics, but it needs serious cleanup when it's working:
>
> Question: should other consts besides integers be allowed? I don’t think it makes sense personally to use strings, floats or sets but maybe I’m wrong.

I would allow them for orthogonality reasons.

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Florian Klämpfl
In reply to this post by Ryan Joseph
Am 09.11.2018 um 03:13 schrieb Ryan Joseph:
>
>
>> On Nov 9, 2018, at 4:28 AM, Florian Klämpfl <[hidden email]> wrote:
>>
>> I like the idea of const in generics, but it needs serious cleanup when it's working:
>> - commit messages like "first commit" are useless
>
> Those are for github so I could share but I need to learn SVN (again) eventually.

This does not prevent you from creating proper commits, especially as the patch series from github could be easily
applied later on to fpc's main repository.

>
>> - the compiler contains already a type called tgenericdef
>
> I think I changed that name in the last commit. Btw I made all those types because I didn’t want to populate the main types like ttypesym with extra bytes but I don’t know what’s best practice in the codebase.

Priority is:
1. maintainability
2. portability
3. speed

Keep also ppu storing/loading in mind.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph


> On Nov 10, 2018, at 7:16 PM, Florian Klämpfl <[hidden email]> wrote:
>
> This does not prevent you from creating proper commits, especially as the patch series from github could be easily
> applied later on to fpc's main repository.

I’ll assume these commits could be public some day then. I originally thought I would have to trash them all and start over for svn anyways.

>
>>
>>> - the compiler contains already a type called tgenericdef
>>
>> I think I changed that name in the last commit. Btw I made all those types because I didn’t want to populate the main types like ttypesym with extra bytes but I don’t know what’s best practice in the codebase.
>
> Priority is:
> 1. maintainability
> 2. portability
> 3. speed
>
> Keep also ppu storing/loading in mind.

Thus far on the compiler I haven’t encountered PPU's at all so I just assumed they were part of the code generator and I didn’t need to worry about them. How are they relevant here? Honestly I barely know what the PPU’s are and how the compiler uses them.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Florian Klämpfl
Am 10.11.2018 um 13:20 schrieb Ryan Joseph:

>>>
>>>> - the compiler contains already a type called tgenericdef
>>>
>>> I think I changed that name in the last commit. Btw I made all those types because I didn’t want to populate the main types like ttypesym with extra bytes but I don’t know what’s best practice in the codebase.
>>
>> Priority is:
>> 1. maintainability
>> 2. portability
>> 3. speed
>>
>> Keep also ppu storing/loading in mind.
>
> Thus far on the compiler I haven’t encountered PPU's at all so I just assumed they were part of the code generator and I didn’t need to worry about them. How are they relevant here? Honestly I barely know what the PPU’s are and how the compiler uses them.
>

If you "export" a generic taking a const from a unit, this info has to be stored in the ppu.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph


> On Nov 10, 2018, at 7:22 PM, Florian Klämpfl <[hidden email]> wrote:
>
> If you "export" a generic taking a const from a unit, this info has to be stored in the ppu.

Can you show a test case I could use to see where this is triggered in the compiler? I don’t even know where to begin looking.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Free Pascal - General mailing list
Am Sa., 10. Nov. 2018, 14:47 hat Ryan Joseph <[hidden email]> geschrieben:


> On Nov 10, 2018, at 7:22 PM, Florian Klämpfl <[hidden email]> wrote:
>
> If you "export" a generic taking a const from a unit, this info has to be stored in the ppu.

Can you show a test case I could use to see where this is triggered in the compiler? I don’t even know where to begin looking.

The important part are the ppuwrite and ppuload methods that are provided by tstoreddef, tstoredsym and tstoredsymtable and overridden by most of their descendants. E.g. tstoreddef itself is also dealing with the generic parameters. 

And for a test: declare the generic in one unit, specialize it in another and compile twice. Usually things go boom if you messed up something. 

By the way: When adding/changing data that is written to the PPU increase the PPUVersion constant in the ppu.pas unit (there is no backwards compatibility for PPUs) and also adjust the ppudump utility in compiler/utils/ppudump. 

Regards, 
Sven 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ben Grasset
In reply to this post by Ryan Joseph
On Thu, Nov 8, 2018 at 11:50 PM Ryan Joseph <[hidden email]> wrote:
Question: should other consts besides integers be allowed? I don’t think it makes sense personally to use strings, floats or sets but maybe I’m wrong.

As a daily FPC user there's nothing I find more annoying than compiler restrictions that are visibly artificial, so I'd say you should definitely allow basically anything that's physically possible to allow in that context.
Someone will find it useful, trust me. I can already think of various uses for string constants as generic parameters that I certainly would have already done in the past if I could have, for example.

On Thu, Nov 8, 2018 at 11:50 PM Ryan Joseph <[hidden email]> wrote:


> On Nov 9, 2018, at 4:28 AM, Florian Klämpfl <[hidden email]> wrote:
>
> I like the idea of const in generics, but it needs serious cleanup when it's working:

Question: should other consts besides integers be allowed? I don’t think it makes sense personally to use strings, floats or sets but maybe I’m wrong.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph
I think I have this done except for cleanup. Here’s the status:

- Integer,string,real,set and nil constant for parameters.
- Consts can have type restrictions which correspond to the above types.
- Const can be assigned to from generic const params.
- PPU loading.

Technical compiler issues:

- I couldn’t figure out how to extend PPU loading for a ttypesym subclass so I put 2 fields into ttypesym. Those are probably best moved to a subclass later.
- We may need new error messages but I just left place holders in for now.

https://github.com/genericptr/freepascal/tree/generic_constants

program generic_constants_types;
type
        generic TList<T, const U:integer> = record
                const
                        ID = U;
                public
                        list: array[0..U-1] of T;
                        procedure dothis (msg:string);
        end;

procedure TList.dothis (msg:string);
begin
        writeln('message:',msg, ' high:', high(ID), ' constsize:', sizeof(ID), ' structsize:', sizeof(self));
end;

type
        TDay = (Mon, Tue, Wed);
        TDays = set of TDay;
const
        kSomeDays:TDays = [Mon, Wed];
var
        a: specialize TList<integer,10>;
        //b: specialize TList<integer,'foo'>;
        //c: specialize TList<integer,0.1>;
        //d: specialize TList<integer,kSomeDays>;
        //e: specialize TList<integer,nil>;
begin
        a.dothis('hello');
end.


Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
12345