Cheat sheet
The whole everyday language on one page. Every line below is from one file that CI typechecks and tests. The handbook explains each part, and the language reference has every rule.
Types
type Id = new Str derive Eq, Show
type Qty = Int where _ > 0
type Item = {id: Id, qty: Qty, tags: List[Str]} derive Eq, Show
type Shape = Circle{r: F64} | Rect{w: F64, h: F64} | Dot
type Pair[A, B] = {first: A, second: B}
type Err = NotFound{id: Str} | Bad
Int (i64) F64 Bool Str Unit, List[T] Opt[T] Res[T, E] Map[K, V] Set[T], tuples (A, B), functions A -> B ! e. No implicit conversions: n.to_f64, x.round, s.to_int.
Functions, contracts and matching
fn area(s: Shape) -> F64
= match s
| Circle{r} => 3.14 * r * r
| Rect{w, h} if w == h => w * w
| Rect{w, h} => w * h
| Dot => 0.0
fn clamp_pct(x: Int) -> Int
pre x > -1000
post r >= 0 and r <= 100
ex clamp_pct(150) == 100
= if x < 0 then 0 else if x > 100 then 100 else x
fn first_tag[T](xs: List[T], f: T -> Bool) -> Opt[T]
= xs.find(f)
fn sides(s: Shape) -> Int
= match s
| Circle | Dot => 0
| Rect => 4
fn add_all(xs: List[Int]) -> Int
= match xs
| [] => 0
| [x, ..rest] => x + add_all(rest)
fn width_or(s: Shape, d: F64) -> F64
= if s is Rect{w} and w > 0.0 then w else d
fn round_ones(xs: List[Shape]) -> Int
= xs.filter(_ is Circle).len
Blocks, loops and lambdas
fn stats(xs: List[Int]) -> (Int, Int) ! log
= do
total = xs.sum
var big = 0
for x in xs
if x > 10 then big := big + 1
log("total {total}, {big} big")
(total, big)
fn labels(xs: List[Int]) -> List[Str]
= xs.map(x => do
half = x / 2
if half > 3 then "big {x}" else "small {x}")
Values
test values = do
it = Item{id: Id("a"), qty: 2, tags: []}
moved = it with qty := 3, tags := ["new"]
p = Pair{first: 1, second: "one"}
(a, b) = (p.first, p.second)
[1, 2, 3].map(_ * 2) == [2, 4, 6] and moved.qty == 3 and a == 1 and b.len == 3 and "n={a}" == "n=1" and some(2).or(0) == 2 and empty_map().put("k", 1).get("k") == some(1)
Errors
fn lookup(items: List[Item], id: Str) -> Item ! fail[Err]
= items.find(_.id.raw == id).ok_or(NotFound{id})
fn qty_or_zero(items: List[Item], id: Str) -> Int
= catch lookup(items, id).qty
| NotFound => 0
| Bad => -1
fn qty_of(items: List[Item], id: Str) -> Int ! fail[Err]
= do
some(item) = items.find(_.id.raw == id) else raise NotFound{id}
item.qty
Effects and handlers
effect ask() -> Int
fn twice() -> Int ! ask
= ask() + ask()
fn answered() -> Int
= handle twice()
| ask() => resume(21)
Traits
trait Named
fn name(x: Self) -> Str
fn greet(x: Self) -> Str = "hi {x.name}"
impl Named for Item
fn name(i: Item) -> Str = i.id.raw
fn greet_all[T: Named](xs: List[T]) -> List[Str]
= xs.map(greet)
Packages
use money.{Money, fmt}
$ sspur test cheat.ssp
3 passed, 0 failed
Spelled differently
| Not this | But this |
|---|---|
&&, ||, ! | and, or, not |
len(xs), xs.length | xs.len |
None, null, Some(x) | none, some(x) |
c ? a : b | if c then a else b |
if let some(x) = o | if o is some(x) then |
let some(x) = o else { return 0 } | some(x) = o else 0 |
[x, ...rest], [x, *rest] | [x, ..rest] |
x += 1 | x := x + 1 on a var |
{ in a string | \{ |
f(g(_)) for x => f(g(x)) | write the lambda; _ binds to the innermost call |
inner match arms at the outer arms’ column | indent the inner arms deeper |
Commands
| Task | Command |
|---|---|
| Run, test, check, format | sspur run F, sspur test F, sspur check F, sspur fmt F --write |
| Property-test and prove contracts | sspur fuzz F, sspur verify F |
| Agent loop on a codebase | sspur start NAME..., sspur q pack NAME, sspur edit --test change.ssp |
| Packages | sspur add NAME (or a path or git URL), sspur deps update, sspur deps tree |
| Services | sspur deploy plan F, sspur deploy local F |
The command line reference has the rest.