Universal Markdown Weaver

SIGNAL Programming Language — Technical Overview

A complete technical introduction to SIGNAL: its type system, execution model, primitives, and canonical use cases.


SIGNAL Programming Language — Technical Overview

A reactive, statically-typed language for event-driven systems, data processing, and domain-driven design.


Table of Contents

  1. Language Philosophy
  2. Minimal Implementation Guide
  3. Lexical Structure
  4. Type System
  5. Variables and Bindings
  6. Operators
  7. Control Flow
  8. Functions
  9. Reactive Core: Signals, Rules, and Emit
  10. Domains (Algebraic Data Types)
  11. Entities (Record Types)
  12. State Machines
  13. Flows (Data Pipelines)
  14. Queries
  15. Pattern Matching
  16. Error Handling
  17. Concurrency
  18. Module System
  19. Standard Library
  20. SIGNAL-Lite (Embeddable Subset)
  21. Grammar Reference
  22. Complexity Analysis

1. Language Philosophy

SIGNAL is built on the Unified Council model — four foundational perspectives that shape every feature:

| Perspective | Influence | Contribution | |---|---|---| | Knuth (Algorithmic) | Complexity guarantees, precise numerics | Every operation has a stated O(·) bound | | Erdos (Graph-theoretic) | Entity relationships, state machines | Structures are graphs; transitions are edges | | Turing (Computational) | Type system, pattern matching, decidability | The type checker terminates; matching is exhaustive | | Shannon (Information-theoretic) | Signals, channels, entropy builtins | Events are discrete messages on noiseless channels |

SIGNAL is:

  • Expression-orientedif, match, loop all return values
  • Reactive-first — signals and rules are primitives, not libraries
  • Strongly typed with inference — types are checked statically but rarely written explicitly
  • Functional-imperative hybrid — first-class functions with controlled mutation via var

2. Minimal Implementation Guide

To build a working SIGNAL interpreter, you need five components:

2.1 Lexer (Tokenizer)

Converts source text into a stream of tokens. Minimum token set:

Keywords:    let  var  fn  return  if  then  else  match  for  in
             while  loop  break  continue  true  false  null
             and  or  not  domain  entity  sig  rule  when  emit
             create  update  delete  try  catch

Literals:    INTEGER  FLOAT  STRING  BOOLEAN  NULL
Identifiers: IDENT  TYPE_IDENT (uppercase-start)
Operators:   +  -  *  /  %  **  ++  ==  !=  <  >  <=  >=
             =  +=  -=  *=  /=  |>  =>  ->
Grouping:    (  )  {  }  [  ]
Punctuation: ,  .  :  ;

The lexer is a single-pass O(n) scanner. Key rules:

  • Identifiers starting with uppercase are TYPE_IDENT (used for domains, entities, signals)
  • // starts a line comment; /* ... */ for block comments
  • Strings support interpolation: "Hello, {name}!"
  • Number literals: 42, 3.14, 0xFF, 0b1010, 1000000

2.2 Parser (AST Builder)

Recursive-descent, no backtracking, O(n). The core grammar:

Program      -> Declaration*
Declaration  -> FnDecl | DomainDecl | EntityDecl | SigDecl
              | RuleDecl | Statement

FnDecl       -> 'fn' IDENT '(' Params ')' ('->' Type)? Block
DomainDecl   -> 'domain' TYPE_IDENT '=' Variant ('|' Variant)*
EntityDecl   -> 'entity' TYPE_IDENT Field*
SigDecl      -> 'sig' TYPE_IDENT '(' Params ')'
RuleDecl     -> 'rule' IDENT 'when' TYPE_IDENT '(' Params ')'
                ('where' Expr)? 'then' Block

Statement    -> LetStmt | VarStmt | ReturnStmt | ExprStmt | Block
LetStmt      -> 'let' IDENT '=' Expr
VarStmt      -> 'var' IDENT '=' Expr

Expr         -> Pipe
Pipe         -> LogicOr ('|>' LogicOr)*
LogicOr      -> LogicAnd ('or' LogicAnd)*
LogicAnd     -> Equality ('and' Equality)*
Equality     -> Comparison (('==' | '!=') Comparison)*
Comparison   -> Addition (('<' | '>' | '<=' | '>=') Addition)*
Addition     -> Multiplication (('+' | '-' | '++') Multiplication)*
Multiplication -> Power (('*' | '/' | '%') Power)*
Power        -> Unary ('**' Unary)?
Unary        -> ('not' | '-') Unary | Call
Call         -> Primary ('(' Args ')' | '.' IDENT | '[' Expr ']')*
Primary      -> Literal | IDENT | '(' Expr ')' | '[' Exprs ']'
              | '{' MapEntries '}' | IfExpr | MatchExpr | Lambda

Operator precedence (lowest to highest):

| Level | Operators | Associativity | |-------|-----------|---------------| | 1 | \|> | Left | | 2 | or | Left | | 3 | and | Left | | 4 | == != | Left | | 5 | < > <= >= | Left | | 6 | + - ++ | Left | | 7 | * / % | Left | | 8 | ** | Right | | 9 | not - (unary) | Right | | 10 | . () [] | Left |

2.3 AST (Abstract Syntax Tree)

Node types your AST must represent:

// Declarations
FnDecl        { name, params[], returnType?, body }
DomainDecl    { name, variants[] }
EntityDecl    { name, fields[], derivedFields[] }
SigDecl       { name, params[] }
RuleDecl      { name, signal, params[], where?, body }

