Writing Bosatsu in 5 minutes
This page is for programmers who already know Python and immutable functional programming (Haskell/Scala style), and want the shortest path to writing useful Bosatsu.
If you need repo/bootstrap setup first, see Getting started. For full syntax details, see the Language guide.
1) Minimal file shape
package Quick/Hello
export (Mood(), greet, tests)
enum Mood: Happy, Sad
def greet(m: Mood) -> String:
match m:
case Happy: "hello"
case Sad: "cheer up"
tests = TestSuite("hello tests", [
Assertion(greet(Happy) matches "hello", "happy greeting"),
])
Top-order bits:
- Every file starts with exactly one
package. - Imports and exports are explicit.
enum/structreplace class + inheritance style modeling.matchis the normal way to branch on data shape.- Tests are regular values (
Assertion,TestSuite).
2) Python to Bosatsu: quick mental map
- Everything is immutable. You can shadow names, but not mutate values.
- Blocks are expressions: the last line is the return value.
- No
while/mutation loops; userecur,int_loop, folds, or comprehensions. - No exceptions as control flow in normal code. Model errors in types.
- Side effects are explicit
Prog[...]values, not implicit execution.
If you are writing I/O code, expect this style:
main = (
_ <- println("hello").await()
pure(0)
)
<- with .await() is the common way to sequence Prog values.
3) Recurring tricks from test_workspace
- Prefer
match+ pattern guards (if ...) for branching. - Use list patterns heavily:
[],[head, *tail]. - Write local helper loops with accumulators for performance and clarity.
- Use
matchesfor concise checks in tests and conditionals. - Keep package APIs intentional with
exportandType()(export constructors).
4) Always-be-compiling with lib check + todo
Run typecheck continuously while editing:
./bosatsu lib check
When you know types but not implementation, use todo to keep moving:
def hard_part(a: String, b: Int) -> Bool:
todo((a, b))
Rules:
todois only available in check-only commands (tool check/lib check).- Commands that emit or run code (
lib test,lib build,tool eval, etc.) fail untiltodois removed. - Keep
todoarguments meaningful (usually tuple all planned inputs) so you do not lose type information during refactors.
Once placeholders are removed, run:
./bosatsu lib test
5) Practical 5-minute loop
- Create one package with one exported function.
- Add a tiny
testsvalue immediately. - Iterate with
lib check, usingtodofor unfinished branches. - Replace
todoincrementally. - Finish with
lib test.
That loop matches how most code in test_workspace evolves: explicit data models, explicit imports/exports, and constant typechecked feedback.
The source code for this page can be found here.