A statically typed language with a high-performance stack-based Virtual Machine written in Go,
created by Estêvão Fonseca. Structs, dynamic arrays, maps, generics, closures, modules and
Go-style concurrency.
Arrays, maps and structs are independent values at any depth —
ref is the only sharing mechanism, and copy-on-write keeps the copies lazy.
🦉 Mascot: Our purple owl symbolizes
wisdom and elegance in software development.
print("Hello from Noxy!")
// Typed variables and f-strings
let x: int = 10
let y: int = 20
print(f"Sum: {x + y}")
// Structs are values
struct Product
id: int
name: string
price: float
end
let product: Product = Product(1, "Laptop", 2500.50)
print(f"Product: {product.name}")
Every variable is declared with a type and keeps it. Primitives, structs, arrays, maps,
exact function types and module-qualified types such as io.File are checked
at compile time; any and bare func mark the explicit dynamic
boundaries.
Arrays, maps and structs are independent values at any depth: assignment, calls, container
reads and channels never alias. Copy-on-write makes the copy free until the first write,
and == compares composites by content.
One mechanism for sharing storage: ref. In-place mutation across calls,
self-referencing structures, channels and routines — never hidden aliasing.
Generic functions and structs (func first<T>,
struct Stack<T>) are monomorphized at compile time and always
instantiated by inference — zero runtime cost, no explicit instantiation syntax.
Exact function types (func(int) -> int), anonymous functions, closures
that capture variables, and functions as arguments and return values.
Go-style routines and channels — spawn, make_chan,
when/case select — plus supervised tasks
(spawn_task/task_await) that report results and failures as
data. Values passed by argument or channel are race-free by construction.
Runtime errors for bugs, result structs for untrusted data. call_result turns
a failure into a value at the boundary, and defer runs cleanup in LIFO order
on every exit path.
use m, use m as alias, use m select f, T. Module state
is read-only from outside, struct identity is nominal across modules, and a Git-based
package manager installs dependencies with noxy --get.
io, strings, time, sys, net, http, json, crypto, sqlite, rand and errors — on a bytecode compiler and stack-based VM written in Go, with an interactive REPL, diagnostics on stderr and proper exit codes.
let x: int = 42
let pi: float = 3.14
let name: string = "Noxy"
let active: bool = true
let data: bytes = b"hello"
print(f"{name}: x = {x}, active = {active}")
print(fmt("pi = %.2f", pi)) // pi = 3.14
// Composites are values: no aliasing
let a: int[] = [1, 2, 3]
let b: int[] = a
b[0] = 99
print(a[0]) // 1
// ref is the only sharing mechanism
let alias: ref int[] = ref a
alias[0] = 99
print(a[0]) // 99
// Dynamic arrays
let nums: int[] = []
append(ref nums, 10)
append(ref nums, 20)
print(length(nums), pop(ref nums), contains(nums, 10))
// Fixed size
let fixed: int[5] = [1, 2, 3, 4, 5]
let zeroed: int[100] = zeros(100)
print(fixed[4], length(zeroed)) // 5 100
// range is a builtin (no import), Python semantics
for i in range(10, 0, -3) do print(i) end // 10 7 4 1
let scores: map[string, int] = {"Alice": 100, "Bob": 95}
scores["Carol"] = 88
print(has_key(scores, "Alice")) // true
print(scores["Carol"]) // 88
for person in scores do
print(person, scores[person])
end
struct Person
name: string
age: int
active: bool
end
let person: Person = Person("John", 25, true)
person.age = 26
// A copy never reaches the original
let copy: Person = person
copy.age = 99
print(person.age) // 26
func add(a: int, b: int) -> int
return a + b
end
// Exact function types
func apply(f: func(int) -> int, v: int) -> int
return f(v)
end
// Closure capturing a variable
let factor: int = 3
let triple: func(int) -> int = func(x: int) -> int
return x * factor
end
print(add(2, 3), apply(triple, 5)) // 5 15
struct Stack<T>
items: T[]
end
func push<T>(s: ref Stack<T>, item: T)
append(ref s.items, item)
end
func peek<T>(s: Stack<T>) -> T
return s.items[length(s.items) - 1]
end
// T is always inferred — here from the annotation
let ints: Stack<int> = Stack([])
push(ref ints, 10)
push(ref ints, 20)
print(peek(ints)) // 20
let x: int = 10
if x > 10 then
print("greater")
elif x == 10 then
print("exactly ten")
else
print("smaller")
end
let i: int = 0
while i < 3 do
i = i + 1
end
for item in ["a", "b", "c"] do
print(item)
end
struct Node
value: int
next: ref Node
end
func push_back(node: ref Node, value: int)
if node.next == null then
let fresh: Node = Node(value, null)
node.next = ref fresh // rebind the field
else
push_back(node.next, value)
end
end
let head: Node = Node(1, null)
push_back(ref head, 2)
print(head.next.value) // 2
use strings
use time as t
use io
print(strings.to_upper("hello")) // HELLO
print(t.now() > 0) // true
// Structs of a module are qualified types
let info: io.FileInfo = io.stat("notes.txt")
print(info.exists)
// io, strings, time, sys, net, http,
// json, crypto, sqlite, rand, errors
use time
use sys
print(time.now()) // unix timestamp
print(length(sys.argv())) // command-line args
print(sys.version) // v0.19.0
let user: map[string, any] = {"name": "Ana", "age": 30}
print(json_dumps(user)) // {"age":30,"name":"Ana"}
func worker(id: int, out: chan string)
chan_send(out, f"hello from {id}")
end
let out: chan string = make_chan(0)
spawn(worker, 1, out)
print(chan_recv(out)) // hello from 1
// Supervised task: outcome as data
let task: any = spawn_task(worker, 2, out)
print(chan_recv(out)) // hello from 2
let outcome: map[string, any] = task_await(task)
print(outcome["status"]) // ok
let a: chan string = make_chan(1)
let b: chan string = make_chan(1)
chan_send(a, "from a")
// Runs the first ready case, exactly once
when
case msg = chan_recv(a) then
print(msg) // from a
case chan_recv(b) then
print("from b")
default
print("nothing ready")
end
use errors select *
func parse(text: string) -> int
return to_int(text) // raises on bad input
end
// call_result turns a runtime failure into data
let r: CallResult = call_result(parse, "abc")
if r.ok then
print(r.value)
else
print("invalid:", r.failure.message)
end
func work()
defer print("cleanup runs last")
print("working")
end
work()
A basic example showing the fundamental syntax of the Noxy language.
print("Hello from Noxy!")
// Basic operations
let x: int = 10
let y: int = 30
print(f"Sum: {x + y}")
// Struct example
struct Product
id: int
name: string
price: float
end
let product: Product = Product(1, "Laptop", 2500.50)
print(f"Product: {product.name}")
print(fmt("Price: %.2f", product.price))
Arrays, maps and structs are independent values at any depth. ref is the only
way to share storage, and copy-on-write means the copy costs nothing until someone
writes.
struct Counter
hits: int[]
end
// No 'ref': the callee gets an independent value at any depth.
func touch(c: Counter)
c.hits[0] = 999
end
// With 'ref': the callee shares the caller's storage.
func bump(c: ref Counter)
c.hits[0] = c.hits[0] + 1
end
let counter: Counter = Counter([1, 2, 3])
touch(counter)
print(counter.hits[0]) // 1 - the copy was mutated, not the original
bump(ref counter)
print(counter.hits[0]) // 2 - ref shares the original
let backup: Counter = counter
bump(ref counter)
print(backup.hits[0]) // 2 - assignment already copied
print([1, 2] == [1, 2]) // true - composites compare by content
Generic functions and structs are monomorphized at compile time. There is no explicit
instantiation syntax: type parameters are always inferred from the arguments or from the
annotation of the receiving let or return type.
struct Pair<K, V>
key: K
value: V
end
func swap<K, V>(p: Pair<K, V>) -> Pair<V, K>
return Pair(p.value, p.key)
end
func largest<T>(items: T[]) -> T
let best: T = items[0]
for item in items do
if item > best then
best = item
end
end
return best
end
let p: Pair<string, int> = Pair("answer", 42)
let s: Pair<int, string> = swap(p)
print(s.key, s.value) // 42 answer
print(largest([3, 9, 4])) // 9
print(largest(["pear", "apple"])) // pear
A binary search tree with in-order traversal. Children are ref Node fields:
a null field is forwarded as a null reference, a filled one shares the node — no subtree
is ever copied.
struct Node
data: int
left: ref Node
right: ref Node
end
func insert(node: ref Node, value: int)
if value < node.data then
if node.left == null then
let fresh: Node = Node(value, null, null)
node.left = ref fresh
else
insert(node.left, value)
end
else
if node.right == null then
let fresh: Node = Node(value, null, null)
node.right = ref fresh
else
insert(node.right, value)
end
end
end
func in_order(node: ref Node)
if node == null then
return
end
in_order(node.left)
print(node.data)
in_order(node.right)
end
let root: Node = Node(50, null, null)
insert(ref root, 30)
insert(ref root, 70)
insert(ref root, 20)
insert(ref root, 40)
in_order(ref root) // 20 30 40 50 70
A singly linked list with insertion and printing. Every function that must reach the
caller's list takes it by ref; the new node is a variable so the
next field can be rebound to it, and current = current.next
moves the cursor without copying the tail.
struct Node
value: int
next: ref Node
end
// 'ref' shares the caller's node; without it the callee gets a copy.
func push_back(node: ref Node, value: int)
if node.next == null then
let fresh: Node = Node(value, null)
node.next = ref fresh
else
push_back(node.next, value)
end
end
func print_list(node: ref Node)
let current: ref Node = node
while current != null do
print(current.value)
current = current.next
end
end
let list: Node = Node(10, null)
push_back(ref list, 20)
push_back(ref list, 30)
print_list(ref list) // 10 20 30
A hashmap with string keys, a polynomial hash function and separate chaining. Walking a
bucket chain through ref keeps the traversal free of copies — reading a slot
into a plain variable would give you a copy to mutate.
struct Entry
key: string
value: string
next: ref Entry
end
let buckets: Entry[16]
func hash(key: string, size: int) -> int
let h: int = 0
for c in key do
h = (h * 31 + ord(c)) % size
end
return h
end
func put(key: string, value: string)
let index: int = hash(key, 16)
if buckets[index] == null then
buckets[index] = Entry(key, value, null)
return
end
// Walk the chain through a ref: no copy, we mutate in place.
let node: ref Entry = ref buckets[index]
while node.next != null do
node = node.next
end
let fresh: Entry = Entry(key, value, null)
node.next = ref fresh
end
func get(key: string) -> string
let index: int = hash(key, 16)
if buckets[index] == null then
return ""
end
let node: ref Entry = ref buckets[index]
while node != null do
if node.key == key then
return node.value
end
node = node.next
end
return ""
end
put("owl", "purple")
put("lang", "noxy")
print(get("owl")) // purple
print(get("lang")) // noxy
print(get("missing")) // (empty)
Result pattern for operations whose failure is an expected outcome: the result struct
carries an ok flag the caller must branch on.
struct Result
is_ok: bool
value: int
error: string
end
func Ok(value: int) -> Result
return Result(true, value, "")
end
func Err(error_name: string) -> Result
return Result(false, 0, error_name)
end
func safe_divide(a: int, b: int) -> Result
if b == 0 then
return Err("DIVISION_BY_ZERO")
end
return Ok(a / b)
end
let result: Result = safe_divide(10, 0)
if result.is_ok then
print(f"Result: {result.value}")
else
print(f"Error: {result.error}")
end
Functions can capture variables from their outer scope, allowing for state encapsulation. The returned function has an exact type, so calls through it are checked at compile time.
func make_account(initial_balance: int) -> func(int) -> int
let balance: int = initial_balance
// Return a function that captures 'balance'
return func(amount: int) -> int
balance = balance + amount
return balance
end
end
let account: func(int) -> int = make_account(100)
print("Initial: 100")
print(f"Deposit 50: {account(50)}") // 150
print(f"Withdraw 30: {account(-30)}") // 120
Built-in HTTP/1.1 server with explicit limits and per-phase deadlines. The handler is a
plain function from HttpRequest to HttpResponse.
use http_server select *
func handler(req: HttpRequest) -> HttpResponse
if req.path == "/" then
return response_text("Hello from Noxy!")
elif req.path == "/json" then
return response_json("{\"status\": \"ok\"}")
end
return response_404()
end
// Create and start the server
let server: HttpServer = new_server("127.0.0.1", 8080)
print("Server running on http://127.0.0.1:8080")
// 'ref server' is required: the module boundary erases the exact
// signature, so the compiler cannot borrow the variable for you.
serve(ref server, handler)
Using channels to communicate between concurrent routines. Arguments to spawn
and values sent over a channel are independent values — data handed to another routine
is race-free by construction.
use time
func worker(id: int, c: chan string)
time.sleep(100)
chan_send(c, f"Message from {id}")
end
func main()
let c: chan string = make_chan(0)
spawn(worker, 1, c)
spawn(worker, 2, c)
// Receive 2 messages
print(chan_recv(c))
print(chan_recv(c))
end
main()
spawn_task launches a routine whose outcome the caller can observe:
task_await returns an envelope with the status, the value, or a structured
failure — a runtime error inside the task becomes data instead of taking the program
down.
func compute(n: int) -> int
if n < 0 then
return to_int("boom") // raises: captured by the task boundary
end
return n * n
end
let ok_task: any = spawn_task(compute, 12)
let bad_task: any = spawn_task(compute, -1)
let done: map[string, any] = task_await(ok_task)
print(done["status"], done["value"]) // ok 144
let failed: map[string, any] = task_await(bad_task)
print(failed["status"]) // error
let failure: map[string, any] = failed["error"]
print(failure["kind"]) // runtime
# Clone the repository
git clone https://github.com/estevaofon/noxy.git
cd noxy
# Build the project
go build -o noxy ./cmd/noxy
# Run a program
./noxy program.nx
# Interactive REPL
./noxy
# Or via go run
go run ./cmd/noxy program.nx
# Install a package from Git into noxy_libs/
./noxy --get github.com/user/repo@v1.0.0
# Then import it by module path
# use github_com.user.repo.module as m