Noting the (non-)mutability aspect of functional languages, but when did this sort of thing enter the mainstream? With a nod to @Pathfinder (if he's still around) did Ada or any other self-professed "safe" language implement that sort of thing?
MarkMLl
I don't know which was the first language to have that, but it is a big thing in C, so it is already pretty old.
In C you have the concept of objects (this is the name that the C standard gives, has nothing to do with OOP) and each object can have certain modifiers. For example:
int i; // this is an int object
int *i; // this is a pointer object pointing to an int object
int const i; // this is an int object with a const modifier
These objects can be constructed sequentially, where each modifier modifies the type to the left of it.
For example
int const * volatile * const i
This is a constant pointer object, pointing to a volatile pointer object pointing to a constant int object.
Note that a modifier in front of a definition only effects the first object in the chain:
const int *i;
// is the same as
int const *i;
// not
int * const i;
Which is a pitfall where many beginners fall into because they assume the pointer is const, but in reality the value pointed to is const.
This allows for very granular access control, for example:
makes the pointer immutable, this could for example be used to make access to shared memory, which has a fixed location in memory, so you can't change the pointer, but you the int value behind that pointer is not modifyable:
i = &othervar; // compiler error, the pointer is const
*i = 42; // works fine
This can be put anywhere
struct Test {
int const *i; // a pointer to a constant integer
int * const p; // A constant pointer to a variable integer
}
I think that this is something that is really lacking in pascal. You can only have this form of access control in function parameters, and then there is only one layer:
procedure foo(constref param1: Integer; const param2: PInteger);
Here you have a pointer to a constant integer as well as a constant pointer to an integer. But you can't go deeper then that, and constref is rather new anyway, so for a long time we didn't even have that.
Also as I noted above this is not enforced, so:
procedure foo(constref param: Integer);
var test: PInteger;
begin
test := @ param;
test^ := 42; // I just changed a constant value, without a compiler error or warning
end;
In C this looks different:
void foo(int const *param) {
int *test = param; // does not work
int const *test2 = param; // only this works
// workaround
test = (int*)param;
}
So you can work around this, but this requires effort, so you need to be at least in some form conciously aware of what you are doing, and in C++ this is even more explicit:
int *test = const_cast<int *>(param);
Making it clear to everyone who reads it that you made a concious decision to remove the const here, which might be neccessary at times, but should require a good bit of explaination, as usually some things are const for a reason.
I really like this and miss that pascal does not have something comparable to this, as especially the possibility to return constant pointers would be great (e.g. give read access to a field of a class without having to worry that data will be changed)