Chasm is statically typed. Every value has a type known at compile time. The compiler infers most types automatically, you only write them when you want to be explicit or when the compiler cannot figure it out.
Variable Declaration
A plain assignment infers the type:
x = 42 # int
y = 3.14 # float
name = "chasm" # string
ok = true # bool
tag = :idle # atomAdd :: followed by a type to annotate explicitly:
x :: int = 42
y :: float = 3.14
name :: string = "chasm"
ok :: bool = true
tag :: atom = :idleBoth forms are valid. The annotation makes the type visible to readers and will cause a compile error if the right-hand side doesn't match.
int
64-bit signed integer. The range is roughly −9.2 × 10¹⁸ to +9.2 × 10¹⁸, far more than enough for any game value.
Literals can be written in decimal or hexadecimal:
lives = 3
score = 0
tile_size = 32
color = 0xffffffff # 32-bit RGBA packed as hex, very common for colors
mask = 0x000000ff # just the alpha channelHexadecimal (0x) is used frequently for colors because 0xRRGGBBAA reads as a natural RGBA value.
Integer arithmetic truncates toward zero, 7 / 2 = 3, not 3.5. To get a float result, ensure at least one operand is a float:
a = 7 / 2 # 3 (integer division)
b = 7.0 / 2 # 3.5 (float division)
c = 7 / 2.0 # 3.5 (float division)float
64-bit IEEE 754 double-precision float. Always write a decimal point to tell the compiler it is a float:
speed = 400.0
gravity = -9.8
pi = 3.14159
zero = 0.0Floats are the default numeric type in game math. Positions, velocities, angles, and lerp factors are all floats.
bool
Boolean: true or false. Used in conditions and returned by comparison operators:
alive = true
paused = false
if alive and not paused do
print("running")
endand, or, and not are the boolean operators, Chasm does not use &&, ||, or !.
string
An immutable UTF-8 byte sequence. String literals use double quotes. Interpolation embeds any expression with #{}:
lang = "Chasm"
major = 1
msg = "#{lang} v#{major}" # "Chasm v1"Strings are immutable, operations like .concat and .upper produce new strings, they do not modify the original. See Strings for all operations.
atom
This type is unique to Chasm (and similar to Elixir atoms). An atom is a compile-time named constant written with a leading colon:
state = :idle
dir = :north
status = :game_overAtoms look like strings but behave like integers under the hood, they compare by identity, not by character content. This makes them efficient for use in case expressions and as state tags:
if @state == :idle do
@state = :running
endThe key practical difference from strings:
| Atom | String | |
|---|---|---|
| Written | :idle | "idle" |
| Comparison | O(1), identity | O(n), character by character |
| Mutable | No | No |
| Use for | State tags, enum-like values | Text content, user-facing messages |
If you find yourself writing if status == "idle", use an atom instead.
Type Annotations with ::
The :: token introduces a type annotation anywhere a type can appear:
# Variable declaration
x :: int = 10
# Function parameter
defp add(a :: int, b :: int) :: int do ...
# Loop variable in for/in
for item :: Bullet in @bullets do ...Explicit Lifetime Annotations
Variables can carry an explicit lifetime in addition to (or instead of) a type:
temp :: frame = compute() # this value reset every tick
cached :: script = load() # reset on hot-reload
saved :: persistent = restore() # never resetLifetime annotations matter most for module attributes (@name). For local variables inside a function, the compiler infers frame unless you explicitly promote. See Attributes & Lifetimes for the full picture.
Type Conversions
Chasm does not implicitly convert between int and float. Use the explicit conversion functions:
i = 7
f = to_float(i) # 7.0, int to float
i2 = to_int(3.9) # 3 , float to int, truncates toward zero
b = to_bool(0) # false, 0 is false, anything else is trueThis is intentional. In game math, accidentally mixing int and float arithmetic is a common source of subtle bugs. Explicit conversion keeps the intent visible.