Due to Python returning shallow copies from functions, we can reassign to fields of values even if the values themselves are immutable.
This behaviour was observed in #181, which contains an ignored test assign_to_inner_non_mut3.
How to Reproduce
class A
def c: C
class C
def my_class: D := D()
def my_field_accessor(self) -> D => self.my_class
class D
def mut my_field: Int := 10
def mut a := A()
# should raise an error but doesn't
a.c.my_field_accessor().my_field := 20
Expected behaviour
We expect a type error because while my_field is mutable, the class in which it is contained, C, stored as my_class within c, is not.
Additional context
It might be best to take a look at Rust's borrow system and take inspiration from there.
In Mamba, we could:
Always return a deep copy of a field using the deepcopy method. This would have to be clearly communicated with users.
This is perhaps not the best alternative, as a lot of people coming from Python might run into unexpected behaviour if they often reassign to variables returned values of functions.
E.g. using getters directly: my_object.get_my_field().inner_field <- 10, will no longer have any effect as we reassign to a deep copy which is immediately discarded.
Give a warning if trying to return a mutable field of self (perhaps this should be a lint), and give an error when trying to return an immutable field of self, meaning that it explicitly has to be copied using deepcopy.
This would, however, result in a lot of unnecessary deepcopy invocations, which while useful in low-level languages, seems rather verbose for a more Python type language.
Similar to above, except in the cases where we return a field of self, we preserve it's mutability when recursively checking calls.
If we do not access a field of self, we take the variable to be mutable.
This, I believe, most closely aligns with the philosophy of Mamba, since it reduces the verbosity, but still preserves the correctness of our type checker.
See Rust's borrow checker, which addresses an issue similar to ours (though their solution is more verbose due to Rust's low level nature).
Description of Bug
Due to Python returning shallow copies from functions, we can reassign to fields of values even if the values themselves are immutable.
This behaviour was observed in #181, which contains an ignored test
assign_to_inner_non_mut3
.How to Reproduce
Expected behaviour
We expect a type error because while
my_field
is mutable, the class in which it is contained,C
, stored asmy_class
withinc
, is not.Additional context
It might be best to take a look at Rust's borrow system and take inspiration from there. In Mamba, we could:
my_object.get_my_field().inner_field <- 10
, will no longer have any effect as we reassign to a deep copy which is immediately discarded.deepcopy
. This would, however, result in a lot of unnecessarydeepcopy
invocations, which while useful in low-level languages, seems rather verbose for a more Python type language.See Rust's borrow checker, which addresses an issue similar to ours (though their solution is more verbose due to Rust's low level nature).