FWIW (not a core dev), I think the way julia handles this is extremely clear and explicit and I don’t understand your problem, at all; and I dislike your proposal even if there was a blank slate to start over.
Mutable means object on the heap; always passed by value-of-pointer; obviously modifications are visible. Immutable means [*] object on the stack / in registers, passed by value, i.e. a logical slot for so and so many bytes of local state (where obviously mutable counts as a pointer aka 8 bytes). Assignment always changes which slot (SSA value) your name is bound to; a.z=b
means setfield!
which is a built-in function-call for modifying memory in the heap via a mutable (aka pointer), as is setindex!
aka x[i]=b
. This is all mutation that exists in julia, and they can be called on mutable objects only [**].
The compiler re-uses local memory for SSA values that it can prove will never be used again (e.g. because all bindings to them were re-bound or went out of scope) or sometimes does not need a stack slot at all (use a register). The gc cleans heap objects that it can prove will never be used again (e.g. because no tracked pointer to them is still living in a living stack slot, nor in any heap object).
[*] Due to compiler limitations many immutables are still heap-allocated. Also I only understand the julia-native ABI up to the rather short documentation and a little experimentation, not meticulous source-code reading (and I may have failed at RTFM on the way!). Also, it might be that 0.7 sometimes manages to put mutables (aka syntactic sugar for Ref{immutable_struct}
) on the stack.
I think the native ABI is not intended as a spec; that is, native-ABI breaking changes are not considered breaking, and the goal is “every julia session produces machine-code that is interoperable within the same session”; people are just not supposed to code against the native ABI. (If my read on this is wrong, I’d be happy to be corrected on this! Also, this is not a critique; this is a perfectly sensible position to take for an internal-only ABI.)
[**] Of course you can define dispatches for immutables as well, like e.g. Base.setindex!(ptr::Ptr{T}, val::T)=unsafe_store!(ptr, val)
, and in 0.7 you could define Base.setfield!
for pointers to structs.
As far as I understood, it is a deliberate decision by the core devs to not provide such a convenient syntax for pointers, in order to discourage their use-- part of their job is not just developing a language but also nudging towards a certain “idiomatic” coding style in the ecosystem (obviously I personally disagree, I’d prefer more convenient pointers; will maybe find (or write) a little package once I switch to 0.7).