By Derek Corniello
- Simple yet powerful: Combines Go-like minimalism with Rust-inspired safety.
- Strong static typing: Helps catch errors early and ensures safer code.
- LLVM-powered: Fast compilation and native performance.
- Flexible memory management: Ease of use through reference counting.
- Extensible: Designed to evolve with features like traits, concurrency, and a standard library.
Check out the docs!
Mux provides multiple installation methods to suit different needs.
Use the official installer script:
curl -fsSL https://raw.githubusercontent.com/DerekCorniello/mux-lang/main/scripts/install.sh | shWindows PowerShell:
iwr -useb https://raw.githubusercontent.com/DerekCorniello/mux-lang/main/scripts/install.ps1 | iexBy default, the installer places the binary in ~/.local/bin and libraries in ~/.local/lib. You can customize this with environment variables if needed:
# Custom installation directory
MUX_INSTALL_DIR=/usr/local/bin MUX_LIB_DIR=/usr/local/lib sh install.sh
# Or on Windows (PowerShell)
$env:MUX_INSTALL_DIR = "C:\Program Files\mux"
$env:MUX_LIB_DIR = "C:\Program Files\mux\lib"After installation, verify your setup:
mux --version
mux doctor # Validate runtime dependencies
mux doctor --dev # Validate LLVM 17 and clang for developmentIf you prefer installing via cargo:
cargo install mux-langNote: LLVM 17 and clang must be installed first for source builds.
For contributing to Mux, use the bootstrap scripts which automatically set up LLVM 17:
Arch Linux:
./scripts/bootstrap-dev.sh
./scripts/dev-cargo.sh buildDebian and Ubuntu:
./scripts/bootstrap-dev.sh
./scripts/dev-cargo.sh buildmacOS:
./scripts/bootstrap-dev.sh
./scripts/dev-cargo.sh buildThe bootstrap scripts detect your OS and install LLVM 17 automatically. The dev-cargo.sh script wraps cargo calls with the correct LLVM environment variables set.
Mux builds a small runtime library the first time you compile or run a program. If you want to do this up front, run:
mux doctorThis verifies dependencies and builds the runtime if it is missing.
Mux is an open-source project, and I welcome contributions from the community! Whether you're interested in adding new features, fixing bugs, improving documentation, or helping with testing, your contributions are valuable. Please check out the CONTRIBUTING.md file for guidelines on how to get started.
While I take pride in this project, please be aware that Mux is still in its early stages of development. The language specification, compiler, and tooling are actively evolving. Expect breaking changes and incomplete features as I work towards a stable release.
I also want to acknowlege that I am aware that there are likely far better ways to do some of the things I have done here. This is a personal project and learning experience for me, and I appreciate your understanding as I continue to improve Mux.
Finally, I want to acknowledge that I have also been using this project as a way to experiment with AI tools to help me write, review, test and document code. While I have made every effort to ensure the accuracy and quality of the content, there may be occasional "bad code", errors, or inconsistencies. I appreciate your understanding as I continue to refine both the language and my use of these tools.
• The runtime now uses a unified representation for optional<T> and result<T, E] at the FFI level: both are boxed Value pointers (*mut Value).
• Compiler-generated code and FFI should treat optionals/results as *mut Value and use the runtime discriminant helpers when inspecting variants. This avoids mismatched representations and related crashes.
Mux (fully "MuxLang") is a statically-typed, reference-counted language that combines:
- Java-style explicit typing with local type inference
- Python-style collection literals
- Rust-style pattern-matching with guards
- Curly-brace syntax and no semicolons
- Minimal trait/Class model (use
isinstead ofimplementslike Java) - Built-in
result<T,E>andoptional<T>for error handling
- Case-sensitive identifiers: letters, digits,
_, not starting with a digit - Whitespace (spaces, tabs, newlines) separates tokens
- Comments:
- Single-line:
// comment - Multi-line:
/* comment */
- Single-line:
- Statement termination: by end-of-line only (no semicolons)
- Underscore placeholder:
_can be used for unused parameters, variables, or pattern matching wildcards - Keywords:
func,returns,const,auto,class,interface,enum,match,if,else,for,while,break,continue,return,import,is,as,in,true,false,common,none,optional,result,ok,err
Type System: Mux uses strict static typing with NO implicit type conversions. All type conversions must be explicit using conversion methods.
int // 64-bit signed integer
float // 64-bit IEEE-754
bool // true | false
char // Unicode code point
string // UTF-8 sequence
Mux requires explicit type conversions for all operations. There are no implicit conversions between types.
// Integer conversions
auto x = 42
auto x_float = x.to_float() // int -> float
auto x_str = x.to_string() // int -> str
auto x_same = x.to_int() // int -> int (identity)
// Float conversions
auto pi = 3.14
auto pi_int = pi.to_int() // float -> int (truncates: 3)
auto pi_str = pi.to_string() // float -> str
auto pi_same = pi.to_float() // float -> float (identity)
// Boolean conversions
auto flag = true
auto flag_int = flag.to_int() // bool -> int (true=1, false=0)
auto flag_float = flag.to_float() // bool -> float (true=1.0, false=0.0)
auto flag_str = flag.to_string() // bool -> string ("true" or "false")
// Char conversions
auto ch = 'A'
auto ch_str = ch.to_string() // char -> str
// Method calls on literals require parentheses
auto num = (3).to_string() // Valid
auto val = (42).to_float() // Valid
// auto bad = 3.to_string() // ERROR: parsed as float 3.0
String and char parsing methods return result<T, string> because they can fail:
// String to number (returns result)
auto num_str = "42"
auto result = num_str.to_int()
match result {
ok(value) {
print("Parsed: " + value.to_string()) // "Parsed: 42"
}
err(error) {
print("Parse error: " + error)
}
}
// String to float
auto float_str = "3.14159"
auto float_result = float_str.to_float()
match float_result {
ok(value) { print(value.to_string()) }
err(msg) { print("Error: " + msg) }
}
// Char to digit (only works for '0'-'9')
auto digit_char = '5'
auto digit_result = digit_char.to_int()
match digit_result {
ok(digit) { print(digit.to_string()) } // "5"
err(msg) { print(msg) }
}
auto letter = 'A'
auto letter_result = letter.to_int()
match letter_result {
ok(_) { print("Unexpected success") }
err(msg) { print(msg) } // "Character is not a digit (0-9)"
}
The following operations are compile-time errors:
// Type mismatches in binary operations
auto bad1 = 1 + 1.0 // ERROR: cannot add int and float
auto bad2 = "hello" + 3 // ERROR: cannot add string and int
auto bad3 = true + false // ERROR: cannot add bool and bool
// Type mismatches in comparisons
auto bad4 = 1 < 1.0 // ERROR: cannot compare int and float
auto bad5 = "a" == 1 // ERROR: cannot compare string and int
// Function argument type mismatches
func takes_string(string s) returns void { }
takes_string(123) // ERROR: expected string, got int
// Correct usage requires explicit conversion
auto good1 = 1 + (1.0).to_int() // OK: 2
auto good2 = "hello" + (3).to_string() // OK: "hello3"
auto good3 = 1.to_float() < 1.0 // OK: true
auto good4 = (true).to_int() + (false).to_int() // OK: 1
| From Type | Method | Returns | Notes |
|---|---|---|---|
int |
.to_string() |
string |
Converts to string representation |
int |
.to_float() |
float |
Converts to floating-point |
int |
.to_int() |
int |
Identity function |
float |
.to_string() |
string |
Converts to string representation |
float |
.to_int() |
int |
Truncates decimal part |
float |
.to_float() |
float |
Identity function |
bool |
.to_string() |
string |
Returns "true" or "false" |
bool |
.to_int() |
int |
Returns 1 or 0 |
bool |
.to_float() |
float |
Returns 1.0 or 0.0 |
char |
.to_string() |
string |
Converts char to string |
char |
.to_int() |
result<int, string> |
Digit value for '0'-'9' only |
string |
.to_string() |
str |
Identity function |
string |
.to_int() |
result<int, string> |
Parses string as integer |
string |
.to_float() |
result<float, string> |
Parses string as float |
Mux uses a single unified type (Value enum) to represent all runtime values, enabling uniform handling in collections and generics.
pub enum Value {
Bool(bool),
Int(i64),
Float(OrderedFloat<f64>),
String(String),
List(Vec<Value>),
Map(BTreeMap<Value, Value>),
Set(BTreeSet<Value>),
Tuple(Box<Tuple>),
optional(Option<Box<Value>>),
result(result<Box<Value>, String>),
Object(ObjectRef),
}All primitives are boxed into *mut Value pointers:
- Allocation:
mux_rc_alloc(value)allocates RefHeader + Value - Storage: Pointers stored in variables, collections, and function returns
- Extraction: Typed accessors (
mux_value_get_int, etc.) unwrap values
This design enables:
- Generic collections:
list<T>works uniformly for all types - Polymorphic functions: Same function can handle any type
- Simple FFI: C code receives consistent
void*pointers
Mux maintains three distinct type representations:
| Representation | Purpose |
|---|---|
TypeNode |
AST representation, source locations |
Type |
Semantic analysis, type resolution |
BasicTypeEnum |
LLVM IR generation |
The separation enables error reporting with source locations while keeping semantic analysis LLVM-independent.
Mux provides essential built-in functions for output and utility operations. These are always available without imports.
Design Note: print is a direct runtime call that outputs to stdout. The runtime handles string formatting and newline appending.
range(int start, int end) -> list<int> - Returns a list of integers from start (inclusive) to end (exclusive). The result is always a list<int>.
// Generate indices for iteration
for i in range(0, 5) {
print(i.to_string()) // Prints 0, 1, 2, 3, 4
}
// Create a list of numbers
auto numbers = range(10, 15) // [10, 11, 12, 13, 14]
Design Note: range() is the primary way to create numeric sequences for iteration, as Mux does not support C-style for (int i = 0; i < n; i++) loops.
read_line() -> string - Reads a line from standard input and returns it as a string (excluding the newline).
print("Enter your name: ")
auto name = read_line()
print("Hello, " + name)
optional<T>
result<T, E>
list<T>
map<K,V>
set<T>
tuple<T, U>
### 3.4.1 Tuples
Tuples are fixed size pairs. A tuple always has exactly two elements.
```mux
auto pair = (1, "one")
tuple<int, string> typed = (2, "two")
print(pair.left.to_string()) // "1"
print(pair.right.to_string()) // "one"
Tuples also support to_string() and a default constructor:
auto empty = tuple<int, string>.new() // (0, "")
### 3.5 Generics
Mux supports Go/Rust-style generics with type parameters and interface bounds using the `is` keyword:
```mux
// Generic function with type constraints
func max<T is Comparable>(T a, T b) returns T {
if a > b {
return a
}
return b
}
// Generic function with Stringable bound for to_string()
func greet<T is Stringable>(T value) returns string {
return "Hello, " + value.to_string()
}
// Arithmetic operators are builtin for primitive numeric types.
// Use explicit methods or domain-specific APIs for generic composition.
// Generic class
class Stack<T> {
list<T> items
func push(T item) returns void {
self.items.push_back(item)
}
func pop() returns optional<T> {
if self.items.is_empty() { return none }
return self.items.pop_back()
}
}
Mux uses compile-time monomorphization for generics, generating specialized code for each type instantiation.
- Type inference: Determine concrete types from function arguments
- Name generation: Create unique identifier:
FunctionName$int$string$ - Type substitution: Replace type parameters with concrete types
- Code generation: Emit specialized function body
- Caching: Store generated methods to avoid regeneration
func identity<T>(T value) returns T {
return value
}
auto a = identity(42) // Generates: identity$int
auto b = identity("hello") // Generates: identity$string
The compiler substitutes types in the AST before code generation:
// Original generic function
func identity<T>(T value) returns T { ... }
// After substitution for T = int
func identity$int(int value) returns int { ... }- Zero runtime cost: No boxing, no vtables, no type checks
- Static dispatch: Methods resolved at compile time
- LLVM optimization: Each specialization can be fully optimized
The tradeoff is increased code size (one copy per type combination).
Mux provides built-in interfaces (traits) for common operations:
| Interface | Callable Method | Description |
|---|---|---|
Stringable |
.to_string() |
Types that can be converted to string |
Equatable |
(none) | Enables == and != operators |
Comparable |
(none) | Enables <, >, <=, >= operators |
Hashable |
(none) | Types that can be used as set/map keys |
Error |
.message() |
Types that can be used as result<T, E> error values |
Note: For Equatable and Comparable, the methods are marker interfaces - you cannot call .eq() or .cmp() on built-in types. Use the operators directly (==, <, etc.) instead.
Primitives and Interfaces:
int: ImplementsStringable,Equatable,Comparable,Hashablefloat: ImplementsStringable,Equatable,Comparable,Hashablestring: ImplementsStringable,Equatable,Comparable,Hashable,Errorbool: ImplementsStringable,Equatable,Hashablechar: ImplementsStringable,Equatable,Comparable,Hashable
result<T, E> requires E to implement Error.
Example: Custom Type Implementing Interface
interface Equatable {
func eq(Self) returns bool
}
class Point is Equatable {
int x
int y
func eq(Point other) returns bool {
return self.x == other.x && self.y == other.y
}
}
}
}
auto p1 = Point.new(1, 2)
auto p2 = Point.new(1, 2)
auto same = p1 == p2 // true
// Using built-in interfaces
func process<T is Stringable>(list<T> items) returns void {
for item in items {
print(item.to_string())
}
}
// Multiple bounds (AND semantics - type must implement all)
func combine<T is Comparable & Stringable>(T a, T b) returns string {
if a < b {
return a.to_string() + " < " + b.to_string()
}
return a.to_string() + " >= " + b.to_string()
}
// Type parameters must be explicitly specified
auto max_int = max<int>(3, 7) // T = int, Comparable bound satisfied
auto names = ["apple", "banana"]
print(greet<string>(names[0])) // T = string, Stringable bound satisfied
- Structs: simple aggregates
- Enums: tagged unions (see §8)
- Classes: with fields + methods (see §9)
const int MAX = 100
// Variables (explicit type required for declarations without inference)
int x = 5
bool flag = true
string name = "MuxLang"
Mux supports both explicit types and type inference with auto:
// Type inferred with 'auto'
auto x = 42 // inferred as int
auto pi = 3.14159 // inferred as float
auto name = "Mux" // inferred as str
// Explicit type annotation
int count = 0
list<string> names = []
map<string, string | int> user = {"name": "Alice", "age": 30}
// Valid inference
auto value = someFunction()
auto numbers = [1, 2, 3]
map<string, string> userMap = {"key": "value"}
// Invalid - no initializer with 'auto'
auto x // ERROR: cannot infer type without initializer
// Function parameters must be explicitly typed
func process(auto item) returns void { } // ERROR
func process(int item) returns void { } // Valid
// Unused parameter
func process(int item, int _) returns void { } // second parameter unused
All declarations require either an explicit type or auto with an initializer; semicolons are not used.
Constants are immutable values that cannot be reassigned or modified after initialization:
// Function-level constants
func calculate() returns int {
const int MULTIPLIER = 10
const float TAX_RATE = 0.08
int value = 100
return value * MULTIPLIER
}
// Constants in classes
class Config {
const int MAX_RETRIES
int current_retry
func increment() returns void {
self.current_retry++ // OK - mutable field
// self.MAX_RETRIES++ // ERROR: Cannot modify const field 'MAX_RETRIES'
}
}
auto cfg = Config.new()
cfg.current_retry = 1 // OK - mutable field
// cfg.MAX_RETRIES = 5 // ERROR: Cannot assign to const field 'MAX_RETRIES'
Const Enforcement:
- Cannot reassign:
const_var = new_value-> ERROR - Cannot use compound assignment:
const_var += 1-> ERROR - Cannot increment/decrement:
const_var++orconst_var---> ERROR - Applies to both identifiers and class fields
- Use
constwhen you want a value that won't change after initialization
func add(int a, int b) returns int {
return a + b
}
func greet(string name, int times = 1) returns void {
for i in range(0, times) {
print("Hello, " + name)
}
}
func processData() returns map<string, int> {
map<string, int> results = {"processed": 100, "skipped": 5}
auto total = results["processed"] + results["skipped"]
results["total"] = total
return results
}
// Function with unused parameters
func callback(string event, int timestamp, string _) returns void {
print("Event: " + event + " at " + timestamp)
// third parameter ignored
}
- Keyword
func - Parameter list with explicit types; default values optional
returnsclause for return type (explicit, no inference)- Body enclosed in
{…}; no semicolons - Local variables within functions can use
autoinference - Use
_for unused parameters
Mux supports standard arithmetic operations with strict type requirements (no implicit conversions):
| Operator | Description | Types | Example |
|---|---|---|---|
+ |
Addition | int, float, string |
5 + 3, "a" + "b" |
- |
Subtraction | int, float |
10 - 4 |
* |
Multiplication | int, float |
6 * 7 |
/ |
Division | int, float |
15 / 3 |
% |
Modulo | int, float |
10 % 3 |
** |
Exponentiation | int, float |
2 ** 3 |
Exponentiation Operator (**):
- Right-associative:
2 ** 3 ** 2equals2 ** (3 ** 2)= 512 - Higher precedence than
*and/:2 * 3 ** 2equals2 * 9= 18 - Works on both
intandfloattypes
auto squared = 5 ** 2 // 25
auto cubed = 2.0 ** 3.0 // 8.0
auto complex = 2 ** 3 ** 2 // 512 (right-associative)
Mux provides postfix increment (++) and decrement (--) operators with specific design constraints:
Design Constraints:
- Postfix only:
counter++is valid,++counteris not supported - Standalone only: Must appear on their own statement line, not within expressions
- Only on mutable variables: Cannot be used on
constdeclarations or literals - Type preservation: Operates on
inttypes only
auto counter = 0
counter++ // Valid: counter is now 1
counter-- // Valid: counter is now 0
// INVALID - cannot use in expressions:
// auto x = counter++ // ERROR: ++ cannot be used in expressions
// auto y = (counter++) + 5 // ERROR: standalone only
// INVALID - prefix not supported:
// ++counter // ERROR: prefix increment not supported
// INVALID - cannot modify const:
const int MAX = 100
// MAX++ // ERROR: cannot modify const
Rationale: The postfix-only, standalone-only design prevents ambiguity and side-effect confusion that can occur with prefix operators or expression-embedded increments.
| Operator | Description | Types | Example |
|---|---|---|---|
== |
Equality | All comparable types | a == b |
!= |
Inequality | All comparable types | a != b |
< |
Less than | int, float, string |
5 < 10 |
<= |
Less than or equal | int, float, string |
x <= 100 |
> |
Greater than | int, float, string |
y > 0 |
>= |
Greater than or equal | int, float, string |
age >= 18 |
&& |
Logical AND | bool |
a && b (short-circuit) |
|| |
Logical OR | bool |
a || b (short-circuit) |
! |
Logical NOT | bool |
!flag |
Short-circuit Evaluation:
&&only evaluates right side if left istrue||only evaluates right side if left isfalse
The && and || operators use LLVM control flow for short-circuit evaluation, not simple boolean operations.
Phi nodes select a value based on which predecessor block executed:
%result = phi i1 [ 0, %left_block ], [ %b_value, %right_block ]- From
left_block: constant0(left was false) - From
right_block: computed%b_value(left was true)
If LLVM generated a && b as a single expression:
- Both
aandbwould always be evaluated (no short-circuit) - No opportunity for branch prediction
- Can't exploit constant operands
The basic block approach preserves semantics while enabling LLVM optimizations (dead code elimination, inlining, vectorization).
The in operator tests for membership/containment with strict type requirements:
| Left Operand | Right Operand | Description |
|---|---|---|
T |
list<T> |
Check if value exists in list |
T |
set<T> |
Check if value exists in set |
string |
string |
Check if substring exists |
char |
string |
Check if character exists in string |
Type Constraints:
- Both operands must have compatible element types
- No implicit type conversions allowed
- Returns
bool
// List containment
auto nums = [1, 2, 3, 4, 5]
auto hasThree = 3 in nums // true
auto hasTen = 10 in nums // false
// Set containment
auto tags = {"urgent", "important"}
auto isUrgent = "urgent" in tags // true
// String containment
auto msg = "hello world"
auto hasWorld = "world" in msg // true
auto hasFoo = "foo" in msg // false
// Character in string
auto hasO = 'o' in msg // true
auto hasZ = 'z' in msg // false
// INVALID - type mismatch:
// auto bad = "1" in nums // ERROR: string not in list<int>
// auto bad2 = 1 in msg // ERROR: int not in string
The + operator is overloaded for collection types with type-specific semantics:
| Types | Operation | result |
|---|---|---|
list<T> + list<T> |
Concatenation | Combined list |
map<K,V> + map<K,V> |
Merge | Combined map (latter overwrites former on key collision) |
set<T> + set<T> |
Union | Set containing all unique elements |
string + string |
Concatenation | Combined string |
Operators are built-in for primitive types and collections.
| Operator | Types |
|---|---|
+ |
int, float, string, list, map, set |
- |
int, float |
* |
int, float |
/ |
int, float |
% |
int, float |
** |
int, float |
== |
all types |
<, >, <=, >= |
int, float, string, char |
The semantic analyzer checks operator types:
// For a + b
let left_type = analyzer.get_expression_type(left_expr)?;
let right_type = analyzer.get_expression_type(right_expr)?;
if !type_supports_addition(&left_type) {
return err(format!("Type {} does not support +", left_type));
}For primitive types, direct LLVM operations:
%result = add i64 %a, %bArithmetic operators are not dispatched through Add/Sub/Mul/Div interfaces.
They are validated semantically and lowered to builtin operations for supported types.
Type Constraints:
- Both operands must be the exact same collection type
- No mixing of collection types (e.g.,
list + setis an error) - No implicit element type conversions
// List concatenation
auto list1 = [1, 2]
auto list2 = [3, 4]
auto combined = list1 + list2 // [1, 2, 3, 4]
// Map merge (latter wins on key collision)
auto map1 = {"a": 1, "b": 2}
auto map2 = {"b": 3, "c": 4} // Note: key "b" exists in both
auto merged = map1 + map2 // {"a": 1, "b": 3, "c": 4}
// Set union
auto set1 = {1, 2, 3}
auto set2 = {3, 4, 5}
auto unioned = set1 + set2 // {1, 2, 3, 4, 5}
// String concatenation
auto greeting = "Hello, " + "World" // "Hello, World"
// INVALID - type mismatch:
// auto bad = [1, 2] + {3, 4} // ERROR: cannot add list and set
// auto bad2 = [1, 2] + [3.0, 4.0] // ERROR: list<int> + list<float>
// Block-form lambda with explicit types and return type
auto square = func(int n) returns int {
return n * n
}
auto doubler = func(float x) returns float {
return x * 2.0
}
// Passing lambdas to functions
auto result = apply(10, func(int x) returns int {
return x + 5
})
// Lambda with unused parameters
auto processFirst = func(int first, int _) returns int {
return first * 2 // second parameter ignored
}
// Block-form lambda with mixed explicit/inferred types
auto filter = func(list<int> nums, func(int) returns bool cond) returns list<int> {
list<int> out = []
for n in nums {
if cond(n) {
out.push_back(n)
}
}
return out
}
- All lambdas use block syntax with
func(params) { ... } - Lambda parameters can use
autowhen type can be inferred from context - Use
_for unused lambda parameters - optional capture list in
[…]form [needs more clarification]
if x > 0 {
print("positive")
} else if x < 0 {
print("negative")
} else {
print("zero")
}
// With type inference
auto message = if x > 0 { "positive" } else { "non-positive" }
match (value) {
some(v) if v > 10 {
auto msg = "large: " + v // local inference
print(msg)
}
some(v) {
print("small: " + v)
}
none {
print("no value")
}
_ {
print("unexpected case") // wildcard pattern
}
}
Match statements can be used as switch statements for any type:
// Match on int literals (like a switch)
auto status = 200
match status {
200 { print("OK") }
404 { print("Not Found") }
500 { print("Server Error") }
_ { print("Unknown status") }
}
// Match on string literals
auto command = "start"
match command {
"start" { print("Starting...") }
"stop" { print("Stopping...") }
"restart" { print("Restarting...") }
_ { print("Unknown command") }
}
// Variable binding in patterns
auto value = 42
match value {
1 { print("one") }
captured { print("got: " + captured.to_string()) }
_ { print("other") }
}
// List literal matching
auto nums = [1, 2, 3]
match nums {
[] { print("empty") }
[1, 2, 3] { print("three elements") }
[first, ..rest] { print("has elements") }
}
// Switch with guards
auto score = 85
match score {
n if n >= 90 { print("A") }
n if n >= 80 { print("B") }
n if n >= 70 { print("C") }
n if n >= 60 { print("D") }
_ { print("F") }
}
for item in myList {
auto processed = transform(item) // type inferred
print(processed)
}
// Iterator with inference
for item in collection {
// item type inferred from collection element type
process(item)
}
// Ignoring loop variables when not needed
for _ in range(0, 10) {
doSomething() // don't care about the index
}
// Destructuring in loops with unused parts
for (key, _) in keyValuePairs {
print("Key: " + key) // value ignored
}
while cond {
auto currentTime = getCurrentTime() // local inference
// ...
}
Works as in C/Java.
enum Shape {
Circle(float radius)
Rectangle(float width, float height)
Square(float size)
}
enum Shape {
Circle(float radius)
Rectangle(float width, float height)
Square(float size)
}
// Usage with inference
auto myShape = Circle.new(5.0) // type inferred as Shape
list<Shape> shapes = [Circle.new(1.0), Rectangle.new(2.0, 3.0)]
// Pattern matching with unused enum data
match (shape) {
Circle(_) {
print("It's a circle") // radius value ignored
}
Rectangle(width, _) {
print("Rectangle with width: " + width) // height ignored
}
Square(size) {
print("Square with size: " + size)
}
}
Each variant may carry data. Pattern-match with destructuring and guards. Use _ to ignore unused enum data in patterns.
interface Drawable {
func draw() returns void
}
class Circle is Drawable, ShapeLike {
float radius // explicit type required for fields
func draw() returns void {
auto message = "Circle radius=" + radius // local inference in methods
print(message)
}
func area() returns float {
const float PI = 3.1415 // inferred as float
return PI * radius * radius
}
// Method with unused parameters
func resize(float newRadius, string _) returns void {
radius = newRadius // second parameter ignored
}
}
// Generic class example
class Stack<T> {
list<T> items
func push(T item) returns void {
items.push_back(item)
}
func pop() returns optional<T> {
if items.isEmpty() {
return none
}
auto item = items.pop_back()
return some(item)
}
}
// Usage with inference
auto circle = Circle.new(5.0) // type inferred as Circle
list<Drawable> shapes = [circle]
Stack<int> intStack = Stack<int>.new() // explicit generic instantiation with .new()
Mux uses the common keyword to declare static (class-level) methods that can be called without an instance. This is distinct from const which declares immutable values.
common vs const:
| Keyword | Purpose | Usage | Example |
|---|---|---|---|
common |
Static methods and factory functions | Called on the class itself, not instances | ClassName.method() |
const |
Immutable constants | Values that cannot change after initialization | const int MAX = 100 |
class Stack<T> {
list<T> items
// Instance method - called on instances
func push(T item) returns void {
self.items.push_back(item)
}
// Static method - called on the class
common func who_am_i() returns string {
return "I'm a Stack!"
}
// Factory pattern - static method that creates instances
common func from(list<T> init_list) returns Stack<T> {
auto new_stack = Stack<T>.new()
new_stack.items = init_list
return new_stack
}
}
// Calling static methods
print(Stack.who_am_i()) // "I'm a Stack!"
auto s = Stack<int>.from([1, 2, 3]) // Factory method
// Calling instance methods
auto stack = Stack<int>.new()
stack.push(42) // Instance method
Key Differences:
- Instance methods (no keyword) operate on
selfand require an instance - Static methods (
common) have noselfand are called on the class - Const fields are immutable instance/class fields, not methods
- Static methods cannot access instance fields (no
selfcontext) - Factory patterns commonly use
common func from(...)to create instances with pre-populated data
Classes are instantiated using the .new() method pattern:
// Basic instantiation
auto circle = Circle.new() // No constructor arguments
auto circle2 = Circle.new(5.0) // With constructor arguments
// Generic class instantiation
auto int_stack = Stack<int>.new()
auto string_stack = Stack<string>.new()
// Using factory methods
auto prebuilt = Stack<int>.from([1, 2, 3])
auto pair = Pair<string, int>.from("key", 42)
Mux objects use Rust's Rc<Arc<ObjectData>> pattern for shared ownership with type information.
struct ObjectData {
ptr: *mut c_void, // User's object data
type_id: TypeId, // Runtime type identifier
size: usize, // Size for deallocation
ref_count: AtomicUsize, // Reference count
}
struct ObjectRef {
data: Rc<ObjectData>, // Shared ownership
}lazy_static::lazy_static! {
static ref TYPE_REGISTRY: Mutex<HashMap<TypeId, ObjectType>> = ...
static ref NEXT_TYPE_ID: AtomicUsize = ...
}
pub struct ObjectType {
pub id: TypeId,
pub name: String,
pub size: usize,
pub destructor: Option<fn(*mut c_void)>,
}Each class registers with the runtime, receiving a unique TypeId.
pub fn alloc_object(type_id: TypeId) -> *mut Value {
let obj_type = TYPE_REGISTRY.lock().get(&type_id);
let size = obj_type.size;
let ptr = std::alloc::alloc(size);
let obj_ref = ObjectRef::new(ptr, type_id, size);
mux_rc_alloc(Value::Object(obj_ref))
}- Type information at runtime:
type_idenables type checks and casts - Proper cleanup:
sizeand optional destructor for cleanup - Shared ownership: Multiple references to same object
Design Note: Mux uses explicit .new() rather than direct constructor calls to distinguish class instantiation from function calls and enum variant construction.
Mux uses static dispatch for interfaces - no runtime vtable lookup.
VTables are generated at compile time:
@vtable_Circle = constant {
i32, // type tag
void (i8*)* // draw method pointer
} {
i32 1, // Circle's type ID
void (i8*)* @Circle.draw
}Methods are prefixed with their class name:
class Circle {
func draw() returns void { ... }
}
Generates LLVM function: Circle.draw
- Zero cost: No pointer indirection, direct function calls
- Inlining: LLVM can inline interface methods
- Optimization: Better branch prediction, no indirect jumps
// Dynamic dispatch (Python, Java)
circle.draw() // Look up vtable, find slot, call
// Static dispatch (Mux)
Circle.draw() // Direct call to Circle.drawThe tradeoff: interfaces cannot be added to types from other modules (no "extension traits").
// Explicit typing
list<int> nums = [1, 2, 3, 4]
map<string, int> scores = {"Alice": 90, "Bob": 85}
// With type inference
auto nums = [1, 2, 3, 4] // inferred as list<int>
map<string, int> scores = {"Alice": 90, "Bob": 85}
// mixed = [1, 2.5, 3] // ERROR: conflicting types, explicit type needed
// Nested collections
list<list<int>> matrix = [[1, 2], [3, 4]]
map<string, list<int>> lookup = {"users": [1, 2, 3], "admins": [4, 5]}
// Complex nested structures
auto users = [
{"name": "Alice", "scores": [95, 87, 92]},
{"name": "Bob", "scores": [78, 85, 90]}
] // inferred as list<map<string, string | list<int>>>
auto data = {
"numbers": [1, 2, 3, 4, 5],
"metadata": {"version": "1.0", "count": 5}
} // inferred as map<string, list<int> | map<string, string | int>>
// Generic collections
list<Pair<int, string>> pairs = [Pair.new(1, "one"), Pair.new(2, "two")]
list<Container<int>> containers = list<Container<int>>()
All collections provide a consistent API for access, mutation, and inspection.
| Method | Returns | Description |
|---|---|---|
.size() |
int |
Returns the number of elements in the list |
.is_empty() |
bool |
Returns true if list has no elements |
.get(int index) |
optional<T> |
Safe access; returns some(value) or none if out of bounds |
[int index] |
T |
Direct access; runtime error if out of bounds |
.push(T item) |
void |
Appends item to the end of the list (alias for push_back) |
.push_back(T item) |
void |
Appends item to the end of the list |
.pop() |
optional<T> |
Removes and returns last item, or none if empty (alias for pop_back) |
.pop_back() |
optional<T> |
Removes and returns last item, or none if empty |
.to_string() |
string |
Returns a string representation of the list |
auto nums = [1, 2, 3]
// Safe access with optional
match nums.get(0) {
some(first) { print(first.to_string()) } // "1"
none { print("Index out of bounds") }
}
// Direct access (runtime error if index invalid)
auto second = nums[1] // 2
// Mutation
nums.push_back(4) // [1, 2, 3, 4]
match nums.pop_back() {
some(last) { print(last.to_string()) } // "4"
none { }
}
// Inspection
print(nums.size().to_string()) // "3"
print(nums.is_empty().to_string()) // "false"
| Method | Returns | Description |
|---|---|---|
.size() |
int |
Returns the number of key-value pairs |
.is_empty() |
bool |
Returns true if map has no entries |
.get(K key) |
optional<V> |
Safe lookup; returns some(value) or none if key not found |
[K key] |
V |
Direct access; runtime error if key not found |
.put(K key, V value) |
void |
Inserts or updates a key-value pair |
.contains(K key) |
bool |
Returns true if key exists in map |
.remove(K key) |
optional<V> |
Removes key and returns value, or none if key not found |
.to_string() |
string |
Returns a string representation of the map |
auto scores = {"Alice": 90, "Bob": 85}
// Safe access
match scores.get("Alice") {
some(score) { print(score.to_string()) } // "90"
none { print("Student not found") }
}
// Direct access
auto bobScore = scores["Bob"] // 85
// Map entries are immutable; reassign to update
scores["Alice"] = 95 // Updates existing key
| Method | Returns | Description |
|---|---|---|
.size() |
int |
Returns the number of elements |
.is_empty() |
bool |
Returns true if set is empty |
.add(T item) |
void |
Adds an item to the set |
.contains(T item) |
bool |
Returns true if item exists in set |
.remove(T item) |
optional<T> |
Removes item and returns it, or none if not found |
.to_string() |
string |
Returns a string representation of the set |
auto tags = {"urgent", "important", "review"}
print(tags.size().to_string()) // "3"
// Add and check membership
tags.add("priority")
if tags.contains("urgent") {
print("Has urgent tag")
}
// Remove item
match tags.remove("review") {
some(removed) { print("Removed: " + removed) }
none { print("Item not found") }
}
Design Note: Collections use consistent method naming across all types. Safe access via .get() returns optional<T>, while direct access with [] provides unchecked access with runtime bounds checking.
Mux's collections (list, map, set) can contain any Value, enabling arbitrary nesting.
| Collection | Rust Type | Use Case |
|---|---|---|
list<T> |
Vec<Value> |
Contiguous array, indexed access |
map<K,V> |
BTreeMap<Value, Value> |
Key-value pairs, sorted keys |
set<T> |
BTreeSet<Value> |
Unique elements, membership test |
Unlike HashMap/HashSet, BTree variants provide:
- Deterministic iteration order: Always the same order
- Ordered operations: First/last element, range queries
- Reproducible output:
to_string()produces consistent results
auto nested = [
{"name": "Alice", "scores": [95, 87, 92]},
{"name": "Bob", "scores": [78, 85, 90]}
]
// Structure: list<map<string, list<int> | string>>
The type system tracks nesting through:
- Parser: Creates nested
TypeNodestructures - Semantic Analyzer: Resolves to
Type::List(Type::Map(...)) - Code Generator: Creates appropriate LLVM types
Collections are RC-allocated and contain RC-allocated values. When freed:
- Collection's refcount reaches zero
- Collection's
Vec<Value>is dropped - Each contained
Valuehas its refcount decremented - Nested collections are freed recursively
func divide(int a, int b) returns result<int, string> {
if b == 0 {
return err("division by zero")
}
return ok(a / b)
}
// Usage with inference
auto result = divide(10, 2) // inferred as result<int, string>
match result {
ok(value) {
auto message = "result: " + value // local inference
print(message)
}
err(error) {
print("Error: " + error)
}
_ {
print("Unexpected result") // wildcard for completeness
}
}
// Ignoring error details when not needed
match result {
ok(value) {
print("Success: " + value)
}
err(_) {
print("some error occurred") // error details ignored
}
}
| Method | Returns | Description |
|---|---|---|
.is_ok() |
bool |
Returns true if the result is an ok variant |
.is_err() |
bool |
Returns true if the result is an err variant |
.to_string() |
string |
String representation |
result<int, string> res1 = ok(42)
result<int, string> res2 = err("error")
print(res1.is_ok().to_string()) // true
print(res1.is_err().to_string()) // false
print(res2.is_ok().to_string()) // false
print(res2.is_err().to_string()) // true
func findEven(list<int> xs) returns optional<int> {
for x in xs {
if x % 2 == 0 {
return some(x)
}
}
return none
}
// Usage with inference
optional<int> maybeEven = findEven([1, 3, 4, 7]) // inferred as optional<int>
match maybeEven {
some(value) {
print("Found even: " + value)
}
none {
print("No even number found")
}
_ {
print("Unexpected optional state")
}
}
// Ignoring the wrapped value when you just care about presence
match maybeEven {
some(_) {
print("Got a value") // don't care what the value is
}
none {
print("Got nothing")
}
}
| Method | Returns | Description |
|---|---|---|
.is_some() |
bool |
Returns true if the optional contains a value |
.is_none() |
bool |
Returns true if the optional is empty |
.to_string() |
string |
String representation |
optional<int> opt1 = some(42)
optional<int> opt2 = none
print(opt1.is_some().to_string()) // true
print(opt1.is_none().to_string()) // false
print(opt2.is_some().to_string()) // false
print(opt2.is_none().to_string()) // true
Use match to unpack results and optionals. Use _ to ignore unused values in patterns.
Both result<T, E> and optional<T> use a uniform runtime representation.
pub struct result<T, E> {
discriminant: i32, // 0 = ok, 1 = err
data: *mut T, // pointer to value
}
pub struct optional<T> {
discriminant: i32, // 0 = none, 1 = some
data: *mut T, // pointer to value
}Same layout enables generic code to work with either type.
Discriminant: Determines which variant is active Data pointer: Points to the contained value (boxed like all other values)
auto opt = some(42) // discriminant=1, data=box(42)
auto res = ok("error") // discriminant=0, data=box("error")
- Single runtime representation: Collections can store either
- No enum overhead: No runtime enum tag beyond discriminant
- Error propagation: Easy to implement with match statements
- Interop: optional and result can wrap the same types
- Reference-counted runtime; deterministic memory management with no manual
free - All objects and collections live on the heap
- Primitives passed by value, objects by reference
Mux uses atomic reference counting for deterministic memory management. Every heap-allocated value is prefixed with a reference count header.
┌──────────────────┬─────────────┐
│ RefHeader │ Value │
│ ref_count: u64 │ (payload) │
└──────────────────┴─────────────┘
^
Allocation pointer
The RefHeader uses AtomicUsize for thread-safe atomic operations. The Value payload contains the actual data.
Increment (mux_rc_inc): Called when creating a new reference:
- Assigning to a new variable
- Passing as a function argument
- Adding to a collection
Decrement (mux_rc_dec): Called when a reference goes out of scope:
- Variable assignment is overwritten
- Function returns (cleanup of local variables)
When mux_rc_dec returns true, the refcount reached zero and memory is freed automatically.
The compiler generates cleanup code using a scope stack:
- Enter scope ->
push_rc_scope()(function entry, if-block, loop-body, match-arm) - Track variable ->
track_rc_variable(name, alloca)for each RC-allocated variable - Exit scope ->
generate_all_scopes_cleanup()iterates through all scopes in reverse order
This ensures proper cleanup order and handles early returns.
Mux uses references for safe memory access and manipulation:
&Tdenotes a reference to typeT&exprcreates a reference toexpr- References are automatically dereferenced when used
- No pointer arithmetic is allowed
- References are non-nullable by default
- Use
Option<&T>for nullable references
// Basic reference usage
int x = 10
auto r = &x // r is of type &int
print("ref value: " + (*r).to_string()) // 10 - explicit dereference with *
*r = 20 // Changes x to 20 via dereference
print("val after ref update: " + (*r).to_string()) // 20
print("x is now: " + x.to_string()) // 20
// References to list elements
auto numbers = [1, 2, 3, 4, 5]
auto first = &numbers[0] // &int
print("first element: " + (*first).to_string()) // 1
// Function taking a reference
func update(&int ref) returns void {
*ref = *ref + 1 // Must explicitly dereference to modify
}
update(&x)
print("val after update: " + x.to_string()) // 21
Reference Syntax:
- Create reference:
&variableor&expression - Dereference:
*reference(required for both reading and writing) - Pass to functions:
func(&int ref)declares parameter,update(&x)passes reference - References to references: Not supported
Design Note: Unlike some languages with automatic dereferencing, Mux requires explicit * for all reference operations. This makes memory access patterns explicit and prevents accidental mutation bugs.
import math
import std.math
import shapes.circle as circle
// Usage with inference
float pi = math.PI // type inferred from math module
float root = math.sqrt(9.0)
auto c = circle.new(5.0) // type inferred from constructor
// Import with unused alias for completeness
import utils.logger as _ // imported but not directly used in this scope
- Python-style imports only
- Module paths map directly to file paths
- Imported symbols can be used with type inference
- Use
_alias when importing for side effects only - Standard library modules are imported as
import std.<module>and used as<module>.<item>
Mux uses Python-style module imports with compile-time resolution.
import math // math.mux in same directory
import shapes.circle // shapes/circle.mux
import std.math // stdlib math module
File paths map to module paths:
import foo->foo.muximport shapes.circle->shapes/circle.muximport std.math-> stdlibmathmodule namespace (math.*)
Functions from imported modules use mangled names:
// math.mux
func fibonacci(int n) returns int { ... }
// main.mux
import math
auto result = math.fibonacci(10)
Generates: math!fibonacci (not fibonacci)
This prevents conflicts when multiple modules define functions with the same name.
Top-level statements in modules become a module initialization function:
// config.mux
const int MAX_USERS = 100
auto initialized = false
func init() returns void {
initialized = true
}
The compiler generates:
define void @config.init() { ... }And calls it before main() executes.
The compiler:
- Parses all imports
- Builds dependency graph
- Processes modules in topological order
- Generates initialization functions for each module
Recommended:
- Local variables with obvious initialization
- Complex generic types that are clear from context
- Temporary variables in calculations
- Iterator variables in loops
// These require explicit types due to ambiguity
list<int> empty = [] // empty collection needs explicit type
auto empty = list<int>() // or explicit constructor
result<int, string> pending // uninitialized variables need explicit type
// Generic instantiation may need explicit types
Stack[int] stack = Stack[int]() // explicit generic parameter
auto pairs = zip<int, string>(numbers, names) // when inference is ambiguous
// Good uses of underscore
func process(int data, string _) { } // ignore second parameter
for _ in range(0, 10) { } // ignore loop counter
match result { ok(_) { } } // ignore success value
// Avoid overusing underscore when names would help readability
// Less clear:
func calculate(int _, int _, float _) returns float { }
// Better:
func calculate(int width, int height, float _) returns float { }
import math
const float PI = 3.14159 // inferred as float
enum MaybeValue<T> {
some(T)
none
}
interface Shape {
func area() returns float
}
class Circle is Shape {
float r // explicit type required for fields
func area() returns float {
return PI * r * r
}
}
// Generic utility function
func map<T, U>(list<T> items, func(T) returns U transform) returns list<U> {
auto result = list<U>()
for item in items {
result.push_back(transform(item))
}
return result
}
func main() returns void {
auto shapes = [Circle.new(2.0), Circle.new(3.5)] // inferred as list<Circle>
for shape in shapes {
float area = shape.area() // inferred as float
string message = "Area: " + area // inferred as str
print(message)
}
// Working with Results and inference
auto results = list<result<float, string>>()
for shape in shapes {
auto areaResult = ok(shape.area()) // inferred as result<float, string>
results.push_back(areaResult)
}
// Using generics with inference and lambdas
auto areas = map(shapes, func(Shape s) {
return s.area() // inferred as list<float>
})
auto descriptions = map(areas, func(string a) {
return "Area: " + a // inferred as list<string>
})
// Pattern matching with underscore
for result in results {
match result {
ok(value) {
print("Success: " + value)
}
err(_) {
print("Error occurred") // don't care about error details
}
}
}
}
The Mux standard library includes assert, math, io, random, datetime, sync, net, env, data, and sql.
Import styles:
import std // use std.assert, std.math, std.io, std.random, std.datetime, std.sync, std.net, std.env, std.data, std.sql
import std.assert // use assert.*
import std.math // use math.*
import std.io // use io.*
import std.random // use random.*
import std.datetime // use datetime.*
import std.sync // use sync.*
import std.net // use net.*
import std.env // use env.*
import std.data // use data.*
import std.sql // use sql.*
import std.(math, random as r)
import std.* // flat import of stdlib items
assert provides test assertions that panic immediately on failure with descriptive error messages.
assert.assert_true(bool condition) -> void- Panics if falseassert.assert_false(bool condition) -> void- Panics if trueassert.assert(bool condition, string message) -> void- Panics with custom message if falseassert.assert_eq(T actual, T expected) -> void- Panics if values differ (generic)assert.assert_ne(T actual, T expected) -> void- Panics if values equal (generic)assert.assert_some(optional<T> value) -> void- Panics if noneassert.assert_none(optional<T> value) -> void- Panics if someassert.assert_ok(result<T, E> value) -> void- Panics if errassert.assert_err(result<T, E> value) -> void- Panics if ok
math provides floating-point constants and functions.
- Constants:
math.pi,math.e - Unary functions:
sqrt,sin,cos,tan,asin,acos,atan,ln,log2,log10,exp,abs,floor,ceil,round - Binary functions:
atan2,log,min,max,hypot,pow
io provides filesystem and path operations with explicit error handling via result<T, string>.
- File operations:
read_file,write_file,exists,remove,mkdir,listdir - Path operations:
is_file,is_dir,join,basename,dirname
random provides pseudorandom generation:
random.seed(int seed) -> voidrandom.next_int() -> intrandom.next_range(int min, int max) -> intrandom.next_float() -> floatrandom.next_bool() -> bool
datetime provides Unix-timestamp based date and time helpers.
datetime.now() -> result<int, string>(seconds since Unix epoch, UTC)datetime.now_millis() -> result<int, string>(milliseconds since Unix epoch, UTC)datetime.year(int ts) -> result<int, string>datetime.month(int ts) -> result<int, string>datetime.day(int ts) -> result<int, string>datetime.hour(int ts) -> result<int, string>datetime.minute(int ts) -> result<int, string>datetime.second(int ts) -> result<int, string>datetime.weekday(int ts) -> result<int, string>where0=Sun ... 6=Satdatetime.format(int ts, string pattern) -> result<string, string>(UTC)datetime.format_local(int ts, string pattern) -> result<string, string>(local timezone)datetime.sleep(int seconds) -> result<void, string>(blocking at call site)datetime.sleep_millis(int milliseconds) -> result<void, string>(blocking at call site)
Format patterns use chrono strftime tokens, for example:
%Afull weekday name%aabbreviated weekday name%Bfull month name%babbreviated month name%Y-%m-%d %H:%M:%S
sync provides basic concurrency primitives.
-
sync.spawn(fn() -> void) -> result<Thread, string> -
sync.sleep(int milliseconds) -> void -
Thread.join() -> result<void, string> -
Thread.detach() -> result<void, string> -
Mutex.new() -> Mutex -
Mutex.lock() -> result<void, string> -
Mutex.unlock() -> result<void, string> -
RwLock.new() -> RwLock -
RwLock.read_lock() -> result<void, string> -
RwLock.write_lock() -> result<void, string> -
RwLock.unlock() -> result<void, string> -
CondVar.new() -> CondVar -
CondVar.wait(Mutex) -> result<void, string> -
CondVar.signal() -> result<void, string> -
CondVar.broadcast() -> result<void, string>
net exposes TCP/UDP sockets plus HTTP client/server primitives with JSON request and response shapes.
net.TcpStream.connect(string addr) -> result<TcpStream, string>net.TcpListener.bind(string addr) -> result<TcpListener, string>listener.accept() -> result<TcpStream, string>net.UdpSocket.bind(string addr) -> result<UdpSocket, string>net.http.request(Json req) -> result<Json, string>net.http.read_request(TcpStream stream) -> result<Json, string>net.http.write_response(TcpStream stream, Json response) -> result<void, string>write_responseserializes body as JSON and defaultsContent-Typetoapplication/jsonunless you set it inheaders.net.TcpStream.read(int size),net.TcpStream.write(list<int> bytes)net.UdpSocket.send_to(list<int> bytes, string addr),net.UdpSocket.recv_from(int size)(all methods returnresult<T, string>when they can fail)
env exposes operating-system environment access with explicit errors.
env.get(string name) -> optional<string>
data.json is the JSON utility layer built on std.json.
data.json.parse(string json) -> result<Json, string>data.json.from_map(map<string, T>) -> result<Json, string>data.json.to_map(Json value) -> result<map<string, Json>, string>
data.csv parses CSV text into structured rows.
data.csv.parse(string csv_text) -> result<Csv, string>data.csv.parse_with_headers(string csv_text) -> result<Csv, string>
sql provides database connectivity and typed SQL values.
sql.connect(string uri) -> result<Connection, string>Connection.close() -> voidConnection.execute(string sql) -> result<int, string>Connection.execute_params(string sql, list<SqlValue> params) -> result<int, string>Connection.query(string sql) -> result<ResultSet, string>Connection.query_params(string sql, list<SqlValue> params) -> result<ResultSet, string>Connection.begin_transaction() -> result<Transaction, string>Transaction.commit() -> result<void, string>Transaction.rollback() -> result<void, string>Transaction.execute(string sql) -> result<int, string>Transaction.query(string sql) -> result<ResultSet, string>ResultSet.rows() -> list<map<string, SqlValue>>ResultSet.next() -> optional<map<string, SqlValue>>ResultSet.columns() -> list<string>
SqlValue constructors:
sql.int(int) -> SqlValuesql.float(float) -> SqlValuesql.bool(bool) -> SqlValuesql.string(string) -> SqlValuesql.bytes(list<int>) -> SqlValuesql.null() -> SqlValue
SqlValue methods:
.is_null() -> bool.as_bool() -> result<bool, string>.as_int() -> result<int, string>.as_float() -> result<float, string>.as_string() -> result<string, string>.as_bytes() -> result<list<int>, string>.to_string() -> string
Current provider support:
- SQLite: supported (
sqlite::memory:,sqlite:///path/to/file.db) - PostgreSQL: supported (
postgres://...,postgresql://...) - MySQL/MariaDB: supported (
mysql://...,mariadb://...) - SQL Server: URI recognized, currently unsupported
dsa provides data structures and algorithms: stack, queue, heap, bintree, graph, and utility functions.
Import styles:
import std.dsa // use dsa.stack, dsa.queue, etc.
import std.dsa.stack // use stack.*
import std.dsa.algorithm // use algorithm.*
import std.dsa.collection.Collection // import the interface
The Collection<T> interface is implemented by all DSA data structures:
| Method | Returns | Description |
|---|---|---|
len() returns int |
Number of elements | |
is_empty() returns bool |
True if empty | |
clear() returns void |
Remove all elements | |
to_list() returns list<T> |
Elements as a list |
A LIFO (last-in, first-out) collection.
| Method | Returns | Description |
|---|---|---|
Stack<T>.new() returns Stack<T> |
Create empty stack | |
push(T value) returns void |
Add element to top | |
pop() returns optional<T> |
Remove and return top element | |
peek() returns optional<T> |
View top element without removing |
Also implements len(), is_empty(), clear(), to_list().
A FIFO (first-in, first-out) collection.
| Method | Returns | Description |
|---|---|---|
Queue<T>.new() returns Queue<T> |
Create empty queue | |
enqueue(T value) returns void |
Add element to back | |
dequeue() returns optional<T> |
Remove and return front element | |
peek() returns optional<T> |
View front element without removing |
Also implements len(), is_empty(), clear(), to_list().
A min-heap collection where the smallest element is always at the top.
| Method | Returns | Description |
|---|---|---|
Heap<T>.new() returns Heap<T> |
Create empty heap | |
push(T value) returns void |
Add element | |
pop() returns optional<T> |
Remove and return minimum | |
peek() returns optional<T> |
View minimum without removing |
Also implements len(), is_empty(), clear(), to_list().
A binary search tree with set semantics (duplicates ignored).
| Method | Returns | Description |
|---|---|---|
BinaryTree<T>.new() returns BinaryTree<T> |
Create empty tree | |
insert(T value) returns void |
Add element | |
remove(T value) returns void |
Remove element | |
contains(T value) returns bool |
Check if element exists |
Also implements len(), is_empty(), clear(), to_list() (inorder traversal).
A directed graph using adjacency lists.
| Method | Returns | Description |
|---|---|---|
Graph<T>.new() returns Graph<T> |
Create empty graph | |
add_vertex(T value) returns void |
Add a vertex | |
add_edge(T from, T to) returns void |
Add directed edge | |
neighbors(T value) returns list<T> |
Get neighbors of vertex | |
bfs(T start) returns list<T> |
Breadth-first search traversal |
Also implements len(), is_empty(), clear(), to_list() (vertices in insertion order).
| Function | Signature | Description |
|---|---|---|
sort |
<T is Comparable>(list<T> items) returns list<T> |
Quicksort |
binary_search |
<T is Comparable, E is Collection<T>>(E items, T target) returns int |
Returns index or -1 |
max |
<T is Comparable & Stringable, E is Collection<T>>(E collection) returns optional<T> |
Maximum element |
min |
<T is Comparable & Stringable, E is Collection<T>>(E collection) returns optional<T> |
Minimum element |
reverse |
<T, E is Collection<T>>(E collection) returns list<T> |
Reversed list |
mux-lang/
├── mux-compiler/
│ ├── src/
│ │ ├── ast/
│ │ │ ├── types.rs
│ │ │ ├── nodes.rs
│ │ │ ├── literals.rs
│ │ │ ├── operators.rs
│ │ │ ├── patterns.rs
│ │ │ ├── error.rs
│ │ │ └── mod.rs
│ │ ├── codegen/
│ │ │ ├── mod.rs
│ │ │ ├── expressions.rs
│ │ │ ├── statements.rs
│ │ │ ├── functions.rs
│ │ │ ├── methods.rs
│ │ │ ├── classes.rs
│ │ │ ├── constructors.rs
│ │ │ ├── operators.rs
│ │ │ ├── generics.rs
│ │ │ ├── types.rs
│ │ │ ├── memory.rs
│ │ │ └── runtime.rs
│ │ ├── semantics/
│ │ │ ├── mod.rs
│ │ │ ├── types.rs
│ │ │ ├── symbol_table.rs
│ │ │ ├── unifier.rs
│ │ │ ├── format.rs
│ │ │ └── error.rs
│ │ ├── lexer/
│ │ │ ├── mod.rs
│ │ │ ├── token.rs
│ │ │ ├── span.rs
│ │ │ └── error.rs
│ │ ├── parser/
│ │ │ ├── mod.rs
│ │ │ └── error.rs
│ │ ├── diagnostic/
│ │ │ ├── mod.rs
│ │ │ ├── emitter.rs
│ │ │ ├── files.rs
│ │ │ └── styles.rs
│ │ ├── module_resolver.rs
│ │ ├── source.rs
│ │ ├── lib.rs
│ │ └── main.rs
│ └── tests/
│ ├── lexer_integration.rs
│ ├── parser_integration.rs
│ ├── semantics_integration.rs
│ ├── executable_integration.rs
│ └── snapshots/
│
├── mux-runtime/
│ ├── src/
│ │ ├── lib.rs
│ │ ├── object.rs
│ │ ├── refcount.rs
│ │ ├── boxing.rs
│ │ ├── bool.rs
│ │ ├── int.rs
│ │ ├── float.rs
│ │ ├── string.rs
│ │ ├── list.rs
│ │ ├── map.rs
│ │ ├── set.rs
│ │ ├── optional.rs
│ │ ├── result.rs
│ │ ├── io.rs
│ │ ├── math.rs
│ │ └── std.rs
│ └── Cargo.toml
│
├── test_scripts/
│ ├── error_cases/
│ │ ├── *.mux
│ └── *.mux
│
├── Cargo.toml
├── Cargo.lock
└── AGENTS.md
Mux is licensed under the MIT license.
