Operators in Vale
Learn arithmetic, comparison, and logical operators in Vale, including the mod keyword, the and/or/not operators, operator precedence, and stdlib math helpers
Operators are how you combine values into expressions. As an imperative systems language with a static, strong, fully-inferred type system, Vale offers a familiar set of arithmetic and comparison operators — but with a couple of choices that set it apart from the C-family languages it resembles. The clearest example is modulo: instead of the % symbol, Vale spells it mod, as a keyword. Likewise, the boolean operators are the words and, or, and not rather than the &&, ||, and ! symbols.
Because Vale performs no implicit numeric conversions (covered in the previous tutorial), operators expect their operands to already be the same type. Integer division truncates toward zero, comparison operators always produce a bool, and reassigning a result back into a binding uses the set keyword you saw with mutable variables. Several operations that other languages bake into the syntax — absolute value, minimum, maximum, sign — are ordinary functions in Vale’s stdlib.math module instead.
This tutorial walks through arithmetic, comparison, and logical operators, the operator-like functions in the math library, and how precedence works when you combine them. Each example is a complete program you can compile and run with the same Docker image used throughout this series.
Arithmetic Operators
Vale’s arithmetic operators are +, -, *, and /, plus the mod keyword for the remainder. Integer division truncates the result toward zero — 17 / 5 is 3, not 3.4 — so if you need a fractional result you must operate on floating-point values.
Create a file named operators.vale:
import stdlib.*;
exported func main() {
a = 17;
b = 5;
// Arithmetic on integers
println("a + b = " + str(a + b)); // addition
println("a - b = " + str(a - b)); // subtraction
println("a * b = " + str(a * b)); // multiplication
println("a / b = " + str(a / b)); // integer division (truncates)
println("a mod b = " + str(a mod b)); // remainder
// Integer division throws away the fractional part. To keep it,
// operate on floats instead — there are no implicit conversions.
x = 7.0;
y = 2.0;
println("x / y = " + str(x / y)); // float division
}
Note that mod is a keyword, written between its operands just like +, so a mod b reads naturally. The str(...) function converts each numeric result to text for concatenation, exactly as it did in the variables tutorial.
Comparison Operators
Comparison operators compare two values of the same type and always return a bool. Vale provides the full set you would expect: ==, !=, <, >, <=, and >=. Because they yield a bool, their results can be printed directly or stored in a binding for later use.
Create a file named comparison.vale:
import stdlib.*;
exported func main() {
a = 17;
b = 5;
println("a == b : " + str(a == b)); // equal to
println("a != b : " + str(a != b)); // not equal to
println("a < b : " + str(a < b)); // less than
println("a > b : " + str(a > b)); // greater than
println("a <= b : " + str(a <= b)); // less than or equal
println("a >= b : " + str(a >= b)); // greater than or equal
}
Equality uses == (a single = is assignment at declaration time), and != is inequality. Since every comparison produces a bool, you can bind the result — b = (a < 17); — and reuse it, which is handy when the same condition is checked more than once.
Logical Operators
Vale’s boolean operators are the keywords and, or, and not. They operate on bool values and are the building blocks of conditions. Each one also has an equivalent function form — and(x, y) and or(x, y) — which can read more clearly when you are combining several conditions.
Create a file named logical.vale:
import stdlib.*;
exported func main() {
t = true;
f = false;
// Keyword operators
println("t and f : " + str(t and f)); // both must be true
println("t or f : " + str(t or f)); // either may be true
println("not t : " + str(not t)); // negation
// Logical operators combine comparison results directly
n = 4;
println("n > 2 and n < 5 : " + str(n > 2 and n < 5));
// Function forms behave identically to the keyword operators
println("and(t, f) : " + str(and(t, f)));
println("or(t, f) : " + str(or(t, f)));
}
Because comparison binds more tightly than the logical operators, n > 2 and n < 5 is read as (n > 2) and (n < 5) without needing parentheses — a detail revisited in the precedence section below.
Operator-Like Functions
Some operations that are operators or built-in functions in other languages live in Vale’s stdlib.math module. Importing stdlib.math.* brings in helpers like abs (absolute value), signum (the sign of a number: -1, 0, or 1), and the two-argument min and max.
Create a file named math_functions.vale:
import stdlib.*;
import stdlib.math.*;
exported func main() {
n = 5;
m = -3;
println("abs(m) = " + str(abs(m))); // absolute value
println("signum(n) = " + str(signum(n))); // sign of a positive number
println("signum(m) = " + str(signum(m))); // sign of a negative number
println("signum(0) = " + str(signum(0))); // sign of zero
println("min(n, m) = " + str(min(n, m))); // smaller of the two
println("max(n, m) = " + str(max(n, m))); // larger of the two
}
Treating these as functions rather than operators keeps Vale’s operator set small and unambiguous, and it means you discover them through the standard library the same way you discover any other function.
Operator Precedence and Assignment
When operators are mixed in one expression, precedence decides the grouping. Multiplication and division bind more tightly than addition and subtraction, and all arithmetic binds more tightly than comparison, which in turn binds more tightly than the logical operators. When in doubt, add parentheses to make the grouping explicit. Reassigning a computed result back into a binding uses set, and the binding must have been declared mutable with a trailing !.
Create a file named precedence.vale:
import stdlib.*;
exported func main() {
a = 2;
b = 3;
c = 4;
// '*' binds tighter than '+', so b * c happens first
println("a + b * c = " + str(a + b * c));
println("(a + b) * c = " + str((a + b) * c));
// Comparison binds tighter than 'and', so no parentheses are needed
println("a < b and b < c = " + str(a < b and b < c));
// Accumulate into a mutable binding using 'set'
total! = 0;
set total = total + a; // total becomes 2
set total = total + b * c; // adds 12, total becomes 14
println("total = " + str(total));
}
The mutable binding total! and the set keyword come straight from the variables tutorial: mutation is always explicit, so it is obvious at a glance where a value changes.
Running with Docker
The same image used for Hello World compiles and runs every example here. Each .vale file is passed to the compiler as a module via mymod=, and the resulting binary lands in build/main.
| |
The compiler prints progress for the frontend, backend, and linker stages; the lines after the final stage are your program’s output.
Expected Output
For operators.vale:
a + b = 22
a - b = 12
a * b = 85
a / b = 3
a mod b = 2
x / y = 3.5
For comparison.vale:
a == b : false
a != b : true
a < b : false
a > b : true
a <= b : false
a >= b : true
For logical.vale:
t and f : false
t or f : true
not t : false
n > 2 and n < 5 : true
and(t, f) : false
or(t, f) : true
For math_functions.vale:
abs(m) = 3
signum(n) = 1
signum(m) = -1
signum(0) = 0
min(n, m) = -3
max(n, m) = 5
For precedence.vale:
a + b * c = 14
(a + b) * c = 20
a < b and b < c = true
total = 14
Key Concepts
- Modulo is a keyword — Vale spells the remainder operator
mod(as ina mod b), not%. - Logical operators are words — Use
and,or, andnotrather than&&,||, and!; each also has a function form likeand(x, y). - Integer division truncates —
17 / 5is3; operate on floats when you need the fractional part, since Vale performs no implicit conversions. - Comparisons return
bool— Every comparison (==,!=,<,>,<=,>=) yields aboolthat can be printed or stored in a binding. - Some operations are functions —
abs,signum,min, andmaxlive instdlib.math, keeping the core operator set small. - Precedence is conventional —
*and/bind tighter than+and-, arithmetic binds tighter than comparison, and comparison binds tighter thanand/or; use parentheses to be explicit. - Reassignment uses
set— Combining and storing a new value requires a mutable binding (name!) and thesetkeyword, so state changes stay visible.
Next Steps
Continue to Control Flow to see how comparison and logical operators drive if/else decisions and while loops in Vale.
Running Today
All examples can be run using Docker:
docker pull codearchaeology/vale:0.2
Comments
Loading comments...
Leave a Comment