Quick Start

Write and run your first Chasm program in five minutes.

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!")
end

Run 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)
end

name, 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)
end

Parameters 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
end

The 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
end

len 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)
end

The @ 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.