Builtin Collections
Claro also rounds out its builtin types with a small set of convenient collection types that allow you to manipulate many values using a single variable. These are provided as builtins for your convenience, but their implementations have been hand selected to cover the majority of your general purpose programming use cases.
Ad-Hoc Declarations
Unlike many other languages (e.g. Java/C++/etc.) that require a formal declaration of any type before it can be instantiated, Claro's builtin collections can all be simply instantiated at will as if the type already exists. For example, any struct-like collection of named fields in Java would first require the declaration of a class, and potentially the declaration of other things like a constructor, hashCode() and equals() implementations. In Claro, you simply skip all the boilerplate.
For example, the following Claro procedure declares a struct {row: int, col: int}
inline as the function's return type
and doesn't need any top-level declaration of that type before it's used:
Fig 1:
function findInNestedList<T>(l: [[T]], t: T) -> struct {row: int, col: int} {
var r = 0;
for (row in l) {
var c = 0;
for (elem in row) {
if (elem == t) {
return {row = r, col = c}; # <-- Just instantiate the struct.
}
++c;
}
++r;
}
return {row = -1, col = -1};
}
[[1, 2],
[3, 4],
[5, 6]]
|> findInNestedList(^, 4)
|> print(^);
Output:
{row = 1, col = 1}
Mutability
All of Claro's builtin collection types come in either a mutable or immutable variant - by default, Claro will assume that any collection literals are intended to be immutable.
Fig 2:
var l = [1, 2, 3]; # Immutable list of ints.
print(l);
# The below line would be illegal as `lists::add` expects a mutable list.
# lists::add(l, 4);
Output:
[1, 2, 3]
The following example demonstrates initialization of a mutable list of integers:
Fig 3:
var l = mut [1, 2, 3];
print("Before: {l}");
lists::add(l, 4); # <-- Mutation happens here.
print("After: {l}");
Output:
Before: mut [1, 2, 3]
After: mut [1, 2, 3, 4]
Mutability Annotations are Shallow
Claro's mutability annotations are shallow by design so that you maintain fine-grained control over creating arbitrarily complex nested data structures that mix mutability and immutability as needed. The following examples demonstrate different combinations of nested mutability annotations:
This example demonstrates a mutable list whose elements are immutable lists.
Fig 4:
var l: mut [[int]] = mut [];
for (i in [1, 2, 3]) {
lists::add(l, [i, i]); # <-- Add an immutable list to the mutable list.
}
print(l);
Output:
[0.001s][warning][perf,memops] Cannot use file /tmp/hsperfdata_runner/6 because it is locked by another process (errno = 11)
mut [[1, 1], [2, 2], [3, 3]]
This example demonstrates an immutable list whose elements are mutable lists.
Fig 5:
var l: [mut [int]] = [mut [], mut [], mut []];
var i = 1;
for (mutList in l) {
lists::add(mutList, i); # <-- Append an int to this inner mutable list.
lists::add(mutList, i++);
}
print(l);
Output:
[mut [1, 1], mut [2, 2], mut [3, 3]]
Data Race Safety via Deep Immutability
See the Concurrency section in this book for more details on how Claro will statically leverage knowledge of whether a type is deeply immutable or not to prevent unsafe data races.