Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Traits & Effects (:~)

The :~ annotation domain attaches trait or effect information to a binding. It describes what capabilities a function uses — console IO, file reads, process execution, and so on. Traits annotations are orthogonal to type annotations (::) and can coexist on the same binding.

Syntax

name :~ traitsExpr

The traits expression is typically a list of effect names:

greet :~ [console]
greet world = world.io.println "hello"

Effect Names

EffectCapabilities covered
consoleprintln, print, readLine
fs.readreadFile, exists
fs.writewriteFile, appendFile, remove
execprocess.exec
envgetEnv

Use [] (empty list) or define a name bound to [] to declare a function as pure:

pure :~ []

add :~ pure
add a b = a + b

Defining Effect Groups

You can define reusable groups of effects:

readonly :~ [console, fs.read]
readwrite :~ [console, fs.read, fs.write]

Then reference those groups in other annotations.

Example

distance :~ []
distance x1 y1 x2 y2 = (x2 - x1)**2 + (y2 - y1)**2

main world =
  world.io.println (distance 0 0 3 4)
25

Combining with Other Domains

All annotation domains can coexist on a single binding:

add :? "Add two numbers"
add :: Num : Num : Num
add :~ []
add a b = a + b

Current Status

Trait annotations are parsed, stored, and enforced by the compiler. The compiler performs taint analysis: it tracks the world value and any names that transitively reference it (via aliasing or closures), then infers the effect set of every binding. If a function’s inferred effects are not a subset of its declared traits, the compiler emits an error.

Functions without a :~ annotation are assumed pure (:~ []). This means any function that performs IO must declare its effects. The only exception is main, which is implicitly granted all capabilities.

For example, declaring :~ [] (pure) but calling world.io.println inside the body is a compile error — and so is omitting the annotation entirely:

-- This is an error: no annotation, so assumed pure, but uses console
helper world = world.io.println "oops"

-- Fix: add trait annotation
helper :~ [console]
helper world = world.io.println "ok"