OxigenLang Type System
OxigenLang is dynamically typed by default, but offers optional type annotations that lock a variable's type for its lifetime in scope. This gives you explicit control over type safety and mutability.
#Supported Types
| Type | Keyword | Zero Value | Example Values |
|---|---|---|---|
| Integer | int | 0 | 42, -7 |
| Float | float | 0.0 | 3.14, -0.5 |
| String | str | "" | "hello", 'world' |
| Char | char | \0 | `a`, `Z` |
| Boolean | bool | False | True, False |
| Array | array | [] | [1, 2, 3] |
| Byte | byte | 0 | byte(65) |
| Uint | uint | 0 | uint(42) |
| Tuple | tuple | () | (1, "hello") |
| Map | map | {} | {"a": 1} |
| Set | set | set() | set(1, 2, 3) |
| Struct | struct name | — | Person("Alice", 30) |
Structs are composite types with typed fields. They are defined with the struct keyword and have their own type identity (e.g., type(p) returns "Person"). Struct names can be used as type annotations to lock a variable to a specific struct type:
struct Person { name <str> age <int> }
p <Person> := Person("Alice", 30)
p = Person("Bob", 25)
p = 42Struct types support as declarations — the instance is created with all fields set to their zero values (e.g., "" for str, 0 for int). See the Structs guide for full details.
#Declaration Forms
OxigenLang offers four ways to declare variables, each with different mutability and type-safety semantics. See the Variables and Assignments guide for a comprehensive walkthrough.
#Untyped (default)
Variables declared with := have no type constraint. Their type can change freely on reassignment.
x := 10
x := "hello"#Typed Strict: x <type> = value
Declares a variable with a locked type and an immutable value. The value must already be the correct type — no conversion is performed.
x <int> = 10
x <int> = "hello"- Type is locked — cannot be reassigned to a different type.
- Value is immutable —
=and++/--are blocked. - Only
:=(walrus) or explicit re-declaration can override the binding.
#Typed Walrus: x <type> := value
Declares a variable with a locked type but a mutable value. Attempts to convert the value to the target type at declaration time.
x <int> := 3.9
x <str> := 42
x <bool> := 0- Type is locked — reassignments must match the type.
- Value is mutable —
=,++/--, and:=work (with type checking).
#Zero-Init: x <type> or x as <type>
Declares a typed variable initialized to its zero value. The value is mutable. The as keyword is optional — x <type> is the preferred convention.
x <int>
y <str>
z <array>
p <Person>#Reassignment Rules
#= (strict assign)
Only works on typed variables. Performs a strict type check — the value must already be the correct type.
x <int> := 10
x = 20
x = "hello"
y := 10
y = 20On immutable variables (declared with <type> =), = is blocked entirely:
x <int> = 10
x = 20#:= (walrus reassign)
For existing typed variables, := performs a strict type check — no implicit conversion. The type is locked.
x <int> := 10
x := 20
x := "hello"For untyped variables, := updates freely as before:
x := 10
x := "hello"#++ / -- (postfix)
Works on mutable integer variables. Blocked on immutable bindings.
x <int> := 5
x++
y <int> = 5
y++#Type Conversion Table
When using typed walrus declarations (x <type> := value), OxigenLang attempts these conversions:
| Target | From int | From float | From str | From char | From bool |
|---|---|---|---|---|---|
int | identity | truncate | parse | ord | 0/1 |
float | widen | identity | parse | — | 0.0/1.0 |
str | display | display | identity | display | display |
char | chr | — | single chr | identity | — |
bool | truthy | truthy | truthy | truthy | identity |
array | — | — | chars | — | — |
byte | 0-255 | — | — | as u8 | 0/1 |
uint | >= 0 | truncate | parse | — | 0/1 |
A — means the conversion is not supported and will produce an error.
#Conversion Examples
x <int> := 3.9
y <str> := 42
z <bool> := 0
w <float> := 10
c <char> := 65
b <byte> := `A`
u <uint> := "100"
a <array> := "hello"#Indexing and Slicing
Arrays, strings, and tuples support indexing ([i]) and slicing ([start:end]). Maps support key-based indexing (m[key]). See the Operators guide for full details.
arr := [10, 20, 30, 40, 50]
arr[0]
arr[1:3]
arr[2:]
arr[:2]
"hello"[1:4]
(1, 2, 3)[0:2]#In-Place Type Conversion
You can convert a variable's type by re-declaring it with a typed walrus:
user_input := "42"
user_input <int> := user_input
user_inputAfter re-declaration the new type constraint is locked:
user_input := "42"
user_input <int> := user_input
user_input := "hello"#Shadowing
Typed variables can be shadowed in inner scopes. The outer binding is unaffected:
x <int> = 10
each i in [1] {
x <str> = "hello"
}
x#Introspection
#is_mut(variable)
Returns True if the variable's value is mutable, False if it is immutable.
a := 10
is_mut(a)
b <int> := 10
is_mut(b)
c <int> = 10
is_mut(c)
d <int>
is_mut(d)#is_type_mut(variable)
Returns True if the variable's type can change (i.e., it has no type constraint), False if the type is locked.
a := 10
is_type_mut(a)
b <int> := 10
is_type_mut(b)
c <int> = 10
is_type_mut(c)
d <int>
is_type_mut(d)#The <generic> Type
<generic> accepts any type. Because it implies the variable can hold values of any type, it is incompatible with strict immutable assignment (=):
x <generic> := 42 // ok — value mutable, accepts any type
x = "hello" // ok — reassignment allowed
y <generic> = 42 // compile error — <generic> cannot be used with '='Use := when you need a variable that can hold any type.
#Summary Table
| Declaration | Value Mutable | Type Locked | = allowed | ++/-- allowed |
|---|---|---|---|---|
x := 10 | yes | no | no | yes |
x <int> = 10 | no | yes | no | no |
x <int> := 10 | yes | yes | yes | yes |
x <int> | yes | yes | yes | yes |
x as <int> | yes | yes | yes | yes |
See also:
- Variables and Assignments — practical guide to declaring and using variables
- Data Types — detailed description of each type
- Structs — struct types and type annotations
- Built-in Functions —
type(),is_mut(),is_type_mut()