Recent

Author Topic: New language features?  (Read 30350 times)

BeniBela

  • Hero Member
  • *****
  • Posts: 959
    • homepage
Re: New language features?
« Reply #15 on: June 17, 2023, 10:55:44 pm »
you can simply put that code into a new function. Pascal has nested functions which are really useful to structure your code.
Also remember the rule of thumb is max 20 lines of code per function, if you have much more than that, you should recosinder splitting it up anyway

but not with fpc

It cannot track uninitialized variables across nested functions, so splitting them causes all kinds of warnings

And each function gets its own implicit finally block, so it becomes very slow when there are too many. Although one could split it into one function having no managed variables and one function having all the managed variables

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12291
  • Debugger - SynEdit - and more
    • wiki
Re: New language features?
« Reply #16 on: June 17, 2023, 11:05:31 pm »
you can simply put that code into a new function. Pascal has nested functions which are really useful to structure your code.
Also remember the rule of thumb is max 20 lines of code per function, if you have much more than that, you should recosinder splitting it up anyway

but not with fpc

It cannot track uninitialized variables across nested functions, so splitting them causes all kinds of warnings

You should design it, so you wouldn't have to.

Quote
And each function gets its own implicit finally block, so it becomes very slow when there are too many. Although one could split it into one function having no managed variables and one function having all the managed variables
Only for managed types, and only if they are needed in each of the functions. Needed as new local. If accessed from outer scope, and used without introducing temps then again: not

And even then, it can be turned off.

Warfley

  • Hero Member
  • *****
  • Posts: 2049
Re: New language features?
« Reply #17 on: June 18, 2023, 12:02:20 am »
But how long will this trend last? In the history of IT, certain trends (fashions) came and went, and then returned after many years. So it was with AI. It's probably 3rd (or 4th) now. It was the same with the so-called "expert systems". They were supposed to solve all problems, many years ago it was predicted that in the future they would solve practically all technical problems. This did not happen (although many of them are useful in narrow applications). Or maybe in a few years there will be a turn towards simplifying programming languages and at the same time pressure to transfer advanced algorithms to libraries? Maybe V, Zig or Carbon are the harbingers of these changes?
I've looked at language design alot over the past few years, and I try learn at least one new language per year. And from my very subjective observations, yes there are different waves. The first real language paradigm boom was in the 70s and 80s, where basically most of the paradigms we see today where invented. E.g. Generics is considered a new feature in most languages, but it was first introduced by ML in 73. There were a lot of great ideas, but also alot that has gone no where, or good on paper but not useful for real software. Like Smalltalk or Forth are really amazing languages that give you a real "awakening" moment in that you will never look at programming the same after learning them, but they aren't really that useful for building real software.

Then there was OOP (imperative OOP that is, while SmallTalk introduced OOP, it was fully expression based and more akin to a functional language, this never cought on). And in the 90s it was just so incredible useful, especially when Java came around with all it's tooling, that basically everyone just stopped experimenting and everyone went on the OOP train. Then after around 20 years of OOP being the dominant paradigm that people started noticing the "cracks" and problems with it, like the problem with everything being nullable, to many levels of indirection, to much implicit state, etc.
And this is how today everyone now looks again around and experiments with "new" or "forgotten" paradigms on how to do things better, and which of those features would work well together with the existing OOP paradigms.

This will not last forever, probably in 10 years or so the experimentation again will have settled and we have some new set of dominant paradigms. But I must say, I really like looking at where some languages are going. Personally I really like Swift, I don't even own any apple products anymore, but just from the language features it's a very nice mix of classical imperative features and some more functional features. I personally do not like the prototype based languages like JavaScript and Python, while they are quite powerful, their reliance on runtime polymorphism results in alot of bugs that a more compiletime defined language would have avoided.

It cannot track uninitialized variables across nested functions, so splitting them causes all kinds of warnings

I personally like nested functions because their visibility is encapsulated. But I think that relying on shared state (i.e. the variables of the parent scope) should be minimized as much as possible. I also try to write my functions as "pure" as possible (i.e. that they solely rely on inputs as parameters and only output in form of return value or out parameters).
Thats may not result in the most efficient code, but it often results in more readable and understandable code.

And each function gets its own implicit finally block, so it becomes very slow when there are too many. Although one could split it into one function having no managed variables and one function having all the managed variables
I personally don't care about performance until I run into problems. Some years ago I've worked on a C++ Project which required every bit of performance possible, with runtimes of days on 15kiloeuro machines, every bit of performance increase was valuable. We even did things like using the upper 16 bits of a 64 bit pointer to store additional data because this reduced copy operations.

