Skip to content

Doxa Language Reference

Core Language Features

Primitive Types

Types in Doxa are grouped in three ways, atomic, molecular, and meta. All together there are 14 of these primitive types to represent data in Doxa.

Atomic

Atomic types are the most basic units of data. Currently Doxa has 3 default number types, with plans for optional dynamic number sizing in the future.

int - i32 float - f64 byte - u8 hex literal string tetra - logical value, see page on tetras for more info nothing - empty type

Molecular

Molecular types are constructed out of atomic types. They are

array - must be homogenous struct - no classes, no inheretance, only composition enum map function - user functions, library functions, etc union - can be handled with the collapse operator, see page on unions for more info

Basic Composition Example

struct Animal {
    name: string
}

struct Dog {
    // Composition instead of inheritance
    animal: Animal,
    breed: string,

    fn bark(self) {
        print(animal.name + " says woof!");
    }
}

var dog is Dog {
    animal: Animal {
        name: "Spot"
        },
    breed: "Labrador"
};

<!-- ### Modules

// math.doxa
export fn add(a :: int, b :: int) -> int {
    return a + b;
}

// main.doxa
import { add } from "./math.doxa";
var sum is add(1, 2);
``` -->

## Data Types

### Basic Types

Standard types include:

- `int`: Integer numbers
- `float`: Floating point numbers
- `string`: Text strings
- `tetra`: Four-valued logic system

The `tetra` type represents a four cornered value with the possible states: `true`, `false`, `both`, and `neither`. For additional information see the tetra page.

````

### Arrays

Arrays are homogeneous collections with type inference:

```doxa
var nums :: int[] is [1, 2, 3];    // Explicit typing
var strs is ["a", "b"];            // Inferred as string[]

// Invalid operations
var mixed is [1, "two", true];     // Error: mixed types
nums.push("four");                // Error: type mismatch
````

### Tuples

Fixed-size collections supporting heterogenus types:

```doxa
var point is (: 10, "hello", true :);         // Simple tuple
var nested is (: (: 1, "hello" :), (: 3, true :) :);    // Nested tuple

point[0];                         // Access first element
nested[1][0];                     // Access nested element

Maps

String-keyed dictionaries:

var scores is {
    "alice": 100,
    "bob": 85
};
scores["alice"];                  // Access value

Control Flow

Pattern Matching

enum Status { Success, Error, Pending }

var result is match status {
    .Success => "all good",
    .Error => "failed",
    else => "waiting"
};

Warning

Match expressions must be exhaustive or include an else clause.

Error Handling

try {
    riskyOperation();
} catch {
    handleError();
}

Special Operators

Peek operator (?)

var x is computeValue();
x?;                              // Prints value with location, name, and type

Type Information

typeof(42);                      // "int"
typeof("hello");                 // "string"
typeof([1,2,3]);                 // "array"

Collection Quantifiers

(∃x ∈ numbers : x > 10) // Logical notation
(exists x in numbers where x > 10) // English prose
(∀x ∈ numbers : x > 0) // Logical notation
(forall x in numbers where x > 0) // English prose

Conditional Expressions

All conditionals are expressions and return values:

var result is if condition then {
    value1
} else {
    value2
};

Note

Expressions without a value return nothing: doxa var x is if (false) { y is 1 }; // x becomes nothing

Function Return Types

Functions can specify return types using either -> or returns syntax:

fn add(a: int, b: int) -> int {
    return a + b;
}

// Alternative syntax
fn add(a: int, b: int) returns(int) {
    return a + b;
}

return types are optional and inferred by default:

// Normal Mode - Valid
fn add(a, b) {
    return a + b;
}

Logic

First order logic

First order logic always produces a true or false value. For more information on tetras see the tetra page.

Doxa has extensive for traditional first order logics that work as expected with true and false values. These can be represented in formal unicode notation:

const arr :: int[] = [1, 2, 3, 4, 5]

// existential quantifier ∃, element of ∈
∃x ∈ arr : x > 3; // true

// universal quantifier ∀, where :
∀x ∈ arr : x > 3; // false

// NOT ¬
¬false; // true

// biconditional ↔
false ↔ false; // true

// XOR ⊕
true ⊕ true; // false

// AND ∧
true ∧ false; // false

// OR ∨
true ∨ false; // true

// NAND ↑
true ↑ false; // true

// NOR ↓
true ↓ false; // false

// implication →
true → false; // false

This unicode support is paired with plaintext keywords which act in an identical fashion:

∃ - exists
∀ - forall
∈ - in
: - where
¬ - not
↔ - iff
⊕ - xor
∧ - and
∨ - or
↑ - nand
↓ - nor
→ - implies

This means formal logical representation can be written in either way:

const arr :: int[] = [1, 2, 3, 4, 5]

¬(∀x ∈ arr : x > 3); // true
not (forall x in arr where x > 3); // true

Trancendental logic

There are currently two trancendental operators, and and not. These can be represented by the following truth tables:

^ T F B N
F F B B N
T B T B N
B B B B N
N N N N N
~
F T
T F
B N
N B