Language Reference

Complete guide to writing Synoema programs

Mental Model

If you know Python or Haskell, override these habits first:

Instead ofWrite in SynoemaWhy
def f(x):f x = bodyNo def keyword
if c then x else y? c -> x : y3 tokens instead of keywords
return x(just the expression)Last expression = result
[1, 2, 3][1 2 3]Space-separated, no commas
s1 + s2 (strings)s1 ++ s2+ is for numbers only
f"x={x}""x=${x}"${} interpolation

Core axioms:

Types

Hindley-Milner type inference. You rarely need to write types.

Primitive Types

TypeExamplesNotes
Int42, -7, 063-bit signed integer
Float3.14, 2.064-bit IEEE 754
Booltrue, false
String"hello", "x=${x}"With ${} interpolation
()()Unit type

Composite Types

TypeExampleNotes
List a[1 2 3], []Homogeneous, space-separated
Records{x = 3, y = 4}Structural (row polymorphism)
ADTMaybe a = Just a | NoneAlgebraic data types
Result a eOk 42, Err "fail"From prelude
Chan achan ()Typed channel

Type Aliases

type Pos = {x : Int, y : Int}
type Transform = Int -> Int
type Pair a b = {fst : a, snd : b}

Functions & Pattern Matching

Functions are defined with name args = body. Multiple equations define pattern matching (first match wins):

-- Simple function
double x = x * 2

-- Pattern matching: multiple equations
fac 0 = 1
fac n = n * fac (n - 1)

-- Wildcard pattern
describe 0 = "zero"
describe _ = "other"

-- Cons pattern (parentheses required!)
head (x:_)  = x
tail (_:xs) = xs
isEmpty []  = true
isEmpty _   = false

-- Lambda
square = \x -> x * x

-- Type annotation (optional)
add : Int -> Int -> Int
add x y = x + y

Verification Contracts

clamp : Int -> Int
  requires x >= 0
  requires x <= 1000
  ensure result >= 1
  ensure result <= 100
clamp x = ? x < 1 -> 1 : ? x > 100 -> 100 : x

Operators

Precedence from lowest to highest (13 levels). Every operator = 1 BPE token.

OpMeaningAssoc
<-IO/monadic bindright
|>Pipe: x |> f = f xleft
||Logical orleft
&&Logical andleft
== !=Equalitynone
< > <= >=Comparisonnone
++String/list concatright
+ -Add/subleft
* / %Mul/div/modleft
**Powerright
>>Compose: f >> gright
- (prefix)Negate
.Field accessleft
juxtapositionFunction application f xleft

Control Flow

Conditional: ? -> :

The ternary operator replaces if/else:

abs x = ? x < 0 -> -x : x

-- Multi-branch chain
fizzbuzz n =
  ? n % 15 == 0 -> "FizzBuzz"
  : ? n % 3 == 0 -> "Fizz"
  : ? n % 5 == 0 -> "Buzz"
  : show n

Local Bindings

hyp a b =
  a2 = a * a
  b2 = b * b
  a2 + b2

Pipes

result = [1 2 3 4 5 6 7 8 9 10]
  |> filter (\x -> x % 2 == 0)
  |> map (\x -> x * x)
  |> sum
-- result = 220

Sequencing

-- Use ; to sequence side effects
main = print "a" ; print "b" ; print "c"

Lists

xs    = [1 2 3 4 5]       -- space-separated, NO commas
empty = []
r     = [1..10]            -- range (inclusive)
both  = [1 2] ++ [3 4]    -- concatenation: [1 2 3 4]
ys    = 1 : [2 3]          -- cons (prepend): [1 2 3]

-- List comprehension
evens = [x | x <- [1..20], x % 2 == 0]
pairs = [[a b] | a <- [1 2], b <- ["x" "y"]]

-- Pattern matching
only [x]     = x           -- exactly one element
sum3 [a b c] = a + b + c   -- exactly three

Gotcha: : is both cons and else-branch in ?->:. In then-branch, parenthesize cons: ? cond -> (x:xs) : rest

Records

-- Create
pt = {x = 3, y = 4}

-- Field access
px = pt.x                          -- 3