After this I developed what I like to call "Performance PTSD", where I would look at a piece of code and see all the unessecary operations, like copies where references should be used, inefficient branches instead of arithmetic operations, etc.
It was so bad that I couldn't write good software anymore, in the sense that all my code was really complicated to be as efficient as possible (like I used raw pointer access to circumvent managed code, used move instead of assignments, using generics instead of virtual methods, etc.). I also just did not finish anything because I wasted so much time trying to write the most optimal code.

It took me quite some time, and another person literally measuring my code and showing me that I literally optimized nanoseconds for code that would be used right between some "Write" calls which would take multiple milliseconds, to realize that everything I did there did not matter in the sligthest, and that I just wrote bad code for no reason what so ever.

Since then I don't care about performance anymore, if I run into performance problems I use a profiler to find the bottleneck and fix this specifically. And whenever I find myself writing very complicated code because it is more efficient, I stop myself and just write the most simplest form and I will make it more performant if I need to.
Like I could use a geometrically growing GetMem where laziely I call "initialize" on each element on premise to collect data into an array. But if it's just a few values doing this:
Code: Pascal  [Select][+][-]
  1. for value in GenerateValues do
  2.   Result += [Value];
is much easier to understand and write.

That said a bit of a difference is when I write libraries, because there I want to not be artifically slowing things more down than necessary.

As a side note: While I don't consider performance to be necessarily a good guiding principle for writing code, I think that programming languages could still be designed to provide the tools to do so and to be easiely optimizable.
One of my favourite examples here is Haskell. It is a functional language that is typically interpreted, and Haskell code is usually orders of magnitude slower than (algorithmically) similar code in C or Pascal. But by enabling compilation and optimization (with or without LLVM) instead of interpretation, and also using more optimized types and non lazy evaluated values, you can easiely be as fast as the other languages. Sure it requires to write some more "performance aware" code, but it is still distinctly Haskell and does feel native to the ideas of that language. You can write fast code if you need it, but can have slower code but with more flexibility when you don't
« Last Edit: June 18, 2023, 12:11:23 am by Warfley »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12766
  • FPC developer.
Re: New language features?
« Reply #18 on: June 18, 2023, 11:13:48 am »
using the upper 16 bits of a 64 bit pointer to store additional data because this reduced copy operations.

(never did that, but I did use the alignment bits of 32-bit pointers trying to get to fit a workload in 2GB on a 32-bit system :))

I personally like nested functions because their visibility is encapsulated. But I think that relying on shared state (i.e. the variables of the parent scope) should be minimized as much as possible. I also try to write my functions as "pure" as possible (i.e. that they solely rely on inputs as parameters and only output in form of return value or out parameters).

I learned to shy away of such absolutes, as it tends to become a style-because-of-the-style issue, while in reality it is only to avoid rampant use, not modest.
« Last Edit: June 18, 2023, 11:16:06 am by marcov »

Warfley

  • Hero Member
  • *****
  • Posts: 2049
Re: New language features?
« Reply #19 on: June 18, 2023, 03:57:44 pm »
Here is a thing about new features, which I think gets a bit lost in the discussion (and I had to remind myself of that as well).

We've got alot of new and very complex new features over the past few years. Operator Overloading, Generics, Managed Records, Function References and Anonymous Functions. And they aren't fully explored yet.
For generics for example there is still a lot of features that are either still developed on or are planned for the future (e.g. nested generics or generic constants). And there are still huge amounts of bugs.

There is much more that is theoretically possible with generics, but doesn't work yet, either through existing bugs, or limitations in what the parser can do. I often joke that when you want to use generics, you better get familiar with "fpc internal error XXX" or "fpc raised an exception internally", and while for the simple cases generics work well enough, for anything more complex than just using types for storing and retrieving data, this is a very real problem. Just today I've discovered a new bug when I wanted to take the "Low(T)" of a generic type and pass it to another generic type, which throws an internal error.

It look similar with other features, e.g. implicit specialization for functions is great, but it still is very much restricted to simple types and something like:
Code: Pascal  [Select][+][-]
  1. generic function Map<TFrom, TTo>(AIterator: specialize IIterator<TFrom>;
  2.   AFunction: specialize TUnaryFunction<TTo, TFrom>): specialize IIterator<TTo>;
