Yes, I know. From functional languages, but different.
Like querying a read-only database: you can only create new values by mutating old ones. Like, a view with one or more calculated fields that reference other fields. Or, like Lisp.
The concept of immutable datatypes as in functional languages, is a bit different than what usually is thought of with immutability in imperative languages. Functional languages like Haskell are stateless (at least the pure ones), where the whole program just consists of expressions. As you don't have single instructions there is no such thing as state that can be changed, and all you can do is take values as input and put them into functions that produce an output.
This is also a common paradigm when working in imperative languages when working with atomic types. Take this example:
You could rewrite this as:
x.SetValue(4);
x.Mul(2);
x.Add(3);
While with type helpers this would be possible, nobody does this, so atomic types in Pascal are mostly immutable, in the sense that you usually don't alter the state of an object but create a new object from an expression. Note that the following is also fllowing this paradigm:
x := 1;
for i:=0 to 10 do
x := x * i;
Even though x is changed, the objects itself are still immutable, because every iteration a new object is created and "just given the same name".
This might seem overcomplicated, or as MarkMLI put it "computersciency crap" when considering atomic types, but this is actually very useful for writing readable code. For example when instead of using integers, we consider the gmp unit. The computation above (3 + 4 * 2) completely using GMP types looks like this:
var
x: mpz_t;
begin
mpz_init_set_ui(x, 4);
mpz_mul_ui(x, x, 2);
mpz_add_ui(x, x, 3);
But the GMP unit also provides wrapper types that are designed to be treated as immutables, by creating temporary objects:
var
x: MPInteger;
begin
x := MPInteger(4) * 2 + 3;
This is much more intuitive to read than the former, and is completely immutable, MPInteger(4) creates a new object, *2 than takes this object and multiplies it by 2 and writes the result in a new object, which is then added with 3 to the final object that is then written into x. During the whole process no Object was modified after it's creation, and it's arguably better readable code.
Also a lot of the classical pascal functions are not following this paradigm:
Take the following:
Inc(x, 3);
// vs
x := x + 3;
// Or
Delete(str, 0, 3);
// vs
str := str.Substring(3);
I personally find this paradigm to produce much more readable code, because the state change is made explicit. When writing str := ... I know just from looking at that, that afterwards str will be something different, while with Delete I have to know that function to know that it changes str. I think that generally var and out parameters should be avoided as much as possible to make any changes explicit and directly spottable.
It also means that there is only one way to change things, if there is a := the variable changes, if not, than you can assume it will still be the same
But these are immutable datatypes, but what the discussion here was about are immutable variabels, to cover also pointer to such variables, immutable references.
Immutable references are different in the sense that you can basically ensure that if you "lend" someone an object, that it will come back unchanged. For example, assume you have a config class, which is used by many other classes to read out things like directories, user config, etc. Simultaniously you might not want to create a copy of said configuration for all cases (e.g. when the user should be able to change said config it would not be wise to have to propagete that change throughout all the classes again).
Therefore if you have a method of making immutable references, you can signal to the code getting this reference that it can be read, but that it is probably not a unique copy and therefore changing it might have other consequences outside the current bit of code. It's a form of documentation, the violation of which is enforced by the compiler.
Of course this can be used with immutable datatypes, but the core difference here is, that you can have an immutable references to mutable object. In the config example above, there might be a class which can actually change the config, e.g. some config editor form or something similar, so the class must be mutable. So we have multiple references to the same object, some mutable, some immutable, while immutable datatypes will always produce immutable.
So I think it is important to distinquish here. Immutable datatypes have their advantages, but they are generally a design paradigm which you have to alter your code around. Immutable references on the other hand are just a security mechanism to enforce assumptions made on the code, but generally the code would look identical if you removed the immutability (e.g. if you have a const variable in c and you remove the const everywhere, the code will work exactly the same, you just have less compiler checking if your ownership assumptions hold true)