The Basics
An SSPUR program is a list of definitions: functions, types and tests. Their order doesn’t matter, and there is nothing outside a definition. Here is a whole program:
fn main() -> Unit ! log
= do
name = "SSPUR"
var count = 0
for word in ["types", "effects", "contracts"]
count := count + 1
log("{count}. {name} checks {word}")
log(greeting(3))
fn greeting(hour: Int) -> Str
= if hour < 12 then "good morning" else "good evening"
A few things to notice:
fn main() -> Unit ! logis the entry point. After->comes the return type, and after!the effects the function may perform.mainwrites to the log, so it says! log. A function that does nothing observable has no!at all.- The body comes after
=. It is one expression, likegreeting’sif, ordofollowed by an indented block. A block’s value is its last line. - Indentation is 2 spaces and it is the only structure. There are no braces and no semicolons.
name = "SSPUR"binds a name once.var count = 0makes a variable you can change with:=. Plain=bindings can’t be reassigned, and the checker says so if you try.for word in [...]loops over a list."{count}. {name}"puts the value of an expression into a string.if c then a else bis an expression and both branches have the same type. You can leave outelseonly when thethenbranch isUnit.//starts a comment. Comments are fine in files, but a codebase managed bysspur editdoesn’t store them.
Values and operators
fn values() -> Str
= do
n = 7 / 2
x = 7.0 / 2.0
big = 1_000_000
mask = 0xff
ok = n == 3 and not (x < 3.0)
"{n} {x} {big} {mask} {ok}"
Int is a 64-bit integer and / on two Ints truncates. F64 is a double. and, or and not are words. Integer overflow and division by zero don’t wrap or return garbage: they stop the program with a message, in the interpreter and in native code alike.
Tests
A test is a named Bool expression next to the code it checks:
test morning = greeting(9) == "good morning"
test evening = greeting(20) == "good evening"
test values_print = values() == "3 3.5 1000000 255 true"
Running it
$ sspur run basics.ssp
1. SSPUR checks types
2. SSPUR checks effects
3. SSPUR checks contracts
good morning
$ sspur test basics.ssp
3 passed, 0 failed
sspur run compiles the file to native code (through clang, cached by content hash) and runs main; run again unchanged, it goes straight to the cached code. sspur test runs every test and prints only the failures and a count.
When it doesn’t check
There are no implicit conversions. Mixing Int and F64 is an error, and the message says how to fix it:
fn half(n: Int) -> F64
= n / 2.0
$ sspur check mixed.ssp
mixed.ssp:2:7 E_TYPE_MISMATCH expected Int, found F64
mixed.ssp:2:3 E_TYPE_MISMATCH expected F64, found Int
hint: convert with '.to_f64'
Every error has a code (here E_TYPE_MISMATCH), a position and usually a hint:. The error reference lists them all. Next: Everyday Types.