// Statements
LetStmt       { name, value }
VarStmt       { name, value }
ReturnStmt    { value? }
BreakStmt     { value? }
ContinueStmt  {}
ExprStmt      { expr }
Block         { statements[] }

// Expressions
BinaryExpr    { left, op, right }
UnaryExpr     { op, operand }
CallExpr      { callee, args[] }
MemberExpr    { object, property }
IndexExpr     { object, index }
PipeExpr      { left, right }
IfExpr        { condition, then, else? }
MatchExpr     { subject, arms[] }
ForExpr       { variable, iterable, body }
WhileExpr     { condition, body }
LoopExpr      { body }
TryExpr       { body, catchParam, catchBody }
LambdaExpr    { params[], body }
EmitExpr      { signal, args[] }
CreateExpr    { entity, fields }
UpdateExpr    { target, fields }
DeleteExpr    { target }

// Literals
IntLit, FloatLit, StringLit, BoolLit, NullLit
ArrayLit      { elements[] }
MapLit        { entries[] }

2.4 Runtime Environment

The environment is a chain of scopes (linked hash maps):

Environment {
    values:  Map<String, Value>
    parent:  Environment?       // lexical scope chain

    get(name)    -> looks up chain until found or error
    set(name, v) -> updates existing binding in-place
    define(name, v) -> creates new binding in current scope
}

Value types the runtime must support:

Value = Int(i64)
      | Float(f64)
      | String(string)
      | Bool(bool)
      | Null
      | Array([]Value)
      | Map(map[string]Value)
      | Function(params[], body, closure)
      | BuiltinFn(name, arity, func)
      | DomainVariant(domain, variant, fields)
      | EntityInstance(entity, fields)

2.5 Evaluator (Tree-Walk Interpreter)

The evaluator recursively walks the AST. Core dispatch:

eval(node, env) -> Value:
    match node:
        IntLit(n)           -> Int(n)
        StringLit(s)        -> String(s)
        BoolLit(b)          -> Bool(b)
        NullLit             -> Null
        Ident(name)         -> env.get(name)
        BinaryExpr(l, op, r)-> applyOp(op, eval(l, env), eval(r, env))
        LetStmt(name, val)  -> env.define(name, eval(val, env))
        VarStmt(name, val)  -> env.define(name, eval(val, env))  // mutable
        CallExpr(fn, args)  -> call(eval(fn, env), args.map(a => eval(a, env)))
        IfExpr(c, t, e)     -> if truthy(eval(c, env)) then eval(t, env) else eval(e, env)
        Block(stmts)        -> eval each stmt in child env; return last
        FnDecl(name, p, b)  -> env.define(name, Function(p, b, env))
        LambdaExpr(p, b)    -> Function(p, b, env)  // closure
        ...

2.6 Reactive Runtime (Signals + Rules)

The reactive system is the heart of SIGNAL. Minimum implementation:

ReactiveRuntime {
    signals:  Map<String, SignalDef>       // sig declarations
    rules:    Map<String, []RuleDef>       // rules keyed by signal name
    entities: Map<String, EntityDef>       // entity schemas
    store:    Map<String, []EntityInstance> // entity instances

    registerSignal(name, params)
    registerRule(name, signalName, params, where?, body)

    emit(signalName, args):
        // O(R) dispatch — iterate rules for this signal
        for rule in rules[signalName]:
            childEnv = bind(rule.params, args)
            if rule.where == null or truthy(eval(rule.where, childEnv)):
                eval(rule.body, childEnv)

    create(entityName, fields) -> instance
    update(instance, fields)   -> updated instance
    delete(instance)           -> removed
}

3. Lexical Structure

3.1 Keywords (Full Language)

SIGNAL defines 170+ keywords across several categories:

Core (30):

let  var  fn  return  if  then  else  elif  match  case  when
for  in  while  loop  break  continue  true  false  null
and  or  not  is  try  catch  finally  throw  import  export

Reactive (12):

sig  signal  rule  emit  domain  entity  derive
create  update  delete  with  where

Concurrency (8):

async  await  spawn  join  select  send  receive  atomic

Infrastructure (16):

server  websocket  resource  component  actor  test  suite
database  queue  config  schedule  migration  extern  query  metric  flow

State Machine (8):

state  machine  initial  transition  guard  action  enter  exit

Modifiers (11):

pub  private  protected  static  final  abstract
override  virtual  mutable  immutable  lazy

Types (22):

Integer  Float  Number  Text  String  Boolean  Void  Any  Never
DateTime  Duration  UUID  Bytes  Chan  Task  Stream
BigInt  Rational  Fixed  Interval  Map  Set

3.2 Operators

Arithmetic: + - / % * String: ++ (concatenation) Comparison: == != <> < > <= >= Logical: and or not && || ! Assignment: = += -= *= /= %= Bitwise: & | ^ ~ << >> Functional: |> (pipe) ~> (compose) -> (arrow) => (fat arrow) Null-safe: ?? (coalesce) ?. (optional chain) ?! (error propagate) Range: .. (inclusive) ..< (exclusive) ... (spread) Channel: <- (receive/send)

Unicode operators:

<=  >=  !=          also written as  ≤  ≥  ≠
element-of          ∈  ∉
set operations      ∩  ∪  ∅
quantifiers         ∀  ∃
math                ∞  λ  π  Σ  Π  μ  σ

