Two companion documents to be created:
docs/guides/quickstart.md -- A standalone written guide for readers who
prefer prose over a step-by-step prompt-and-response format. Covers the same
ground as the tutorial but with more explanation, links to deeper guides, and
code blocks they can paste or run from a file.
docs/guides/repl-tutorial.md -- A 22-step interactive tutorial designed
to be followed at the REPL (tur repl or the web REPL at the Try Turmeric
page). Each step shows the exact expression to type, the expected output, and
a short explanation. Optional "Try it yourself" prompts invite experimentation
before moving on.
Both documents share the same step order and concept progression so a reader can bounce between them freely.
A parallel machine-readable form (tutorials/quickstart.yaml) mirrors every
step so the :tutorial quickstart meta-command engine (see
archive/try-turmeric-and-tutorial-plan.md) can consume the same content
without duplicating authoring work.
tur repl in a terminal or has the web REPL open.Step 1 -- Starting the REPL and your first expression
Concept: the REPL banner, prefix notation, integer arithmetic.
> (+ 1 2)
3
> (* 6 7)
42
REPL note: :help lists all meta-commands. :quit or Ctrl-D exits.
Try it: evaluate (- 100 58) and (/ 144 12).
Step 2 -- Strings and booleans
Concept: string literals (:cstr), true/false, println.
> (println "Hello, Turmeric!")
Hello, Turmeric!
> true
true
> (= 1 1)
true
Try it: print your own greeting. Use (not true) and see what you get.
Step 3 -- Binding names with let
Concept: let introduces local, immutable bindings scoped to its body.
> (let [x 10 y 20] (+ x y))
30
REPL note: multi-line input works -- keep typing after an open paren; the REPL waits until parentheses are balanced. A blank line abandons an incomplete expression.
Try it: bind three values and compute a sum.
Step 4 -- Defining functions with defn
Concept: defn, parameter list, return-type annotation, top-level persistence.
> (defn square [x :int] :int (* x x))
> (square 9)
81
> :doc square
square : (:int -> :int)
REPL note: :type <expr> shows the inferred type without evaluating.
Try it: define a cube function and call it.
Step 5 -- Conditionals: if and cond
Concept: if as an expression (always has a value), cond for multiple branches.
> (defn abs [n :int] :int (if (< n 0) (- 0 n) n))
> (abs -5)
5
> (defn sign [n :int] :int
(cond (> n 0) 1 (< n 0) -1 :else 0))
> (sign -3)
-1
Try it: write a clamp function that keeps a value within [lo, hi].
Step 6 -- Recursion
Concept: recursive defn, base case, accumulator pattern.
> (defn factorial [n :int] :int
(if (<= n 1) 1 (* n (factorial (- n 1)))))
> (factorial 10)
3628800
Try it: write a recursive fib function. What happens with (fib 30)?
Step 7 -- when and unless
Concept: the when and unless macros for one-arm conditionals; nil results.
> (when true (println "yes"))
yes
> (unless false (println "also yes"))
also yes
Try it: guard a print behind a condition of your choice.
Step 8 -- Option: constructors and predicates
Concept: option-some, option-none, option-some?, option-none? --
representing a value that may or may not be present.
> (option-some 42)
> (option-some? (option-some 42))
true
> (option-none)
> (option-none? (option-none))
true
> (option-some? (option-none))
false
REPL note: nil results print nothing; that is expected for (option-none).
Try it: evaluate (option-none? (option-some 0)) -- is a some of zero still
some?
Step 9 -- Option: safe unwrapping
Concept: option-unwrap, option-unwrap-or; writing a function that returns
an Option instead of crashing.
> (option-unwrap (option-some 99))
99
> (option-unwrap-or (option-none) -1)
-1
> (defn safe-div [a :int b :int]
(if (= b 0) (option-none) (option-some (/ a b))))
> (safe-div 10 2)
> (option-unwrap (safe-div 10 2))
5
> (option-unwrap-or (safe-div 10 0) -1)
-1
Try it: write safe-head that returns (option-none) for an empty vector and
(option-some (vec-get v 0)) otherwise.
Step 10 -- Result: success or failure
Concept: ok, err, ok?, err?, result-unwrap-or -- a two-case type
that carries either a success value or an error value.
> (ok 100)
> (err 404)
> (ok? (ok 100))
true
> (err? (ok 100))
false
> (result-unwrap-or (err 0) -1)
-1
Try it: rewrite safe-div to return (ok (/ a b)) on success and
(err 0) on division by zero.
Step 11 -- Branching on Option and Result with cond
Concept: composing predicates inside cond to dispatch on wrapped values --
a lightweight alternative to full pattern-match syntax.
> (defn describe-result [r]
(cond (ok? r) (println "ok!")
(err? r) (println "err!")
:else (println "unknown")))
> (describe-result (ok 1))
ok!
> (defn describe-option [o]
(cond (option-some? o) (println "some!")
(option-none? o) (println "none!")
:else (println "unknown")))
> (describe-option (option-none))
none!
Try it: write show-div that calls safe-div and prints either the result
or "division by zero".
Step 12 -- Vectors
Concept: vec-new, vec-push!, vec-get, vec-len, mutable growable arrays.
> (let [v (vec-new)]
(vec-push! v 10)
(vec-push! v 20)
(vec-push! v 30)
(println (vec-len v))
(println (vec-get v 1)))
3
20
Try it: build a vector of the first five squares using a loop (see step 13).
Step 13 -- The for macro
Concept: for as a counted loop; combining for with a vector.
> (for i 0 5 (println i))
0
1
2
3
4
REPL note: for is a macro defined in stdlib/macros.tur; :doc for shows
its signature.
Try it: accumulate squares in a vector inside a for loop.
Step 14 -- Closures and fn
Concept: anonymous functions with fn, capturing lexical scope.
> (let [add5 (fn [x :int] :int (+ x 5))]
(add5 10))
15
> (defn make-adder [n :int] (fn [x :int] :int (+ x n)))
> (let [add3 (make-adder 3)] (add3 7))
10
Try it: write a make-multiplier that returns a closure.
Step 15 -- Passing functions as arguments
Concept: functions are first-class values; passing a closure to another function.
> (defn apply-twice [f x :int] :int (f (f x)))
> (apply-twice (fn [x :int] :int (* x 2)) 3)
12
Try it: write apply-n that applies a function n times.
Step 16 -- Defining structs
Concept: defstruct, field access via generated getters, constructing instances.
> (defstruct Point [x :int y :int])
> (let [p (Point 3 4)]
(println (Point-x p))
(println (Point-y p)))
3
4
REPL note: :type (Point 1 2) confirms the struct type.
Try it: define a Rect struct with width and height fields and write an
area function for it.
Step 17 -- Declaring and performing effects
Concept: defeffect, perform -- declaring a named effect and invoking it.
Without a handler in scope the runtime raises an unhandled-effect error; that
error is intentional and is resolved in the next step.
> (defeffect Log [msg :cstr] :void)
> (defn do-work [] :void
(perform (Log "starting"))
(perform (Log "done")))
> (do-work)
error: unhandled effect Log
Try it: declare a second effect Warn with the same signature and call it
inside do-work.
Step 18 -- Handling effects
Concept: handle block, the continuation k, resume -- intercepting an
effect and choosing what happens next.
> (handle (do-work)
(Log [msg] k)
(do (println msg) (resume k (nil-value))))
starting
done
REPL note: k is a one-shot continuation; calling (resume k v) continues
do-work from the point of the perform.
Try it: change the handler body to print a line count alongside each message.
Step 19 -- Effects for dependency injection
Concept: swapping handlers to change behavior without touching do-work -- the
same computation, different interpretation.
> (handle (do-work)
(Log [msg] k)
(do (println (str-concat "[LOG] " msg)) (resume k (nil-value))))
[LOG] starting
[LOG] done
Try it: write a "silent" handler that discards log messages entirely and
confirms that do-work still completes without error.
Step 20 -- Effects that return values
Concept: an effect whose return type is not :void; the handler supplies the
value that the perform expression evaluates to.
> (defeffect Ask [] :int)
> (defn use-ask [] :int
(+ 1 (perform (Ask))))
> (handle (use-ask)
(Ask [] k) (resume k 41))
42
Try it: change the handler to return a different integer and observe how the
result of use-ask changes.
Step 21 -- Loading a file with :reload
Concept: writing code to a .tur file and loading it into the REPL session.
Instruction: save the following to hello.tur:
(defn greet [name :cstr] :void
(println (str-concat "Hello, " (str-concat name "!"))))
Then in the REPL:
> :reload hello.tur
reloaded hello.tur
> (greet "world")
Hello, world!
Try it: add a second function to the file, reload, and call it.
Step 22 -- Where to go next
Concept: orientation within the broader ecosystem.
No code to type -- just a curated reading list:
docs/guides/error-handling-guide.md -- deeper Result/Option/panicdocs/guides/effects-system-guide.md -- full effects referencedocs/guides/hkt-guide.md -- Functor, Monad, Applicativedocs/guides/threading-guide.md -- OS threads and channelsdocs/guides/stm-tutorial.md -- software transactional memorydocs/guides/module-system-guide.md -- modules and namespacingdocs/html/api/index.html -- generated API referenceREPL tip: :doc <sym> works for any stdlib name. Start there when you
encounter an unfamiliar function.
docs/guides/repl-tutorial.md# Turmeric Interactive REPL Tutorial
## How to use this tutorial
(start tur repl, follow steps, type the shown expressions)
## Part 1 -- Expressions and the REPL
Step 1 ... Step 4
## Part 2 -- Control Flow and Recursion
Step 5 ... Step 7
## Part 3 -- Data: Option and Result
Step 8 ... Step 11
## Part 4 -- Collections
Step 12 ... Step 13
## Part 5 -- Higher-Order Functions and Closures
Step 14 ... Step 15
## Part 6 -- Structs
Step 16
## Part 7 -- Algebraic Effects
Step 17 ... Step 20
## Part 8 -- Wrap-up
Step 21 ... Step 22
Each step follows this template:
### Step N -- Title
**What you'll learn:** one sentence.
Type this:
```turmeric
(expression)
expression
Expected output:
result
What happened: two or three sentences.
REPL tip: (optional meta-command or shortcut relevant to this step)
Try it yourself: open-ended variation to reinforce the concept.
### `docs/guides/quickstart.md`
(tur repl, web REPL link)
(prose covering steps 1-4, copyable code blocks)
(prose covering steps 5-7)
(prose covering steps 8-11)
(prose covering steps 12-13)
(prose covering steps 14-15)
(prose covering step 16)
(prose covering steps 17-20, with a self-contained example)
(prose covering step 21)
(curated links -- same as step 22)
---
## Tutorial Engine YAML Format
Each step in `repl-tutorial.md` has a 1-to-1 counterpart in
`tutorials/quickstart.yaml` so the `:tutorial quickstart` meta-command can
drive the same content interactively. The YAML schema follows the format
defined in `archive/try-turmeric-and-tutorial-plan.md`.
Example for step 8:
```yaml
- id: option_constructors
title: "Option: constructors and predicates"
instruction: |
Evaluate (option-some 42) and check whether it is some using option-some?.
Then create an empty option with (option-none) and check option-none?.
expected: "(option-none? (option-none))"
alternate_accept:
- "(option-some? (option-some 42))"
- "(option-none? (option-none))"
hints:
- "Use option-some to wrap a value and option-none for the empty case."
- "option-some? and option-none? are the predicates."
success_message: "Correct! option-some holds a value; option-none is the empty case."
verify: "(option-none? (option-none))"
Fields that map from the Markdown template:
| Markdown field | YAML field |
|---|---|
| Step title | title |
| "Type this" block | expected (primary) + alternate_accept |
| "Try it yourself" | omitted from YAML (free-form, not validated) |
| REPL tip | hints last entry |
| "What happened" | success_message |
The full tutorials/quickstart.yaml will contain one entry per step (22
total). Steps with no single "correct" expression (step 22 -- orientation)
use verify: nil and expected: "" to signal that the engine just displays
the instruction and advances.
The step structure maps directly to the tutorial panel described in
archive/try-turmeric-and-tutorial-plan.md. When the web tutorial UI is built:
[Hint] button (revealed one at a time).current_step / 22.No changes to repl-tutorial.md are needed to support this -- the YAML file
is the bridge. Authors edit the Markdown; a script (to be written) regenerates
the YAML from it, or both files are maintained in parallel.
:tutorial meta-command -- Both a Markdown guide and a parallel
tutorials/quickstart.yaml will be produced. The YAML schema is documented
above so the engine can consume it when built. Resolved.
Web REPL integration -- Documented above. The YAML fields map 1-to-1 to the tutorial panel UI. Resolved.
str-concat -- Added to stdlib/str.tur as a two-argument
:cstr -> :cstr function that heap-allocates a new NUL-terminated buffer.
Step 21 nests two calls: (str-concat "Hello, " (str-concat name "!")).
Resolved.
Effects handler syntax -- Confirmed against docs/guides/effects-system-guide.md.
Syntax is (handle expr (Name [p] k) body) with (resume k value).
Steps 17-20 use this form throughout. Resolved.
Scope of steps -- Expanded from 20 to 22: Option/Result gained one step (constructors/predicates split from unwrapping), Effects gained one step (value-returning effects). Resolved.