Recent

Author Topic: FPC Unleashed (inline vars, statement expr, tuples, match, indexed/lazy labels)  (Read 40829 times)

440bx

  • Hero Member
  • *****
  • Posts: 6532
I have already mentioned my concerns about the last two.  If I had to choose one of them, I'd select the array indexing as it looks cleaner and is less of a departure from current syntax. 
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Fibonacci

  • Hero Member
  • *****
  • Posts: 1000
  • Behold, I bring salvation - FPC Unleashed
Thanks for the feedback. I will think it over and find a reasonable compromise. BTW, AI strongly favors the _1/_2 version :)
FPC Unleashed - inline vars, tuples, statement expressions, array equality, compound assignments, indexed/lazy labels, no-RTTI & more. ⭐ Star it on GitHub!

440bx

  • Hero Member
  • *****
  • Posts: 6532
Thanks for the feedback.
My pleasure.

I will think it over and find a reasonable compromise. BTW, AI strongly favors the _1/_2 version :)
Have you asked it why it favors that form ?  if not, it would be interesting to find out why.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Fibonacci

  • Hero Member
  • *****
  • Posts: 1000
  • Behold, I bring salvation - FPC Unleashed
I will think it over and find a reasonable compromise. BTW, AI strongly favors the _1/_2 version :)
Have you asked it why it favors that form ?  if not, it would be interesting to find out why.

I usually avoid pasting AI-generated responses - I hate stumbling upon them and I skip them without reading, for me that is "AI slop". But since you asked, here is what AI had to say:

Code: Text  [Select][+][-]
  1. Because tuples are records under the hood, not arrays. This has several consequences:
  2.  
  3. 1. Heterogeneous types
  4.  
  5. (Integer, String) - field _1 is Integer, field _2 is String. The compiler knows this at compile time. If result[0] returned Integer and result[1] returned String - what would be the type of result[i] where i is a variable? You cannot express that in a statically typed language.
  6.  
  7. 2. Rust and C# do the same
  8.  
  9. Rust: tuple.0, tuple.1 (field access, not indexing)
  10. C#: tuple.Item1, tuple.Item2 (field access)
  11. Swift: tuple.0, tuple.1
  12.  
  13. Only Python uses tuple[0] - but Python tuples are dynamically-typed sequences, not typed records.
  14.  
  15. 3. Consistency with named tuples
  16.  
  17. Named: result.x, result.y
  18. Positional: result._1, result._2
  19.  
  20. Both are field access. result[0] would force two different access paradigms for the same underlying mechanism.
  21.  
  22. 4. Zero runtime overhead
  23.  
  24. _1 is a compile-time offset. [0] implies bounds checking, indirection, runtime dispatch.
  25.  
  26. 5. Pascal is 1-based
  27.  
  28. Strings, default arrays - Pascal starts from 1. _1 is natural. [0] is a C-ism.
  29.  
FPC Unleashed - inline vars, tuples, statement expressions, array equality, compound assignments, indexed/lazy labels, no-RTTI & more. ⭐ Star it on GitHub!

440bx

  • Hero Member
  • *****
  • Posts: 6532
I understand about being reluctant to post A.I outputs.  I just find it often useful to find out what's their "reasoning" for favoring one option over another.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

creaothceann

  • Sr. Member
  • ****
  • Posts: 375
I rarely have to return several values as a function result - out variables are also an option, or passing the record as a var parameter by the caller and to be filled out by the subroutine. But, if I were to use this tuple feature I'd probably refer to the fields via names.


5. Pascal is 1-based

Strings, default arrays - Pascal starts from 1. _1 is natural.
  • is a C-ism.
Well, dynamic arrays also start at zero.

- - -

This has nothing to do with any existing feature of FPCU, but:

While I'm coding a state machine, I also define a macro with the current index. I can then use this macro as a constant value in conditional compiling:

Code: Pascal  [Select][+][-]
  1. const
  2.         SingleByteOpcodes =
  3.                 [      $08, $0A, $0B
  4.                 ,      $18, $1A, $1B
  5.                 ,      $28, $2A, $2B
  6.                 ,      $38, $3A, $3B
  7.                 , $40, $48, $4A, $4B
  8.                 ,      $58, $5A, $5B
  9.                 ,      $68, $6A, $6B
  10.                 ,      $78, $7A, $7B
  11.                 , $80, $88, $8A, $8B
  12.                 ,      $98, $9A, $9B
  13.                 ,      $A8, $AA, $AB
  14.                 ,      $B8, $BA, $BB
  15.                 ,      $C8, $CA, $CB
  16.                 ,      $D8, $DA, $DB
  17.                 ,      $E8, $EA, $EB
  18.                 ,      $F8, $FA, $FB];
  19.  
  20. //...
  21.  
  22.         // unconditional fetch
  23.         {$define  Fetch_FirstParam  :=  begin
  24.                 MDR := Fetch(PC, MDR);
  25.                 {$if not (Op in SingleByteOpcodes)}  PC := u16(PC + 1);  {$endif}
  26.         end}
  27.  
  28. // ...
  29.  
  30.         {$define Op := $00}  Opcode[Op]:  // BRK (software interrupt) or IRQ/NMI/RESET (hardware interrupt)
  31.                 if stop then goto Unload;  // loop exit
  32.                 Resume:                    // loop entry
  33.                 {1} Fetch_FirstParam;
  34.                 {2} MDR := PCH;  if is_RESET then MDR := Pull (S,  MDR) else Push(S, MDR);  Dec_S;              //                     push PC
  35.                 {3} MDR := PCL;  if is_RESET then MDR := Pull (S,  MDR) else Push(S, MDR);  Dec_S;              //                     push PC
  36.                 {4} MDR := P;    Get_Vector;             Push (S,  MDR);                    Dec_S;              // store vector in PC, push P
  37.                 {5} i   := 1;                     MDR := Fetch(PC, MDR);  Data  := MDR;     Inc_PC;             // disable IRQs,       fetch pointer
  38.                 {6}                               MDR := Fetch(PC, MDR);  DataH := MDR;     Inc_PC;             //                     fetch pointer
  39.                 {7} NMI := high(NMI);             MDR := Fetch(PC, MDR);  IR    := MDR;     Inc_PC;  Dispatch;  // clear NMI,          fetch opcode (no interrupt check)
  40. // ...

The {$if} compiler directive is very basic. It doesn't seem to understand arrays, so I have to define SingleByteOpcodes as a set. Maybe this could be expanded to any constant value... just an idea.
« Last Edit: April 06, 2026, 06:54:18 pm by creaothceann »

Fibonacci

  • Hero Member
  • *****
  • Posts: 1000
  • Behold, I bring salvation - FPC Unleashed
Ucoming feature: Tuples

What are tuples?

Lightweight anonymous record types in parentheses. Under the hood they are plain records, so everything records can do (copy semantics, managed types, pass by value/var/const) works out of the box. Gated by modeswitch TUPLES, on by default in {$mode unleashed}.

Quick example:

Code: Pascal  [Select][+][-]
  1. {$mode unleashed}
  2.  
  3. procedure DoSomethingAt(coords: (Integer, Integer));
  4. begin
  5.   WriteLn('name-based:  ', coords._1, 'x', coords._2);
  6.   WriteLn('index-based: ', coords[0], 'x', coords[1]);
  7. end;
  8.  
  9. function GetCurrentCoords: (Integer, Integer);
  10. begin
  11.   Result := (10, 50);
  12. end;
  13.  
  14. begin
  15.   DoSomethingAt(GetCurrentCoords);
  16.   ReadLn;
  17. end.
  18.  

Positional and named:

Code: Pascal  [Select][+][-]
  1. function GetPair: (Integer, String); // positional: _1, _2
  2. function Coords: (x, y: Integer);    // named: x, y
  3. begin
  4.   Result := (x: 10, y: 20);          // named literal
  5. end;
  6.  

Inline var, destructuring, swap, for-in:

