Recent

Author Topic: What is your favorite "built in" procedure/function of FP?  (Read 889 times)

TBMan

  • Full Member
  • ***
  • Posts: 200
What is your favorite "built in" procedure/function of FP?
« on: June 12, 2025, 06:43:46 pm »
I was amazed when I learned recently of the WriteStr procedure. As an old Turbo Pascal coder, this slipped by me. What else is out there, buried in the pages of documentation, that you love to use?

Warfley

  • Hero Member
  • *****
  • Posts: 1933
Re: What is your favorite "built in" procedure/function of FP?
« Reply #1 on: June 12, 2025, 09:44:49 pm »
Some intrinsics are fine, for example the math intrinsics (sin, cos, etc.) are nice because they allow the compiler to optimize on certain architectures that have those builtin. Others are necessary like SizeOf, High, Low, etc.
I'm still not the biggest fan of them because: If it looks like a function it should behave like a function. Those listed above behave nearly like functions, but not fully, so for example you can't take the pointer to those functions:
Code: Pascal  [Select][+][-]
  1. var
  2.   fp: function(x: Double): Double;
  3. begin
  4.   fp := @sin; // Error
  5. end;

Even worse it becomes with functions like Read(ln) Write(ln) or str. Those functions are not just special implementations, they also have special syntax. First the obvious, the arbitrary number and type of parameters:
Code: Pascal  [Select][+][-]
  1. WriteLn('String', True, 3.14, 42);
I mean this is a cool feature, but it's odd that there is only like a few functions like that and they aren't extensible. Why not allow the user to write their own compiletime varardic functions? You can do in C++:
Code: C  [Select][+][-]
  1. void WriteLn() {
  2.     std::cout << std::endl;
  3. }
  4.  
  5. template <typename T, typename ...Args>
  6. void WriteLn(T const &val, Args &&...args) {
  7.     std::cout << val;
  8.     WriteLn(args...);
  9. }
  10.  
  11. int main() {
  12.     WriteLn("String", 'c', 3.14, 42, true);
  13.     return 0;
  14. }
Which now works pretty much exactly the same as WriteLn in pascal, but because it's an in language construct it's extensible. The following doesn't work in Pascal:
Code: Pascal  [Select][+][-]
  1. type
  2.   TMyRec = record
  3.     a, b: Integer;
  4.   end;
  5.  
  6. var
  7.   r: TMyRec;
  8. begin
  9.   WriteLn(r);
  10. end.
But because in C++ it's not compiler magic but in language definitions, I can extend the functionality as much as I like:
Code: C  [Select][+][-]
  1. struct MyStruct {
  2.     int a, b;
  3. };
  4.  
  5. template <typename ...Args>
  6. void WriteLn(MyStruct const &val, Args &&...args) {
  7.     std::cout << "a=" << val.a << ",b=" << val.b;
  8.     WriteLn(args...);
  9. }
  10.  
  11. int main() {
  12.     MyStruct s = {.a=42, .b=32};
  13.     WriteLn(s);
  14.     return 0;
  15. }

The behavior of WriteLn is pretty cool, but also very limited, it would be much better if it could be amended. Other languages do so by having a default string serialization method, toString in Java or C# or str(x) in Python. It's not as flexible as C++ but still alows you to re-use those magic functions like print for more than just base types.

But even worse, WriteLn has a special syntax for formatting:
Code: Pascal  [Select][+][-]
  1. WriteLn(MyVar:4); // Write with padding of 4
Like the : syntax is used no where else, it's a special construct only for Write(ln) and Str functions, something that is also completely unnecessary, because you could just have a function that takes two arguments instead of one (or tuple types) and use this instead.

Like I'm all for flexibility in writing code, but it should be in one consistent way that allows it to be used very flexible and applied in many different situations, not a special magic solution that is only usable for one very specific thing
« Last Edit: June 12, 2025, 09:51:17 pm by Warfley »

LV

  • Sr. Member
  • ****
  • Posts: 303
Re: What is your favorite "built in" procedure/function of FP?
« Reply #2 on: June 12, 2025, 10:59:36 pm »
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. var
  4.   fp: function(x: double): double;
  5.  
  6.   function sin_(x: double): double;
  7.   begin
  8.     Result := sin(x);
  9.   end;
  10.  
  11. begin
  12.   fp := @sin_;       // No Error
  13.  
  14.   writeln(Pi);      //  3.1415926535897931E+000
  15.   writeln(fp(Pi));  //  1.2246467991473532E-016
  16.   readln;
  17.  
  18. end.  
  19.  

