| name | shards |
| description | Write, verify, and run Shards programs. Shards is a data-flow language that type-checks whole programs at compose time (before running), so the workflow is generate → verify → repair against the `shards` CLI. Use this skill whenever reading or writing .shs files, or driving the shards binary. |
Shards
Shards is a data-flow language: values flow left-to-right through shards joined by
|. The whole program is type-checked at compose time, before anything runs — so
never guess an API; ask the binary, and verify before running.
This skill is self-contained:
- This file — the mental model, a working cheat-sheet, and the CLI loop. Enough to
read and write everyday Shards.
GUIDE.md (next to this file) — the complete language guide. Read it before
writing anything non-trivial.
examples/ — small, runnable, check-passing programs.
- The shard catalog is live (not a file):
shards enumerate / shards docs --json.
Mental model
Variables are channels, not containers. | passes the value along; a newline is an
implicit | (flow continues across lines until something stops it). Everything is a
shard — the assignment operators are just sugar over shards.
"world" | Set(name) // name channels the string
["Hello " name "!"] | String.Format | Log // -> "Hello world!"
Cheat-sheet
Variables — use the word forms
=/>=/>/>> are sugar and collide with other languages' priors. Write the word
forms:
| Word form | Operator | Meaning |
|---|
Set(x) | >= x | create/assign a mutable variable |
Ref(x) | = x | bind an immutable variable |
Update(x) | > x | update an existing mutable variable |
Push(s) | >> s | append to a sequence |
0 | Set(counter)
counter | Add(1) | Update(counter)
item | Push(items)
Literals & types
42 3.14 "text" true false none // int float string bool none
@f3(1 2 3) @i2(4 5) @color(0.8 0.2 0.2) // vectors
[1 2 3] // sequence
{name: "Alice" age: 30} // table
Calling shards
Add(5) // positional
Http.Get(URL: "https://x" Timeout: 30) // named (positional MUST come before named)
"a,b" | String.Split(",") // input flows in from the left
Control flow
v | If({IsMore(10)} {"big" | Log} {"small" | Log}) // predicate is a {block}
v | When({IsMore(10)} {"big" | Log}) // no else
v | Match(["a" {"A"} "b" {"B"} none {"?"}] Passthrough: false)
v | Cond([{Is("a")} {"A" | Log} {true} {"default" | Log}])
Repeat({"tick" | Log} Times: 3)
[1 2 3] | ForEach({Mul(2) | Log}) // $0 is the current element
Maybe({ risky } { "failed" | Log }) // try / catch
Wires & mesh (the runtime)
@wire(main {
Once({ 0 | Set(counter) }) // runs once; allocations happen here
counter | Add(1) | Update(counter) | Log
} Looped: true)
@mesh(root)
@schedule(root main)
@run(root FPS: 60)
Do(wire) runs inline sharing state; Detach(wire) schedules a copy (non-blocking);
Step, Spawn, Branch exist too — see GUIDE.md.
Templates, defines, includes
@define(api "https://api.example.com")
@template(greet [name] { ["Hello " name] | String.Format })
@greet("world") | Log
@include("utils.shs" Once: true)
Gotchas that bite generated code
= is Ref (immutable), not assignment. Use Set/Update. This is the #1 error.
And/Or have flow-control side effects — use them only inside predicate blocks
(If/When/Cond), not in plain flow.
Take ≠ Slice ≠ Limit: Take(i) = element at index; Slice(a b) = range;
Limit(n) = first n.
- Sub-blocks
{...} preserve the input: 1 | {Add(2)|Log} | Log logs 3 then 1.
- Dynamic types need
Expect*: ExpectString, ExpectTable, … when the type isn't
known at compose time.
- Comments are
// (or /* */), never ;.
- Namespaces use
/ (fbl/set-tracked); :: is only the enum separator
(LogLevel::Info).
The loop — discover, verify, never guess
shards search http
shards enumerate --filter CSV
shards enumerate --json | jq ...
shards docs Http.Get --json
shards docs LogLevel --type enum --json
shards check myscript.shs --json
shards run myscript.shs
check resolves @includes relative to the file (-I <dir> adds roots) and accepts
the same trailing key:value defines as run (e.g. shards check app.shs env:prod),
injected before compose. So a script that references command-line defines — or whose
composition branches on one — checks in the configuration it will actually run in. A
fragment that depends on definitions injected by an outer file (not the command line)
should still be checked via that entry-point file, not in isolation.
Quick reference
| Command | Purpose |
|---|
shards enumerate [--filter S] [--json] | one-line index of all shards |
shards search <query> [--json] | keyword search over name + summary |
shards docs <name> [--type shard|enum] [--json] | full signature (drill-down) |
shards check <file> [--json] [key:value ...] | type-check (parse + compose), never runs; takes run's defines |
shards run <file> | execute |
shards format <file> [-i] | format source |
shards ast <file> | dump the JSON AST |
When in doubt: enumerate/search to find it, docs --json to learn it, check to
prove it. The binary is the source of truth.