-- Nested
circle = {center = {x = 0, y = 0}, radius = 5}
cx = circle.center.x               -- 0

-- Punning: {x, y} = {x = x, y = y}
point x y = {x, y}

-- Pattern matching (destructure)
dist {x, y} = x * x + y * y

-- Record update (spread syntax)
move_x pt dx = {...pt, x = pt.x + dx}

Algebraic Data Types

-- Define sum types
Maybe a = Just a | None
Shape = Circle Float | Rect Float Float | Point

-- Construct
x = Just 42
s = Circle 3.0

-- Pattern match
fromMaybe def None     = def
fromMaybe _   (Just x) = x

area (Circle r)  = r * r
area (Rect w h)  = w * h
area Point       = 0

-- Derive
Color = Red | Green | Blue derive (Show, Eq, Ord)

Type Classes

-- Define a trait
trait Show a
  show : a -> String

-- Implement for a type
Color = Red | Green | Blue

impl Show Color
  show Red   = "red"
  show Green = "green"
  show Blue  = "blue"

-- Constrained implementation
impl Show (Maybe a) ? Show a
  show None     = "None"
  show (Just x) = "Just " ++ show x

Modules

-- Single-file module
mod Math
  square x = x * x
  pi = 3.14159

use Math (square pi)        -- selective import
use Math (*)                -- wildcard import

-- Multi-file
-- main.sno
import "math.sno"
use Math (square)
main = square 5

Error Handling

-- Result type (from prelude)
Result a e = Ok a | Err e

safe_div x 0 = Err "division by zero"
safe_div x y = Ok (x / y)

-- Chain with and_then
parse_and_double input =
  parse input |> and_then (\n -> safe_div n 2)

-- Monadic bind with <-
main =
  a <- safe_div 10 2
  b <- safe_div a 3
  Ok (a + b)

-- Unrecoverable error
head []    = error "empty list"
head (x:_) = x

String Interpolation

name = "world"
msg = "Hello ${name}"              -- "Hello world"
sum = "${a} + ${b} = ${a + b}"     -- expressions allowed
esc = "\$ is literal dollar"       -- escape with \$

Standard Library

List Operations

FunctionTypeDescription
length[a] -> IntList length
head[a] -> aFirst element
tail[a] -> [a]All but first
map(a -> b) -> [a] -> [b]Transform each
filter(a -> Bool) -> [a] -> [a]Keep matching
foldl(b -> a -> b) -> b -> [a] -> bLeft fold
zip[a] -> [b] -> [(a,b)]Pair elements
index[a] -> Int -> a0-based index
takeInt -> [a] -> [a]First n elements
dropInt -> [a] -> [a]Skip first n
reverse[a] -> [a]Reverse
sum[Int] -> IntSum elements
sort[a] -> [a]Natural ordering
any(a -> Bool) -> [a] -> BoolAny match?
all(a -> Bool) -> [a] -> BoolAll match?
find(a -> Bool) -> [a] -> Result a StringFirst match or Err
elema -> [a] -> BoolMembership
flatten[[a]] -> [a]Flatten nested
partition(a -> Bool) -> [a] -> Pair [a] [a]Split by predicate

String Operations

FunctionTypeDescription
str_lenString -> IntString length
str_sliceString -> Int -> Int -> StringSubstring (from, to)
str_findString -> String -> Int -> IntFind substring (-1 = not found)
str_starts_withString -> String -> BoolPrefix check
str_trimString -> StringTrim whitespace
splitString -> String -> [String]Split by separator
joinString -> [String] -> StringJoin with separator
replaceString -> String -> String -> StringReplace all occurrences
upperString -> StringUppercase
lowerString -> StringLowercase
wordsString -> [String]Split on whitespace
linesString -> [String]Split on newlines
charsString -> [String]Characters as list
json_escapeString -> StringEscape for JSON

Math

FunctionTypeDescription
sqrtFloat -> FloatSquare root
floorFloat -> FloatRound down
ceilFloat -> FloatRound up
roundFloat -> FloatRound to nearest
absInt -> IntAbsolute value
mina -> a -> aMinimum
maxa -> a -> aMaximum
even / oddInt -> BoolParity check

Type Conversion