Code: Pascal  [Select][+][-]
  1. var tuple := (123, 456, 'hello');   // inline var tuple
  2. writeln(tuple._1, tuple[2]);        // field access by name or index
  3.  
  4. var (a, b) := GetPair;              // inline var destructuring
  5. (a, b) := (b, a);                   // swap
  6. for var (key, val) in dict do       // for-in destructuring
  7.   WriteLn(key, '=', val);
  8. var (first, _, _, last) := GetQuad; // wildcard _ to skip fields
  9.  

Parameters, case..of, WriteLn:

Code: Pascal  [Select][+][-]
  1. procedure Show(p: (Integer, Integer));
  2. procedure Process((x, y): (Integer, Integer)); // x, y available directly
  3.  
  4. case GetCoords of
  5.   (0, 0): WriteLn('origin');
  6.   (1, 0): WriteLn('right');
  7. end;
  8. WriteLn(GetPair);                              // auto-expands to per-field output
  9.  

Nesting, generics, arrays, structural compat:

Code: Pascal  [Select][+][-]
  1. var n: (Integer, (String, Integer));
  2. n := (5, ('label', 42));
  3.  
  4. var n: (Integer, (String, Integer)) := (5, ('label', 42));
  5.  
  6. var n:= (5, ('label', 42));
  7.  
  8. generic function MakePair<A, B>(x: A; y: B): (A, B);
  9.  
  10. var pairs: array of (Integer, Integer);
  11. pairs := [(1, 2), (3, 4)];
  12.  

Positional tuples are structurally compatible with named tuples of the same shape, so (10, 20) can be assigned to (x, y: Integer).

Full IDE autocomplete support in Lazarus Unleashed - dot-completion shows tuple fields with inferred types for variables, function results and tuple literals.
FPC Unleashed - inline vars, tuples, statement expressions, array equality, compound assignments, indexed/lazy labels, no-RTTI & more. ⭐ Star it on GitHub!

Fred vS

  • Hero Member
  • *****
  • Posts: 3945
    • StrumPract is the musicians best friend
Code: Pascal  [Select][+][-]
  1. program app;
  2.  
  3. {$mode unleashed}
  4.  
  5. procedure main;
  6. begin
  7.   // *** INLINE VARIABLES ***
  8.  
  9.   var s := 'hi there';
  10.   var i := 42;
  11.   // or
  12.   var s2: string := 'hello';
  13.   var i2: integer := 66;
  14.  

Ah, I see a lot of AI that will love it, many of them (ChatGPT, Grok, Gemini, ...) use it in their (wrong) Pascal code.
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

Fibonacci

  • Hero Member
  • *****
  • Posts: 1000
  • Behold, I bring salvation - FPC Unleashed
Ah, I see a lot of AI that will love it, many of them (ChatGPT, Grok, Gemini, ...) use it in their (wrong) Pascal code.

They'll adapt once Unleashed mode becomes more common :D
FPC Unleashed - inline vars, tuples, statement expressions, array equality, compound assignments, indexed/lazy labels, no-RTTI & more. ⭐ Star it on GitHub!

Fred vS

  • Hero Member
  • *****
  • Posts: 3945
    • StrumPract is the musicians best friend
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

440bx

  • Hero Member
  • *****
  • Posts: 6532
@Fibonacci: Maybe you could unleash this too:
https://forum.lazarus.freepascal.org/index.php/topic,26541.msg163162.html#msg163162
It's unfortunate that such a simple and logical feature needs to be in a separate fork.  It should really be part of the standard FPC release.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Fibonacci

  • Hero Member
  • *****
  • Posts: 1000
  • Behold, I bring salvation - FPC Unleashed
@Fibonacci: Maybe you could unleash this too:
https://forum.lazarus.freepascal.org/index.php/topic,26541.msg163162.html#msg163162

Will consider.



For reference, re-posting the original suggestion:

Hello.

I woud not be against =>
Code: [Select]
Var
bool1, bool2, bool3 : boolean = true;

Nor against =>
Code: [Select]
Var
int1, int2, int3 : integer = 20;

Thanks.

Fre;D
FPC Unleashed - inline vars, tuples, statement expressions, array equality, compound assignments, indexed/lazy labels, no-RTTI & more. ⭐ Star it on GitHub!

Fibonacci

  • Hero Member
  • *****
  • Posts: 1000
  • Behold, I bring salvation - FPC Unleashed
New feature: Block-scoped inline variables

Inline variables now support block scoping. Pushed to main.

Example

Code: Pascal  [Select][+][-]
  1. {$mode unleashed}
  2.  
  3. procedure main;
  4. var
  5.   g: integer = 9; // variable scoped to the main procedure - visible throughout its body
  6. begin
  7.   begin
  8.     var i := 1;
  9.     var a := 33;
  10.     writeln(i);  // breakpoint here
  11.                  // GDB correctly shows local variables: i, a and the outer variable g
  12.                  // FpDebug shows only g - no support for block-scoped variables
  13.   end;
  14.  
  15.   begin
  16.     var i := 2;
  17.     var b := 44;
  18.     writeln(i);   // breakpoint here
  19.                   // GDB correctly shows local variables: i, b and the outer variable g
  20.                   // FpDebug shows only g - no support for block-scoped variables
  21.   end;
  22. end;
  23.  
  24. begin
  25.   main;
  26. end.



Also fixed:
- IDE now promotes inference-typed Chars (var s := 'a' would be a Char, not a String) to Strings for auto-completion with String methods such as .Trim() or .Split() (requires SysUtils in uses).
- FPC compiler also promotes inference-typed Chars to Strings, so that IDE auto-completion matches the actual type seen by the compiler.
- Other auto-completion fixes in the IDE regarding inline-vars.
- Fixed a few Internal Errors, one of which caused one of my bigger projects to fail to compile on subsequent builds (something wrong with PPU files). Considering to MR this patch to the official repo once I confirm the IE is really gone - it was (or still is?) pretty specific: some part of the code in one of many units needs to be changed while others don't. Weird thing.
- Inference-typed inline variable in a for-loop wasn't promoted to Integer - now it is.
« Last Edit: April 07, 2026, 03:05:25 pm by Fibonacci »
FPC Unleashed - inline vars, tuples, statement expressions, array equality, compound assignments, indexed/lazy labels, no-RTTI & more. ⭐ Star it on GitHub!

440bx

  • Hero Member
  • *****
  • Posts: 6532
can inline var types not be inferred ?  IOW, can the type be explicitly stated, as in:
Code: Pascal  [Select][+][-]
  1. {$mode unleashed}
  2.  
  3. procedure main;
  4. var
  5.   g: integer = 9; // variable scoped to the main procedure - visible throughout its body
  6. begin
  7.   begin
  8.     var i : integer := 1;
  9.     var a : integer := 33;
  10.     writeln(i);  // breakpoint here
  11.                  // GDB correctly shows local variables: i, a and the outer variable g
  12.                  // FpDebug shows only g - no support for block-scoped variables
  13.   end;
  14.  
  15.   begin
  16.     var i : integer  := 2;
  17.     var b : integer  := 44;
  18.     writeln(i);   // breakpoint here
  19.                   // GDB correctly shows local variables: i, a and the outer variable g
  20.                   // FpDebug shows only g - no support for block-scoped variables
  21.   end;
  22. end;
  23.  
  24. begin
  25.   main;
  26. end.
  27.  
Lastly your post seems to imply that you've implemented inline declaration of a "for" loop index, e.g, "for var i := 0 to somevalue do ...", if that is the case, can the type of the index also be explicitly stated (as in "for var i : integer := 0 to ...") ?
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Fibonacci

  • Hero Member
  • *****
  • Posts: 1000
  • Behold, I bring salvation - FPC Unleashed
Yes, you can explicitly state the type in both cases:

Code: Pascal  [Select][+][-]
  1. var i: Int64 := 1;              // explicit type
  2. var j := 1;                     // inferred type (Integer)
  3.  
  4. for var n: Int64 := 0 to 5 do ; // explicit type
  5. for var k := 0 to 5 do ;        // inferred type (Integer)



All features will be documented soon.
« Last Edit: April 07, 2026, 08:56:05 am by Fibonacci »
FPC Unleashed - inline vars, tuples, statement expressions, array equality, compound assignments, indexed/lazy labels, no-RTTI & more. ⭐ Star it on GitHub!

 

TinyPortal © 2005-2018