4. Type System

SIGNAL uses bidirectional type inference — the compiler can synthesize types from expressions and check expressions against expected types.

4.1 Primitive Types

let age: Int = 42
let pi: Float = 3.14159
let name: String = "Ada"
let active: Bool = true
let nothing: Null = null

4.2 Collection Types

let nums: [Int] = [1, 2, 3, 4, 5]
let config: Map<String, Any> = {"host": "localhost", "port": 8080}
let pair: (String, Int) = ("hello", 42)

4.3 Function Types

let transform: (Int) -> Int = x => x * 2
let predicate: (String) -> Bool = s => len(s) > 0

4.4 Optional and Result Types

let maybe: Int? = null                         // optional
let result: Result<Int, String> = Ok(42)       // success or error
let option: Option<String> = Some("hello")     // some or none

4.5 Advanced Numeric Types (Knuth Precision)

let big: BigInt = 99999999999999999999999
let ratio: Rational = 1/3                // exact, no floating-point loss
let money: Fixed = 19.99                 // fixed-point decimal
let range: Interval = [2.99, 3.01]       // interval arithmetic

4.6 Union Types

let id: Int | String = "abc-123"

4.7 Type Aliases

type UserId = Int
type Callback = (String) -> Void
type Pair<A, B> = (A, B)

5. Variables and Bindings

5.1 Immutable Bindings (let)

let x = 42
let greeting = "Hello, " ++ name
// x = 99  // ERROR — let bindings cannot be reassigned

5.2 Mutable Bindings (var)

var count = 0
count += 1       // OK — var allows mutation
count = count * 2

5.3 Constants (const)

const MAX_SIZE = 1024
const PI = 3.14159265358979

5.4 Destructuring

let [first, second, ...rest] = [1, 2, 3, 4, 5]
let {name, age} = user
let (x, y) = getPoint()

6. Operators

6.1 Pipe Operator (|>)

The pipe operator passes the left-hand value as the first argument to the right-hand function. It transforms nested calls into readable left-to-right chains:

// Without pipe:
sort(filter(map(data, x => x * 2), x => x > 10))

// With pipe:
data
  |> map(x => x * 2)
  |> filter(x => x > 10)
  |> sort()

6.2 Function Composition (~>)

Creates a new function from two existing functions (Church style — left applies first):

let double = x => x * 2
let addOne = x => x + 1
let doubleAndAdd = double ~> addOne

doubleAndAdd(5)   // 11  (double first: 10, then add one: 11)

6.3 String Concatenation (++)

let full = firstName ++ " " ++ lastName

6.4 Null Coalescing (??)

let port = config.port ?? 8080
let name = user?.name ?? "Anonymous"

6.5 Error Propagation (?!)

fn loadConfig(path: String): Result<Config, String> {
    let raw = readFile(path)?!           // propagates Err automatically
    let parsed = jsonParse(raw)?!
    Ok(parsed)
}

6.6 Range Operators

let inclusive = 1..10     // [1, 2, 3, ..., 10]
let exclusive = 1..<10    // [1, 2, 3, ..., 9]

for i in 0..<len(items) {
    println(items[i])
}

7. Control Flow

7.1 If Expressions

if is an expression — it returns a value:

let status = if score >= 90 then "A"
             else if score >= 80 then "B"
             else "C"

// Block form:
let result = if condition {
    computeA()
} else {
    computeB()
}

7.2 Match Expressions

Exhaustive pattern matching:

let label = match statusCode {
    200 => "OK"
    404 => "Not Found"
    500 => "Server Error"
    code if code >= 400 => "Client Error"
    _   => "Unknown"
}

Matching on domain variants:

domain Shape = Circle(r: Float) | Rect(w: Float, h: Float) | Triangle(a: Float, b: Float, c: Float)

fn area(s: Shape) -> Float {
    match s {
        Circle(r)      => 3.14159 * r ** 2
        Rect(w, h)     => w * h
        Triangle(a,b,c) => {
            let s = (a + b + c) / 2
            sqrt(s * (s-a) * (s-b) * (s-c))
        }
    }
}

7.3 For Loops

for item in collection {
    println(item)
}

for (key, value) in map {
    println(key ++ ": " ++ toString(value))
}

for i in 0..<10 {
    println(i)
}

7.4 While Loops

var n = 1
while n <= 100 {
    if n % 15 == 0 { println("FizzBuzz") }
    else if n % 3 == 0 { println("Fizz") }
    else if n % 5 == 0 { println("Buzz") }
    else { println(n) }
    n += 1
}

7.5 Loop (Infinite)

var attempts = 0
let result = loop {
    attempts += 1
    let r = tryConnect()
    if isOk(r) { break unwrap(r) }
    if attempts > 5 { break null }
}

8. Functions

8.1 Named Functions

fn add(a: Int, b: Int) -> Int {
    a + b
}

// Expression shorthand:
fn double(x) => x * 2
fn square(x) = x * x

8.2 Lambda Expressions

let inc = x => x + 1
let multiply = (a, b) => a * b
let greet = name => "Hello, " ++ name ++ "!"

8.3 Higher-Order Functions

fn apply(f, x) => f(x)
fn compose(f, g) => x => g(f(x))

let nums = [1, 2, 3, 4, 5]
let evens = filter(x => x % 2 == 0, nums)
let doubled = map(x => x * 2, nums)
let total = reduce((acc, x) => acc + x, 0, nums)

