Language tour
This tour is a quick, practical overview of mu’s core syntax and semantics.
Every snippet below is a complete program. Paste one into the playground to run it, and look any name up in the library reference.
Hello, mu
fn main() {
print("Hello World!")
}
main()
Variables and control flow
x := 10
if x > 5 {
print("x is big")
} else {
print("x is small")
}
i := 0
while i < 3 {
print(i)
i = i + 1
}
Functions and closures
A closure captures the variable, not a copy of its value, so it and the enclosing function see each other’s writes:
fn make_counter() {
n := 0
return fn() {
n = n + 1
return n
}
}
counter := make_counter()
print(counter())
print(counter())
That also means a callback can accumulate into a local of the function that created it, and a lambda can refer to itself:
fn totalise(items) {
total := 0
i := 0
while i < len(items) {
total = total + items[i]
i = i + 1
}
return total
}
fact := nil
fact = fn(n) {
if n <= 1 { return 1 }
return n * fact(n - 1)
}
Lists and maps
nums := [1, 2, 3]
nums[1] = 42
print(nums)
user := {"name": "ada", "age": 42}
print(user.name)
Numbers
Integers are 64-bit. 3.14 is a decimal literal — exact fixed-point, not
binary floating point — so the sum that goes wrong in most languages comes out
right here:
print(0.1 + 0.2) // 0.3 exactly
print(19.99 * 3) // 59.97
print(3.14 + 6) // 9.14 mixes with integers, either order
Division has to be told how many digits to keep, since a quotient like 22/7 does
not terminate. A bare / picks a default; decimal.ratio lets you choose the
scale and the rounding mode:
import "decimal"
print(1.0 / 3) // 0.333333333333333
print(decimal.ratio(22, 7, 5, "half_even")) // 3.14286
Operators on your own types
A map carrying an __ops table defines what the operators mean for its own
values, and __type names it:
Money := fn(cents) {
return {
"__type": "Money",
"cents": cents,
"__ops": {
"+": fn(a, b) { return Money(a.cents + b.cents) },
"str": fn(a) { return "$" + str(a.cents / 100) },
},
}
}
print(str(Money(500) + Money(250))) // $7
print(type(Money(500))) // Money
This is the only __dunder__ convention in mu, and it is opt-in. The host
consults __ops only where an operation would otherwise have failed, so
ordinary arithmetic pays nothing for it. Decimals are built entirely from this —
the language itself knows nothing about them.
Errors are values
import "io"
data := io.read_file("missing.txt")
if type(data) == "ERROR" {
print("read failed:", data)
}
Truthiness
Falsey values include false, nil, 0, empty strings, empty lists/maps, and empty buffers.
status := if "" { "truthy" } else { "falsey" }
print(status)
A type with an __ops["bool"] entry answers for itself, which is why 0.0 is
falsey rather than “a non-empty map, therefore true”.
Next steps
- Learn the execution model in the runtime guide.
- Explore the standard library.
- Read the specification for exact semantics.