How so? Mutability is a property of the type of something and you cannot change the type of something. Therefore you cannot change whether something is mutable or not.
Interestingly, making a mutable wrapper around an immutable object is actually useful and efficient, even though at first blush it seems to violate immutability.
Could you elaborate on that?
I wrote a paragraph about it and linked to a rather long issue discussing the technique in detail. What specifically would you like more detail about?
Is there a general mechanism in Julia (like a macro) with which one can “derive” an immutable object from a mutable one?
No, not automatically, at least not at this point. I would potentially be possible through metaprogramming and generating types.
What do you think about the first part of my proposal, distinguishing between assignment and equality in the syntax?
Julia already distinguishes assignment syntactically from both mutation and equality. There are the following assignment-like syntaxes:
-
x = ...
is assignment. The namex
is bound to the value that the...
evaluates to. Local bindings never leak out of their scope. It doesn’t matter whatx
was bound to before this happens, that value is not affected in any way. No object is mutated by this and no other binding besidesx
is changed. -
x.f = ...
is equivalent tosetproperty!(x, :f, ...)
which, by default mutates the objectx
by changing its fieldf
to the value of the expression...
. Ifx
is visible in another scope or by another bindings, this change will be seen everywhere. No bindings are changed by this. -
x[i] = ...
is equivalent tosetindex!(x, ..., i)
which, for arrays mutates the arrayx
by changing itsi
th slot to refer to the value of the expression...
. Ifx
is visible in another scope or by another bindings, this change will be seen everywhere. No bindings are changed by this.
There are equality operators ==
and ===
which check for value-based equality and identity-based equality, respectively. There is also already a syntax for creating a constant binding as opposed to a variable binding:
const x = ...
The const
annotation is not currently supported in local scopes. But as I said, that’s a fairly straightforward feature which has not been added because it has limited benefits – both humans and compilers can easily tell if a local binding could potentially be reassigned in the same scope or not. That is, of course, not the case for global bindings which is where the const
declaration is really useful.
We could have bindings constant by default, but decided (consciously) not to. In local scope, defaulting to constant bindings is just annoying and has little benefit – if you want a constant local binding, just don’t re-assign something; as a human or a compiler, it’s easy to see if a local is assigned multiple times. In global scope it would be quite beneficial for bindings to be constant by default. However, this would be very annoying for interactive usage in the REPL where it’s really common to reuse and overwrite variable names. Since code that depends on non-constant global bindings is also usually quite slow in Julia (precisely because it’s hard to analyze statically), there’s a rather strong incentive not to use non-constant global bindings in code where performance matters at all. Some have suggested that this performance hazzard is actually a feature since it discourages the use of mutable global state.