Argentum has no built-in Arrays
Other languages have built-in arrays (and some even have built-in maps), but these built-in data structures quickly become insufficient. So the parallel containers from the standard libraries emerge. They quickly surpass and replace the built-in ones in everyday use:
- in C++ it's
Type[]
vsstd::array
(std::vector
etc.). - in Java it's
Type[]
vsArrayList
etc.
This creates mess and multiple incompatibilities. That's why Argentum does not have built-in arrays. Instead its arrays are just runtime library classes. And since all argentum classes are open to adding new methods and interfaces, different libraries can extend existing arrays without need of creating new ones.
using sys { Array }
// `arrays` module adds lots of extensions to Array classes
// including `each` and `resize`
using arrays;
a = Array(String).resize(10);
x = a[3];
a[0] := "Hello";
a.each(i \ log(i : "None");
Array is just an object.
- Operator
a[i]
is just a handy form of call to methoda.getAt(i)
, - Operator
a[i] := v
calls method a.setAt(i, v).
Array indexation produces optional<T> results
Result of array indexing is an optional, where none
value indicate the index-out-of-bounds condition.
fn doSomething(a Array(String)){
log(a[0]); // error: log expects String, while a[0] returns ?String
log(a[0] : "None"); // ok, operator `:` unwraps optional with "None" default
a[0] ? log(_); // ok, operator `?` unwraps optional LHS and calls RSH if it has value
}
As a result you cannot write code that doesn't handle indexation errors. And this handling is extremely simple.
Semantic of Arrays reflects the semantic of pointers
There are three standard arrays of pointers:
- Array - that holds @OwningPointers, has ownership over its mutable elements.
It models 1-parent-many-children relations in tree-like structure of ownership.
Examples: when paragraphs belong to a text block, when functions reside in a compilation unit, when controls live inside UI panel or window, there is always exists some Array-field in the parent object. - SharedArray - holds *SharedFrozenPointers. It shares immutable elements with other SharedArrays and scalar shared pointers.
It models many-owners-to-many-immutable-resources relation.
Examples: when a document holds a collection of icons or fonts, this document probably has somewhere in its field a SharedArray of pointers to immutable resources, that can be referenced not only from this document, but from other places too, but since these resources are immutable, it's ok. - WeakArray - stores &WeakReferences to objects stored elsewhere.
It models one-knows-about-many non-owning relations.
Example: a graph node stores the outgoing edges to the other nodes.
class Cat{
name = *"";
color = rgb(0, 0, 0);
init(aName *String, aColor Color) this { name:= aName; color := aColor }
}
cats = Array(Cat); // aggregating array of owning unique pointers to mutable Cats
cats.add(Cat.init("Bars", rgb(100, 0, 0)); // Ok, storing new instance
cats[0] ? cats.add(_) // Error, Cat returned by cats[0] already owned somewhere
cats[0] ? cats.add(@_) // Ok, we insert into array a copy of Cat
cats[1] ? _.name := "Lucky"; // we change the name of this exact copy/instance of cat
class Person {
name = *"";
// Person knows about cats, but cat can outlive Person
// So lifetimes of person and pet are independent,
// and as such it is not ownership but a association/relation.
// One to one relation is &WeakPointer, one to many - WeakArray
pets = WeakArray(Cat);
}
p = Person;
cats[0] ? p.pets.insert(_);
cats[4] ? p.pets.insert(_);
class Person {
// Person can have many nicknames,
// Nicknames are immutable strings, and as such they can be shared among many persons.
nickNames = SharedArray(String);
}
p.nickNames.add("Bars");
// Now the same immutable String is referenced by p.nickNames[0] and cats[0].name
There are no Arrays of temporary pin-pointers or arrays of conform pointers. Because arrays are objects and their elements play roles of their fields, but pin-pointers and conform pointers can't be stored in object fields.
Scalar fields of type @OwnPointer support "splice" operation a @= b
. Splice operation allows to extract an object from one hierarchy and insert it to another one without making copy. Of course it's performed will all necessary runtime checks to guarantee from loops in graph of ownership (details: reattachments). Array class has a spliceAt
method that does the same for array items.
Arrays have variable size
All arrays are initially created empty without any allocated space for objects. All arrays have methods that insert and delete elements and move groups of elements inside the container.
Immutable arrays
Arrays like all other objects in Argentum can be frozen and become shared-immutable instances. Thus Argentum has no separate "immutable array" classes.
Arrays of value types
Right now Argentum has one class Blob
that represents a sequence of bytes of variable size. These bytes can be accessed by indexes as 8-16-32-64-bit data.
Blob
has methods that insert, and delete byte ranges and copy them within single Blob
and between Blobs
. There are methods to byte-serialize utf8 characters in Blobs
and to create Strings out of Blobs' byte ranges.
Blobs can be frozen, shared and unfrozen (by creating separate mutable copy) with standard Argentum operators.
Blobs can be sub-classed with defining getAt
/setAt
methods to turn them in into generic byte-word-dword-qword-double-float-bit arrays. It is not done yet, but if needed, it can be implemented in pure Argentum.