Variables & Types

Chasm's type system, variable declarations, type annotations, and the atom type.

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       # atom

Add :: followed by a type to annotate explicitly:

x    :: int    = 42
y    :: float  = 3.14
name :: string = "chasm"
ok   :: bool   = true
tag  :: atom   = :idle

Both 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 channel

Hexadecimal (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.0

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

and, 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_over

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

The key practical difference from strings:

AtomString
Written:idle"idle"
ComparisonO(1), identityO(n), character by character
MutableNoNo
Use forState tags, enum-like valuesText 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 reset

Lifetime 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 true

This is intentional. In game math, accidentally mixing int and float arithmetic is a common source of subtle bugs. Explicit conversion keeps the intent visible.