Mutability Coercion on Copy
Claro's builtin copy(...)
function supports the ability to coerce the mutability of the data being copied. This is
primarily a matter of convenience to, in as many cases as possible, avoid Claro programmers to needing to manually write
custom copy implementations.
In order to convey that a mutability coercion is being requested, the return type of the copy(...)
call simply needs
to be constrained to some variant of the original value's type with mutability annotations updated as desired. Claro
will automatically codegen the appropriate logic to perform the requested copying + coercion. Note that this feature
relies on compile-time knowledge to ensure that any coercions would not actually invalidate any language semantics or
violate type system rules.
In the below example, a mut [[int]]
is copied, with the type simultaneously coerced to [mut [int]]
:
Fig 1:
var original = mut [[1, 2, 3], [4, 5], [6]];
var coercedCopy: [mut [int]] = copy(original);
type(coercedCopy);
print("Elements of `coercedCopy` match `original`?: {checkElementsMatch(original, coercedCopy)}\n");
# Now demonstrate that the lists are now independent.
print("Before mutation -");
print("original: {original}");
print("coercedCopy: {coercedCopy}\n");
original[0] = [-11111111];
coercedCopy[0][0] = -22222222; # <-- Outer list is now immutable, so modifying now mutable inner list.
print("After mutation -");
print("original: {original}");
print("coercedCopy: {coercedCopy}");
function checkElementsMatch(original: mut [[int]], coercedCopy: [mut [int]]) -> boolean {
# ...
var i = 0;
while (i < len(original)) {
var j = 0;
while (j < len(original[i])) {
if (original[i][j] != coercedCopy[i][j]) {
return false;
}
++j;
}
++i;
}
return true;
}
Output:
[mut [int]]
Elements of `coercedCopy` match `original`?: true
Before mutation -
original: mut [[1, 2, 3], [4, 5], [6]]
coercedCopy: [mut [1, 2, 3], mut [4, 5], mut [6]]
After mutation -
original: mut [[-11111111], [4, 5], [6]]
coercedCopy: [mut [-22222222, 2, 3], mut [4, 5], mut [6]]
Mutability Coercion Can Apply to Type Parameters of a User Defined Type
It's worth noting explicitly that Claro's newtype
declarations statically encode the mutability any collections they
happen to wrap. Claro's builtin copy(...)
cannot be used to invalidate these explicit mutability declarations,
for example:
Fig 2:
# There's nothing that can possibly be done to make Foo<T> wrap a mutable list.
newtype Foo<T> : [T]
However, parameterized User Defined Types may accept any concrete type in the place of the generic type parameter, and
Claro's builtin copy(...)
function can be used to do mutability coercion on these values.
The below example demonstrates setting the concrete type T = mut tuple<string, int>
meaning that Foo<T>
originally
wraps the type [mut tuple<string, int>]
. Then, upon copying the original value, the type is coerced to
T = tuple<string, int>
resulting in Foo<T>
wrapping the deeply immutable type [tuple<string, int>]
:
Fig 3:
newtype Foo<T> : [T]
var original: Foo<mut tuple<string, int>> = Foo([mut ("original", 1)]);
var coercedCopy: Foo<tuple<string, int>> = copy(original);
unwrap(original)[0][0] = "UPDATED";
unwrap(original)[0][1] = 2;
print("original: {original}");
print("coercedCopy: {coercedCopy}");
Output:
original: Foo([mut (UPDATED, 2)])
coercedCopy: Foo([(original, 1)])