Recent

Author Topic: FPC Unleashed (async/await, parallel for, match, string interpolation & more)  (Read 46318 times)

jamie

  • Hero Member
  • *****
  • Posts: 7852
I would prefer the use of " anonymous" like this     " anonymous :TPoint" to embed that within another record.

 The compiler of course needs to check for duplicates within the same scope.

There is no need to create new reserved words when you can advance what you already have.

Jamie

The only true wisdom is knowing you know nothing

Thausand

  • Hero Member
  • *****
  • Posts: 560
I not know if I have make understood correct you remark @jamie:

You write is like say make identifier that have name anonymous have special meaning ? If that is what mean what is write then have ask why is need any modifier / keyword for language ?

Code: Pascal  [Select][+][-]
  1.   TFoo = record
  2.     One   : integer;
  3.     Two   : byte;
  4.     Three : char;
  5.     Four  : string;
  6.   end;
  7.  
  8.   TBar = record
  9.     OneTo   : integer;
  10.     TwoTo   : byte;
  11.     ThreeTo : char;
  12.     FourTo  : string;
  13.   end;
  14.  
  15.   TFooBar = record
  16.     embed TFoo;
  17.     embed TBar;
  18.   end;
  19.  

I think this is good for practise solution. May be better is use modifier anonymous that is ignore field name identifier (but this is break how is work existing modifier).

PS: is there other language that have embed anonymous structure and have good solution (example, I not like golang solution and only have type or not have field identifier) ?
A docile goblin always follow HERMES.md

Fibonacci

  • Hero Member
  • *****
  • Posts: 1035
  • Behold, I bring salvation - FPC Unleashed
@jamie:

Good to see experienced Pascal folks warming up to the new stuff. @Thaddy softened a bit too - likes some of it, not all. I'm never going to please everyone, and that's fine: there'll always be things one person loves and another can't stand. Even I don't use everything in Unleashed myself - take indexed/lazy labels: I don't use labels at all. But I like that lazy labels are there - I can drop one in without declaring it if I ever want to, even though I haven't actually needed it anywhere yet (since introduced).

On embed - it's already baked in, so I'm leaving it as is. Funny part: there wasn't supposed to be an "embed" keyword at all. The bare, keyword-less form was the original plan; "embed" only got added because @440bx (IIRC) didn't like having "nothing" there. He's still only lukewarm on it - but that's the compromise we (or I) settled on. Which is the whole point of this thread: you've got to show up while a feature is being hashed out, because that's exactly when decisions like this get made. It could have ended up being "anonymous" if you had proposed it earlier.



@flowCRANE:

Static locals are on my todo list. And they'll work inline too - same as "inline var" - so you'll be able to declare a static right where you use it, not just in a declaration block.



@440bx:

So "Type()" would be a compile-time intrinsic that returns literally the type of a given variable - so you can declare another variable of the same type, or use it anywhere a type is required. That's the gist, right?

And "Type()" is the spelling you're proposing? Let's give this idea some time to discuss. I noticed others are questioning - why not generics, they can already do that.
FPC Unleashed: async/await, parallel for, match, tuples, string interpolation, inline vars, autofree, no-RTTI & tons more. Star on GitHub

Thaddy

  • Hero Member
  • *****
  • Posts: 19464
  • Glad to be alive.
I know we can simulate them by declaring a typed constant inside a function, but this approach depends on the {$WRITEABLECONST} directive, so modifying such constants may not be possible.
Only in single threaded applications. typed consts are global even if declared local. Example:
Code: Pascal  [Select][+][-]
  1. {$apptype console}
  2. {$ifdef fpc}{$mode delphi}{$endif}{$J+}
  3. uses
  4.   sysutils,classes;
  5. type
  6.   TMyClass = class(TThread)
  7.   private
  8.     procedure execute;override;
  9.   end;
  10.  
  11.   procedure TMyClass.Execute;
  12.   const
  13.     aConst:integer = 0;
  14.   var
  15.     i:integer;
  16.   begin
  17.     for i := 0 to 999 do
  18.     begin
  19.       inc(aConst);
  20.       writeln(aConst);
  21.     end;
  22.   end;
  23.  
  24. var
  25.   mc1,mc2:TMyClass;
  26. begin
  27.   mc1 :=TMyClass.Create(false);mc2 :=TMyClass.Create(false);
  28.   mc1.waitfor;mc2.waitfor;
  29.   mc1.free;mc2.free;
  30.   readln;
  31. end.
This counts to 2000, not two times to 1000...in other words it is a global static variable.
A true local static variable needs a local record/object or class field. Because fields are truly static.
So yes, true local static variables would be nice.

(btw the same goes for Delphi)

The current solution looks like this, replace this in the above example:
Code: Pascal  [Select][+][-]
  1.   procedure TMyClass.Execute;
  2.   var
  3.     i:integer;
  4.     a:record aconst:integer;end;//local static
  5.   begin
  6.     for i := 0 to 999 do
  7.     begin
  8.       inc(a.aConst);
  9.       writeln(a.aConst);
  10.     end;
  11.   end;

It would be really nice if this concept of true static variables is solved without the need to declare  a structure.
« Last Edit: June 05, 2026, 11:04:39 am by Thaddy »
Any "programmer" that knows only one programming language is not a programmer

creaothceann

  • Sr. Member
  • ****
  • Posts: 390
@440bx:

So "Type()" would be a compile-time intrinsic that returns literally the type of a given variable - so you can declare another variable of the same type, or use it anywhere a type is required. That's the gist, right?

And "Type()" is the spelling you're proposing? Let's give this idea some time to discuss. I noticed others are questioning - why not generics, they can already do that.

This might be confused with type aliases/clones, perhaps another keyword could be used (e.g. TypeOf)?

https://www.freepascal.org/docs-html/ref/refse19.html#x49-690003.8
https://wiki.freepascal.org/Type#type_clone
« Last Edit: June 05, 2026, 10:59:09 am by creaothceann »

Fibonacci

  • Hero Member
  • *****
  • Posts: 1035
  • Behold, I bring salvation - FPC Unleashed
@Thaddy @flowCRANE

So what's the expected behavior for static? A true per-program static (one location -> counts to 2000), or "per instance" (if inside an instance -> 2x to 1000)?

The per-instance variant makes little sense to me though - an instance can already have its own field for that, so what would it be for? And it complicates things a lot. In every other language static means a single location in the program anyway, whether it's inside an instance or not.



@creaothceann

"TypeOf()" is already taken, whereas "Type()" as a function is still available (I think...).
« Last Edit: June 05, 2026, 12:03:10 pm by Fibonacci »
FPC Unleashed: async/await, parallel for, match, tuples, string interpolation, inline vars, autofree, no-RTTI & tons more. Star on GitHub

Thaddy

  • Hero Member
  • *****
  • Posts: 19464
  • Glad to be alive.
You should not focus on the use in classes. Local statics have a different purpose. And you are wrong about true local statics in e.g. C and C++:
Code: C  [Select][+][-]
  1. void f(void) {
  2.     static int counter = 0;  // LOCAL scope!
  3.     counter++;
  4. }
But in C++ we also use static struct with the same effect.
It would be nice to have a truly local static, because it maintains local state for the local procedure and that is the most common case.
Ok, an experienced programmer would write a local static as a record, but I know few who do....You can't use {$writeableconst} for that. Hence my demo.
Forget about the classes, think procedure level.
Any "programmer" that knows only one programming language is not a programmer

Fibonacci

  • Hero Member
  • *****
  • Posts: 1035
  • Behold, I bring salvation - FPC Unleashed
@Thaddy:

Code: C  [Select][+][-]
  1. #include <stdio.h>
  2. #include <pthread.h>
  3.  
  4. void* execute(void* arg) {
  5.     static int aConst = 0;  // shared across all calls
  6.     int i;
  7.     for (i = 0; i < 1000; i++) {
  8.         aConst++;
  9.         printf("%d\n", aConst);
  10.     }
  11.     return NULL;
  12. }
  13.  
  14. int main(void) {
  15.     pthread_t mc1, mc2;
  16.     pthread_create(&mc1, NULL, execute, NULL);
  17.     pthread_create(&mc2, NULL, execute, NULL);
  18.     pthread_join(mc1, NULL);
  19.     pthread_join(mc2, NULL);
  20.     getchar();
  21.     return 0;
  22. }

This counts to 2000 too - same single location as the typed const. So a real local static does count to 2000; the scope is local (never argued otherwise), the storage is one place.

So again: What's the expected behavior for static? A true per-program static (one location -> counts to 2000), or "per instance" (if inside an instance -> 2x to 1000)? Or maybe per scope?
« Last Edit: June 05, 2026, 02:29:44 pm by Fibonacci »
FPC Unleashed: async/await, parallel for, match, tuples, string interpolation, inline vars, autofree, no-RTTI & tons more. Star on GitHub

flowCRANE

  • Hero Member
  • *****
  • Posts: 986
@flowCRANE:

Static locals are on my todo list. And they'll work inline too - same as "inline var" - so you'll be able to declare a static right where you use it, not just in a declaration block.

Great! Do you plan to use the static modifier, as with the contents of objects/classes, or are you planning to use a dedicated syntax for static variables? With the modifier, the syntax would look something like this:

Code: Pascal  [Select][+][-]
  1. var
  2.   Foo: Boolean; static; // static variable
  3.   Bar: Boolean;         // regular local variable
  4. begin
  5.   var Foo: Boolean; static; // inline static variable
  6.   var Bar: Boolean;         // inline local variable

However, with a dedicated syntax, we could do it this way:

Code: Pascal  [Select][+][-]
  1. static
  2.   Foo: Boolean; // static variable
  3. var
  4.   Bar: Boolean; // regular local variable
  5. begin
  6.   static Foo: Boolean; // inline static variable
  7.   var    Bar: Boolean; // inline local variable

On the one hand, a dedicated section for static variables would improve readability and provide clear separation of declared elements (types, labels, constants, local variables, static variables), but on the other hand, the modifier also looks good and is consistent with its use in Free Pascal (in terms of static fields and methods).

Which solution do you think would be better and more syntactically consistent with Pascal?

After all, you have to take into account declarations combined with initialization, as well as all other combinations related to variable declarations supported by Free Pascal and Unleashed Pascal.
« Last Edit: June 05, 2026, 02:50:40 pm by flowCRANE »
Lazarus 4.6 with FPC 3.2.2, Windows 11 — all 64-bit

Working solo on a top-down retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

Fibonacci

  • Hero Member
  • *****
  • Posts: 1035
  • Behold, I bring salvation - FPC Unleashed
or are you planning to use a dedicated syntax for static variables?

This one.

However, with a dedicated syntax, we could do it this way:

Code: Pascal  [Select][+][-]
  1. static
  2.   Foo: Boolean; // static variable
  3. var
  4.   Bar: Boolean; // regular local variable
  5. begin
  6.   static Foo: Boolean; // inline static variable
  7.   var    Bar: Boolean; // inline local variable



The question remains - static per program (single memory location), per instance, or per scope?
FPC Unleashed: async/await, parallel for, match, tuples, string interpolation, inline vars, autofree, no-RTTI & tons more. Star on GitHub

Thaddy

  • Hero Member
  • *****
  • Posts: 19464
  • Glad to be alive.
It only makes sense for local scope. But global - if declared global - should still work.
The second execute method in my example shows why.
Any "programmer" that knows only one programming language is not a programmer

flowCRANE

  • Hero Member
  • *****
  • Posts: 986
In my opinion, static variables should only be allowed to be declared inside functions (in a dedicated block or declared as inlined), because that is their purpose—they are meant to physically exist in memory in the same place as global variables, but their scope should be local to a function.

Declaring static variables outside of functions—for example, in the implementation section—defeats their purpose and is no different from the declaration and purpose of a regular, global variable. So, in my opinion, if static variables are to be supported, their declaration should be restricted to inside functions only, so as not to allow a given variable to be declared in two different ways, intended for two different uses.



It would also be a good idea to consider supporting static thread variables, as they would also be useful in multithreaded programs. In other words, not only should it be possible to declare static variables with a single instance for the entire program, but also static thread variables with a single instance for each worker.

This calls for a slightly different syntax for declaring static variables, so as to distinguish between regular and thread-specific static variables, and to align the syntax with the current Free Pascal syntax. My suggested syntax:

Code: Pascal  [Select][+][-]
  1. // declaration sections
  2. var
  3.   Foo: Integer;         // regular local variable
  4.  
  5. static var
  6.   Foo: Integer = $BEEF; // regular static variable
  7.  
  8. static threadvar
  9.   Foo: Integer = $BEEF; // static variable, one per thread worker
  10.  
  11. // function body
  12. begin
  13.   // inlined regular local variable
  14.   var Foo: Integer;
  15.  
  16.   // inlined regular static variable
  17.   static var Foo: Integer = $BEEF;
  18.  
  19.   // inlined static variable, one per thread worker
  20.   static threadvar Foo: Integer = $BEEF;
  21.  
  22.   {..}
  23. end;

What do you thing about it?
Lazarus 4.6 with FPC 3.2.2, Windows 11 — all 64-bit

Working solo on a top-down retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

440bx

  • Hero Member
  • *****
  • Posts: 6559
@440bx:

So "Type()" would be a compile-time intrinsic that returns literally the type of a given variable - so you can declare another variable of the same type, or use it anywhere a type is required. That's the gist, right?

And "Type()" is the spelling you're proposing? Let's give this idea some time to discuss. I noticed others are questioning - why not generics, they can already do that.
Yes, it can be done with generics but, I see two problems with using generics for that.  The first one is that generics in general need a lot of syntax and, for such a simple feature, it really seems over the top.  The second one is that, that's not the purpose of generics at all, the purpose of generics is to generate type-customized code, not declare that a variable should be of the same type as another.  Succinctly, there are many simple cases where this would be useful, e.g, declaring a temporary variable for swapping, that generics would be way overkill to just make one variable be the same type as another.