FunctionTypeDescription
showa -> StringConvert any value to string
to_intString -> Result Int StringParse string to integer
to_floatString -> Result Float StringParse string to float

Result Combinators (prelude)

FunctionTypeDescription
map_ok(a -> b) -> Result a e -> Result b eTransform Ok value
map_err(e -> f) -> Result a e -> Result a fTransform Err value
unwrapResult a e -> aExtract Ok (panic on Err)
unwrap_ora -> Result a e -> aExtract with default
is_ok / is_errResult a e -> BoolCheck variant
and_then(a -> Result b e) -> Result a e -> Result b eChain operations
errorString -> aRuntime panic

JSON

FunctionTypeDescription
json_parseString -> Result JsonValue StringParse JSON string
json_getString -> JsonValue -> Result JsonValue StringGet field from object
json_escapeString -> StringEscape string for JSON

I/O & Networking

Console

FunctionTypeDescription
printa -> ()Print with newline
readlineStringRead line from stdin

File I/O

FunctionTypeDescription
file_readString -> StringRead entire file
fd_openString -> IntOpen for reading
fd_open_writeString -> IntOpen for writing
fd_readlineInt -> StringRead one line
fd_writeInt -> String -> ()Write string
fd_closeInt -> ()Close handle
fd_popenString -> IntRun command, get stdout

TCP Networking

FunctionTypeDescription
tcp_listenInt -> IntListen on port
tcp_acceptInt -> IntAccept connection
-- Minimal HTTP server
handle fd =
  req = fd_readline fd
  fd_write fd "HTTP/1.0 200 OK\r\n\r\nHello!"
  fd_close fd

main =
  listener = tcp_listen 8080
  loop l = handle (tcp_accept l) ; loop l
  loop listener

HTTP Client

FunctionTypeDescription
http_getString -> Result String StringGET request
http_postString -> String -> Result String StringPOST with body

HTTP only — no HTTPS. Use fd_popen "curl https://..." for HTTPS.

Environment

FunctionTypeDescription
env_orString -> String -> StringGet env var with default
args[String]Command-line arguments

Concurrency

-- Structured concurrency
main = scope {
  c = chan ()
  spawn (send c 42)
  print (recv c)        -- prints 42
}

-- Parallel map
result = pmap (\x -> x * x) [1 2 3 4 5]
FunctionTypeDescription
scope { ... }aJoin all spawned threads before returning
spawn expr()Run expression in new OS thread
chanChan aCreate typed channel
sendChan a -> a -> ()Send (non-blocking)
recvChan a -> aReceive (blocking)
pmap(a -> b) -> [a] -> [b]Parallel map

Time

FunctionTypeDescription
now() -> IntCurrent Unix time (ms)
elapsedInt -> IntMilliseconds since timestamp
sleepInt -> ()Sleep for N milliseconds

Testing

-- Doctests (in doc comments)
--- Compute factorial.
--- example: fact 5 == 120
fact 0 = 1
fact n = n * fact (n - 1)

-- Unit tests
test "base case" = fact 0 == 1
test "fact 10"   = fact 10 == 3628800

-- Property tests
test "reverse involution" =
  prop xs -> reverse (reverse xs) == xs
test "positive factorial" =
  prop n -> fact n >= 1 when n >= 0 && n <= 10

Run: synoema test file.sno or synoema test directory/

CLI Reference

CommandDescription
synoema run file.snoRun with interpreter
synoema jit file.snoRun with Cranelift JIT (~4.4x faster)
synoema eval "expr"Evaluate expression
synoema build file.snoCompile to bytecode
synoema test dir/Run tests (doctests + unit + property)
synoema doc file.snoGenerate documentation
synoema doc --contracts file.snoContract summary table
synoema watch run file.snoWatch + re-run on change
synoema init myappScaffold new project
synoema installInstall to ~/.synoema/bin
synoema --errors json run file.snoJSON error output for LLM

Interpreter vs JIT

Featurerun (interpreter)jit (Cranelift)
All language featuresYesYes
File/Network I/OYesNo
Concurrency (chan/send/recv)YesNo
Constant folding / DCEYes
Speed1x~4.4x

Use run for development and I/O. Use jit for performance.