This documentation is also published as Markdown for efficient machine reading: the whole site is indexed at /llms.txt, and every page has a clean Markdown copy at the same URL with .md appended. These are generated from the same source and cost far fewer tokens to read than this rendered HTML.

Skip to main content Skip to navigation
Language

Operators

Precedence

Tightest first. An operator binds its operands more tightly than anything below it in this table.

Level Operators Groups
15 a[i] left
14 f(x) left
13 a.b  a::b left
12 !a  -a  +a right
11 *  /  % left
10 +  - left
9 is left
8 as left
7 <  <=  >  >= left
6 ==  != left
5 && left
4 \|\| left
2 a ? b : c right
1 =  +=  -=  *=  /=  %= right

Parentheses group, and a grouped expression is whatever is inside it.

Important

as and is bind looser than arithmetic, which is not how they read. Both take a type on the right rather than an expression, so everything the arithmetic operators have already claimed is the left operand.

a + b as i64 is (a + b) as i64, not a + (b as i64).

mew
use std;
  
let a: i32 = 7;
let b: i32 = 2;
  
let together = a + b as i64;   // (a + b) as i64
let separate = a + (b as i64); // the other reading, written out
  
println($"{together} {separate}");

The two answer the same here, but they do not in general: the cast applies to a different value, so a sum that overflows an i32 overflows before it widens. Write the parentheses when the difference matters.

Member access binds tighter than unary, so -point.x negates the field rather than the value. A call binds tighter than the access it is part of, which is why value.doubled() reads as one thing.

Arithmetic

Operator On Produces
* / % - two numbers their coercion
+ two numbers their coercion
+ two strings string
+ a string and a char string
mew
use std;
  
println($"{7 * 6}");
println($"{7 / 2}");
println($"{7 % 2}");
println("Hello, " + "world");
println("x" + 'y');

+ joins text only for those two cases. To put a number or a bool into a string, interpolate it.

mew
let text = "count: " + 3;

Comparison

<, <=, > and >= compare two numbers and answer a bool. A char takes part as its code point.

mew
use std;
  
println($"{1 < 2}");
println($"{'a' < 'b'}");

== and != work on the numeric types, string, bool and char, the types that have a value to compare.

mew
use std;
  
let name = "Ada";
  
println($"{1 == 1}");
println($"{name == "Ada"}");
println($"{true != false}");
println($"{'x' == 'x'}");

They do not work on a type you declare, on a union, on an array, or on any. There is no structural comparison and no way to give a type one.

mew
pub type Point {
    pub field x: i32;
}
  
let a = new Point { x: 1 };
let b = new Point { x: 1 };
let same = a == b;

To compare two values of your own type, write a method that says what comparing them means.

mew
use std;
  
pub type Point {
    pub field x: i32;
    pub field y: i32;
}
  
impl Point {
    pub fn equals(other: Point) -> bool {
        return self.x == other.x && self.y == other.y;
    }
}
  
println($"{new Point { x: 1, y: 2 }.equals(new Point { x: 1, y: 2 })}");

To ask which case a union holds, match is what asks.

Logical

&&, || and ! work on bool and answer one. && and || short circuit; everything else evaluates both sides, left before right, before the operator is applied.

mew
use std;
  
let ready = true;
let done = false;
  
println($"{ready && !done}");
println($"{done || ready}");

Unary

Operator On Produces
! bool the negation
- a number the negation
+ a number the operand unchanged

There is no negative literal, so -1 is this - applied to 1. That matters only where it changes the reading: -a.b is -(a.b).

mew
use std;
  
let value = 5;
  
println($"{-value}");
println($"{+value}");
println($"{!(value > 10)}");

Type operators

is asks what a value is and answers a bool. as performs a conversion that is not implicit. Both take a type on the right, and both bind looser than arithmetic.

mew
use std;
  
let boxed: any = 32;
  
if boxed is i32 {
    println($"{boxed as i32}");
}

Conditional

a ? b : c produces one of two values. The condition is a bool, and exactly one of the two branches is evaluated.

mew
use std;
  
let count = 3;
  
println(count == 1 ? "one" : "many");
println($"{count < 0 ? -count : count}");

It groups to the right, so a chain of them reads as a run of tests.

mew
use std;
  
let count = 3;
let name = count == 0 ? "zero" : count == 1 ? "one" : "many";
  
println(name);

The two branches decide the type between them. Either they already agree, or one of them converts implicitly to the other and that one is the type of the whole expression. null brings no type of its own, so the other branch decides.

mew
use std;
  
let wide: i64 = 1 > 0 ? 1 : 2;
let name = 1 > 0 ? "mew" : null;
  
println($"{wide} {name}");

Branches with nothing in common are an error.

mew
let mixed = 1 > 0 ? 1 : "one";

Everything except assignment binds tighter, so a comparison in front of the ? is the condition rather than part of it. Writing one as a statement is an error: it produces a value and a statement discards it, so if is what runs one of two statements.

mew
use std;
  
let ready = true;
  
ready ? println("saved") : println("discarded");

Assignment

= and the compound operators are the loosest of all, and they group right to left. Assignment is an expression that produces the value it assigned, which Assignment covers.

What is not here

There is no bitwise operator, no shift, no increment or decrement, and no way to give a type an operator of its own. A method is how a type says what an operation on it means.

Mew is a programming language under construction.