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

Lifetime Annotations

For local variables inside a function, you never write a lifetime annotation. The compiler infers it from the right-hand side:

a = 42                   # persistent (integer literal)
b = copy_to_script(a)    # script (result of copy_to_script)
c = persist_copy(a)      # persistent (result of persist_copy)
d = @my_script_attr + 1  # script (max lifetime of @script attr + persistent literal)

For module attributes (@name), the default lifetime is script — no annotation needed. Only write :: persistent or :: frame when you need a non-default:

@score                 = 0          # script (default) — omit the annotation
@best  :: persistent   = 0          # must survive hot-reload
@buf   :: frame        = array_fixed(64, 0)  # auto-clears each tick

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.