Beginner

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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Pull the image
docker pull codearchaeology/vale:0.2

# Compile and run the arithmetic example
docker run --rm -v $(pwd):/app -w /app codearchaeology/vale:0.2 \
  bash -c '/opt/vale/valec build mymod=operators.vale --output_dir build && ./build/main'

# Compile and run the comparison example
docker run --rm -v $(pwd):/app -w /app codearchaeology/vale:0.2 \
  bash -c '/opt/vale/valec build mymod=comparison.vale --output_dir build && ./build/main'

# Compile and run the logical operators example
docker run --rm -v $(pwd):/app -w /app codearchaeology/vale:0.2 \
  bash -c '/opt/vale/valec build mymod=logical.vale --output_dir build && ./build/main'

# Compile and run the math functions example
docker run --rm -v $(pwd):/app -w /app codearchaeology/vale:0.2 \
  bash -c '/opt/vale/valec build mymod=math_functions.vale --output_dir build && ./build/main'

# Compile and run the precedence example
docker run --rm -v $(pwd):/app -w /app codearchaeology/vale:0.2 \
  bash -c '/opt/vale/valec build mymod=precedence.vale --output_dir build && ./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 in a mod b), not %.
  • Logical operators are words — Use and, or, and not rather than &&, ||, and !; each also has a function form like and(x, y).
  • Integer division truncates17 / 5 is 3; operate on floats when you need the fractional part, since Vale performs no implicit conversions.
  • Comparisons return bool — Every comparison (==, !=, <, >, <=, >=) yields a bool that can be printed or stored in a binding.
  • Some operations are functionsabs, signum, min, and max live in stdlib.math, keeping the core operator set small.
  • Precedence is conventional* and / bind tighter than + and -, arithmetic binds tighter than comparison, and comparison binds tighter than and/or; use parentheses to be explicit.
  • Reassignment uses set — Combining and storing a new value requires a mutable binding (name!) and the set keyword, 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
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining