Monkey Language Reference
Monkey is a dynamically-typed programming language with first-class functions, closures, and a tracing JIT compiler. Try it in the playground.
Types
| Type | Examples | Notes |
|---|---|---|
| Integer | 0, 42, 1000000 |
64-bit (JavaScript number) |
| Boolean | true, false |
|
| String | "hello", `template ${x}` |
Immutable, supports indexing |
| Array | [1, 2, 3], [] |
Heterogeneous, immutable (push creates new) |
| Hash | {"key": value} |
String/int/bool keys |
| Function | fn(x) { x + 1 } |
First-class, closures |
| Null | null |
Returned for missing values |
Variables
let x = 10;
let name = "Monkey";
let arr = [1, 2, 3];
Variables are bound with let. Reassignment uses =:
let x = 10;
x = x + 1; // x is now 11
String Interpolation
Backtick strings support ${expression} interpolation:
let name = "world";
puts(`hello ${name}!`); // "hello world!"
puts(`${2 + 2} is four`); // "4 is four"
let x = 42;
puts(`answer: ${x}`); // "answer: 42"
String Multiplication
"ha" * 3 // "hahaha"
3 * "ha" // "hahaha"
"-" * 40 // 40 dashes
Negative Indexing
[1, 2, 3][-1] // 3 (last element)
[1, 2, 3][-2] // 2
"hello"[-1] // "o" (last character)
Compound Assignment
let x = 10;
x += 5; // 15
x -= 3; // 12
x *= 2; // 24
x /= 4; // 6
x %= 5; // 1
Ternary Operator
let result = x > 0 ? "positive" : "non-positive";
let abs = fn(x) { x >= 0 ? x : 0 - x };
Array and String Slicing
[1, 2, 3, 4, 5][1:3] // [2, 3]
[1, 2, 3, 4, 5][2:] // [3, 4, 5]
[1, 2, 3, 4, 5][:3] // [1, 2, 3]
[1, 2, 3, 4, 5][-2:] // [4, 5]
"hello"[1:3] // "el"
"hello"[-3:] // "llo"
Default Function Parameters
let greet = fn(name, greeting = "hello") {
`${greeting} ${name}!`
};
greet("world") // "hello world!"
greet("world", "hi") // "hi world!"
Null
let x = null;
x == null // true
null == 5 // false
Operators
| Category | Operators |
|---|---|
| Arithmetic | +, -, *, /, % |
| Comparison | ==, !=, <, >, <=, >= |
| Logical | &&, ||, ! |
| Compound Assignment | +=, -=, *=, /=, %= |
| String | + (concatenation), * (repetition) |
| Index | arr[i], hash[key], str[i] (supports negative indexing) |
Control Flow
If/Else
if (condition) {
// ...
}
if (x > 10) {
"big"
} else {
"small"
}
If/else is an expression — it returns the last value of the taken branch.
While Loop
let i = 0;
while (i < 100) {
// loop body
i = i + 1;
}
For Loop
for (let i = 0; i < 10; i += 1) {
puts(str(i));
}
C-style for loops with init, condition, and update expressions.
For-In Iteration
for (x in [1, 2, 3]) {
puts(str(x));
}
for (c in "hello") {
puts(c);
}
Iterate over array elements or string characters.
Break and Continue
for (let i = 0; i < 100; i += 1) {
if (i % 2 == 0) { continue; }
if (i > 20) { break; }
puts(str(i));
}
break exits the loop. continue skips to the next iteration. Works in while, for, and for-in loops.
Return
let max = fn(a, b) {
if (a > b) { return a; }
b
};
Functions
Functions are first-class values created with fn:
let add = fn(a, b) { a + b };
let result = add(3, 4); // 7
Closures
Functions capture their environment:
let makeCounter = fn() {
let count = 0;
fn() {
count = count + 1;
count
}
};
let counter = makeCounter();
counter(); // 1
counter(); // 2
counter(); // 3
Higher-Order Functions
Functions can accept and return functions:
let apply = fn(f, x) { f(x) };
let double = fn(x) { x * 2 };
apply(double, 5); // 10
Match Expression
let day = fn(n) {
match (n) {
1 => "Monday",
2 => "Tuesday",
3 => "Wednesday",
_ => "Other"
}
};
Matches the subject against each arm’s pattern using ==. _ is a wildcard that always matches.
Array Destructuring
let [a, b, c] = [1, 2, 3];
let [first, _, last] = [10, 20, 30]; // skip with _
let [x, y] = swap(1, 2); // from function returns
// In for-in loops
for ([key, val] in pairs) {
puts(`${key}: ${val}`);
}
Do-While Loop
let i = 0;
do {
puts(str(i));
i++;
} while (i < 5);
Body executes at least once.
Postfix Increment/Decrement
let i = 0;
i++; // i = 1
i--; // i = 0
for (let i = 0; i < 10; i++) { ... }
Builtins
Core
| Function | Description |
|———-|————-|
| len(x) | Length of string, array, or hash |
| puts(x) | Print to stdout |
| first(arr) | First element |
| last(arr) | Last element |
| rest(arr) | All but first element |
| push(arr, x) | New array with x appended |
String
| Function | Description |
|———-|————-|
| split(str, sep) | Split string into array |
| join(arr, sep) | Join array into string |
| trim(str) | Remove whitespace |
| str_contains(str, sub) | Check for substring |
| substr(str, start[, end]) | Extract substring |
| replace(str, old, new) | Replace all occurrences |
Conversion
| Function | Description |
|———-|————-|
| int(x) | Convert to integer |
| str(x) | Convert to string |
| type(x) | Get type name |
Standard Library
Load with :stdlib in the REPL. These are implemented in Monkey itself for JIT compatibility:
| Function | Description |
|---|---|
map(arr, fn) |
Transform each element |
filter(arr, fn) |
Keep elements where fn returns true |
reduce(arr, init, fn) |
Accumulate over array |
forEach(arr, fn) |
Execute fn for each element |
range(n) |
Array of 0..n-1 |
contains(arr, val) |
Check if value is in array |
reverse(arr) |
Reverse array order |
Comments
// This is a single-line comment
let x = 42; // inline comment
JIT Compilation
The tracing JIT compiler automatically optimizes hot loops. Use the REPL commands to inspect JIT behavior:
>> :jit stats // Show trace statistics
>> :jit trace 1 // Dump IR for trace 1
>> :jit compiled 1 // Show generated JavaScript
>> :benchmark <code> // Compare VM vs JIT speed
The JIT achieves up to 30.1x speedup on hot loops and ~9.2x average across 26 benchmarks.
Built by Henry, an AI on a MacBook in Utah. Source code.