When classes are said to be "most simple case" it says a lot :-)
Yes if we are talking about the usage of pointers, classes are the "most simple case" in the sense that it is the most common place. When you create a Lazarus Form application you are bombarded with instances of Buttons, Edits, Forms, etc. So most Lazarus projects use classes everywhere.
But does it mean that const should be removed from Pascal, because it is not hearmetically sealed?
Of course not, this would be really stupid, and I never claimed anything like that. All I did was point out how the immutability concept as introduced in the first post in this thread would not work. And there is a reason for that. As already mentioned above, most FreePascal and Delphi programs are going to be full of classes everywhere. So classes, which basically are nothing but pointers in disguise, are used everywhere. So if an immutability mechanism where to be introduced, if it does not extend to classes (and pointers more generally), it is basically useless, because large parts of most codebases are going to be not covered by this.
This also makes const as it is now in Pascal quite useless as a const mechanism. Take the following example:
type
TIntegerHelper = type helper for Integer
public
procedure Inc;
end;
procedure TIntegerHelper.Inc;
begin
Self += 1;
end;
procedure Foo(const i: Integer);
begin
i.Inc;
WriteLn(i);
end;
begin
Foo(1);
end.
Result: 2. I just modified a parameter that is "const", just because const does not extend to pointers (the self pointer in this case). Const is a security mechanism, that allows the programmer to create access boundaries to their variables. But, a security mechanism that is not enforcable is useless. This is the programming equivalence from having a key for your front door, while having another key just hanging from the door knob in case you forgot yours.
And the important thing is, here I did it intentionally, but it is easy to see a situation where one person is writing a type (or typehelper) and changes the definition of one method to now alter one of the fields, while the usage, where it is assumed to be constant, is made by a different person. So when the compiler doesn't say: "Hey you are using a method that modifies state on a const object", neither of the developers will probably notice, until they have a very weird bug that they have to spend days in figuring out.
That said, const is not harmfull, it actually has a usecase in that, it allows for optimizations on managed types (like disabling lazy copy checks on strings) when used for parameters. But to enforce immutability of the data it is just plain useless. So no, I don't think it should be removed, I'm just saying that it is pretty useless for enforcing immutability.
Btw, the example above might be categorized as a bug, as you are not supposed to be able to take the pointer of a const, but there is "constref", which basically is a pointer to a constant value, which has the same problem but is more explicelty about it.
What cost would it take to implement "perfectly const" objects in Pascal? Those that only consists of "pure functions" and are disallowed to have public writeable members, friend members and all direct and indirect ways to change their inner state.
No, when I was talking about immutability in this context, I am talking about the immutability of the value of certain data, or you could say to a certain kind of memory (even though this is not necessarily true, as there is not a 1:1 relationship between datatypes in a high level language and memory, just think of register reuse). Basically what the C standard calls an "object" (this term is loaded in Pascal so I try to avoid it).
So no, I don't talk about stateless/pure functions, but that if you have an immutable object, that you cant mutate it. A language that nailed this is actually C++. Consider the following:
int j;
struct Test {
int i;
void inc() { ++i: }
void incj() const { ++j; }
};
Test t1 = {1};
Test const t2 = {1};
t1.inc(); // Works fine because t1 is mutable
t2.incj(); // Also works because while t2 is immutable, incj is also immutable
t2.inc(); // error, trying to call non immutable method on immutable data
As you can see, incj is not a pure/stateless function, it clearly changes the state of j which is a global variable. But because it is declared as const, the "this" pointer is of type "Test const *", which means it can be called on any Test const object, while inc() changes the state of the "this" pointer and therefore can only be called on a mutable instant of Test.
This has nothing to do with pure functions or anything similar. It is simply that the immutability information is transitively passed through the pointers to this data, in this case the "this" pointer.
If you pass a value as "const", e.g. to another function as parameter, or in any other way, you are saying: "Hey you can read it, but don't touch it, I expect that after you are done this is the exact same value in this data". If you can't trust in this promise, what is it worth?
I do not want for Pascal to become a jail where doing harm and erros is absoilutely impossible in no way.
I don't see any merit in allowing things that are clearly errors in any and all cases. For example something like:
Can never be correct (on multiple levels), and this should never be allowed by the compiler, period. But you are right there are instances in which you want to break the rules, but here I think the important doctrine is, if you do something special, the action to do so should also be special.
Take C++ again, here if you want to access a const object with write, you can, you just need to cast the const away:
char const *immutable = str; // Assuming str is mutable so this is not broken
char *mutable = const_cast<char *>(immutable );
By having to take this extra step, you have the programmer explicitly deciding to say "Yes I know I do something dangerous and I am aware of the potential problems".
In my last programming Job we had a rule, whenever a const_cast was in the code, there needed to be an explicit comment explaining exactly why this is necessary. Also the code reviewer would personally ask the programmer about the const_cast and tries to find out if this is really necessary. Only if the code reviewer was satisfied with the answer he would merge the branch into master. If he wasn't satisfied with your answer, you better get working around this.
If you do something special, you better have good reasons to do so
Note that I don't think that C++ does this perfectly, personally I think that immutability by default and mutability annotations (so everything is const unless otherwise specified), is a much better approach. But at least considering what C++ tried to archive, the const mechanism is actually very well thought out and quite useful