I propose "Type()" because "typeof" is already in use for RTTI.  It also describes the intent very well and, in addition to that, it is parser friendly because all the parser has to do is inspect the next token (which it would do anyway) to figure out that the presence of "(" means it is being used as a function.  Simple, clear, no ambiguity, no conflicts, good stuff.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

BildatBoffin

  • Jr. Member
  • **
  • Posts: 51
PS: is there other language that have embed anonymous structure and have good solution (example, I not like golang solution and only have type or not have field identifier) ?

D has "alias this'

Code: D  [Select][+][-]
  1. struct Inner {
  2.     int a;
  3. }
  4.  
  5. struct Front1 {
  6.     int a;
  7.     Inner i;
  8.     alias i this ;
  9. }
  10.  
  11. struct Front2 {
  12.     int b;
  13.     Inner i;
  14.     alias i this ;
  15. }
  16.  
  17. void testF1()
  18. {
  19.     Front1 f1;i
  20.     f1.i.a = 0; // you can disambguate, no shadowing
  21.     f1.a = 0;   // refers to the direct member
  22. }
  23.  
  24. void testF2()
  25. {
  26.     Front2 f2;
  27.     f2.a = 0;   // refers to the aliased member
  28. }

As explained in the code comment two variables can have the same name because the language allows you to make the difference.

Otherwise I think to Odin which has a feature similarly designed to what people are talking about there... Problem is that Odinusers just found what you were mentioning (https://github.com/odin-lang/Odin/issues/6743).

And finally I will also mention macro-based solutions. E.g you define a macro within your record, that macro expands to an inner member. For example in STYX

Code: D  [Select][+][-]
  1. unit temp;
  2.  
  3. struct Inner
  4. {
  5.     var s32 member;
  6. }
  7.  
  8. struct Front
  9. {
  10.     var Inner i;
  11.     alias member => i.member; // this an expression macro
  12. }
  13.  
  14. function main(): s32
  15. {
  16.     (var Front f).member = 0; // expands to
  17.                               // (var Front f).i.member = 0;
  18.     return 0;
  19. }

Fibonacci

  • Hero Member
  • *****
  • Posts: 1035
  • Behold, I bring salvation - FPC Unleashed
New feature: static local variables

On by default in {$mode unleashed}. Outside: {$modeswitch staticsection} and/or {$modeswitch inlinestatic}.

Program-wide lifetime, block-local scope - the [static int x]-inside-a-function from C. Think of it as a typed constant that is writeable. Two flavors.

Section static

A declaration block next to var/const, compile-time constant initializers, zero runtime cost (baked into the binary as data):

Code: Pascal  [Select][+][-]
  1. procedure Bumper;
  2. static
  3.   cnt: Integer = 0;
  4. begin
  5.   Inc(cnt);
  6.   WriteLn(cnt);
  7. end;
  8.  
  9. begin
  10.   Bumper;  // 1
  11.   Bumper;  // 2
  12.   Bumper;  // 3
  13. end.

No initializer means zero-init; := infers the type:

Code: Pascal  [Select][+][-]
  1. static
  2.   x: Integer;                 // zero-init
  3.   y: Integer = 42;            // explicit
  4.   greet := 'hello';           // inferred type
  5.   buf: array[16] of Byte;     // zeroed

Inline static

static name := expr; anywhere in a body. Runtime initializer, evaluated once on first reach and cached:

Code: Pascal  [Select][+][-]
  1. function Squared(n: Integer): Integer;
  2. begin
  3.   static base := n*n;   // computed once, on the first call
  4.   Result := base;
  5. end;
  6.  
  7. begin
  8.   WriteLn(Squared(5));  // 25
  9.   WriteLn(Squared(9));  // 25 - cached, init skipped
  10. end.

A hidden guard is set before the initializer runs, so a raising initializer doesn't retry, and reentrant init sees the still-zero value (deterministic, documented).

Rules
  • Both forms are allowed only inside function/procedure/method bodies. At unit/program level plain var already has program lifetime, so top-level use is an error.
  • Section static takes compile-time constant initializers only. Need a runtime value? Use inline static.
  • Not thread-local: all threads share one instance, you handle the locking. Use unit-level threadvar for per-thread.
What stays unchanged

The existing static method/field modifier in classes is untouched - the new section and inline forms only apply inside routine bodies. Strictly additive, no breaking changes.
« Last Edit: June 06, 2026, 10:00:22 am by Fibonacci »
FPC Unleashed: async/await, parallel for, match, tuples, string interpolation, inline vars, autofree, no-RTTI & tons more. Star on GitHub

 

TinyPortal © 2005-2018