Where TUnaryFunction<T, U> is just "function(param: U): T;". Still does not work. And I'm not confident that it will work for the next release. Similarly there are still alot of open bug reports for some edge cases for Anonymous functions where probably some will not be fixed for the first release, and managed records still have alot of issues, even though they are in a stable release.

Aside from these just practical issues, thare is also the fact that these features haven't been explored thoroughy by the users. The reason why I run into one bug after another is not because the Pascal developers are Lazy, but because each of these new features opens up a meriad of new possibilities that first have to be explored and tested to be usefull.

So to me there is the very real question, if the speed at which new features are currently being added, may be simply to much to ensure a high quality of said features. C++ is able to provide a myriad of new features every few years because its huge community, both on the compiler developer side but also on the user side. When a new feature is developed, thousands of programmers will look at it and check out what the weirdest things are they can do with it.
FPC can't, every time a new feature comes around, it will be functional for the very basic usecases, but anything more "creative" will probably end up with a bunch of errors, bugs or just plain compiler limitations.

VisualLab

  • Hero Member
  • *****
  • Posts: 720
Re: New language features?
« Reply #20 on: June 18, 2023, 07:20:12 pm »
In the case of generic types, it would be very useful to be able to limit them to a specific group of simple types (ordinal, enumerated, numeric, floating point).

Warfley

  • Hero Member
  • *****
  • Posts: 2049
Re: New language features?
« Reply #21 on: June 18, 2023, 08:32:04 pm »
In the case of generic types, it would be very useful to be able to limit them to a specific group of simple types (ordinal, enumerated, numeric, floating point).

While this is interesting (even though C++ can show how complicated this can get), this is defenetly quite low on my list of generic features, there are much more basic stuff. Some from the top of my head:
Function types as generic parameters
Code: Pascal  [Select][+][-]
  1. type
  2.   generic TTest<TProc> = class
  3.   public
  4.     Procedure Run(AProc: TProc);
  5.   end;
  6.  
  7. procedure TTest.Run(AProc: TProc);
  8. begin
  9.   AProc(); // Error: Illegal Expression
  10. end;
Generic Sets:
Code: Pascal  [Select][+][-]
  1. type
  2.   generic TTest<T> = record
  3.     arr: Array of T; // Works
  4.     s: Set of T; // Doesn't
  5.   end;
Ranges as generic types:
Code: Pascal  [Select][+][-]
  1. specialize TTest<1..5> // Doesn't work
  2. // requires special type definition
  3. type
  4.   TTestRange = 1..5;
  5. var
  6.   t: specialize TTest<TTestRange>
  7.  
Nested Generics:
Code: Pascal  [Select][+][-]
  1. type
  2.   generic TList<T> = class
  3.     ...
  4.     generic function Map<U>(MapFunc: TUnaryFunction<T, U>); // Nested generics are not yet supported
  5.   end;
Generic Typehelpers:
Code: Pascal  [Select][+][-]
  1. type
  2.   generic TArrayHelper<T> = type helper for array of T
  3.   function Length: SizeInt;
  4.   end;

But all of these are new functionalities of varying levels of complexity. The most important thing I would say is to get the existing Generic functionality working. As I said above, when you do anything more complex than using a single generic parameter to pass as argument or return type, you will get a bunch of "internal errors" or "fpc raised an exception internally" errors.
And it just happens way to often that I end up in a situation where I either have to trail and error my way ot of these FPC crashes, e.g. in my STAX code I've found out that fpc crashes when trying to use the forward definition of a generic type, which I finally circumvented by adding a type helper afterwards (which may sound really straight forward, but if all the information you have is "fpc raised an exception internally", it takes hours to find this stuff out).
In other situations where you don't find such a workaround, you just redesign your application to not rely on the specific part that makes FPC sad. E.g. just today I've written a generic range datatype, where I wanted to have this function:
Code: Pascal  [Select][+][-]
  1. generic Range<T, TRange>: specialize TRange<T>;
  2. begin
  3.   Result := specialize Range<T>(T(Low(TRange)), T(High(TRange)));
  4. end;
Would be neat (especially when instead of TMySubRange it could just be start..end), but it's not that essential that I can't live without it.

But sometimes the error is so deep within your project design, that the only thing you can do is give up and start from 0. And this happend to me over the past 5 years or so at least 5 times, with projects I partly already put in a week of effort, just to find out that as soon as they reach a certain complexity, FPC strikes and I just can't do anything anymore.

Personally I don't write commercial software, I'm just trying to do new cool stuff with FPC, with no need of any of it going anywhere or being actually useful. So I can live with these limitations. It's frustrating, but it's also somewhat fun to figure out what the boundaries of the compiler are. That said, I don't see how any of this could be acceptable in any professional environment.

SymbolicFrank

  • Hero Member
  • *****
  • Posts: 1315
Re: New language features?
« Reply #22 on: June 19, 2023, 03:22:52 pm »
There are still features in OOP that don't work very well. Like, inheriting from a class requires lots of casting and overriding all methods that return an instance of the class. There is no way to to specify something like: treat Self as TypeOf calling instance.

And I'm on the fence about anonymous functions, as they tend to make code less readable in general. Most useful if you've written many micro-services and think that is the best way to do it. Otherwise, I don't see the point.

I agree that generics is the most important one to improve, as I encounter the limits often.

Warfley

  • Hero Member
  • *****
  • Posts: 2049
Re: New language features?
« Reply #23 on: June 19, 2023, 03:37:59 pm »
And I'm on the fence about anonymous functions, as they tend to make code less readable in general. Most useful if you've written many micro-services and think that is the best way to do it. Otherwise, I don't see the point.
I think that anonymous functions as they are currently right now are absolutely code ruining. I think anonymous functions should be in the sense of (mathematical) lambdas be exactly one expression, that you can write something like:
Code: Pascal  [Select][+][-]
  1. MyList.Sort(lambda a, b: a < b);
  2. // or more brief haskell style syntax:
  3. MyList.Sort(\a, b -> a < b);
Because especially with the fact that you can't pass operators as function pointers, having to declare a special compare function that does nothing but calling the operator, is just waste of space (personally I would preferr to simply being able to reference the operator als .Sort(@operator <), but thats a different story all together).
If you need anything more complex than a single statement, just write a (nested) function.

I can't comprehend how someone at Embarcadero looked at this thing and thought "Thats a good idea, we should defenetly build this":
Code: Pascal  [Select][+][-]
  1. MyList.Sort(function(const A, B: TElementType): Boolean begin Result := A < B; end);
« Last Edit: June 19, 2023, 03:41:35 pm by Warfley »

PascalDragon

  • Hero Member
  • *****
  • Posts: 6381
  • Compiler Developer
Re: New language features?
« Reply #24 on: June 19, 2023, 10:07:45 pm »
There are still features in OOP that don't work very well. Like, inheriting from a class requires lots of casting and overriding all methods that return an instance of the class. There is no way to to specify something like: treat Self as TypeOf calling instance.

In an ideal OOP world you wouldn't need that, cause you'd only work against interfaces (in the general sense of a “abstraction”, not necessarily a Object Pascal interface) anyway.

And I'm on the fence about anonymous functions, as they tend to make code less readable in general. Most useful if you've written many micro-services and think that is the best way to do it. Otherwise, I don't see the point.
I think that anonymous functions as they are currently right now are absolutely code ruining. I think anonymous functions should be in the sense of (mathematical) lambdas be exactly one expression, that you can write something like:
Code: Pascal  [Select][+][-]
  1. MyList.Sort(lambda a, b: a < b);
  2. // or more brief haskell style syntax:
  3. MyList.Sort(\a, b -> a < b);

I still plan to play around with a shorthand lambda syntax that would look like this (and that would indeed only support a single expression):

Code: Pascal  [Select][+][-]
  1. MyList.Sort(uses a, b as a < b)

A simplified syntax like that would also open the door to having the compiler convert that to some serialized representation in the binary so that one could do something like LINQ from .NET.

Warfley

  • Hero Member
  • *****
  • Posts: 2049
Re: New language features?
« Reply #25 on: June 19, 2023, 10:57:03 pm »
I still plan to play around with a shorthand lambda syntax that would look like this (and that would indeed only support a single expression):

Code: Pascal  [Select][+][-]
  1. MyList.Sort(uses a, b as a < b)

A simplified syntax like that would also open the door to having the compiler convert that to some serialized representation in the binary so that one could do something like LINQ from .NET.
Great to hear, but just from how this reads, this uses-as doesn't ring right to me. Maybe it's just that as a German my view an English linguistics may be a bit off, but if I didn't know anything I would consider uses ... as ... to be something like what the current with statement does maybe with a cast or introducing an alias.
I think uses ... in ...  Or uses ... For ... Would make more sense.

I assume you want to use "uses" because it's an already established keyword, to not break anything by introducing a new reserved keyword. Otherwise I think there are more fitting phrases like "apply ... in ..." Or just saying plain old "lambda" as python does.
Another alternative existing keyword to uses which may fit better may also be "for" like "for a, b do a < b" or "for a,b out a<b"
« Last Edit: June 20, 2023, 01:03:08 am by Warfley »

VisualLab

  • Hero Member
  • *****
  • Posts: 720
Re: New language features?
« Reply #26 on: June 20, 2023, 12:17:17 am »
I can't comprehend how someone at Embarcadero looked at this thing and thought "Thats a good idea, we should defenetly build this":
Code: Pascal  [Select][+][-]
  1. MyList.Sort(function(const A, B: TElementType): Boolean begin Result := A < B; end);

Because it is very "pascalish" in notation. But at the same time long-winded.

Regarding anonymous functions, I fully agree with:

And I'm on the fence about anonymous functions, as they tend to make code less readable in general. Most useful if you've written many micro-services and think that is the best way to do it. Otherwise, I don't see the point.

I agree that generics is the most important one to improve, as I encounter the limits often.

The presence of anonymous functions is justified in languages such as JavaScript. In Pascal and Object Pascal there are pointers to functions/procedures. So anonymous functions are not some "amazing solution". It's more of a kind of syntactic sugar.

Anyway, since they came to Delphi, I haven't had the need to use them once. They obfuscate the source code. On the other hand, I've been using generics quite heavily since they came out in Delphi (and in FPC). Hence my expectation that FPC (and Delphi) will someday allow the use of constraints in the form of simple types in declarations of own generic types (in particular: enumeration types, ordinal types, numbers (in general) and floating point numbers).

Warfley

  • Hero Member
  • *****
  • Posts: 2049
Re: New language features?
« Reply #27 on: June 20, 2023, 01:12:46 am »
There is something new anonymous functions bring to the table, and this is function references and their ability to capture the environment

Code: Pascal  [Select][+][-]
  1. function Adder(A: Integer): Reference to Function(B: Integer): Integer;
  2. Begin
  3.   Result := function(B: Integer)
  4.   Begin
  5.     Result := A+ B;
  6.   End;
  7. End;
  8.  
  9. Add5 := Adder(5);
  10. WriteLn(Add5(10)); // returns 15
As you can see the anonymous function captures the parameter to be used within that function even after the stack frame where this parameter has been located is non existent anymore.
That said, there is no reason why you need anonymous functions for this, this could (and now with the new function reference update is) also implemented for nested functions, so you can simply return a pointer to a nested function that uses variables or parameters.
So while anonymous functions are just syntactic sugar, the function references that enable them are a huge new feature.

I personally also refuse to use this hideous construct for anonymous functions and will rather use references to nested functions as they can do the same and aren't aweful.

PascalDragon

  • Hero Member
  • *****
  • Posts: 6381
  • Compiler Developer
Re: New language features?
« Reply #28 on: June 20, 2023, 09:46:48 pm »
I still plan to play around with a shorthand lambda syntax that would look like this (and that would indeed only support a single expression):

Code: Pascal  [Select][+][-]
  1. MyList.Sort(uses a, b as a < b)

A simplified syntax like that would also open the door to having the compiler convert that to some serialized representation in the binary so that one could do something like LINQ from .NET.
Great to hear, but just from how this reads, this uses-as doesn't ring right to me. Maybe it's just that as a German my view an English linguistics may be a bit off, but if I didn't know anything I would consider uses ... as ... to be something like what the current with statement does maybe with a cast or introducing an alias.
I think uses ... in ...  Or uses ... For ... Would make more sense.

I don't see a real difference between usesas, usesin or usesfor in so far as my lingustic view is concerned no matter if English or German...
However changing the second token would be the least of my problems in implementing this 😅 (as long as it's an existing one; see below).

I assume you want to use "uses" because it's an already established keyword, to not break anything by introducing a new reserved keyword. Otherwise I think there are more fitting phrases like "apply ... in ..." Or just saying plain old "lambda" as python does.
Another alternative existing keyword to uses which may fit better may also be "for" like "for a, b do a < b" or "for a,b out a<b"

Correct. I'd like to avoid adding new keywords if possible. Also a introductory keyword is necessary, because I want to avoid ambiguities with functionality (like inline generic specializations have in mode Delphi when used as expressions).

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12291
  • Debugger - SynEdit - and more
    • wiki
Re: New language features?
« Reply #29 on: June 20, 2023, 11:17:34 pm »
Maybe
Code: Pascal  [Select][+][-]
  1. yList.Sort(with a, b do a < b)
  2.  

 

TinyPortal © 2005-2018