Warfley

  • Hero Member
  • *****
  • Posts: 1933
Re: What is your favorite "built in" procedure/function of FP?
« Reply #3 on: June 13, 2025, 12:02:27 am »
Yes this is exactly my point, instead of just using a regular language feature you must make an akward manual workaround to make this work. There is no reason that you would need to do that. The RTL could contain a function for sin, cos, etc. and then the fpc could either use the intrinsic when it's a direct call, or the function if a function pointer is taken without the user having to add any custom code to make this work.

This limitation is completely unecessary and just makes the life harder for the programmer

440bx

  • Hero Member
  • *****
  • Posts: 5596
Re: What is your favorite "built in" procedure/function of FP?
« Reply #4 on: June 13, 2025, 12:31:17 am »
Why not allow the user to write their own compiletime varardic functions?
Simple: because there is no type checking in such functions.

That said, some compilers in some cases can do type checking on variadic functions (MSVC does) but, it's still a far cry from what a standard Pascal compiler can do but, the price is: there is no way for a programmer to implement the necessary compiler "magic" to write such functions.

If a compiler can give itself the "luxury" of throwing type safety out the window or not even bother to implement such "frivolities" (which is in very soul of the C language) then there is almost no limit to the "features" that can be implemented.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

Thaddy

  • Hero Member
  • *****
  • Posts: 17477
  • Ceterum censeo Trumpum esse delendum (Tnx Charlie)
Re: What is your favorite "built in" procedure/function of FP?
« Reply #5 on: June 13, 2025, 06:35:51 am »
@TBMan
Some info:
writestr is defined in ISO 10206:1991 so it post-dates earlier versions of TP.
In FPC it was implemented as part of Extended Pascal mode, but made available in all other modes.

Favorite? This changes daily, but I really like the build-in Observer Pattern provided in TPersistent.
I use it a lot in non-gui server type applications.
https://www.freepascal.org/docs-html/rtl/classes/ifpobserved.html
« Last Edit: June 13, 2025, 09:03:47 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Warfley

  • Hero Member
  • *****
  • Posts: 1933
Re: What is your favorite "built in" procedure/function of FP?
« Reply #6 on: June 13, 2025, 10:55:21 am »
That said, some compilers in some cases can do type checking on variadic functions (MSVC does) but, it's still a far cry from what a standard Pascal compiler can do but, the price is: there is no way for a programmer to implement the necessary compiler "magic" to write such functions.
I mean thats just plain wrong, the code shown above is fully resolved at compiletime with full type checking. It's also partly possible in pascal, take the following:
Code: Pascal  [Select][+][-]
  1. {$mode Delphi}
  2. {$ModeSwitch implicitfunctionspecialization}
  3.  
  4. uses
  5.   SysUtils;
  6.  
  7. function ToString(s: String): String; overload;
  8. begin
  9.   Result := s;
  10. end;
  11.  
  12. function ToString(i: Integer): String; overload;
  13. begin
  14.   Result := IntToStr(i);
  15. end;
  16.  
  17. function ToString(d: Double): String; overload;
  18. begin
  19.   Result := FloatToStr(d);
  20. end;
  21.  
  22. procedure PrintLn<T>(const AValue: T);
  23. begin
  24.   Write(ToString(AValue));
  25.   WriteLn;
  26. end;
  27.  
  28. begin
  29.   PrintLn('Hello World'); // Works because ToString(String) exists
  30.   PrintLn(42); // Works because ToString(Integer) exists
  31.   PrintLn(3.14); // Works because ToString(Double) exists
  32.   PrintLn(True); // Does not compile because ToString(Boolean) does not exists
  33. end.
As you can see, you have full typechecking at compiletime allowing you to use the same function with different types as long as there is an overload for it. Which is exactly what my C++ code does.

The difference is, C++ allows with is, it allows to recursively define this over multiple arguments not just one as in this case, first we have the base case:
Code: C  [Select][+][-]
  1. void WriteLn() {
  2.     std::cout << std::endl;
  3. }
So if you call WriteLn without an argument, this function will be called, which will just print a newline and flush the buffer (same as empty WriteLn in Pascal). Then the followup case:
Code: C  [Select][+][-]
  1. template <typename T, typename ...Args>
  2. void WriteLn(T const &val, Args &&...args) {
  3.     std::cout << val;
  4.     WriteLn(args...);
  5. }
Says: If there is at least one parameter val and an arbitrary number of followup arguments, print the first character, using the overloaded operator << (if there is no overload for that type it will throw an error as  the pascal code above) and then call for the remaining arguments.

So what happens here is for the call:
Code: C  [Select][+][-]
  1. WriteLn("String", 42, 3.14);
The following functions are generated (for brevity removing & and const):
Code: C  [Select][+][-]
  1. void WriteLn1Param(double val) {
  2.     std::cout << val;
  3.     WriteLn();
  4. }
  5. void WriteLn2Params(int val, double p2) {
  6.     std::cout << val;
  7.     WriteLn1Param(p2);
  8. }
  9. void WriteLn3Params(string val, int p2, double p3) {
  10.     std::cout << val;
  11.     WriteLn2Params(p2, p3);
  12. }
Which after inline will be a single function:
Code: C  [Select][+][-]
  1. void WriteLn(string p1, int p2, double p3) {
  2.     std::cout << p1;
  3.     std::cout << p2; // inlined from WriteLn2Params
  4.     std::cout << p3; // inlined from WriteLn3Params
  5.     std::cout << std::endl; // inlined from base WriteLn();
  6. }
  7.  
Giving just one neat function with full on typechecking, which is basically exactly the same the WriteLn intrinsic does in FPC, just that in C++ we built it ourselves.

korba812

  • Sr. Member
  • ****
  • Posts: 476
Re: What is your favorite "built in" procedure/function of FP?
« Reply #7 on: June 13, 2025, 11:24:36 am »
My favorite functionality in Pascal is RTTI
Code: Pascal  [Select][+][-]
  1. type
  2.   TEnum = (First, Second, Last);
  3. var
  4.   E: TEnum;
  5. begin
  6.   for E in TEnum do
  7.     WriteLn(E);
  8. end.
  9.  

cdbc

  • Hero Member
  • *****
  • Posts: 2266
    • http://www.cdbc.dk
Re: What is your favorite "built in" procedure/function of FP?
« Reply #8 on: June 13, 2025, 11:32:49 am »
Hi
Mine has to be 'TObject.GetInterface()', just as big a marvel as the built-in  observer-pattern...  8-)
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

PascalDragon

  • Hero Member
  • *****
  • Posts: 6051
  • Compiler Developer
Re: What is your favorite "built in" procedure/function of FP?
« Reply #9 on: June 13, 2025, 10:32:09 pm »
Even worse it becomes with functions like Read(ln) Write(ln) or str. Those functions are not just special implementations, they also have special syntax. First the obvious, the arbitrary number and type of parameters:
Code: Pascal  [Select][+][-]
  1. WriteLn('String', True, 3.14, 42);
I mean this is a cool feature, but it's odd that there is only like a few functions like that and they aren't extensible. Why not allow the user to write their own compiletime varardic functions?

Oh, I don't know... maybe because that functionality predates variadic functions by around 30 years?

You can do in C++:
Code: C  [Select][+][-]
  1. void WriteLn() {
  2.     std::cout << std::endl;
  3. }
  4.  
  5. template <typename T, typename ...Args>
  6. void WriteLn(T const &val, Args &&...args) {
  7.     std::cout << val;
  8.     WriteLn(args...);
  9. }
  10.  
  11. int main() {
  12.     WriteLn("String", 'c', 3.14, 42, true);
  13.     return 0;
  14. }
Which now works pretty much exactly the same as WriteLn in pascal, but because it's an in language construct it's extensible.

I'm still wreaking my brain for a good syntax, cause with the C++ one I regularly get eye twitches (considering that I earn my money with C++ that's very regular ;) )

Warfley

  • Hero Member
  • *****
  • Posts: 1933
Re: What is your favorite "built in" procedure/function of FP?
« Reply #10 on: June 13, 2025, 10:51:14 pm »
I'm still wreaking my brain for a good syntax, cause with the C++ one I regularly get eye twitches (considering that I earn my money with C++ that's very regular ;) )
Well theres also generative programming like Terra where a meta language can be used to generate code for the main language. Then people can produce bugs in two languages instead of one xD

 

TinyPortal © 2005-2018