5.3.4  Vectors

Vectors and arrays are the heart and soul of languages like Fortran. Open up any Fortran source file at random, and you will probably be looking at a do loop over a vector or array. Fortran compiler writers sweat blood trying to squeeze one percent more performance out of such loops.

Mythryl is not like that. The heart and soul of Mythryl is recursive processing of recursive datastructures like lists. Mythryl compiler writers sweat blood trying to squeeze a little more performance out of such recursive functions.

But Mythryl does have vectors, and occasionally they are just the right tool for the job.

The fundamentally interesting properties of vectors are:

Accessing the one-hundredth element in a list requires one hundred operations, stepping down the list, but accessing the one-hundredth element in a vector takes no longer than accessing the first element.

Vectors are like tuples in that they efficiently store a sequence of elements which may be accessed by slot number. The crucial difference is that tuple elements may be of different types, and tuple slots may be accessed using only constant slot numbers, whereas vector elements must all be of the same type, and may be fetched using slot index variables as well as slot index constants.

The fundamental operations of interest on vectors are

Here is a transcript of creating a three-element vector, accessing each of its elements, and then getting its length:

    linux$ my

    eval:  v = #[ 11, 22, 33 ];
    #[11, 22, 33]

    eval:  v[ 0 ];

    eval:  v[ 1 ];

    eval:  v[ 2 ];

    eval:  vector::length v;

Mythryl vectors are type-agnostic (“polymorphic” — literally, “many shaped”): They may contain elements of any type, so long as all the elements of a given vector are the same type. Here is the above example repeated with a vector of Strings:

    linux$ my

    eval:  v = #[ "one", "two", "three" ];
    #["one", "two", "three"]

    eval:  v[ 0 ];

    eval:  v[ 1 ];

    eval:  v[ 2 ];

    eval:  vector::length v;

You will often wish to construct a vector from elements computed on the fly at runtime. Most frequently you will have accumulated these in a list while computing them. The vector::from_list function does what you need in this case:

    linux$ my

    eval:  v = vector::from_list (reverse( [ "one", "two", "three" ] ) );
    #["three", "two", "one"]

Another frequent operation is to apply some function to all the elements of a vector and return a vector containing the results — the vector equivalent of the list map function. Here is an example where we add one to each element of a vector:

    linux$ my

    eval:  v = vector::from_list (1..10);
    #[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    eval:  w = vector::map  (\\ i = i + 1)  v;
    #[2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

The vector::apply function is just like the vector::map function except that no result value is constructed. It is useful when we are applying a function to the vector elements purely for its side effects:

    linux$ my

    eval:  v = #[ "q", "w", "e", "r", "t", "y" ];
    #["q", "w", "e", "r", "t", "y"]

    eval:  vector::apply print v;

If you are in a C-flavored frame of mind, you can do the same thing with an explicit for loop:


    v = #[ "q", "w", "e", "r", "t", "y" ];

    for (i = 0;  i < vector::length v;  ++i) {
        print v[i];

    print "\n";

Running the above yields:

    linux$ ./my-script

The v[i] notation for accessing the i-th element of a vector is readable for general use, but a nuisance if you want to pass the vector-get function around as an argument, say to use it in conjunction with functions like map. For cases like this, the vector-get operation is also available as vector::get:

    linux$ my

    eval:  v = vector::from_list (shuffle (1..10));
    #[10, 9, 8, 3, 4, 5, 6, 7, 2, 1]

    eval:  vector::get (v, 0);

A final useful function is vector::set. This constructs a new vector which differs from a pre-existing vector in one specified slot:

    linux$ my

    eval:  v = #[ 3, 1, 4, 1, 6 ];             # Make a vector.
    #[3, 1, 4, 1, 6]

    eval:  w = vector::set( v, 1, 222 );       # Make a new vector differing in one slot.
    #[3, 222, 4, 1, 6]

    eval:  v;                                  # The original vector is unchanged.
    #[3, 1, 4, 1, 6]

A number of other convenience operations are predefined on vectors, which we will cover in a later tutorial (peek at Vector if impatient), but the above is enough to get started with vanilla Vectors.

The other major vector flavor is Rw_Vector.

Vanilla vectors are immutable. Once created, they cannot be changed. Some people call such values pure. Values which can be changed after creation via side-effects are termed mutable or impure.

Mythryl programmers work with pure values when they can and impure values when they must.

Pure values result in code which is much easier to understand, since you never have to worry about pure values changing in unexpected ways at inconvenient moments, due perhaps to other threads running on other cores.

But sometimes you really do need to to modify the an existing value, perhaps because the algorithm requires it (e.g. many matrix algorithms), perhaps because you are precisely interested in broadcasting information to other threads in a multithread program.

For such cases Mythryl provides Rw_Vectors, which as the name suggests are writable as well as readable.

The Rw_Vector api is almost identical to the Vector api; the significant difference is just that the rw_vector::set operation modifies its argument vector in place:

    linux$ my

    eval:  v = rw_vector::from_list [ 3, 1, 4, 1, 6 ];
    [|3, 1, 4, 1, 6|]

    eval:  rw_vector::set( v, 1, 222 );

    eval:  v;
    [|3, 222, 4, 1, 6|]

We will have more to say about vectors later, but first it is time to discuss pattern matching.

Comments and suggestions to: