Getting Started with EK9 · Appendix A
The Operator Reference
The Using Operators chapter grouped EK9's operators by the job they do. This appendix lists the whole set in one place, for looking things up.
The Default? column says whether the compiler can write the operator for you. A $\checkmark$ means
you may ask for it by name with default operator <symbol>, or get it along with all the others from
the blanket default operator. Everything else is yours to write, because it carries a decision the
compiler cannot guess. The defaultable set is small, and deliberately so: comparing, checking,
converting, hashing and copying are mechanical; adding, merging and promoting are not.
Comparing is special. A type decides its sameness once: <=> if it can be ordered, or == if it can
only be told equal or not. The compiler then generates the rest of the family from it — from <=>
it writes ==, <>, <, <=, > and >=; from == it writes <>. Those are marked generated
below: you never write them and never ask for them (E07181), so they can never disagree with the
<=>. And a defaulted <=> needs the fields to support it — a record whose fields cannot themselves
be ordered gets == compared field by field, and no <.
Comparing
| Symbol | Name | What it does | Default? |
|---|---|---|---|
<=> |
comparator | Negative, zero or positive: sorts before, same, after. | $\checkmark$ |
< |
less than | Is the left smaller? | generated |
<= |
less than or equal | Smaller, or the same? | generated |
> |
greater than | Is the left larger? | generated |
>= |
greater than or equal | Larger, or the same? | generated |
== |
equal | Are the two the same? | $\checkmark$ (or generated) |
<> |
not equal | Are the two different? | generated |
<~> |
fuzzy compare | How similar — a closeness score. |
The comparator <=> — often called the "spaceship" — is the root of the family: every other comparison
is generated from it. == is marked both ways: it is generated from a <=>, but a type that can only be
told equal (a point on a map has no "smaller") declares == as its sameness instead.
Note. You may write
!=in an expression as a C-style alias for<>(a != bmeansa <> b). But you never define the not-equal operator at all:<>is generated from the type's sameness, and writingoperator !=is a compile error (E07640, "use<>for the not equal operator"). So: use either symbol in an expression; define neither.
Arithmetic
| Symbol | Name | What it does | Default? |
|---|---|---|---|
+ |
add | Add, or join together. | |
- |
subtract | Take away. | |
* |
multiply | Multiply. | |
/ |
divide | Divide. | |
^ |
power | Raise to a power. | |
mod |
modulus | The modulus (always positive). | |
rem |
remainder | The remainder after division. | |
++ |
increment | Move on by one. | |
-- |
decrement | Move back by one. | |
! |
factorial | The factorial. | |
abs |
absolute | The size, ignoring sign. | |
sqrt |
square root | The square root. |
Note.
++and--differ from the rest of this table. They change the value in place and return nothing, so each is a statement on its own line,count++;+and-produce a new value instead. Because they change the value in place, on a constrained type++and--keep the value's own type and check its rule again (Constrained Types). To flip a number's sign, write a unary minus:-count.
Arithmetic that assigns
These do the sum and store the answer back, so they change the value they are used on.
| Symbol | Name | What it does | Default? |
|---|---|---|---|
+= |
add and assign | Add to, and keep the result. | |
-= |
subtract and assign | Take away from, and keep the result. | |
*= |
multiply and assign | Multiply by, and keep the result. | |
/= |
divide and assign | Divide by, and keep the result. |
Converting
| Symbol | Name | What it does | Default? |
|---|---|---|---|
$ |
to text | The human-readable form. | $\checkmark$ |
$$ |
to JSON | The JSON form, for crossing a boundary. | $\checkmark$ |
#? |
hashcode | A number standing for the value. | $\checkmark$ |
#^ |
promote | Become another type automatically. One step only. |
Asking about state
| Symbol | Name | What it does | Default? |
|---|---|---|---|
? |
is set | Does this hold a meaningful value? | $\checkmark$ |
empty |
is empty | Is there nothing in it? | |
length |
length | How long, or how many. | |
contains |
contains | Is that inside this? | |
matches |
matches | Does it match this pattern? |
Every type that holds fields must answer ? — it is the set/unset check the whole language rests on.
Copying and merging
| Symbol | Name | What it does | Default? |
|---|---|---|---|
:=: |
copy | Copy the other's contents into this one. | $\checkmark$ |
:^: |
replace | Replace this one's contents with the other's. | |
:~: |
merge | Merge the other into this one, filling gaps. |
For your own type the default :=: is a shallow copy: the copy's fields refer to the same objects
as the original's.
Logic and bits
| Symbol | Name | What it does | Default? |
|---|---|---|---|
~ |
not | Flip a true/false value. The not keyword reads the same in an expression. |
|
and |
and | True when both are true. | |
or |
or | True when either is true. | |
xor |
exclusive or | True when exactly one is true. | |
<< |
shift left | Shift the bits left. | |
>> |
shift right | Shift the bits right. |
The rest
| Symbol | Name | What it does | Default? |
|---|---|---|---|
#< |
prefix | The leading part of the value. | |
#> |
suffix | The trailing part of the value. | |
close |
close | Release whatever the value is holding — a file, a connection. | $\checkmark$ |
You never call close yourself — writing x.close() is a compile error (E81060). It is called for
you: a try closes the values it opened when it ends, and a type's default operator close closes the
closeable fields it holds.
The stream pipe, |, also counts as an operator; it joins the stages of a pipeline together
(Streams and Pipelines).
That is the complete list of operators you can give to a type of your own. You cannot add to it or invent a new symbol, and each entry has a settled meaning you are expected to keep. In return, an operator you meet in anyone's code is one you already understand.
Operators you use, but never define
A last handful belong to the language rather than to any one type. You cannot write your own version of these, because they are built on the set/unset idea that every EK9 value already has. You met them in Guards and Safe Code.
| Symbol | Name | What it does |
|---|---|---|
?: |
Coalesce (Elvis) | a ?: b — use a if it is set, else b. |
<? |
comparison coalescing (lesser) | The smaller of the two. |
>? |
comparison coalescing (greater) | The larger of the two. |
:=? |
assign-if-unset | Assign only if the variable is unset. |
?: checks that a value is genuinely set, and it is the one to use. (EK9 has no ?? operator, as in
JavaScript, C# or Swift: EK9 is designed so that a value is never absent, though it may be unset, so there
would be nothing for ?? to test. Writing ?? is not allowed - it is a compile error, E01096 - and ?: is what you want.)
The two comparison-coalescing operators (<? and >?) share one rule: a set value is always
preferred to an unset one, and only when both are set does the comparison actually happen. That is what
makes them safe on values which may not have arrived yet — exactly where a plain < would hand you a
meaningless answer. When the two values are equal, both give back the left one; swap the two sides
if you want the right one.
In the EK9 reference: EK9 Operators