8.4 Closures

Functions capture their lexical environment:

fn makeCounter(start: Int) {
    var count = start
    fn next() {
        count += 1
        count
    }
    next
}

let counter = makeCounter(0)
println(counter())    // 1
println(counter())    // 2
println(counter())    // 3

8.5 Generators

gen fibonacci() {
    var a = 0
    var b = 1
    loop {
        yield a
        let temp = a
        a = b
        b = temp + b
    }
}

let fibs = fibonacci()
for i in 0..<10 {
    println(fibs())
}

8.6 Async Functions

async fn fetchData(url: String) -> Result<String, String> {
    let response = await httpGet(url)
    if response.status == 200 {
        Ok(response.body)
    } else {
        Err("HTTP " ++ toString(response.status))
    }
}

8.7 Annotations

@tailrec
fn factorial(n: Int, acc: Int = 1) -> Int {
    if n <= 1 then acc
    else factorial(n - 1, n * acc)
}

@memo
fn fib(n: Int) -> Int {
    if n <= 1 then n
    else fib(n - 1) + fib(n - 2)
}

@pure
fn add(a, b) => a + b

@complexity("O(n log n)")
fn mergeSort(arr) { ... }

9. Reactive Core: Signals, Rules, and Emit

This is the defining feature of SIGNAL. The reactive system models events as discrete signals on noiseless channels (Shannon's channel model).

9.1 Declaring Signals

A signal is a named event shape — it defines what can happen:

sig UserCreated(id: UUID, name: String, email: String)
sig OrderPlaced(orderId: Int, userId: UUID, total: Float)
sig PaymentReceived(orderId: Int, amount: Float)
sig TemperatureReading(sensorId: String, celsius: Float)

9.2 Defining Rules

A rule declares a reaction — when a signal fires, what happens:

rule logNewUser when UserCreated(id, name, email) then {
    println("New user: " ++ name ++ " (" ++ email ++ ")")
}

rule sendWelcome when UserCreated(id, name, email)
    where endsWith(email, "@company.com")
    then {
        emit SendEmail(email, "Welcome, " ++ name ++ "!")
    }

The optional where clause filters which emissions trigger the rule.

9.3 Emitting Signals

emit UserCreated(uuid(), "Alice", "alice@company.com")
emit OrderPlaced(1001, userId, 59.99)

When emit fires:

  1. The runtime finds all rules registered for that signal name — O(R) where R is the rule count
  2. Each rule's parameters are bound to the emitted arguments
  3. If a where clause exists, it is evaluated — skip if falsy
  4. The rule body executes in a child environment

9.4 Signal Chaining

Rules can emit further signals, creating event cascades:

sig OrderPlaced(id: Int, user: UUID, total: Float)
sig InventoryReserved(orderId: Int)
sig PaymentCharged(orderId: Int, amount: Float)
sig OrderConfirmed(orderId: Int)

rule reserveStock when OrderPlaced(id, user, total) then {
    // ... reserve inventory
    emit InventoryReserved(id)
}

rule chargePayment when InventoryReserved(orderId) then {
    let order = getOrder(orderId)
    emit PaymentCharged(orderId, order.total)
}

rule confirmOrder when PaymentCharged(orderId, amount) then {
    emit OrderConfirmed(orderId)
    println("Order #" ++ toString(orderId) ++ " confirmed for $" ++ toString(amount))
}

10. Domains (Algebraic Data Types)

Domains define sum types — a value is exactly one of several variants:

10.1 Simple Enumerations

domain Color = Red | Green | Blue
domain Direction = North | South | East | West

10.2 Variants with Data

domain Shape
    = Circle(radius: Float)
    | Rectangle(width: Float, height: Float)
    | Triangle(a: Float, b: Float, c: Float)
    | Point

domain Result = Ok(value: Any) | Err(message: String)
domain Option = Some(value: Any) | None

10.3 Recursive Domains

domain Expr
    = Num(value: Float)
    | Add(left: Expr, right: Expr)
    | Mul(left: Expr, right: Expr)
    | Neg(inner: Expr)

fn eval(e: Expr) -> Float {
    match e {
        Num(v)    => v
        Add(l, r) => eval(l) + eval(r)
        Mul(l, r) => eval(l) * eval(r)
        Neg(i)    => -eval(i)
    }
}

let expr = Add(Mul(Num(3), Num(4)), Neg(Num(1)))
println(eval(expr))  // 11.0

10.4 Pattern Matching with Domains

domain Animal = Dog(name: String) | Cat(name: String, indoor: Bool) | Fish(species: String)

fn describe(a: Animal) -> String {
    match a {
        Dog(name)           => name ++ " is a good dog"
        Cat(name, true)     => name ++ " is an indoor cat"
        Cat(name, false)    => name ++ " roams freely"
        Fish(species)       => "A " ++ species
    }
}

11. Entities (Record Types)

Entities are product types — mutable records with named fields. They model real-world objects with CRUD operations.

11.1 Declaration

entity User
    id:         UUID
    name:       String
    email:      String
    role:       String = "member"        // default value
    bio:        String?                  // optional (nullable)
    created_at: DateTime = now()

    derive displayName = name ++ " (" ++ role ++ ")"
    derive isAdmin = role == "admin"

11.2 CRUD Operations

// Create
let alice = create User {
    id: uuid(),
    name: "Alice",
    email: "alice@example.com"
}

// Read (field access)
println(alice.name)
println(alice.displayName)   // derived field, computed on access

// Update
update alice with {
    role: "admin",
    bio: "Engineering lead"
}

// Delete
delete alice

11.3 Derived Fields

Derived fields are computed properties — pure functions of other fields:

entity Rectangle
    width:  Float
    height: Float

    derive area      = width * height
    derive perimeter = 2 * (width + height)
    derive isSquare  = width == height

let r = create Rectangle { width: 5.0, height: 3.0 }
println(r.area)       // 15.0
println(r.perimeter)  // 16.0
println(r.isSquare)   // false

12. State Machines

State machines model entities with discrete states and guarded transitions.

12.1 Declaration

state machine OrderStatus {
    initial: pending

    state pending {
        enter { println("Order created, awaiting payment") }
    }

    state paid {
        enter { emit OrderPaid(orderId) }
    }

    state shipped {
        enter { emit OrderShipped(orderId) }
        exit  { println("Order has left the warehouse") }
    }

    state delivered
    state cancelled

    transition pay:       pending   -> paid      guard paymentValid
    transition ship:      paid      -> shipped   guard inventoryAvailable
    transition deliver:   shipped   -> delivered
    transition cancel:    pending   -> cancelled
    transition cancel:    paid      -> cancelled  guard refundProcessed
}

12.2 Transitions

Each transition has:

  • Name — a label for the transition
  • SourceTarget — the state change
  • Guard (optional) — a boolean condition that must be true
  • Action (optional) — code to execute during transition
transition approve: pending -> approved
    guard { user.role == "admin" and request.valid }
    action { emit RequestApproved(request.id) }

12.3 State Entry/Exit Hooks

state active {
    enter { startMonitoring() }
    exit  { stopMonitoring(); saveSnapshot() }
}

13. Flows (Data Pipelines)

Flows are declarative, reactive data transformation pipelines:

flow ProcessOrders
    from OrderPlaced.*
    | filter total > 100.0
    | map { orderId, total, category: categorize(total) }
    | group by category
    | window tumbling 1h
    | select { category, count: count(), avgTotal: avg(total) }
    -> Dashboard

13.1 Flow Operations

| Operation | Description | |-----------|-------------| | filter | Keep events matching predicate | | map | Transform each event | | group by | Partition by field | | window | Time-based windowing (tumbling, sliding, session) | | select | Project/aggregate fields | | sort by | Order results | | limit | Cap output count | | distinct | Remove duplicates |

13.2 Window Types

// Non-overlapping 5-minute windows:
| window tumbling 5m

// 10-minute windows sliding every 1 minute:
| window sliding 10m step 1m

// Group by inactivity gaps:
| window session 30m

14. Queries

Named, parameterized data retrievals:

query ActiveUsers(minAge: Int)
    = User
        where role != "banned"
        and age >= @minAge
        and lastLogin >= today - 30d
        order by name
        limit 100

query OrderStats(year: Int)
    = Order
        where dateYear(created_at) == @year
        group by month
        select { month, total: sum(amount), count: count() }

Usage:

let users = ActiveUsers(18)
let stats = OrderStats(2026)

15. Pattern Matching

15.1 Literal Patterns

match x {
    0 => "zero"
    1 => "one"
    _ => "many"
}

15.2 Guard Clauses

match temperature {
    t if t < 0   => "freezing"
    t if t < 20  => "cold"
    t if t < 30  => "comfortable"
    _            => "hot"
}

15.3 Destructuring Patterns

match point {
    (0, 0)    => "origin"
    (x, 0)    => "on x-axis at " ++ toString(x)
    (0, y)    => "on y-axis at " ++ toString(y)
    (x, y)    => "at (" ++ toString(x) ++ ", " ++ toString(y) ++ ")"
}

15.4 Domain Variant Patterns

domain Tree = Leaf(Int) | Branch(Tree, Tree)

fn sum(t: Tree) -> Int {
    match t {
        Leaf(n)       => n
        Branch(l, r)  => sum(l) + sum(r)
    }
}

16. Error Handling

16.1 Try/Catch

try {
    let data = readFile("config.json")
    let config = jsonParse(data)
    println(config.host)
} catch err {
    println("Config error: " ++ err)
}

16.2 Try/Catch/Finally

let db = dbOpen("app.sqlite")
try {
    dbExec(db, "INSERT INTO logs (msg) VALUES (?)", [message])
} catch err {
    println("DB error: " ++ err)
} finally {
    dbClose(db)
}

16.3 Result Type

fn divide(a: Float, b: Float) -> Result<Float, String> {
    if b == 0 {
        Err("Division by zero")
    } else {
        Ok(a / b)
    }
}

match divide(10, 3) {
    Ok(v)  => println("Result: " ++ toString(v))
    Err(e) => println("Error: " ++ e)
}

16.4 Error Propagation

The ?! operator unwraps Ok or early-returns Err:

fn processFile(path: String) -> Result<Data, String> {
    let raw = readFileSafe(path)?!
    let parsed = parseCsv(raw)?!
    let validated = validateData(parsed)?!
    Ok(validated)
}

17. Concurrency

17.1 Spawn and Await

let task1 = spawn { heavyComputation(dataA) }
let task2 = spawn { heavyComputation(dataB) }

let resultA = await task1
let resultB = await task2

17.2 Channels

let ch = chan(10)     // buffered channel, capacity 10

spawn {
    for i in 0..<100 {
        ch <- i       // send
    }
}

spawn {
    loop {
        let msg = <- ch   // receive
        println(msg)
    }
}

17.3 Select (Multiplexing)

select {
    msg <- inbox     => handleMessage(msg)
    tick <- timer    => handleTick()
    timeout 5s       => handleTimeout()
}

17.4 Atomic Blocks

var balance = 1000

atomic {
    if balance >= amount {
        balance -= amount
        emit PaymentProcessed(amount)
    }
}

17.5 Actor Model

actor AccountManager {
    var accounts = {}

    handle CreateAccount(name, initialBalance) {
        accounts[name] = initialBalance
        emit AccountCreated(name)
    }

    handle Transfer(from, to, amount) {
        if accounts[from] >= amount {
            accounts[from] -= amount
            accounts[to] += amount
            emit TransferComplete(from, to, amount)
        }
    }
}

18. Module System

18.1 Module Declaration

module myapp.auth.handlers

18.2 Imports

import { UserService, AuthToken } from myapp.auth
import { hash, verify } from crypto.bcrypt
import http.server as srv

18.3 Exports

export fn authenticate(token: String) -> Result<User, String> { ... }
export entity Session { ... }

// or use the pub modifier:
pub fn publicFunction() { ... }

19. Standard Library

19.1 Math (30+ functions)

abs(-5)                 // 5
min(3, 7)               // 3
max(3, 7)               // 7
floor(3.7)              // 3
ceil(3.2)               // 4
round(3.5)              // 4
sqrt(144)               // 12.0
pow(2, 10)              // 1024
log(100)                // 4.605...
log2(1024)              // 10.0
sin(PI / 2)             // 1.0
clamp(15, 0, 10)        // 10

19.2 Collections (40+ functions)

len([1,2,3])                        // 3
head([1,2,3])                       // 1
tail([1,2,3])                       // [2,3]
append([1,2], 3)                    // [1,2,3]
concat([1,2], [3,4])                // [1,2,3,4]
reverse([1,2,3])                    // [3,2,1]
sort([3,1,2])                       // [1,2,3]
unique([1,2,2,3,3])                 // [1,2,3]
flatten([[1,2],[3,4]])              // [1,2,3,4]
zip([1,2,3], ["a","b","c"])         // [(1,"a"),(2,"b"),(3,"c")]
range(1, 5)                         // [1,2,3,4,5]

// Functional
map(x => x * 2, [1,2,3])           // [2,4,6]
filter(x => x > 2, [1,2,3,4])      // [3,4]
reduce((a,x) => a + x, 0, [1,2,3]) // 6
find(x => x > 2, [1,2,3,4])        // 3
every(x => x > 0, [1,2,3])         // true
some(x => x > 5, [1,2,3])          // false
groupBy(x => x % 2, [1,2,3,4])     // {0:[2,4], 1:[1,3]}
partition(x => x > 2, [1,2,3,4])    // [[3,4],[1,2]]

19.3 Strings (35+ functions)

len("hello")                    // 5
upper("hello")                  // "HELLO"
lower("HELLO")                  // "hello"
trim("  hi  ")                  // "hi"
split("a,b,c", ",")             // ["a","b","c"]
strJoin(["a","b","c"], "-")     // "a-b-c"
replace("hello", "l", "r")     // "herro"
startsWith("hello", "hel")     // true
endsWith("hello", "llo")       // true
contains("hello", "ell")       // true
substring("hello", 1, 3)       // "el"
repeat("ab", 3)                // "ababab"
padLeft("42", 5)               // "   42"
charAt("hello", 0)             // "h"

19.4 Type Inspection and Conversion

typeOf(42)          // "Int"
typeOf("hello")     // "String"
isInt(42)           // true
isString(42)        // false
isNull(null)        // true
isArray([1,2])      // true
isFunction(x => x)  // true

toString(42)        // "42"
parseInt("42")      // 42
parseFloat("3.14")  // 3.14

19.5 Information Theory (Shannon)

entropy([1,0,1,1,0,1,0,0])         // Shannon entropy H(X)
surprisal(0.5)                       // -log2(0.5) = 1.0 bit
mutual_information(x, y)             // I(X;Y)
kl_divergence(p, q)                  // D_KL(P||Q)
cross_entropy(p, q)                  // H(P,Q)
joint_entropy(x, y)                  // H(X,Y)
conditional_entropy(x, y)            // H(X|Y)

19.6 File I/O

let text = readFile("data.txt")
writeFile("output.txt", result)
appendFile("log.txt", message)
let lines = readLines("data.csv")
let files = listDir("./src")
exists("config.json")              // true/false
mkdir("output")
remove("temp.txt")

19.7 HTTP

let res = httpGet("https://api.example.com/data")
println(res.status)    // 200
println(res.body)      // response body

let res = httpPost("https://api.example.com/users", {
    name: "Alice",
    email: "alice@example.com"
})

19.8 Database

let db = dbOpen("sqlite:app.db")
let users = dbQuery(db, "SELECT * FROM users WHERE active = ?", [true])
dbExec(db, "INSERT INTO logs (msg) VALUES (?)", [message])
dbClose(db)

19.9 JSON

let obj = jsonParse('{"name":"Alice","age":30}')
let str = jsonStringify({name: "Alice", age: 30})

19.10 Regex

reMatch("^[a-z]+$", "hello")           // true
reCapture("(\\d+)-(\\d+)", "42-99")    // ["42", "99"]
reFindAll("\\d+", "a1b2c3")            // ["1","2","3"]
reReplace("\\d", "a1b2", "*")          // "a*b*"
reSplit("\\s+", "hello   world")       // ["hello", "world"]

19.11 Cryptography

sha256("message")                       // hex hash
bcryptHash("password123")               // bcrypt hash
bcryptVerify("password123", hash)       // true/false
let key = rsaGenerateKey(2048)
let encrypted = aesEncrypt(data, key)
let decrypted = aesDecrypt(encrypted, key)
randomBytes(32)                         // 32 random bytes

19.12 DataFrames

let df = dfFromCsv("data.csv")
let filtered = dfFilter(df, row => row.age > 18)
let grouped = dfGroupBy(df, ["department"])
let summary = dfDescribe(df)
dfToCsv(filtered, "output.csv")

19.13 NLP / Text Processing

let tokens = tokenize("The quick brown fox")
let freq = wordFreq(text)
let top = topWords(text, 10)
let score = sentiment("I love this product!")
let lang = detectLanguage("Bonjour le monde")

19.14 Tensors and ML

let t = tensorCreate([[1,2],[3,4]])
let result = tensorMatmul(t, weights)
let activated = tensorRelu(result)
let probs = tensorSoftmax(activated)

let model = onnxLoad("model.onnx")
let output = onnxRun(model, input)

19.15 Combinators

let factorial = fix(f => n => if n <= 1 then 1 else n * f(n - 1))
factorial(10)   // 3628800

let fib = memo_fix(f => n => if n <= 1 then n else f(n-1) + f(n-2))
fib(50)         // 12586269025 (fast — memoized)

20. SIGNAL-Lite (Embeddable Subset)

SIGNAL-Lite is a decidable 41-keyword subset designed for embedding in host applications (e.g., PHP, JavaScript). It retains the reactive core while dropping infrastructure concerns.

20.1 Keyword Set

| Category | Keywords | |----------|----------| | Bindings | let var fn return | | Control | if then else match for in while loop break continue | | Literals | true false null | | Logic | and or not is | | Reactive | domain entity sig rule when emit | | CRUD | create update delete with where | | State | state machine initial transition guard | | Error | try catch | | Module | import export |

Plus 3 anaphoric referents: this that it

20.2 Implementation Components

A minimal SIGNAL-Lite interpreter requires:

| Component | Purpose | Size | |-----------|---------|------| | Lexer | Tokenization | ~300 lines | | Ast | Node definitions | ~200 lines | | Parser | Recursive descent | ~800 lines | | Runtime | Environment + values | ~400 lines | | ReactiveRuntime | Signals + entities + state machines | ~500 lines | | Evaluator | Tree-walk interpreter | ~1200 lines |

Total: ~3,400 lines for a complete implementation.

20.3 Host Integration

SIGNAL-Lite can register host builtins — native functions callable from SIGNAL code:

// PHP example: register a database query builtin
$engine->registerBuiltin('db_query', function($sql, $params) use ($pdo) {
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    return $stmt->fetchAll();
});
// SIGNAL-Lite code using the host builtin
let users = db_query("SELECT * FROM users WHERE active = ?", [true])
for user in users {
    println(user.name)
}

21. Grammar Reference

21.1 EBNF (Core)

program     = { declaration } ;
declaration = fn_decl | domain_decl | entity_decl | sig_decl
            | rule_decl | state_machine | statement ;

fn_decl     = "fn" IDENT "(" [ params ] ")" [ "->" type ] ( block | "=>" expr | "=" expr ) ;
domain_decl = "domain" TYPE_IDENT "=" variant { "|" variant } ;
entity_decl = "entity" TYPE_IDENT { field_decl | derive_decl } ;
sig_decl    = "sig" TYPE_IDENT "(" [ params ] ")" ;
rule_decl   = "rule" IDENT "when" TYPE_IDENT "(" [ params ] ")"
              [ "where" expr ] "then" block ;

variant     = TYPE_IDENT [ "(" [ params ] ")" ] ;
field_decl  = IDENT ":" type [ "=" expr ] ;
derive_decl = "derive" IDENT "=" expr ;

params      = param { "," param } ;
param       = IDENT [ ":" type ] [ "=" expr ] ;
type        = TYPE_IDENT [ "<" type { "," type } ">" ] [ "?" ] | type "|" type ;

block       = "{" { statement } "}" ;
statement   = let_stmt | var_stmt | return_stmt | break_stmt
            | continue_stmt | expr_stmt ;

let_stmt    = "let" IDENT [ ":" type ] "=" expr ;
var_stmt    = "var" IDENT [ ":" type ] "=" expr ;
return_stmt = "return" [ expr ] ;
break_stmt  = "break" [ expr ] ;
continue_stmt = "continue" ;
expr_stmt   = expr ;

expr        = pipe_expr ;
pipe_expr   = or_expr { "|>" or_expr } ;
or_expr     = and_expr { "or" and_expr } ;
and_expr    = eq_expr { "and" eq_expr } ;
eq_expr     = cmp_expr { ( "==" | "!=" ) cmp_expr } ;
cmp_expr    = add_expr { ( "<" | ">" | "<=" | ">=" ) add_expr } ;
add_expr    = mul_expr { ( "+" | "-" | "++" ) mul_expr } ;
mul_expr    = pow_expr { ( "*" | "/" | "%" ) pow_expr } ;
pow_expr    = unary_expr [ "**" unary_expr ] ;
unary_expr  = ( "not" | "-" ) unary_expr | postfix_expr ;
postfix_expr= primary { "(" [ args ] ")" | "." IDENT | "[" expr "]" } ;
primary     = INT | FLOAT | STRING | "true" | "false" | "null"
            | IDENT | "(" expr ")" | "[" [ exprs ] "]" | "{" [ map_entries ] "}"
            | if_expr | match_expr | for_expr | while_expr | loop_expr
            | try_expr | lambda ;

if_expr     = "if" expr ( "then" expr [ "else" expr ] | block [ "else" block ] ) ;
match_expr  = "match" expr "{" { match_arm } "}" ;
match_arm   = pattern [ "if" expr ] "=>" expr ;
for_expr    = "for" IDENT "in" expr block ;
while_expr  = "while" expr block ;
loop_expr   = "loop" block ;
try_expr    = "try" block "catch" IDENT block [ "finally" block ] ;
lambda      = IDENT "=>" expr | "(" [ params ] ")" "=>" ( expr | block ) ;

22. Complexity Analysis

Performance guarantees for core operations (Knuth perspective):

| Operation | Time | Space | Notes | |-----------|------|-------|-------| | Lexing | O(n) | O(n) | n = source length | | Parsing | O(n) | O(n) | Recursive descent, no backtracking | | Type inference | O(n + c) | O(n) | c = constraint count | | Signal emit | O(R) | O(1) | R = rules for that signal | | Pattern match | O(m) | O(1) | m = match arms | | Entity create | O(1) | O(1) | Hash table insert | | Entity update | O(1) | O(1) | In-place field mutation | | Entity query | O(E) | O(E) | E = total entities (linear scan) | | State transition | O(T) | O(1) | T = transitions from current state | | Array map/filter | O(n) | O(n) | n = array length | | Array sort | O(n log n) | O(n) | Merge sort | | Map lookup | O(1) avg | O(1) | Hash map | | Closure creation | O(1) | O(k) | k = captured variables | | Channel send/recv | O(1) | O(b) | b = buffer capacity |


Appendix: Complete Example — Task Management System

module taskapp.core

// ── Types ─────────────────────────────────────────────────────

domain Priority = Low | Medium | High | Critical

entity Task
    id:          UUID
    title:       String
    description: String?
    priority:    Priority = Medium
    assignee:    String?
    created_at:  DateTime = now()
    due_date:    DateTime?

    derive isOverdue = due_date != null and due_date < now()
    derive daysLeft  = if due_date != null then dateDiff(due_date, now()) else null

// ── State Machine ─────────────────────────────────────────────

state machine TaskStatus {
    initial: open

    state open      { enter { println("Task opened") } }
    state active    { enter { emit TaskStarted(taskId) } }
    state review
    state done      { enter { emit TaskCompleted(taskId) } }
    state cancelled

    transition start:   open     -> active
    transition review:  active   -> review
    transition approve: review   -> done
    transition reject:  review   -> active
    transition cancel:  open     -> cancelled
    transition cancel:  active   -> cancelled
}

// ── Signals ───────────────────────────────────────────────────

sig TaskCreated(task: Task)
sig TaskAssigned(taskId: UUID, assignee: String)
sig TaskStarted(taskId: UUID)
sig TaskCompleted(taskId: UUID)
sig DeadlineApproaching(taskId: UUID, daysLeft: Int)

// ── Rules ─────────────────────────────────────────────────────

rule notifyAssignee when TaskAssigned(taskId, assignee) then {
    println("Notifying " ++ assignee ++ " about task " ++ toString(taskId))
}

rule logCompletion when TaskCompleted(taskId) then {
    println("Task " ++ toString(taskId) ++ " completed at " ++ toString(now()))
}

rule escalateUrgent when DeadlineApproaching(taskId, daysLeft)
    where daysLeft <= 1
    then {
        println("URGENT: Task " ++ toString(taskId) ++ " due in " ++ toString(daysLeft) ++ " days!")
    }

// ── Query ─────────────────────────────────────────────────────

query OverdueTasks()
    = Task
        where isOverdue == true
        order by due_date
        limit 50

// ── Flow ──────────────────────────────────────────────────────

flow TaskMetrics
    from TaskCompleted.*
    | window tumbling 1d
    | select { date: today, completed: count() }
    -> Dashboard

// ── Application Logic ─────────────────────────────────────────

fn createTask(title: String, priority: Priority) -> Task {
    let task = create Task {
        id: uuid(),
        title: title,
        priority: priority
    }
    emit TaskCreated(task)
    task
}

fn assignTask(task: Task, person: String) {
    update task with { assignee: person }
    emit TaskAssigned(task.id, person)
}

// ── Main ──────────────────────────────────────────────────────

let bug = createTask("Fix login timeout", Critical)
assignTask(bug, "Alice")

let overdue = OverdueTasks()
for t in overdue {
    println(t.title ++ " — " ++ toString(t.daysLeft) ++ " days overdue")
}

SIGNAL v1.0 — Designed by the Unified Council: Knuth, Erdos, Turing, Shannon.

All posts