You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I've described the rationale behind this model in the blog post, so I'll keep it short. These are the rules we should follow for deep immutability:
mut expressions
mut becomes a qualifier that can be used on any variable or field.
mut_expression:='mut''@'?identifier
It's valid both as an assignment target and an expression, described later.
Variable immutability
Variables become immutable by default. Each assignment shadows the old variable.
x = 1
x = 2 # x is shadowed
do
x = 3 # x is shadowed until `end`
end
assert(x == 2)
To create or shadow an existing variable with one that's mutable, one can use the mut expression with a bare identifier as an assignment target.
mut x = 1
x = 2 # x is NOT shadowed
do
x = 3
end
assert(x == 3)
mut variables can only be shadowed by other mut variables.
Field immutability
Fields, just like variables, become immutable by default. The mutability is determined in the first constructor. Later constructors inherit mutability from the first constructor.
Since this makes the order of constructor declarations matter, this can (and should) be changed later on in development.
(uses yet unimplemented syntax from #54)
impl struct Example
func new() constructor
# @x can only be read from.
@x = 1
# @y can be read from and written to.
mut @y = 2
end
func read_x() @x end
func read_y() @y end
func write_y(y)
@y = y
end
# This is illegal:
# func write_x(x)
# @x = x
# end
end
Object immutability
"Object" is a term used in the implementation when referring to any value that requires a heap allocation. The heap allocation is the object.
Values that hold objects are now called references. A reference can be either mutable, or immutable.
Reading a variable or field using a bare identifier (eg. x or @x) decays the mutability of the reference stored inside the variable, except when a bare identifier is the LHS of a dot expression, eg. x.example. In that case, the mutability is preserved as long as the source variable or field is also mutable.
Mutability is also preserved when a variable or field is read using a mut expression. mut expressions can only be used on variables and fields that are themselves mutable.
self is declared as an immutable variable by default. This can be changed to a mutable variable, by adding the mut keyword after func. A function with an immutable self cannot write to mutable fields, only read from them.
impl struct Example
func new() constructor
mut @x = 1
end
func mut set_x(x)
@x = x
end
func x() @x end
end
Here's an example to sum things up from the caller's perspective. Lists are used, because they're an object that's easy to instantiate.
# Object creation yields a mutable reference.
x = []
# However, because the source variable is immutable, each read from `x` decays the mutable reference into an immutable one.
# As such, the list inside `x` cannot be modified; only read from.
assert(x.len == 0) # len/0 does not mutate from the list and as such, is fine to call
# x.push(1) # push/1 mutates the list and thus cannot be called, because `x` decays to an immutable reference
do
# Here, we declare x as mutable, such that it's possible for us to delay when it decays into immutable.
mut x = [1, 2, 3]
# To make another mutable reference to the same object, the `mut` keyword has to be used.
# Again, for the reference to not decay immediately we need to store it in a mutable variable.
mut y = mut x
# Note that our mutable reference rules are not as strict as Rust's, because we can hold multiple mutable
# references to a single object.
x.push(4)
y.push(5)
assert(x == y)
assert(x == [1, 2, 3, 4, 5])
end
Function parameters
One thing I forgot to mention in my post is function parameters. I think they should be declared as immutable by default, with the option of making them mutable explicitly using the mut keyword.
func push_one(mut list)
list.push(1)
end
mut list = []
push_one(mut list)
The text was updated successfully, but these errors were encountered:
Successor to #51.
I've described the rationale behind this model in the blog post, so I'll keep it short. These are the rules we should follow for deep immutability:
mut
expressionsmut
becomes a qualifier that can be used on any variable or field.It's valid both as an assignment target and an expression, described later.
Variable immutability
Variables become immutable by default. Each assignment shadows the old variable.
To create or shadow an existing variable with one that's mutable, one can use the
mut
expression with a bare identifier as an assignment target.mut
variables can only be shadowed by othermut
variables.Field immutability
Fields, just like variables, become immutable by default. The mutability is determined in the first constructor. Later constructors inherit mutability from the first constructor.
Since this makes the order of constructor declarations matter, this can (and should) be changed later on in development.
(uses yet unimplemented syntax from #54)
Object immutability
"Object" is a term used in the implementation when referring to any value that requires a heap allocation. The heap allocation is the object.
Values that hold objects are now called references. A reference can be either mutable, or immutable.
Reading a variable or field using a bare identifier (eg.
x
or@x
) decays the mutability of the reference stored inside the variable, except when a bare identifier is the LHS of a dot expression, eg.x.example
. In that case, the mutability is preserved as long as the source variable or field is also mutable.Mutability is also preserved when a variable or field is read using a
mut
expression.mut
expressions can only be used on variables and fields that are themselves mutable.self
is declared as an immutable variable by default. This can be changed to a mutable variable, by adding themut
keyword afterfunc
. A function with an immutableself
cannot write to mutable fields, only read from them.Here's an example to sum things up from the caller's perspective. Lists are used, because they're an object that's easy to instantiate.
Function parameters
One thing I forgot to mention in my post is function parameters. I think they should be declared as immutable by default, with the option of making them mutable explicitly using the
mut
keyword.The text was updated successfully, but these errors were encountered: