This page walks through writing and running a real Chasm program, explaining decisions along the way.
Hello, World
Create a file called hello.chasm:
def main() do
print("Hello, Chasm!")
endRun it:
chasm run hello.chasm
# Hello, Chasm!
def main() is the entry point for standalone programs. def declares a public function. print writes a value followed by a newline, it works on int, float, bool, and string.
Adding Variables
def main() do
name = "world"
x = 42
msg = "hello #{name}, x is #{x}"
print(msg)
endname, x, and msg are local variables, they exist only for the duration of the function call. The compiler infers their types: string, int, and string.
The #{} syntax interpolates any expression directly into a string. No str(x) calls needed.
Functions
Use def for public functions (callable from the outside) and defp for private helpers. Private functions require an explicit return type annotation:
def main() do
print(fib(10)) # 55
end
defp fib(n :: int) :: int do
if n <= 1 do
return n
end
return fib(n - 1) + fib(n - 2)
endParameters always need a type annotation (n :: int). The :: int after the parameter list is the return type. return exits early, the last expression is also implicitly returned so you can often omit it.
Structs
A struct groups related values into a single named type. Structs are value types, copying a struct copies all its fields:
defstruct Vec2 do
x :: float
y :: float
end
defp length(v :: Vec2) :: float do
sqrt(v.x * v.x + v.y * v.y)
end
def main() do
pos = Vec2{ x: 3.0, y: 4.0 }
print(length(pos)) # 5.0
pos2 = pos with { x: 0.0 } # copy with x overridden
print(pos2.y) # 4.0, y is unchanged
endThe with keyword creates a modified copy of a struct. Only the listed fields change.
Arrays
An array is an ordered collection of values. The argument to array_new is the initial capacity, how much space to pre-allocate. It starts empty (len = 0):
def main() do
scores = array_new(4) # capacity 4, length 0
scores.push(100)
scores.push(85)
scores.push(92)
print(scores.len) # 3, elements stored
print(scores.cap) # 4, space reserved
print(scores.get(0)) # 100
total = 0
for i in 0..scores.len do
total = total + scores.get(i)
end
print(total) # 277
endlen and cap are different things. len is how many elements are actually stored. cap is how much space is available before the array needs to grow.
A Game Loop Script
Chasm shines in game engines. Here is a minimal script that tracks a player moving across the screen:
# Module attributes, module-level state that survives between frames
@player_x :: script = 0.0
@player_y :: script = 0.0
@speed :: script = 200.0
def on_tick(dt :: float) do
# dt is the time since the last frame, typically 0.016 at 60fps
# Multiplying by dt keeps movement frame-rate independent
dx = 0.0
if key_down(:right) do dx = @speed end
if key_down(:left) do dx = -@speed end
@player_x = @player_x + dx * dt
end
def on_draw() do
draw_rectangle(@player_x, @player_y, 32.0, 32.0, 0x4488ffff)
endThe @ prefix marks a module attribute, unlike local variables, they survive across calls to on_tick. The :: script lifetime means they persist until the script is hot-reloaded. See Attributes & Lifetimes for the full picture.
Next step:
Continue to Variables & Types for a complete tour of the type system.