With the knowledge of values and how they are managed, let's look at some ways that they can be manipulated.
Almost all of the basic operators are actually defined as methods on the types of objects. For example, the expression
1 + 2 calls the
+ method on the value
2 as an argument. This method-based approach means that these operators can be overridden on any type at any time.
The examples in this section will almost exclusively use literal values to clearly show the types involved in the operations, but all of the operators can be used with variables or sometimes even types or modules instead with the same effects.
Most primitive values define the basic arithmetic operations,
%. Some examples:
Collections also implement some operations like
For Maps, keys are unique, so adding two Maps that contain the same key will give the key in the result the value of the second Map. For example:
This can be useful for quickly applying default values to a Map, but may also lead to some unexpected behavior if it is not understood beforehand.
Most value types define a number of other mathematic operations, such as
List#-. There are too many to cover comprehensively in this guide, so be sure to check the documentation for a more complete look at the operations that are available.
To access individual values within a collection, Myst provides access notation via square braces (
]) with an index argument between them:
Lists use zero-based indexing, meaning the first element of a List is at position
0, and the last element of the List is at position
n is the number of elements in the List.
In a List, the index argument is generally only allowed to be an Integer. For Maps, the index can be any type, but will generally be a Symbol, following the preference that Map keys should use Symbols.
In any case, if the element at the given index does not exist (e.g., the key is not in the Map, of the index is past the end of a List), the access will return
It is also possible to directly assign an individual element of a collection using access-assignment notation. This looks just like a collection access used as the target of an assignment. For example:
Notice that this directly modifies the collection itself, but is not considered an assignment to the collection.
Myst provides two native logical operations, "or" (
||) and "and" (
&&). These operate based on the truthiness of the first operand. The result of a logical operation is the value that determined the action taken by the operation - no conversions to Booleans or other operations are done to the value.
All values are considered truthy unless they are either
"" are all consider truthy.
Logical operations in Myst are also short-circuiting, meaning that the right side operand will only be evaluated if the left side operand does not satisfy the operation. For an Or operation, that is when the left side operand is falsey, or truthy for an And operation.
Below are some examples of logical operations and the results they would yield.
It's worth noting that since there are two falsey values, the result of a failed logical operation (one that is not satisfied) can be either
false, but no other value. For example:
Unlike most other operators in Myst,
&& are native to the language, and cannot be overridden. Operators that are overridable have a receiver (the left) and an argument (the right), but the logical operators are defined by the two operands equally. Because of that, it does not make sense for logical operations to be overridable.
Myst provides all of the common comparison operators:
>. These operations should always return a boolean value, but can be changed by overridden behavior.
All types define
!=. These are also the only comparison operations that booleans and
nil define by default.
An important thing to note in the example above is that
nil will only ever be equal to
nil. The fact that
false are both falsey does not apply to equality operations.
Equality operations are always based on values rather than object ids, as in Java and other similar languages. That means there should never be a concern about using
.eql or other special methods instead of
== for things like Strings.
The remainder of the comparison operators are implemented selectively for each type. For numeric types, the default implementations for all of these operators all perform their mathematic definition:
Operations between a Float and an Integer operand on either side will also work as expected.
Collection types also implement
<= to check whether the left side is a subset of the right side. Specifically,
< is a proper subset check, meaning the right side must be a larger collection than the left side, while
<= also allows the collections to be equal. The order of elements in the collection is not important for either of these operations.
Unary operations are operations with a single operand (the receiver), and are always written as prefixes for the operand. The three unary operations supported by Myst are
*, any combination of which can be chained together.
! performs a logical "not" on the operand. Truthy values become
false, while falsey values become
true. The result is always a Boolean. For example:
The last line above is an example of a "double not". This is a common pattern for quickly getting the truthiness of a value as a Boolean.
The unary version of
- is a negation. For numeric types, this is essentially equivalent to the binary operation with
0 as the receiver (i.e.,
-2 is the same as
0 - 2).
- can be chained to perform "double negation", though this is not immediately useful with any of the native types.
* is known the "splat" operator. Usage of the splat operator goes far beyond unary operation usage, and will be further explained in later sections, but the general idea of the operation is to return a List representation of the operand.
Map#* returns a List of 2-element Lists with keys as the first elements and values as the second elements. For example:
The splat operator for a List (at least as a unary operation) will simply return the List itself.
There generally are not many use cases for the splat operator as a unary operation. It is much more commonly used in the special cases described later on, namely in pattern matching and expanding arguments for functions.
As mentioned in the previous chapter, Myst provides a shorthand to perform an operation on a variable and assign it back into the same variable, known as operational assignment.
Operational assignments take the form
op is the operator to apply before re-assigning the variable. The semantic expansion of operational assignments goes from
a op= b to
a = a op b. Some examples:
Operational assignment works for every binary operation shown in this chapter. However, the logical operators (
&&) have special behavior when written as operational assignments.
||=, referred to as "or-assignment" loosely expands from
a ||= b to
a || a = b, rather than the normal
a = a || b. This is a "loose" expansion, because
a does not have to exist before the assignment is written.
a ||= b will always assign
a, so long as the
a is not a truthy value. If
a does not yet exist, it is considered falsey by the assignment and created as a new variable with the value of
If the value of
a is truthy, the assignment will not occur, and the existing value for
a will be returned as the result.
A consequence of how
||= works means that the target is guaranteed to exist after the assignment, making it useful for ensuring default values for variables that may not exist beforehand.
&&= ("and-assignemnt") acts exactly like
||=, but with an
&& operation instead. In this case, the left side must be truthy for the assignment to occur.
The last two lines above may be a little surprising - one of the few cases of potentially unintuitive behavior in Myst. As mentioned above,
&&= acts exactly like
||=, and a consequence of
||= is that the target will always exist after the operation is completed.
However, since the assumed value of a non-existent variable is falsey, the assignment in an
&&= operation is not guaranteed to happen. Logically, this makes sense, but practically it can become annoying. For example, consider the code required if
&&= did not always initialize the target variable:
Here, we're trying to say that if
z does not exist, create it with a
nil value, then do the and-assignment. But, as you may have picked up, this actually won't always work, because if
z exists beforehand but is falsey, the or-assignment will occur, potentially changing the value of
z (if it was
false, it would become
To avoid this extra line that is still not guaranteed to work, Myst defaults the target variable to
nil, which will ensure that the assignment does not occur (since a non-existent value is considered falsey), but will also ensure that the target variable is assigned after the operation is completed (matching the semantics of