Introducing a "using" keyword for automatic resource management lazarus / 64-FPC development ---------------------------------------------------------------------- circular · 2024-03-07 08:53 Hello Lazarus and Free Pascal Community, I've been exploring ways to enhance our programming experience with Pascal, focusing on memory management and code simplification. Today, I'd like to propose an idea that, to the best of my knowledge, hasn't been discussed in our forums before: introducing a using keyword for automatic resource management in Pascal. Motivation One of the common challenges we face in Pascal is managing the lifecycle of objects, especially ensuring that objects are properly freed even when exceptions occur. While our current approach with try...finally blocks is effective, it can lead to verbose and nested code, especially with multiple objects. Proposal My proposal is to introduce a using keyword that would automatically manage the lifetime of objects within a block of code. This syntax would be similar to C# syntax. Here's a basic illustration of the syntax and functionality: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i using myInstance1 := TMyObject.Create(nil),       myInstance2 := TMyObject.Create(myInstance1) dobegin  myInstance1.DoSomething;  myInstance2.DoSomething;  ...end; This would be functionally equivalent to: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i myInstance1 := TMyObject.Create(nil);try  myInstance2 := TMyObject.Create(myInstance1);  try    myInstance1.DoSomething;    myInstance2.DoSomething;    ...  finally    myInstance2.Free;  end;finally  myInstance1.Free;end;  Benefits Simplicity: reduces boilerplate code, making it easier to read and maintain.Safety: ensures that objects are automatically freed, even if an exception occurs, reducing the risk of memory leaks. Compatibility Consideration To address compatibility concerns, the using keyword could be designed to be context-sensitive. This means it would only be interpreted as the new keyword when followed by an identifier, ensuring minimal impact on existing codebases. Based on your feedback, I'm open to refining this proposal, drafting a more detailed document for further discussion, and exploring the possibility of creating a proof-of-concept. Thank you for considering this idea. I believe it could make a significant positive impact on our development experience and I look forward to your constructive feedback. ---------------------------------------------------------------------- Martin_fr · 2024-03-07 09:56 But what if your deallocation isn't Free/Destroy? What if you have "ReleaseReference" (which then may or may not destroy) or "Close"/"Disconnect", or "Commit"? Or if you need a simple "Clear" at the end? Also, leaving out the "free"/"Destroy" is to unspecific. The reader has no clue what happens at the end. Just to remember: "Free" can be reintroduced.... So there even is a difference between Free and Destroy. ---------------------------------------------------------------------- alpine · 2024-03-07 10:46 Quote from: circular on March 07, 2024, 09:53:23 amI've been exploring ways to enhance our programming experience with Pascal, focusing on memory management and code simplification. Today, I'd like to propose an idea that, to the best of my knowledge, hasn't been discussed in our forums before: introducing a using keyword for automatic resource management in Pascal. I believe FPC has sufficient instrumentation for you to implement such a management, e.g. TInterfacedObject, advanced records. Thaddy's 'Smart pointers revisited': https://forum.lazarus.freepascal. org/index.php/topic,46306.msg469864.html#msg469864 (your participation) ---------------------------------------------------------------------- gidesa · 2024-03-07 10:51 Hello Martin, in last Free Pascal reference guide v. 3.2.2 there is no information on "ReleaseReference", "Close"/"Disconnect", or "Commit" for a generic TObject instance. Where is the info about that? ---------------------------------------------------------------------- circular · 2024-03-07 11:27 Quote from: Martin_fr on March 07, 2024, 10:56:19 amBut what if your deallocation isn't Free/Destroy? What if you have "ReleaseReference" (which then may or may not destroy) or "Close"/"Disconnect", or "Commit"? Or if you need a simple "Clear" at the end?Thank you, Martin_fr, for your insightful feedback. My proposal is about memory management of the objects and not their specific features. The using syntax would still be fit though if the destructor would actually Close/Disconnect. This would actually make sense to do so in many cases. QuoteAlso, leaving out the "free"/"Destroy" is to unspecific. The reader has no clue what happens at the end. Just to remember: "Free" can be reintroduced.... So there even is a difference between Free and Destroy.This is a good point regarding the implementation. We can refine the code and replace the call to Free by a conditional call to Destroy if that's more reliable. Quote from: alpine on March 07, 2024, 11:46:51 amI believe FPC has sufficient instrumentation for you to implement such a management, e.g. TInterfacedObject, advanced records.Thank you, Alpine, for your input. You're right that FPC offers mechanisms like TInterfacedObject and advanced records for managing object lifecycles, which are indeed powerful tools. However, my proposal aims to reduce the need for defining special objects or dealing with the boilerplate code often associated with these mechanisms. Furthermore, while these existing solutions work well for objects with parameterless constructors, they become less convenient when dealing with constructors that require parameters. ---------------------------------------------------------------------- Martin_fr · 2024-03-07 11:32 Quote from: gidesa on March 07, 2024, 11:51:38 amHello Martin, in last Free Pascal reference guide v. 3.2.2 there is no information on "ReleaseReference", "Close"/"Disconnect", or "Commit" for a generic TObject instance. Where is the info about that? Those are things you could have on your own objects. E.g. -  if you have a database, then you may need try/finally to make sure a transaction is written (or aborted). But you may not actually want to destroy the db object. - Close/Disconnect could be on networking objects. A server may re-use an object to accept the next connection, so it wont destroy it, but must make sure it disconnected the previous connection. - Similar for the other methods.   (ReleaseReference is on TRefCountedObject in LazUtils / manually ref counted) Destroying an object is only a small fraction of the use cases for try/finally. ---------------------------------------------------------------------- Martin_fr · 2024-03-07 11:51 Quote from: circular on March 07, 2024, 12:27:47 pmThank you, Martin_fr, for your insightful feedback. My proposal is about memory management of the objects and not their specific features. But that is a really minor usecase... The more important memory management becomes, the more an app should avoid throw-away instances. But without throw away instances it becomes all about resource management.  And those are done via the features like close/disconnect/... That is why there are things like pools for resource using objects. But in order to return them, the using code must finish up its work. That said, yes a lot of code uses throw away instances when it really shouldn't. But should that be encouraged? Looking at your proposal, I think it goes way to far from what we have. The top part (calling constructors) really does not need anything that try/finally does not have. And try/finally does not make it more complex... In the case that a constructor can fail Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i a := nil; b:= nil; //....try  a:= foo.create;  b:= foo.create;//....Yes, it is some extra nil assignments. But for something that is a rare special case, those are not enough to justify a new feature.... For the finally block, there is a difference. You need a finally block, that does execute each statement (or top level compound statement) in it. Currently finally only runs to the first failure. And wrapping each line in finally into its own try/except or try/finally.... Well, there is a case that might have enough value to discuss. So then, if anything at all Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i try//...finally do each  b.free;  a.free;    begin foo; bar; end; // one statement, not guarding the sub statementsend;And it would run each of the statement, even if any one in there raises an exception Of course it creates the same problem like your proposal. If there is an exception in each of the destructors, then at the end of that block you have several exceptions... Which one to raise for the code that follows? ---------------------------------------------------------------------- rvk · 2024-03-07 11:55 Quote from: circular on March 07, 2024, 09:53:23 amCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   myInstance2 := TMyObject.Create(myInstance1);  try    //...  finally  myInstance2.Free;  end; And isn't this considered "bad practice"? Freeing an object that belongs to another object. In this case, myInstance1 would be the one who needs to free myInstance2, not directly though code. ---------------------------------------------------------------------- alpine · 2024-03-07 11:56 Quote from: circular on March 07, 2024, 12:27:47 pmQuote from: alpine on March 07, 2024, 11:46:51 amI believe FPC has sufficient instrumentation for you to implement such a management, e.g. TInterfacedObject, advanced records.Thank you, Alpine, for your input. You're right that FPC offers mechanisms like TInterfacedObject and advanced records for managing object lifecycles, which are indeed powerful tools. However, my proposal aims to reduce the need for defining special objects or dealing with the boilerplate code often associated with these mechanisms. Furthermore, while these existing solutions work well for objects with parameterless constructors, they become less convenient when dealing with constructors that require parameters. Not so inconvenient for constructing, consider (modeled after the Thaddy's one): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  generic TAuto = record  strict private type    TWatcher = class(TInterfacedObject)      private        FWatched: TObject;      public        constructor Create(const AWatched: TObject);        destructor Destroy; override;      end;  strict private    FValue: T;    FLifetime: IInterface;  public    constructor Create(const AValue: T);     class operator :=(const AValue: T): TAuto;    property Value: T read FValue;  end; implementation constructor TAuto.Create(const AValue: T);begin  FValue := AValue;  FLifetime := TWatcher.Create(FValue);end; class operator TAuto. :=(const AValue: T): TAuto;begin  Result := TAuto.Create(AValue);end; and then: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  var   A: specialize TAuto;begin  A := TMyClass.Create({parameters ...});  A.Value.Foo(); {accessing the instance} ---------------------------------------------------------------------- circular · 2024-03-07 14:25 Quote from: rvk on March 07, 2024, 12:55:14 pmAnd isn't this considered "bad practice"? Freeing an object that belongs to another object. In this case, myInstance1 would be the one who needs to free myInstance2, not directly though code. It goes without saying that in the example, myInstance2 uses myInstance1 rather than being a child of it. Quote from: Martin_fr on March 07, 2024, 12:51:57 pmBut that is a really minor usecase... In my experience, I find that it does happen a lot. QuoteThat said, yes a lot of code uses throw away instances when it really shouldn't. But should that be encouraged?Are you suggesting to create a pool of objects for each and every class? Looking at your proposal, I think it goes way to far from what we have. QuoteCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i a := nil; b:= nil; //....try  a:= foo.create;  b:= foo.create;//....Yes, it is some extra nil assignments. But for something that is a rare special case, those are not enough to justify a new feature....Well I do that sometimes, but I dont find it well structured. Now there are more cases where only one object is created, but even in that case, I would prefer the using statement. QuoteIf there is an exception in each of the destructors, then at the end of that block you have several exceptions... Which one to raise for the code that follows?Handling this manually is cumbersome. The nested try...finally are the best solution in my view. But I don't like to write them. ---------------------------------------------------------------------- alpine · 2024-03-07 15:30 Quote from: circular on March 07, 2024, 03:25:29 pmQuote from: Martin_fr on March 07, 2024, 12:51:57 pmBut that is a really minor usecase... In my experience, I find that it does happen a lot. QuoteThat said, yes a lot of code uses throw away instances when it really shouldn't. But should that be encouraged?Are you suggesting to create a pool of objects for each and every class? Looking at your proposal, I think it goes way to far from what we have. IMHO what Martin_fr wants to say is that the context here is very different from C#. What we have is a complete control over the memory management - just the opposite of C# (GC) where you don't have to destroy anything, but it happens what? : using statement - ensure the correct use of disposable objects Quote from: circular on March 07, 2024, 03:25:29 pmQuoteIf there is an exception in each of the destructors, then at the end of that block you have several exceptions... Which one to raise for the code that follows?Handling this manually is cumbersome. The nested try...finally are the best solution in my view. But I don't like to write them. Speaking about personal preferences, I do not like to program in C#, so I'm using the FPC instead. ---------------------------------------------------------------------- circular · 2024-03-07 15:57 Quote from: alpine on March 07, 2024, 04:30:07 pmWhat we have is a complete control over the memory management - just the opposite of C# (GC) where you don't have to destroy anything, but it happens what? : using statement - ensure the correct use of disposable objectsYes, it is different because the GC can take some time to actually free the objects. In Pascal, the Free method plays the same role as Dispose and also frees the memory. QuoteSpeaking about personal preferences, I do not like to program in C#, so I'm using the FPC instead.Preferences are subjective. Though I am not speaking of preferences, but of the fact that writing the nested try...finally is not satisfactory whatever the language. Ok, it does what we want, but it gets out of hand with only a few objects. Speaking about personal preferences, I do not like to program in C# either. But would you argue against try...finally blocks because they are in C#? ---------------------------------------------------------------------- Thaddy · 2024-03-07 17:05 The model you are referring to is called ARC memory management - automatic reference counting for everything - as opposed to GC memory management. Delphi has officially introduced it and subsequently abandoned it. (They could not get it to work with their new LLVM back-end if I remember correctly) But it still can be achieved with rigorous and strict keeping to only instantiation through and to com interfaces and variables. ---------------------------------------------------------------------- circular · 2024-03-07 17:13 Hi Thaddy, I am not referring to reference counting. It is rather a syntax that simplifies a use case where we allocate and free an object. In fact, my idea here is that we don't need much reference counting nor garbage collection: we can have a lighter syntax that will include the freeing mechanism implicitly. ---------------------------------------------------------------------- alpine · 2024-03-07 17:23 Quote from: circular on March 07, 2024, 04:57:44 pmQuoteSpeaking about personal preferences, I do not like to program in C#, so I'm using the FPC instead.Preferences are subjective. Though I am not speaking of preferences, but of the fact that writing the nested try...finally is not satisfactory whatever the language. Ok, it does what we want, but it gets out of hand with only a few objects. Agreed. That's why I'm using the TAuto<> extensively. Honestly, I'd like to have a static versions of the classes, like in C++, but I realize all the implications of that - so I should (better) live with the simpler ObjectPascal model. Quote from: circular on March 07, 2024, 04:57:44 pmSpeaking about personal preferences, I do not like to program in C# either. But would you argue against try...finally blocks because they are in C#? I would not.  Try...finally is just great. What I don't like is that the FPC (actually Delphi) becomes bit by bit like scripting crap which I despise. ---------------------------------------------------------------------- circular · 2024-03-07 17:31 Quote from: alpine on March 07, 2024, 06:23:56 pmThat's why I'm using the TAuto<> extensively. Honestly, I'd like to have a static versions of the classes, like in C++, but I realize all the implications of that - so I should (better) live with the simpler ObjectPascal model.TAuto<> is indeed a great tool to simplify the code. Unfortunately, I am not confortable using it in a library, where smallest memory usage and best performance are needed by the users. I reckon that making it static can be a serious challenge and that could take up quite some of our limited time. I believe that the using construct strikes a balance between being convenience and complexity to implement. QuoteWhat I don't like is that the FPC (actually Delphi) becomes bit by bit like scripting crap which I despise.Can you elaborate on the "scripting" aspect of things? ---------------------------------------------------------------------- marcov · 2024-03-07 19:38 I would have needed something like this most in a previous job, for a backoffice application server (mostly used via web(-hub)). Leaks could accumulate, so at least a good first order memory safety was key. But 9 times out of ten, releasing memory would not be enough and additionally a database roll-back or commit would be needed to abort or persist the transaction. I agree with Martin that it is is a bit limited in practice. ---------------------------------------------------------------------- Thaddy · 2024-03-07 19:45 Quote from: circular on March 07, 2024, 06:31:25 pmQuote from: alpine on March 07, 2024, 06:23:56 pmThat's why I'm using the TAuto<> extensively. Honestly, I'd like to have a static versions of the classes, like in C++, but I realize all the implications of that - so I should (better) live with the simpler ObjectPascal model.TAuto<> is indeed a great tool to simplify the code. Unfortunately, I am not confortable using it in a library, where smallest memory usage and best performance are needed by the users. I reckon that making it static can be a serious challenge and that could take up quite some of our limited time. I believe that the using construct strikes a balance between being convenience and complexity to implement. That is a bit of a contradiction in terms, because an implementation would not save much space to what I proposed, even for the compiler internals. Such a feature adds code. There are no shortcuts. But now we have function references, I think I can improve on TAuto<>. If you are careful (which you are ) try my ClassOnStack for performance critical things with auto release. (And yes, that is in production in a -very - large shop. only use for small objects liker tickers) It is a bit like choose the tools you want, create the accidents you want. ---------------------------------------------------------------------- Martin_fr · 2024-03-07 20:24 Quote from: circular on March 07, 2024, 03:25:29 pmQuote from: Martin_fr on March 07, 2024, 12:51:57 pmBut that is a really minor usecase... In my experience, I find that it does happen a lot. And that is cases other than TList.Create? Because the only way for that to raise an exception (or rather crash) is either out of mem, or mem corruption. Both cases, for which I would think your exception handler has no graceful solution. To be more specific, in your case that happens in such a way, that those constructors raise a meaningful exception, which (after finally took care of mem leaks) is caught by the apps code, handled, and leaves the app in a well defined state? If so, then I would be glad if you could widen my horizon with some examples. However, I still think that if such an issue exists, and if inspiration is taken from some other languages "using" like feature, we stick to looking at the functionality provided, and keep the syntax based on what Pascal has. So extending try/finally rather than creating an alternative. With that I also stick to keeping a finally block, and most definitely stating the method of releasing resources (e.g. obj.free or freeandnil(obj) or obj.Foo). This does not only leave the reader with essential info. It also keeps the entire construct open to many more use cases, because the release can be anything the author specifies. I am not sure how to implement a try-header to accommodate the "mark objects as needing finally". Btw, the using header is very limited anyway. It wouldn't allow for "if obj = nil then obj := tfoo.create". Meaning a pre existing object could be used (and then resource bound to this would be released at the end). Quote from: circular on March 07, 2024, 04:57:44 pm but of the fact that writing the nested try...finally is not satisfactory whatever the language That I agree on, but "using" is not a good alternative. It only supports a very narrow selection of what can be done in a nested try/finally (and the fact that this may much your personal need, is not an argument for me ...) If a replacement/extension would be done, it should cover more cases. (after all, adding something to avoid nested try/finally shouldn't exclude lots of cases, and leaves us with the nested try finally for those....) That is, the sentence "nested try/finally is not satisfactory"  maybe nails it. If it is the "nested syntax". IMHO, functionality wise nested try/finally are satisfactory, very much so. And in that case the full functionality of this should be considered, and preserved, by simply finding the minimum syntax amendments. Well, ok, there is one point... the nested try/finally does not auto- initialize variables that will be used with to nil. On in any other way tells you to which point the "setup" had gotten before an exception occurred. Then again "using" requires all setup on top. Which is another limitation... (extra objects may well be needed in the middle of processing) Maybe something like in transactions, where you can have savepoints. (slightly different in behaviour...). ---------------------------------------------------------------------- circular · 2024-03-08 11:34 I want to thank everyone who has taken the time to engage so far with the idea of introducing a using `keyword`. Your answers illustrate the diversity of our programming practices. To clarify, the primary intent behind this proposal is to simplify memory management, especially for disposable objects, without altering the fundamental approach of Pascal to memory management. The concerns raised by Martin_fr and marcov about objects that require custom deallocation methods (like ReleaseReference, Close, Disconnect, etc.) are valid. The proposal could be refined to include a mechanism for specifying custom disposal actions. I appreciate alpine's reminder of FPC's existing mechanisms like TInterfacedObject and advanced records, as well as the smart pointers approach. There are situations where these mechanisms are fit. Quote from: Thaddy on March 07, 2024, 08:45:10 pmIf you are careful (which you are ) try my ClassOnStack for performance critical things with auto release. (And yes, that is in production in a -very - large shop. only use for small objects liker tickers) It is a bit like choose the tools you want, create the accidents you want. Well I don't think we want accidents, but I like the idea of using the stack for performance. I've looked at your proposal, which seems great for some cases where the only thing we need is to free the memory block. It seems though it is for Windows only. Quote from: Martin_fr on March 07, 2024, 09:24:43 pmAnd that is cases other than TList.Create? Because the only way for that to raise an exception (or rather crash) is either out of mem, or mem corruption. Both cases, for which I would think your exception handler has no graceful solution.Basically every time I instantiate any object that needs to be released in the same scope, I would be prefer to cater for exceptions. QuoteHowever, I still think that if such an issue exists, and if inspiration is taken from some other languages "using" like feature, we stick to looking at the functionality provided, and keep the syntax based on what Pascal has. So extending try/finally rather than creating an alternative.I am open to discuss the syntax. Quotethe fact that this may much your personal need, is not an argument for me ...)This was rather a response to the fact you were putting forward your personal experience. But we'll agree to see beyond that. A way forward would be for us to compile real-world scenarios where a using construct could simplify code without sacrificing performance or clarity. This will help us better understand its potential impact and refine its design. ---------------------------------------------------------------------- Martin_fr · 2024-03-08 13:08 Well maybe there is a decission at first. There are 2 fundamental approaches (logic wise, ignore implicit vs explicit presence of the underlying code). 1) There is a list of "init" statements. They store there result in some variables, and those variables are initialized to nil, if the "init" block aborts first. In that scenario all "release" statement are always run. They recognize the nil value, and deal with that (in case of object destruction, this requires "Free" not "destroy"). This is what happens in a constructor, that will call destroy if an error happens within. 2) Each statement in the "init" list, has a statement in the "release" list (the lists are in opposite order. Statements in "release" list are only executed, if the corresponding "init" statement was executed (without error, otherwise it could not have set a result). This the "strict nested" try/finally (just without all the try/finally) Both serve a different subset, and both introduce some limits. About the lists. The using example has coma separated statement.... Not nice, imho. I would really suggest that both lists must be made of statements (any statement, not just assignments). The separator must be the normal semicolon. Compound statements are treated as just one list entry. For version 1 the requirements are - list of variables to be initialized NIL for the block - TRY - normal code, including any "init" statements. (they need no special treatment, since the variables are initialzed).   This means, they can also be in the middle of the try/finally. Which greatly improves the flexibility. Issue: I don't think a "auto initialize variables" list is ever going to happen For version 2 ... - keyword to start "init" list   normal code of init statement (magically tracked for progress, so the correct "release" entries can be executed   That is, each "init" statement gets a invisible "try" that will be matched by a finally in the "release" list. - TRY normal code - FINALLY   list of "release statements" Both examples should contain ONE full try/finally block. And then have the necessary additions to define the extras. Not necessarily  a final syntax, but a concept... Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i try (var1, var2, ...)  initfoo; ...  initbar;...finally...   releasefoo;  releasebar; // will be executed, even if releasefoo raisedend versus Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i do // must have the same amount of statements as finally has  initfoo;  initbar;try finally  releasebar;  releasefoo;end ---------------------------------------------------------------------- Martin_fr · 2024-03-08 13:13 IMHO the 1st is more flexible... For the 2nd, labels in both lists could help matching statements (and even re-order execution of release) ---------------------------------------------------------------------- circular · 2024-03-11 17:09 Hello, Thanks Martin_fr for the suggestions. Based on the feedback, I propose a revision to the using statement that explicitly links initialization and disposal actions. This approach addresses concerns about clarity and flexibility, ensuring that the management of each resource is directly associated with its corresponding disposal method. Here's how it could look. Note that here the explicit Free is redundant, that would be the default action if not specified. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i using myInstance1 := TMyObject.Create(nil) then myInstance1.Free,      myInstance2 := TMyObject.Create(myInstance1) then myInstance2.Free dobegin  myInstance1.DoSomething;  myInstance2.DoSomething;  ...end;  To accommodate more complex scenarios, such as managing objects retrieved from a pool, the revised using statement could also explicitly handle the acquisition and release of such resources: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i using myInstance1 := pool1.GetInstance then pool1.Release(myInstance1),    myInstance2 := pool2.GetInstance then pool2.Release(myInstance2) dobegin  myInstance1.DoSomething;  myInstance2.DoSomething;  ...end;  By explicitly pairing each initialization with its corresponding disposal action, the proposed syntax makes the management intentions clear, enhancing code readability and maintainability. Also, it would implicitly nested try/finally blocks, so that a finalization is called for each successfully assigned objects. You mentionnend that you did not like the coma separator between objets. Can you expand on that? Regarding the semi-colon as separator, I am not sure, it doesn't seem consistent for me, as this is one big construct. And it is not needed to have a semi-colon at the end of every instruction. Consider if/else for example. Still, if we would use this semi-colon, that would look like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i using myInstance1 := pool1.GetInstance then pool1.Release(myInstance1);    myInstance2 := pool2.GetInstance then pool2.Release(myInstance2); dobegin  myInstance1.DoSomething;  myInstance2.DoSomething;  ...end;  Note that we coud as well make the using have only one parameter: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i using myInstance1 := pool1.GetInstance then pool1.Release(myInstance1) dousing myInstance2 := pool2.GetInstance then pool2.Release(myInstance2) dobegin  myInstance1.DoSomething;  myInstance2.DoSomething;  ...end; ---------------------------------------------------------------------- alpine · 2024-03-11 17:39 And what will happen if some constructor failed with an exception (implicit Destroy)? ---------------------------------------------------------------------- Martin_fr · 2024-03-11 19:20 One other generic concern I have forgotten to mention. The word "using" itself may be a bad choice. It bares similarities with "with", and it implies there is something "to use" rather than "to do". - "to use" would be a list of objects/values (which is also what coma- separation implies to me). - "to do" is a list of statements. Effectively it is followed by a list of statements. Never mind that in your case the produce values. It could be an existing object "connection.accept". So imho different word. And semicolons as separator. It ought to be a code block. Pulling the "free" (or release) up, seems to me uncommon for pascal? And also not very "read able", as the code is then deliberately in an order that differs from execution. And, if this is purely about "not nested try/finally" then there should be no reason for that either. While it probably isn't the case, the whole "trying to change how/when to free" reminds me of the re-occurring discussions on "auto free".  But that is me reading the wrong stuff into it. Since we don't need to worry about changing how/where we write the Free/release statement. Removing nested try/finally is simple about making sure they all get executed (without having each an explicit try/finally). Sorry for the discourse. (Imho, when it is about free/destroy, the only way *not* to have the free at the end (where it should be executed) is interfaces and smart- pointers - That is implicitly auto-ref-counted types) It probably is just the influence of having been exposed to the "using" syntax from other languages to often. So it takes some extra attention to split the functionality from that syntax. IMHO there should be - a setup block at the top. - And a tear down at the bottom. Calling them and thinking of them as: setup / teardown  might also help to avoid imposing limits (like making it only about object lifetime). As for the "keywords", I don't really have many ideas there.  If the setup block is before the "try" keyword, then words like "Exec" or "do" could start it. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Exec // or Do//setuptryfinallyend; The other way would be to start with "try" (though then that makes it hard to distinguish from the existing "try") Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i try// setupdofinally end;  Again, there may be way better keywords. If the entire construct is identifiable by the "setup" block, then the "finally" keyword can be kept, but now makes sure that every statement within the block will be executed, even if there are errors. But if a new keyword should make it more distinct, ok. I am not sure, if there needs to (or even should be) a one to one relation between statements in setup and statements in tear down. Additional tasks for the teardown could arise in the middle of the main block. (not all objects may be prepared in the setup block). And yes, that is not what the "using" in other languages does. But that is about reducing nested try/finally. I have an idea, but it feels not right yet (because it adds some of the blocks back / though not in a real nested fashion). I did mention labels. Initially I though of them in the setup block, to match in the teardown. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Execlbl1: foo;       bar;lbl2: other;try  do_stufffinallylbl2: undo_other;lbl1: unfoo;  nobar;end; The question is, should the block "lbl2" in the teardown be executed - as soon as the setup started with the block lbl1 - if the setup finished the block lbl1   (the 2nd could be achieved by putting the label after the statements, but that is crap) Then taking the above: Really the setup block is no longer different from the try block? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i trylbl1: foo;       bar;lbl2: other;lbl_stuff:  do_stufffinally // no lbl_stufflbl2: undo_other;lbl1: unfoo;  nobar;end; It does reduce a bit on the nested try/finally. However not as much as just implicitly assuming a label for each statement in the setup, and then reverse-order assign the teardown statements. It does however improve read ability on the nested try/finally. Having 5 nested try/finally would make it hard to match the blocks up. But well named labels would be immediately recognizable. So that would be a huge bonus. ---------------------------------------------------------------------- circular · 2024-03-11 20:14 Quote from: alpine on March 11, 2024, 06:39:40 pmAnd what will happen if some constructor failed with an exception (implicit Destroy)? It would work as if you would write the nested try/finally. The failed construction stops the execution at the current level, doesn't do the finalization for this level, and the exception continues at the outer level and runs the finalization there. ---------------------------------------------------------------------- circular · 2024-03-11 21:03 Quote from: Martin_fr on March 11, 2024, 08:20:35 pmOne other generic concern I have forgotten to mention. The word "using" itself may be a bad choice. It bares similarities with "with", and it implies there is something "to use" rather than "to do". - "to use" would be a list of objects/values (which is also what coma- separation implies to me). - "to do" is a list of statements. Effectively it is followed by a list of statements. Never mind that in your case the produce values. It could be an existing object "connection.accept".That's a interesting point. I would rather have something less generic for various reasons. The idea of using is that it only accepts assignments. The compiler can then do some checks, for example, it can be illegal to use the same variable in a nested block. And if the dispose action is not specified, to call Free on the object. If it is not an object, of course the dispose action would need to be explicit. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i using myInstance1 := TMyObject.Create,      myInstance1 := TMyObject.Create do // raises a compile errorbegin  myInstance1.DoSomething;  myInstance2.DoSomething;  ...end; This may cover most use cases: you can allocate an object or initialize it from a function. In the case would only call functions of an existing object, like "connection.accept" and in the end "connection.close", this would not be adequate. In fact, if you do "connection.accept", it may return a boolean and so would need to check the result before continuing. QuoteAnd semicolons as separator. It ought to be a code block.I heard that. But what are the arguments for it? Nevertheless, here is how to change it to make it more statement-like: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i using begin // "begin" to introduce a series of initializations  myPoolObject := pool.GetInstance    then pool.Release(myPoolObject);  myInstance1 := TMyObject.Create(myPoolObject);   myInstance2 := TMyObject.Create(myInstance1); // last semi-colon is optionalend dobegin  myPoolObject.DoSomethingElse;  myInstance1.DoSomething;  myInstance2.DoSomething;  ...end;  QuotePulling the "free" (or release) up, seems to me uncommon for pascal? And also not very "read able", as the code is then deliberately in an order that differs from execution.The Free would not need to be pulled because that would be the default, but indeed, with a custom disposal, it is not the the usual order. I think it is still better, because the finalization is in reverse order and it is not easy to maintain as such. On need to do some gymnastic of the brain to reverse the order. Though, following your idea, labels would indeed be a neat way of clarifying what goes with what. Then I would change the order: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i try  lbl1:   foo;    bar;  lbl2:     other;  lbl_stuff:    do_stufffinally   lbl1:     unfoo;    nobar;  lbl2:     undo_other;  // no lbl_stuffend;It would be clear at glance that finalization matches the initialization. ---------------------------------------------------------------------- Martin_fr · 2024-03-11 22:37 Quote from: circular on March 11, 2024, 10:03:27 pmThe idea of using is that it only accepts assignments.Which are statements. Which can contain function calls, with side effects. QuoteThe compiler can then do some checks, for example, it can be illegal to use the same variable in a nested block. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i using myInstance1 := TMyObject.Create,      myInstance1 := TMyObject.Create do // raises a compile errorbegin  myInstance1.DoSomething;  myInstance2.DoSomething;  ...end;Ok, ... Though, as long as the setup is a separate block from the rest of the "try" part, any variable assigned in that block could be treated this way. Though there may be cases were one would want to explicitly nil a variable like "myInstance1" in the main-block. E.g. if it was added to a list, and that list guarantees to take care of it. Or instead of nil, create the next value, and later (in a loop) add it to the list too. Quote And if the dispose action is not specified, to call Free on the object.That I have an issues with. It is not part of the "get rid of nested try/finally" It is about having an automated free/destroy at the end of a scope. That has been discussed countless times. It isn't even special to try/finally, because freeing objects is required independent if the code may have exceptions or not. Also this is what I understand "smart pointers" will bring you. They even include the try/finally without you need to specify it. Though they may only destroy at the end of the function, and you need to have them refer each other, to specify an order. But that actually brings up a point. Not only would "not specifying free" sneak in the sub-scoping of a procedure for automatic free. It would in this proposal even sub-scope the one try/finally scope to create scopes for ordering. In any case, it is not related to "reducing nested try/finally". If you want to re-open that discussion please do it with proper announcement what you try to get. (In that case, my contribution to that part will be: "I don't like it, and prefer if it wasn't added to fpc" / but it isn't my decision in the end) QuoteThis may cover most use cases: you can allocate an object or initialize it from a function. In the case would only call functions of an existing object, like "connection.accept" and in the end "connection.close", this would not be adequate. In fact, if you do "connection.accept", it may return a boolean and so would need to check the result before continuing. If you initialize an object from a function, it could be nil. That also then needs to be checked by whatever code wants to deal with the object. The check does not need to be immediate... And with the label solution, it would work even if you do an immediate check. QuoteQuoteAnd semicolons as separator. It ought to be a code block.I heard that. But what are the arguments for it? Assignments are statements (and if it isn't limited to assignment then its still a statement list). Statement separator is semicolon? Quote QuotePulling the "free" (or release) up, seems to me uncommon for pascal? And also not very "read able", as the code is then deliberately in an order that differs from execution.The Free would not need to be pulled because that would be the default, but indeed, with a custom disposal, it is not the the usual order. I think it is still better, because the finalization is in reverse order and it is not easy to maintain as such. On need to do some gymnastic of the brain to reverse the order. You need to do the work either once you write it. Or every time you read it. Reading it, one expects the order in which it happens (and that is reverse). Only issue is if you want to copy and paste the setup, and then multi- caret turn it into the teardown. Then you need an IDE feature to reverse the order of lines. Still, I wouldn't compromise the syntax/grammar for that. ---------------------------------------------------------------------- lainz · 2024-03-12 03:13 Why don't use Tinterfacedobject with say a big interface. Then implement all members in the same class. TBGRABitmap = class(Tinterfacedobject, IBGRABitmap) Var   Ibgra: IBGRABitmap; begin   Ibgra := TBGRABitmap.Create   Ibgra.fill(clRed)   // no need to free end ---------------------------------------------------------------------- circular · 2024-03-12 11:30 Hi Martin_fr, I appreciate your expertise and the depth of thought you've put into your responses. It's clear we share a commitment to preserving the integrity and principles of Pascal as we explore potential enhancements. I understand your concerns about introducing a new syntax that might disrupt established practices. My intention with the using proposal is not to circumvent Pascal's traditional handling of object lifecycles but to explore ways to streamline and clarify resource management, particularly in complex scenarios where nested try...finally blocks can become cumbersome. You're main point, if I understand correctly, is to have flexibility in how to use the disposing mechanism, even if that doesn't follow a simple pattern of object creation and a simple function call. And so the keyword using would not express that wish. Also you would prefer to have explicit call to Free. It seems we're looking at the core challenge from different perspectives, comparing to what I've presented at the beginning of this thread. I am open though to discuss an alternative approach that enhances the existing try...finally blocks as you suggested. This idea would be to maintain the explicitness of disposal actions while aiming to improve the code's readability and maintainability. With this in mind, here is a proposal for embedding disposal actions: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i try  nameOfInstance := 'instance';  myInstance1 := TMyObject.Create(nil, nameOfInstance);  then myInstance1.Free;    nameOfInstance += '2';  myInstance2 := TMyObject.Create(myInstance1, nameOfInstance);  then begin    myInstance2.NotifyOtherInstance;    myInstance2.Free;  end;   myInstance1.DoSomething;  myInstance2.DoSomething;  ...end;By specifying a then keyword (or another appropriate term) to signal disposal actions, we could ensure these actions are executed in reverse order, emulating an implicit nesting of try...finally blocks. This could potentially reduce the back-and-forth typically involved in managing resource disposal, especially in complex scenarios, and thus, enhance both readability and maintainability. ---------------------------------------------------------------------- Martin_fr · 2024-03-12 12:10 Quote from: circular on March 12, 2024, 12:30:11 pmMy intention with the using proposal is not to circumvent Pascal's traditional handling of object lifecycles but to explore ways to streamline and clarify resource management, particularly in complex scenarios where nested try...finally blocks can become cumbersome. And yet, any attempt to move or remove the explicit free at/from the place were it takes effect, is unfortunately changing that handling. And, it is a proposal that has been discussed before. It was discussed independent of try/finally, but that does not make it different from being discussed as part of try/finally =>  effectively with or without try/finally destructors need to be called. If you have a large code block that can never raise, and you create 10 objects on top of it, you need to destroy them at the end. Exactly the same as in a try finally block. Therefore essentially there are 2 topics here. 1) Reducing nested try/finally 2) Moving "free/destroy" / in your words "handling of object lifecycles" The 2nd is entirely independent of try/finally, and it is an exact repeat of prior discussions (well, it adds some depth, by adding ordering within the scope of lifetime). As far as "scoped lifetime" goes, there are interfaces, ref-counted types, and likely coming up "smart pointers". All of them have a scope that is defined by the enclosing procedure/function (though afaik the compiler may optimize in some cases, not sure...) Your "using" is simply setting up a sub-scope within a procedure/function and binds the lifetime of a variable to that scope.  That has been asked (what feels like) a thousand times for ref-counted interfaces. Without calling "free" explicitly, the scope is the procedure. Anyway, I understand you want to change that. I don't want that to change. And all else is for the future to see what will happen. (I will keep lobbying against it, and let you do the pro-lobbying). IMHO, (and I may interject comments to that effect) it should be made sure (explicitly mentioned in the discussion) in future discussions on part (2) that it is not really related to try/finally (even if it creates a try finally block). And most definitely not related to reduction of nesting in try/finally. That is at best coincidental. As soon as you need further resources in the middle of the block, after you already done work (so the first "using" clause has already been left, you will need a nested "using" clause. In fact, looking at "object lifecycles", I would imagine that there is (within the faction of those supporting it) a large group that would like this to be available without try/finally, for at least platforms on which try/finally has a cost on execution time. All that said, and that being 2 topics... If - hypothetically -  both of those topics would be implemented in some form, then it would still be reasonable to ask for them work together nicely, and with zero overhead when combined. But for that, each topic has to be defined on its own. Starting to define how the combination of 2 things should look, when neither thing is defined on its own, well that sound like a sub-optimal approach. As for (1) "reducing nesting": I have made my points. In consequence of the strict separation of the 2 topics, all of them require a full list of explicit release/free/destroy/... at the bottom, where those are actually called. Any proposal that does not build on that, is outside the scope of what I am participating here (other than lobbying against). If there are further ideas to be discussed that embrace the explicit final free/release.... => I will make comment, if I have any ideas that may be worth a comment. ---------------------------------------------------------------------- nanobit · 2024-03-12 14:49 To reduce nesting of try-finally, initialize (obj1 := nil; obj2 := nil;) has been mentioned. A minor new feature would help to reduce repetitious notation: obj1, obj2 := nil; which is equivalent to: obj2 := nil; obj1 := obj2; ( many languages (C, C#, Javascript, Python) support such chained assignment) For nil (constant), the compiler would produce: obj1 := nil; obj2 := nil; ---------------------------------------------------------------------- circular · 2024-03-12 16:26 Quote from: lainz on March 12, 2024, 04:13:31 amWhy don't use Tinterfacedobject with say a big interface. Then implement all members in the same class. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TBGRABitmap = class(Tinterfacedobject, IBGRABitmap)Var  Ibgra: IBGRABitmap;begin  Ibgra := TBGRABitmap.Create  Ibgra.fill(clRed)  // no need to freeend Hi Leandro, thanks for popping by. Interfaces can be handy indeed, however this require to change the class itself. In some cases though, that's a valid option. Here I am looking for a grammar to explicitly say when the object is to be disposed. This allows to define the sequence in which the objects need to be freed. This matters when objects depend on each other or when specific actions need to be done. Of course in the end, everything could be defined as reference counted and encapsulated, but I am looking for a solution that can be easily applied to existing classes without any overhead. ---------------------------------------------------------------------- lainz · 2024-03-13 00:04 Quote from: circular on March 12, 2024, 05:26:58 pmQuote from: lainz on March 12, 2024, 04:13:31 amWhy don't use Tinterfacedobject with say a big interface. Then implement all members in the same class. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TBGRABitmap = class(Tinterfacedobject, IBGRABitmap)Var  Ibgra: IBGRABitmap;begin  Ibgra := TBGRABitmap.Create  Ibgra.fill(clRed)  // no need to freeend Hi Leandro, thanks for popping by. Interfaces can be handy indeed, however this require to change the class itself. In some cases though, that's a valid option. Here I am looking for a grammar to explicitly say when the object is to be disposed. This allows to define the sequence in which the objects need to be freed. This matters when objects depend on each other or when specific actions need to be done. Of course in the end, everything could be defined as reference counted and encapsulated, but I am looking for a solution that can be easily applied to existing classes without any overhead. Yes, and all 'normal' objects are outside this rule. As well you have duplicate 'interface' for this. The thing is that if I forget to add .Free, I can forget as well to add 'using', so for me is remembering one of the two things. ---------------------------------------------------------------------- circular · 2024-03-13 10:04 I understand your point. One need to remember to specify this. Nevertheless, if you get the habit of writing the free action next to the object creation, it would become a habit. It is not like you need to remember it for later. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  bgra: TBGRABitmap;begin  try // using "trye" and Ctrl-J    bgra := TBGRABitmap.Create then bgra.Free; // you would write this in one go  bgra.Fill(clRed);    ... // then use the object without needing to think about it  except    writeln('Something went wrong');    ... // alternate action if error  end;end; You can type this without going back and forth. ---------------------------------------------------------------------- Martin_fr · 2024-03-13 11:04 Quote from: circular on March 13, 2024, 11:04:26 amNevertheless, if you get the habit of writing the free action next to the object creation, it would become a habit. It is not like you need to remember it for later. Ah, but in Pascal that habit does not exist.... And, well yes, I have to switch between Languages too. And especially when I have used one Language quite intensively and switch to another, there is a period were my brain tries to use the concepts of the language from which I switched away. And it takes a while until I stop attempting to write the crappiest code ever, and embrace the way of the language that I am using now. But, that can't be a reason to change all languages to be the same (same concept). If it were, there would be only one single language. (Maybe a few Syntax variants, but no other difference). There are other threads, that I guess come from the same switching woes. Lately "method chaining". If you come from a language were you do: MyData.split(...).map(...).filter(...).sort(...).each(...)  then you gonna miss that. That does not mean, that this should be the future look of Pascal.  Different languages promote different coding styles. That is a good thing and should be embraced. Attempting to "simulate" a different style - even if possible - is not recommendable. Pascal style, does not have the order "create / forward declare destroy / do work". And the Pascal style of having destruction at the end, is same as easy to maintain - with the exception for that "switching" period, in which every difference between the languages is perceived a pain in the "touching the chair part". But here, your own argument actually can be used "get the habit of" => To write Pascal code one needs to have gotten into the habit of writing code in Pascal. ---------------------------------------------------------------------- circular · 2024-03-18 16:45 Hi Martin_fr, I appreciate your perspective on the importance of embracing the unique coding styles and paradigms that different languages offer. My intention with the proposal is not to erase those distinctions or to suggest that Pascal should conform to practices from other languages. Rather, I aim to explore ways in which we can address specific challenges within Pascal's own framework and philosophy, enhancing its robustness and expressiveness. Moreover, I want to clarify that my proposal is not born out of frustration with the differences between languages. On the contrary, I recognize and celebrate the diversity and unique strengths that different programming languages bring to the table. My motivation is rooted in a proactive approach to addressing specific areas within Pascal where we have the opportunity to make the language even more user-friendly. ---------------------------------------------------------------------- circular · 2024-03-18 17:10 Quote from: nanobit on March 12, 2024, 03:49:19 pmTo reduce nesting of try-finally, initialize (obj1 := nil; obj2 := nil;) has been mentioned. A minor new feature would help to reduce repetitious notation: obj1, obj2 := nil; which is equivalent to: obj2 := nil; obj1 := obj2; ( many languages (C, C#, Javascript, Python) support such chained assignment) For nil (constant), the compiler would produce: obj1 := nil; obj2 := nil;Hi nanobit, I agree with your method of setting to nil. It is an approach I have used in the past. Your suggestion to reduce the repetitiveness of initializing objects to nil by allowing chained assignment would indeed simplify the method you propose. It's interesting to note its adoption in several other languages like C, C#, JavaScript, and Python. However, it is not as structured one can expect of the Pascal language. It amounts to flattening the try...finally blocks. The approach might not ensure that all finalizations are executed reliably if one fails, potentially leaving resources improperly managed. Additionally, introducing assignment expressions that return values could lead to less readable code, diverging from Pascal's tradition of prioritizing clarity. Another point to consider is how to apply it in additional cases that have been mentioned by @Martin_fr and @marcov, such as when objects are acquired from a pool or when managing resources that are not objects. I'm curious to hear your thoughts on how we might address these broader challenges while still simplifying the syntax where possible. ---------------------------------------------------------------------- Martin_fr · 2024-03-18 17:39 Quote from: circular on March 18, 2024, 05:45:17 pmI appreciate your perspective on the importance of embracing the unique coding styles and paradigms that different languages offer. My intention with the proposal is not to erase those distinctions or to suggest that Pascal should conform to practices from other languages. Rather, I aim to explore ways in which we can address specific challenges within Pascal's own framework and philosophy, enhancing its robustness and expressiveness. Moreover, I want to clarify that my proposal is not born out of frustration with the differences between languages. On the contrary, I recognize and celebrate the diversity and unique strengths that different programming languages bring to the table. My motivation is rooted in a proactive approach to addressing specific areas within Pascal where we have the opportunity to make the language even more user-friendly. Well, yes - far be it from me to imply what triggered your proposal.  In any case, if the below is not a reference to a habit acquired elsewhere (aka from another language), then it basically is a nil statement (or I am missing something) Quote from: circular on March 13, 2024, 11:04:26 amNevertheless, if you get the habit of writing the free action next to the object creation, it would become a habit. It is not like you need to remember it for later.Any new feature (however well or bad it matches the concept of the language to which it is added) will require getting into the habit of it. (And yes, of course that "getting into the habit" may take shorter or longer...). The point shouldn't be if it needs "getting into the habit", but if it is worth "getting into the habit". That latter bit relies on many factors. One of them may be, if it helps with something (makes something easier). It appears to me, that you seek a way to specify "release" statements at the top. - that may make it easier to you - but making them placed out of order may make it harder to read to many others. So there is a big question if such a re-ordering (in whatever way it may be done) is helpful in the grand picture. (summing up the effect on all the different people). This isn't that I (and others like me) would possible - out of habit - forget to put them on top. I would still have the option to put them at the bottom. But I would constantly start reporting bugs on other peoples code that I have to read through (for plenty of reasons outside my control), until I realize that I overlooked those missing "free". ---------------------------------------------------------------------- VisualLab · 2024-03-18 18:46 Quote from: Martin_fr on March 13, 2024, 12:04:05 pmQuote from: circular on March 13, 2024, 11:04:26 amNevertheless, if you get the habit of writing the free action next to the object creation, it would become a habit. It is not like you need to remember it for later. Ah, but in Pascal that habit does not exist.... And, well yes, I have to switch between Languages too. And especially when I have used one Language quite intensively and switch to another, there is a period were my brain tries to use the concepts of the language from which I switched away. And it takes a while until I stop attempting to write the crappiest code ever, and embrace the way of the language that I am using now. I think this applies to most programmers who use multiple programming languages (me included, anyway). Quote from: Martin_fr on March 13, 2024, 12:04:05 pmThere are other threads, that I guess come from the same switching woes. Lately "method chaining". If you come from a language were you do: MyData.split(...).map(...).filter(...).sort(...).each(...)  then you gonna miss that. That does not mean, that this should be the future look of Pascal.  Different languages promote different coding styles. That is a good thing and should be embraced. Attempting to "simulate" a different style - even if possible - is not recommendable. Excessively abbreviated instructions become illegible (or at least not very legible) at some point. Especially since the example shown is just a very exaggerated syntactic frosting (i.e.: an anti-example). Quote from: Martin_fr on March 13, 2024, 12:04:05 pmPascal style, does not have the order "create / forward declare destroy / do work". And the Pascal style of having destruction at the end, is same as easy to maintain - with the exception for that "switching" period, in which every difference between the languages is perceived a pain in the "touching the chair part". That is, it shows what is really happening and in what order. Quote from: Martin_fr on March 13, 2024, 12:04:05 pmBut here, your own argument actually can be used "get the habit of" => To write Pascal code one needs to have gotten into the habit of writing code in Pascal. I agree with it. It is not certain whether copying the syntax and behavior of languages whose output code is bytecode run in a VM (Java, C#, etc.) is a good idea. There is a lot of "magic" in them that actually costs money (CPU, RAM) and is performed by the VM. In the case of Object Pascal (but also e.g. C++) only the "magic" of the compiler is available. Won't excessive use of compiler "magic" make Object Pascal as confusing as C++? And since Object Pascal's popularity is lower than that of C++, this may (paradoxically) result in its even greater marginalization, instead of the expected improvement in popularity. ---------------------------------------------------------------------- VisualLab · 2024-03-18 19:26 Quote from: circular on March 18, 2024, 06:10:33 pmQuote from: nanobit on March 12, 2024, 03:49:19 pmTo reduce nesting of try-finally, initialize (obj1 := nil; obj2 := nil;) has been mentioned. A minor new feature would help to reduce repetitious notation: obj1, obj2 := nil; which is equivalent to: obj2 := nil; obj1 := obj2; ( many languages (C, C#, Javascript, Python) support such chained assignment) For nil (constant), the compiler would produce: obj1 := nil; obj2 := nil;Hi nanobit, I agree with your method of setting to nil. It is an approach I have used in the past. Your suggestion to reduce the repetitiveness of initializing objects to nil by allowing chained assignment would indeed simplify the method you propose. It's interesting to note its adoption in several other languages like C, C#, JavaScript, and Python. Hmm... This is, to put it mildly, a highly debatable/controversial argument. C# uses the "magic" of VM, so elements of its syntax (and behavior) are not always advisable/useful in languages compiled to machine code. JavaScript and Python, on the other hand, are anti- patterns of programming languages. These are probably the worst designed languages. Therefore, providing them as examples rather suggests that their features should certainly not be introduced into Pascal and Object Pascal. Moreover, their code is interpreted and their purpose is (or at least originally was) different than languages with code compiled into machine code (typical software). ---------------------------------------------------------------------- circular · 2024-03-20 01:47 Hi nanobit, I'd like to correct an oversight in my previous response regarding your suggestion. I suppose I was thinking of obj1 := obj2 := nil; when I've mentioned that assignment expressions that return values could lead to less readable code. However you're suggestion to write obj1, obj2 := nil; doesn't imply this, as the values to be assigned to are separated by commas. Your proposal indeed simplifies syntax without compromising clarity, aligning with Pascal's principles. Apologies for the confusion, and thank you for your valuable suggestion. ---------------------------------------------------------------------- circular · 2024-03-20 02:22 Hi Martin_fr, Thank you for your response. My proposal is indeed about enhancing Pascal and not trying to make it look like other popular languages. I want to express that referring to my statement as a "nil statement" made me feel somewhat uncomfortable. I would like my point of view to be acknowledged among the diversity of the people joining the discussion. When talking about writing the Free statement, we already have the habit. The later version of my proposal, that included the consideration of object pools, is only a matter of moving where we write the finalization. For example, if one would go down the code to write the Free statement in advance, one would instead write it after the object creation. And if one would write the Free statement after the body, then at this moment, they can opt for going up to write it after the initialization instead. Quote from: Martin_fr on March 18, 2024, 06:39:12 pmIt appears to me, that you seek a way to specify "release" statements at the top. - that may make it easier to you - but making them placed out of order may make it harder to read to many others. It was not my intention to propose a solution that would be only beneficial to me nor to create division among us. What may seem clearer or more intuitive to one person can be less so to another. This highlights the importance of our discussions here. ---------------------------------------------------------------------- nanobit · 2024-03-20 05:27 Quote from: circular on March 20, 2024, 02:47:13 amHi nanobit,... Your proposal indeed simplifies syntax without compromising clarity, aligning with Pascal's principles. Indeed, faster to read: a,b,c := nil; In most use cases the ordering of assignments does not matter, but it would be strictly defined. I adopted the common assignment order (right-to-left associativity) from C-style languages. Due to the chaining effect (although mostly not relevant), it's not just an alternative notation, but is a new feature. This might be a moot point, but I've selected the C-style behavior to actually prevent confusion. Example: "a,b,c := random();"  would mean: "c := random(); b := c; a := b;" which is different from "a := random(); b := random(); c := random();" Edited: I meant the chaining only in an abstract sense (ordering and final results). The implementation would use temp variable, or would be much simpler (in case of constants). ---------------------------------------------------------------------- Martin_fr · 2024-03-20 07:43 Quote from: circular on March 20, 2024, 03:22:51 amI want to express that referring to my statement as a "nil statement" made me feel somewhat uncomfortable. I haven't joined that part of the topic. It's relatively low impact to me (as in: wouldn't "disturb" me, if done). There is a thread of different Syntaxes somewhere (IIRC). Discussion should include, if this is a general multi assignment, or limit to nil/0/default. QuoteQuote from: Martin_fr on March 18, 2024, 06:39:12 pmIt appears to me, that you seek a way to specify "release" statements at the top. - that may make it easier to you - but making them placed out of order may make it harder to read to many others. It was not my intention to propose a solution that would be only beneficial to me nor to create division among us. It was indeed misleading (actually wrong) when I wrote "you". I do of course fully acknowledge that there are others who would find it useful. QuoteWhat may seem clearer or more intuitive to one person can be less so to another. that is exactly what I tried to express too. Only I am reversing the association of which is which. QuoteThis highlights the importance of our discussions here. +1 I said earlier that I let you de the pro-lobbying. Again, "you" wasn't meant literal "just you". But anyone with interest in it. (an then of course on the non-pro side, anyone else) ---------------------------------------------------------------------- dseligo · 2024-03-20 08:18 Quote from: nanobit on March 20, 2024, 06:27:34 amThis might be a moot point, but I've selected the C-style behavior to actually prevent confusion. Example: "a,b,c := random();"  would mean: "c := random(); b := c; a := b;" which is different from "a := random(); b := random(); c := random();" To me it would be more logical that "a,b,c := random();" is "a := random(); b := random(); c := random();". I don't see how you prevent confusion, with such constructs you bring confusion. ---------------------------------------------------------------------- nanobit · 2024-03-20 08:30 It's easy to remember:  - One call, one value (like a, b, c := tempVar) The right-hand side of ":=" is resolved at first. And in the majority (98%+) of cases it would be used for constants (nil, 0.0, ....). Comparison of readability: a := nil; b := nil; c := nil; a,b,c := nil; ---------------------------------------------------------------------- Martin_fr · 2024-03-20 11:09 Quote from: nanobit on March 20, 2024, 09:30:45 amIt's easy to remember:  - One call, one value (like a, b, c := tempVar) The right-hand side of ":=" is resolved at first. IIRC, there was a thread with alternative Syntaxes. One was "(a,b,c) := (nil,nil,nil);" (or maybe with "[" "]"...) Not much of a saver in this context, though. However, if we see this as something that is meant particular for initialization, that is "nil" (or more generic "Default()", then there could be "(a, b, c) := nils;" Some special value (name/representation to be decided) that is expanded to the list of correct size. To me it is (at least in the described usecase) still just syntactic sugar. But also, despite that not particular problematic if it existed, and I had to come across it in some code. ---------------------------------------------------------------------- alpine · 2024-03-20 14:03 Quote from: nanobit on March 20, 2024, 09:30:45 amIt's easy to remember:  - One call, one value (like a, b, c := tempVar) The right-hand side of ":=" is resolved at first. And in the majority (98%+) of cases it would be used for constants (nil, 0.0, ....). Comparison of readability: a := nil; b := nil; c := nil; a,b,c := nil; That's a substantial change of the Pascal semantics, you're changing the assignment statement to an assignment operator (by cited decomposition): Quote from: nanobit on March 20, 2024, 06:27:34 amExample: "a,b,c := random();"  would mean: "c := random(); b := c; a := b;" which is different from "a := random(); b := random(); c := random();" and introducing a right-to-left association. Further it will compromise the evaluation by means of side effects (as dseligo said, consider also subscription expressions), e.g. just like the shorthand assignments, see: https://gitlab.com/freepascal.org/fpc/source/-/issues/39206 https://forum.lazarus.freepascal.org/index.php/topic,55338.msg411546.h tml#msg411546 ---------------------------------------------------------------------- nanobit · 2024-03-20 15:34 Multiple assignment (a,b,c := nil;) is only for ":=", no other operator. Right-to-left is the most common behavior for multiple assignment. There would be no problem, it's a viable variant for shorter notation. Only one evaluation (one value) on the right side, it could not be simpler. Of course, the wish for shorter notation would need to be relevant in the first place. But in general, there is interest in short notations for initialization (assignments) and finalization of object groups. ---------------------------------------------------------------------- Edson · 2024-03-20 15:51 Quote from: nanobit on March 20, 2024, 04:34:42 pmMultiple assignment (a,b,c := nil;) is only for ":=", no other operator. Right-to-left is the most common behavior for multiple assignment. There would be no problem, it's a viable variant for shorter notation. Only one evaluation (one value) on the right side, it could not be simpler. Multiple assignment is not the Pascal way to do things. Maybe it's OK in C when assignments returns a value. Maybe something like SetValueTo(nil, a, b, c) or SetValueTo(nil, [a, b, c]) would be more Pascalish. On the other hand, I'm not sure USING keyword would be appropriate for FPC but I think a good help on destroying objects automatically would be welcome. I like the idea Delphi for inline variable declaration https://docwiki .embarcadero.com/RADStudio/Alexandria/en/Inline_Variable_Declaration I see that's not so drastic. Probably I will be including this in my compiler. Even more I would include an auto destruction for objects at the end of the block where the object is declared: var my_object := TMyObject.Create(); Autodestroyed; Programming languages are not religion. They must evolve or die. Just my humble opinion. ---------------------------------------------------------------------- nanobit · 2024-03-20 19:58 Quote from: Edson on March 20, 2024, 04:51:54 pmMultiple assignment is not the Pascal way to do things. Multiple assignment would be useful only for specific cases. Not enough to get implemented, so I will leave this topic... ---------------------------------------------------------------------- circular · 2024-03-20 21:23 Quote from: Edson on March 20, 2024, 04:51:54 pmvar my_object := TMyObject.Create(); Autodestroyed; Programming languages are not religion. They must evolve or die. Just my humble opinion. That's an interesting suggestion. Indeed, adding a little word afterwards to say to free the object. Surely shorter than: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i my_object := TMyObject.Create() 'but temporarily because I am not going to use this instance anywhere else'; ---------------------------------------------------------------------- circular · 2024-03-20 21:25 Quote from: nanobit on March 20, 2024, 08:58:04 pmMultiple assignment would be useful only for specific cases. Not enough to get implemented, so I will leave this topic... Thanks for popping by. ---------------------------------------------------------------------- alpine · 2024-03-20 22:12 Quote from: nanobit on March 20, 2024, 08:58:04 pmQuote from: Edson on March 20, 2024, 04:51:54 pmMultiple assignment is not the Pascal way to do things. Multiple assignment would be useful only for specific cases. Not enough to get implemented, so I will leave this topic... Consider something as perplexing as: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function f: Integer; const N: Integer = 0; begin  Inc(N); Exit(N);end;var A,B,C: array [1..10] of Integer;begin  (A[f], B[f], C[f] := A[f]);end; (did you read the links I've provided?) So, Ltr, Rtl or C-ish shorthand way of evaluation: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i // Ltr by simple substitution (A[f] := A[f]; B[f] := A[f]; C[f] := B[f];) => (A[2] := A[1]; B[4] := A[3]; C[6] := B[5];)// Rtl by simple substitution(C[f] := A[f]; B[f] := A[f]; A[f] := B[f];) => (C[2] := A[1]; B[4] := A[3]; A[6] := B[5];)// Ltr by value (assignment as operator)(temp := A[f]; A[f] := temp; B[f] := temp; C[f] := temp;)  => (temp := A[1]; A[2] := temp; B[3] := temp; C[4] := temp;) // Rtl by value (assignment as operator, C-ish)(temp := A[f]; C[f] := temp; B[f] := temp; A[f] := temp;)  => (temp := A[1]; C[2] := temp; B[3] := temp; A[4] := temp;) QuoteOnly one evaluation (one value) on the right side, it could not be simpler.Not quite. IMO the more natural way of managing would be the rehabilitation of the old style objects in some form. That will render the try/finally clause unnecessary as with C++. Another thing that I'd like to see is the TAuto<> included in the RTL. ---------------------------------------------------------------------- Thaddy · 2024-03-21 04:12 Quote from: alpine on March 20, 2024, 11:12:43 pmAnother thing that I'd like to see is the TAuto<> included in the RTL. That would only be useful if Maciej's original default property proposal would be implemented after all. Then there is also no reason to implement using. Both avk and me - mine heavily based on Barry Kelly's work - have presented reasonable implementations. HNB(Maciej) has quite a lot of the compiler patches necessary. Sadly they are probably lost in oblivion. For the casual reader I have to take you back about 5-6 years in time, when Maciej proposed extending the syntax for default properties like Tlist.Items[] --> Tlist[] to also be supported like so: Tauto.value --> Tauto() which was a brilliant idea. (and 99% ready, well maybe 97%) My box[]/unbox[] syntax stems from that, for lack of Maciej's patches being committed. That was at the time quite reasonable btw, since such a patch is really complex, as would using be. And with hindsight they boil down to the same thing. ---------------------------------------------------------------------- nanobit · 2024-03-22 05:34 Quote from: alpine on March 20, 2024, 11:12:43 pmSo, Ltr, Rtl or C-ish shorthand way of evaluation: Thanks for the example. Yes, I meant the last line with temp. The difference is intentional. Multiple assignment (single source via temp) is unlike a userwritten series of single-assignments (series of sources). For multiple assignment, the preferred use-case would be initialization of local variables, provided that the names are not too long and if order does not matter. I like the short notation, and don't see too much risk that it could be used beyond this (when single-assignments are better). But meanwhile I view this as off-topic, because it won't be implemented anyway .... ---------------------------------------------------------------------- circular · 2024-03-22 07:30 Quote from: alpine on March 20, 2024, 11:12:43 pmIMO the more natural way of managing would be the rehabilitation of the old style objects in some form. That will render the try/finally clause unnecessary as with C++. Another thing that I'd like to see is the TAuto<> included in the RTL. While it does handle some cases, one of the motivation for using or finalization along initialization is to make nesting implicit. If you have objects that depend on each other, the order in which they are freed matters. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var a,b: TMyObject;begin  try    a := TMyObject('a', nil) then a.Free;    b := TMyObject('b', a) then b.Free;  end;end;means to do b.Free and after that a.Free. In fact, the try...end keyword may not even be necessary. We could write it as: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var a,b: TMyObject;begin  a := TMyObject('a', nil) finally a.Free;  b := TMyObject('b', a) finally b.Free;end; With automatic freeing at the end of the scope, the finalization will be called in the order of variable declaration as far as I've tested, though I am not sure it is guaranteed. Anyway this means that to have the proper order one would need to carefully put the variables in reverse order: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var b, a: specialize TAuto;begin  a := TMyObject.Create('a', nil);  b := TMyObject.Create('b', a.Value);   end; Code: [Select]Creating a depending on Creating b depending on a Destroying b depending on a Destroying a depending on One way to ensure ordering of finalization would be to declare variables locally within nested begin/end blocks. For example using allocation on stack: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i begin  var a: TMyObject('a', nil) on stack;  begin    var b: TMyObject('b', a) on stack;    ...  end; // free bend; // free a ---------------------------------------------------------------------- Martin_fr · 2024-03-22 09:34 Quote from: circular on March 22, 2024, 08:30:56 amCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var a,b: TMyObject;begin  try    a := TMyObject('a', nil) then a.Free;    b := TMyObject('b', a) then b.Free;  end;end;means to do b.Free and after that a.Free. Why not have a "MyFreeList := TReverseFreeObjectList.create;" Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  MyFreeList := TReverseFreeObjectList.create;  try    foo := TFoo.create; MyFreeList.Add(foo);    bar := TFoo.create; MyFreeList.Add(bar);    // ....  finally    MyFreeList.Free;  end; - Does 100% what you want. No extra initialization. Pure Pascal. - The list can have try finally blocks to deal with risk in the destructors. - Bonus: You can use     MyFreeList.Insert(n, foo);  if you need different destruction order. Compared to a "then/later" syntax - One variable - Creation of the list => 1 statement - MyFreeList.Add => no overhead, just a different Syntax for "later".  (The variable name can be less verbose) - finally with 1 statement (the "end" is needed in your construct too) And if you think about it, your desired construct even works the same way. Only difference is, that in your version the list happens at compile time. So if you compare, then overall this has 1 var declaration, 2 statements and 1 keyword (if you don't have any finally at all left over in your version) more. But that does not grow, never mind how many objects you need to take care off. But then you could get rid of the variables foo/bar. And refer to them via e.g. TFoo(MyFreeList[n])  (If it is a generic, that can return a common base class). And that also means, you can do (which your static construct can't do) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  MyFreeList := TReverseFreeObjectList.create;  try    for i := 0 to x do MyFreeList.Add(TFoo.Create);    // ....  finally    MyFreeList.Free;  end; ---------------------------------------------------------------------- alpine · 2024-03-22 09:38 QuoteWith automatic freeing at the end of the scope, the finalization will be called in the order of variable declaration as far as I've tested, though I am not sure it is guaranteed. Anyway this means that to have the proper order one would need to carefully put the variables in reverse order:The order is guaranteed by the ref-counting. Thus holding a `de-referenced' TAuto<> is not advisable. You can introduce a scope with a with do : Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i with   {a:=}TAuto.Create(...),    {b:=}TAuto.Create(...) dobegin  {b.}Value := xxx; {a.Value?}end; but they would be anonymous temporaries, time of destruction not guaranteed, neither it is distinguishable (with scope visibility rules). BTW, a way for aliasing the variable references into the with statement is something that I badly miss. ---------------------------------------------------------------------- Martin_fr · 2024-03-22 09:41 Even more... With anonymous functions (not that I am a fan of those) you can do (or use a separate named function instead, even better) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TMyFreeList.Run( // Run is a constructor, that automatically calls it's own destructor   function(l: TMyFreeList) var ...; // even get your scoped variables   begin     foo := TFoo.create; l.Add(foo);    bar := TFoo.create; l.Add(bar);    // ....  end;); And if you want rename TMyFreeList into  "class Using = " and write "using.run();" ---------------------------------------------------------------------- circular · 2024-03-22 16:27 Quote from: Martin_fr on March 22, 2024, 10:34:32 amWhy not have a "MyFreeList := TReverseFreeObjectList.create;" I like the idea that this doesn't need to rewrite existing classes. It can be made less verbose and avoid heap allocation using extended records: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  b, a: TMyObject;  scope: specialize TFinalizer;begin  a := scope.Use(TMyObject.Create('a', nil));  b := scope.Use(TMyObject.Create('b', a));  raise exception.Create('hey');end.With the following TFinalizer definition: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  generic TFinalizer = record  private    FObjects: array[0..9] of T;    FObjectCount: integer;  public    class operator Initialize(var AFinalizer: TFinalizer);    class operator Finalize(var AFinalizer: TFinalizer);    function Use(AObject: T): T;  end; class operator TFinalizer.Initialize(var AFinalizer: TFinalizer);begin  FillChar(AFinalizer.FObjects, sizeof(AFinalizer.FObjects), 0);  AFinalizer.FObjectCount := 0;end; class operator TFinalizer.Finalize(var AFinalizer: TFinalizer);var  i: Integer;begin  for i := AFinalizer.FObjectCount - 1 downto 0 do    AFinalizer.FObjects[i].Free;end; function TFinalizer.Use(AObject: T): T;begin  if FObjectCount > high(FObjects) then    raise exception.Create('Too many objects');  FObjects[FObjectCount] := AObject;  inc(FObjectCount);  result := AObject;end; Quote- Does 100% what you want. No extra initialization. Pure Pascal. - The list can have try finally blocks to deal with risk in the destructors. - Bonus: You can use     MyFreeList.Insert(n, foo);  if you need different destruction order. Sure, it caters for the case object. It doesn't cater though for other cases, like object pools or when there isn't even objects involved, as you and @marcov suggested. QuoteAnd if you think about it, your desired construct even works the same way. Only difference is, that in your version the list happens at compile time.Agreed. Slightly more verbose but that's okay. ---------------------------------------------------------------------- circular · 2024-03-22 16:27 Quote from: alpine on March 22, 2024, 10:38:00 amThe order is guaranteed by the ref-counting. Thus holding a `de-referenced' TAuto<> is not advisable.Ok. However it requires adapting existing classes to hold a TAuto reference. QuoteBTW, a way for aliasing the variable references into the with statement is something that I badly miss. Is it in order not to have to declare a variable? Something I miss is to reference the only with variable, because sometimes there is ambiguity with the members of the variable and other names. In VB, the members are accessed by prefixing "." which makes it clear we're talking about a member. I suppose that would be possible in Pascal syntax, as long as we keep the ";" at the end of statements. ---------------------------------------------------------------------- alpine · 2024-03-22 17:05 Quote from: circular on March 22, 2024, 05:27:42 pmQuote from: alpine on March 22, 2024, 10:38:00 amThe order is guaranteed by the ref- counting. Thus holding a `de-referenced' TAuto<> is not advisable.Ok. However it requires adapting existing classes to hold a TAuto reference. Agreed, not feasible. Quote from: circular on March 22, 2024, 05:27:42 pmQuoteBTW, a way for aliasing the variable references into the with statement is something that I badly miss. Is it in order not to have to declare a variable? Yes. Quite often I use it that way: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i with TMyDlg.Create(Nil) do {or whatever constructor}  try    case ShowModal of ...  finally    Free;  end; Quote from: circular on March 22, 2024, 05:27:42 pmSomething I miss is to reference the only with variable, because sometimes there is ambiguity with the members of the variable and other names. In VB, the members are accessed by prefixing "." which makes it clear we're talking about a member. I suppose that would be possible in Pascal syntax, as long as we keep the ";" at the end of statements. Namely that ambiguity and also it is impossible to use it (cite it) as a parameter to a subroutine.  Also I have tried to use it for automatic acquire/release of MT sync objects for a block, e.g. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i with TAutoLock.Acquire(CS) do {here TAutoLock is TAuto<> descendant}begin  {critical} end; but ended up by enclosing critical blocks into nested subroutines with local TAutoLock variables, just to be sure they're destroyed on the stack wind-up. ---------------------------------------------------------------------- Martin_fr · 2024-03-22 17:50 Quote from: circular on March 22, 2024, 05:27:15 pmQuote- Does 100% what you want. No extra initialization. Pure Pascal. - The list can have try finally blocks to deal with risk in the destructors. - Bonus: You can use     MyFreeList.Insert(n, foo);  if you need different destruction order. Sure, it caters for the case object. It doesn't cater though for other cases, like object pools or when there isn't even objects involved, as you and @marcov suggested. Well, this was made to match your idea of not having to write a "free" (or free-like statement) at the bottom of the code. At least I did want to keep my frees at the bottom... It does also solve the nested finally (in your case). Though that is actually a side effect (I didn't really have that in mind when I came up with the idea). Then again, that is nothing but a by-product. No "free" needed, no finally needed. No nested of something that is not there any more at all... But I am glad it works for you. ---------------------------------------------------------------------- Martin_fr · 2024-03-22 17:57 QuoteCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  class operator TFinalizer.Finalize(var AFinalizer: TFinalizer);var  i: Integer;begin  for i := AFinalizer.FObjectCount - 1 downto 0 do    AFinalizer.FObjects[i].Free;end; You didn't nest that into try/finally. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  class operator TFinalizer.Finalize(var AFinalizer: TFinalizer);  procedure DoFinallyFree;  begin    try      while AFinalizer.FObjectCount > 0 do begin        dec(AFinalizer.FObjectCount);        AFinalizer.FObjects[AFinalizer.FObjectCount].Free;       end;    finally      if AFinalizer.FObjectCount > 0 then DoFinallyFree; // do the rest of the work    end;  end;begin  DoFinallyFree;end; ---------------------------------------------------------------------- circular · 2024-03-22 20:11 Quote from: Martin_fr on March 22, 2024, 06:57:52 pmYou didn't nest that into try/finally.Yes I was wondering what would be the most elegant way to implement it. The recursive function is a good idea. It doesn't need to be nested, does it? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i class operator TFinalizer.Finalize(var AFinalizer: TFinalizer);begin  try  while AFinalizer.FObjectCount > 0 do begin      dec(AFinalizer.FObjectCount);      AFinalizer.FObjects[AFinalizer.FObjectCount].Free;     end;  finally  if AFinalizer.FObjectCount > 0 then Finalize(AFinalizer); // do the rest of the work  end;end; It could as well be adapted to work with any method call, so that it could be more versatile as you wished. Something like: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  b, a: TMyObject;  scope: TFinally;begin  a := TMyObject.Create('a', nil); scope.OnExit(@a.Free);  b := TMyObject.Create('b', a); scope.OnExit(@b.Free);  raise exception.Create('hey');end. Thank you for highlighting the TReverseFreeObjectList approach, which indeed offers a pragmatic solution within the current capabilities of Pascal. I'm glad we could explore solutions that address the challenges of resource management effectively. While I'm excited about the possibilities within current Pascal frameworks, I remain interested in exploring how we might further evolve the language to support developers' needs. I think it's also important to recognize the practical challenges faced by the FPC development team. The bandwidth for implementing changes is naturally limited by resources, time, and the prioritization of work to ensure stability and compatibility. ---------------------------------------------------------------------- circular · 2024-03-22 20:16 Quote from: alpine on March 22, 2024, 06:05:53 pmYes. Quite often I use it that way: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i with TMyDlg.Create(Nil) do {or whatever constructor}  try    case ShowModal of ...  finally    Free;  end; I suppose that in this case, it is quick to use Ctrl-Shift-C to add the variable declaration. This way you could as well pass it as a parameter. Quotebut ended up by enclosing critical blocks into nested subroutines with local TAutoLock variables, just to be sure they're destroyed on the stack wind-up.Maybe the TFinalizer record could be adapted to this use case? ---------------------------------------------------------------------- alpine · 2024-03-23 11:56 Quote from: circular on March 22, 2024, 09:16:10 pmQuotebut ended up by enclosing critical blocks into nested subroutines with local TAutoLock variables, just to be sure they're destroyed on the stack wind-up.Maybe the TFinalizer record could be adapted to this use case? Not a big deal (my case). Just as a long time C++ practitioner, I was used to utilize the local block inline variables for boxing critical MT sections of the code. Since FPC doesn't have such inline variables, it appeared to me with do can be used for the purpose. It worked actually, but then I was dissuaded. Discussion was RAII related, thus very similar to this one though. After all, nested subroutines turn out to be perfect for the purpose.