Recent

Author Topic: Const record parameters by default  (Read 23413 times)

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9845
  • Debugger - SynEdit - and more
    • wiki
Re: Const record parameters by default
« Reply #15 on: April 01, 2024, 11:49:45 am »
In my experience default const is what the programmer wants

I don't think so.

I haven't done myself, but search for code that passes TPoint or TRect.
- Both a records.
- There are probably countless procedures that take a TPoint/TRect as parameter, and the change the x/y/h/w of those records (mind the local copy is changed).

So making those "const" would certainly not be what the programmer wants.

Ryan J

  • Full Member
  • ***
  • Posts: 112
Re: Const record parameters by default
« Reply #16 on: April 01, 2024, 02:34:16 pm »
Also "pointer to record" has existed since the dawn of time....

Whenever a record should not be passed by value, then the correct way is usually to explicitly define a pointer to the record type, and pass a pointer.
Using "const" (or "constref") is not the preferred way for this.

Then what is the point of const? I would say it's also because you don't want to pass by value and copy things you don't need to. It's easier for various reasons than using pointer semantics.

Ryan J

  • Full Member
  • ***
  • Posts: 112
Re: Const record parameters by default
« Reply #17 on: April 01, 2024, 02:44:21 pm »

I see that one of the problems you have is distinguishing between a pointer being constant and what the pointer points to being constant.  Those are two very different things and unfortunately Pascal does not provide fine control over cases like that.

Consider a case like this.... a large record
Code: Pascal  [Select][+][-]
  1. type
  2.   PLARGE_RECORD = ^TLARGE_RECORD;
  3.   TLARGE_RECORD
  4.     field : packed array[0..1000] of char;
  5.   end;
  6.  
  7. < some more stuff here... whatever you want>
  8.  
  9. procedure DoSomething(const L : PLARGE_RECORD { notice it is pointer });
  10. begin
  11.   L^.field[0] := 'a';  { that's legal and perfectly sensible }
  12. end;
  13.  

In this example I would want const to indeed fail that assignment. It's useful to know when calling DoSomething that my data won't change. That should be covered by const.

As for your other example I'm struggling to understand why it wouldn't be ideal for all records to be passed by const by default since there's a potential for an optimization. I don't have data to support it but I'm pretty sure the common case is you don't modify passed records anyways. Martin says otherwise and mentions some specific types, which may be true in those cases. Hard to know without auditing a large code base.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9845
  • Debugger - SynEdit - and more
    • wiki
Re: Const record parameters by default
« Reply #18 on: April 01, 2024, 02:50:46 pm »
Also "pointer to record" has existed since the dawn of time....

Whenever a record should not be passed by value, then the correct way is usually to explicitly define a pointer to the record type, and pass a pointer.
Using "const" (or "constref") is not the preferred way for this.

Then what is the point of const? I would say it's also because you don't want to pass by value and copy things you don't need to. It's easier for various reasons than using pointer semantics.

"const" can be used for this too. (And I have done so myself).

Technically "const" has one advantage (if you are aware of the rules I pointed out): "const" lets the compiler decide, if it uses a reference or not. ("constref" is always by reference)

On the other hand "const" obscures that it is (or may be) a pointer. Making it necessary to have that extra knowledge.

"const" also prevents some "mistakes", because you let the compiler know you don't want to use the pointer as a means to modify the original. But as pointed out, what you actually say is that you are not modifying the referenced value at all. Not by any means, nowhere...

So "const" is different. And "constref" is different too.



I did not make the decision to introduce "const", nor do I have first hand info why it was made.

From what I learned, it was introduced as a means to "allow the compiler certain optimizations". (so a bit like "inline").

Currently that optimization(s) is/are
- sometimes passing by ref
- sometimes omitting ref-counting

That may change / That is not guaranteed.



Sorry, that is not a full answer to "what is it for" especially with comparing to "normal pointers".

BildatBoffin

  • New member
  • *
  • Posts: 7
Re: Const record parameters by default
« Reply #19 on: April 01, 2024, 05:15:54 pm »
To elaborate on 440bx's statement

The below program is erroneous.

Code: Pascal  [Select][+][-]
  1. program Test;
  2. type TFoo = record a,b: Integer; end;
  3. var  Foo: TFoo;
  4.  
  5. procedure ChangeFoo;
  6. begin
  7.   Foo.a := 1;
  8. end;
  9.  
  10. procedure OptimizeMe(const x: TFoo);
  11. begin
  12.   writeln(x.a); // writes 2
  13.   ChangeFoo;
  14.   writeln(x.a); // in future, maybe writes "2" again, or "1" at random...
  15. end;
  16.  
  17. begin
  18.   Foo.a := 2;
  19.   Foo.b := 3;
  20.   OptimizeMe(Foo);
  21. end.
  22.  

https://www.freepascal.org/docs-html/ref/refsu67.html
Quote
Note that specifying const is a contract between the programmer and the compiler. It is the programmer who tells the compiler that the contents of the const parameter will not be changed when the routine is executed
"ChangeFoo" is executed when (while) "OptimizeMe" still runs.

The problem in your example is not related to constness, it's related to purity. `OptimizeMe` is impure because it modifies the global state, in consequence `OptimizeMe` is transitively impure, which breaks the constness guarantee on the `x` parameter.

But ObjFPC does not have the concept of purity baked in the semantic checks, so for now the programmer has to rely on self-discipline, I'd say.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9845
  • Debugger - SynEdit - and more
    • wiki
Re: Const record parameters by default
« Reply #20 on: April 01, 2024, 05:58:04 pm »
The problem in your example is not related to constness, it's related to purity. `OptimizeMe` is impure because it modifies the global state, in consequence `OptimizeMe` is transitively impure, which breaks the constness guarantee on the `x` parameter.
Same thing as I said? Just calling it by a different name?

Besides, "impure-ness" may be considered a bad design, but it's not an error. And the language as such supports it, and allows it.

And as you pointed out, it only becomes an issue if used as a callee by code having a relevant "const param". So it is the wrong usage of "const".

"const" itself is fine too. It works exactly as documented. The problem is using it incorrectly.

440bx

  • Hero Member
  • *****
  • Posts: 4011
Re: Const record parameters by default
« Reply #21 on: April 01, 2024, 09:40:36 pm »
In this example I would want const to indeed fail that assignment.
But it would be incorrect for it to do so.  _Only_ the pointer has been declared as "const", the compiler has _not_ been told anything about the data the pointer points to and it would be incorrect to extend the "const-ness" to what it points to (what if you want a constant pointer that points to something that is not "const", how could that be declared if "const" were to be extended to the data instead of just applying to the pointer ?)

Just for the record, for what you want to do in that case, L should be the data type itself (instead of the pointer to the type) then it should be passed as "constref" which tells the compiler that the _data_ is constant.  The pointer itself is also constant but that's because the compiler isn't giving access to that address/pointer (but, that pointer isn't immune from some really dirty tricks)


As for your other example I'm struggling to understand why it wouldn't be ideal for all records to be passed by const by default since there's a potential for an optimization.
I suppose it could be done that way.  There are languages out there that default all parameters to functions/procedures to be immutable.  Personally, I like the way Pascal does it because at the hardware level it is _not_ immutable.  It's the actions (or inactions) of the compiler that make a parameter immutable.

While that's my preference, I think there is a perfectly reasonable case for immutability but Pascal's choice is perfectly fine, there is absolutely nothing wrong with it.

Lastly, I think your "estimate" of 95% of record parameters being "const" is tinted by the fact that the code you look at is likely, mostly, user interface code.  That means the side effects of the code are not the data, instead the side effect is changing (outputting) something on a window (console or gui window.)

There are an extremely large number of applications (probably all Engineering applications) where side effects occur mostly on the data first then after millions upon millions of calculations (read: side effects) the result is output somewhere.  A lot of the data the code is using in all those calculations isn't "const".   Definitely not 95%.

(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Joanna

  • Hero Member
  • *****
  • Posts: 748
Re: Const record parameters by default
« Reply #22 on: April 02, 2024, 10:57:13 am »
When I first learned pascal const parameters were not even mentioned. The non var parameters were sent as a Copy and could be modified inside the procedure with no affect on the original values. The var parameter was passed directly and any changes to it were permanent.

I don’t understand what the problem is using CONST parameters on things you don’t want changed. I do it all the time. Some things like objects I believe the fields inside can still be changed even if const is used?
Please correct me if I’m wrong.
✨ 🙋🏻‍♀️ More Pascal enthusiasts are needed on IRC .. https://libera.chat/guides/ IRC.LIBERA.CHAT  Ports [6667 plaintext ] or [6697 secure] channel #fpc  Please private Message me if you have any questions or need assistance. 💁🏻‍♀️

BildatBoffin

  • New member
  • *
  • Posts: 7
Re: Const record parameters by default
« Reply #23 on: April 02, 2024, 12:05:18 pm »
The problem in your example is not related to constness, it's related to purity. `OptimizeMe` is impure because it modifies the global state, in consequence `OptimizeMe` is transitively impure, which breaks the constness guarantee on the `x` parameter.
Same thing as I said? Just calling it by a different name?

Besides, "impure-ness" may be considered a bad design, but it's not an error. And the language as such supports it, and allows it.

And as you pointed out, it only becomes an issue if used as a callee by code having a relevant "const param". So it is the wrong usage of "const".

"const" itself is fine too. It works exactly as documented. The problem is using it incorrectly.

Consteness and purity are not exactly the same thing. What we can imagine is an attribute to statically enforce purity. Even transitive `const` would not solve the problem. "pass-by-value" would not either. A field can be a pointer so even if blitting occurs the pointer target remains identical.

For the time being and in absence of `pure` attribute I agree with you, it's a matter of discipline.

Ryan J

  • Full Member
  • ***
  • Posts: 112
Re: Const record parameters by default
« Reply #24 on: April 02, 2024, 04:20:42 pm »
I don’t understand what the problem is using CONST parameters on things you don’t want changed. I do it all the time. Some things like objects I believe the fields inside can still be changed even if const is used?
Please correct me if I’m wrong.

I brought this up because new languages are doing const by default and looking at my own experience I actually think this is what you want most of the time, thus it should be the default. It's not 1980 anymore and people have decades of experience now to draw from. This is obviously debatable though.

However, I learned a lot in this topic and I don't think this would be safe in Pascal unless the compiler was overhauled to do static analysis of functions and detect they're not actually writing to const parameters. If you made a mistake you could end up with modifying some global state (or member fields) and make a debugging nightmare.

Ryan J

  • Full Member
  • ***
  • Posts: 112
Re: Const record parameters by default
« Reply #25 on: April 02, 2024, 04:23:14 pm »
In this example I would want const to indeed fail that assignment.
But it would be incorrect for it to do so.  _Only_ the pointer has been declared as "const", the compiler has _not_ been told anything about the data the pointer points to and it would be incorrect to extend the "const-ness" to what it points to (what if you want a constant pointer that points to something that is not "const", how could that be declared if "const" were to be extended to the data instead of just applying to the pointer ?)

I know, I'm saying it would be nice to have const mean that also though. Currently there's no way to know if you pass something to a function that it won't be mutated. That would be useful to know right? If you saw on the parameter it was really constant you could reason about the state of your program easier.

440bx

  • Hero Member
  • *****
  • Posts: 4011
Re: Const record parameters by default
« Reply #26 on: April 02, 2024, 05:02:47 pm »
Currently there's no way to know if you pass something to a function that it won't be mutated. That would be useful to know right?
Yes, indeed it would be very useful and fortunately, if and when the programmer understands how "const" is applied by the compiler, it is clear whether or not a parameter is mutable or not.

If you saw on the parameter it was really constant you could reason about the state of your program easier.
Of course.  It isn't my intention to be flippant but, from your comments, I'd say you need to acquire a better, read: more accurate, understanding of how "const" works in Pascal.

IMO, the problem in Pascal with "const" is that unlike in C, "const" isn't a general modifier, i.e, one that can be used when defining an identifier.  That limits when "const" can be used which in turn limits its functional domain.  IOW, "const" in Pascal is limited and, can be confusing if/when not fully understood in the context of the language.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

BildatBoffin

  • New member
  • *
  • Posts: 7
Re: Const record parameters by default
« Reply #27 on: April 02, 2024, 05:36:22 pm »
The problem in your example is not related to constness, it's related to purity. `OptimizeMe` is impure because it modifies the global state, in consequence `OptimizeMe` is transitively impure, which breaks the constness guarantee on the `x` parameter.
Same thing as I said? Just calling it by a different name?

Besides, "impure-ness" may be considered a bad design, but it's not an error. And the language as such supports it, and allows it.

And as you pointed out, it only becomes an issue if used as a callee by code having a relevant "const param". So it is the wrong usage of "const".

"const" itself is fine too. It works exactly as documented. The problem is using it incorrectly.

Consteness and purity are not exactly the same thing. What we can imagine is an attribute to statically enforce purity. Even transitive `const` would not solve the problem. "pass-by-value" would not either. A field can be a pointer so even if blitting occurs the pointer target remains identical.

For the time being and in absence of `pure` attribute I agree with you, it's a matter of discipline.

To go further, let's observe a language that has the concept of purity

Code: D  [Select][+][-]
  1. module runnable;
  2.  
  3. struct S
  4. {
  5.     void* pv;
  6. }
  7.  
  8. S s;
  9.  
  10. void touchImpure()
  11. {
  12.     s.pv = null;
  13. }
  14.  
  15. void touchPure() pure
  16. {
  17.     s.pv = null;
  18. }
  19.  
  20. void use1(const S s)
  21. {
  22.     touchImpure();
  23. }
  24.  
  25. void use2(const S s) pure
  26. {
  27.     touchImpure();
  28. }
  29.  
  30. void use3(const S s) pure
  31. {
  32.     touchPure();
  33. }
  34.  
  35. void main()
  36. {
  37.     use1(s); // OK but dangerous, this is similar to the current state of ObjFPC
  38.     use2(s); // NG triggers: `pure` function `runnable.use2` cannot call impure function `runnable.use1`
  39.     use3(s); // NG triggers: `pure` function `runnable.touchPure` cannot access mutable static data `s`
  40. }
  41.  

(online https://godbolt.org/z/477r9hEEv)

So here you can see that everything is passed by value but purity is focused on the global state.
This gives a guarantee that the parameter will not be modified during the execution of the body.

`const` and `pure` are complementary.
« Last Edit: April 02, 2024, 07:53:50 pm by BildatBoffin »

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9845
  • Debugger - SynEdit - and more
    • wiki
Re: Const record parameters by default
« Reply #28 on: April 02, 2024, 08:22:06 pm »
Consteness and purity are not exactly the same thing.
Yes, and neither did I claim such thing...

Ryan J

  • Full Member
  • ***
  • Posts: 112
Re: Const record parameters by default
« Reply #29 on: April 03, 2024, 03:26:12 am »
Currently there's no way to know if you pass something to a function that it won't be mutated. That would be useful to know right?
Yes, indeed it would be very useful and fortunately, if and when the programmer understands how "const" is applied by the compiler, it is clear whether or not a parameter is mutable or not.

If you saw on the parameter it was really constant you could reason about the state of your program easier.
Of course.  It isn't my intention to be flippant but, from your comments, I'd say you need to acquire a better, read: more accurate, understanding of how "const" works in Pascal.

IMO, the problem in Pascal with "const" is that unlike in C, "const" isn't a general modifier, i.e, one that can be used when defining an identifier.  That limits when "const" can be used which in turn limits its functional domain.  IOW, "const" in Pascal is limited and, can be confusing if/when not fully understood in the context of the language.

Well I had higher hopes for const and you seem to be giving justifications for its shortcomings. It's not fine just because they wrote something in the manual. Const should mean constant. Looks like what D is doing with pure is getting closer to something actually helpful.

I did learn some things though, mainly const is basically just like "inline", a hint to the compiler. I thought it was something more powerful but it's really nothing more.

 

TinyPortal © 2005-2018