Turmeric's backtracking support is implemented as a list monad in
stdlib/backtrack.tur. Computations that may yield multiple results (or no
result) are represented as linked lists of int64_t values. This gives you
classic nondeterministic/backtracking semantics with a familiar monadic API.
| Function | Signature | Description |
|---|---|---|
mzero |
() → :int |
The empty list — zero alternatives (failure) |
mreturn |
(x) → :int |
Wrap a single value — one alternative (success) |
mplus |
(xs ys) → :int |
Concatenate two alternative lists |
mbind |
(ma f) → :int |
Flatmap: apply f to each alternative |
guard |
(pred :bool) → :int |
Keep current branch if pred is true, else fail |
fresh |
(lo hi) → :int |
Return all integers in [lo, hi) as alternatives |
once |
(xs) → :int |
Truncate to first alternative |
interleave |
(xs ys) → :int |
Fair interleaving of two streams |
run-backtrack |
(xs) → :int |
Identity — return all results |
run-backtrack-depth |
(xs n) → :int |
Return first N results only |
;; Return all even numbers from 1..10
(let [evens (mbind (fresh 1 11) (fn [x]
(if (= (mod x 2) 0)
(mreturn x)
(mzero))))]
(bt-print (run-backtrack evens)))
Output:
2
4
6
8
10
backtrack-do MacroThe backtrack-do macro provides Haskell-do-notation-style sequencing for
the backtracking monad:
;; Pythagorean triples with a+b+c = 24
(backtrack-do
a (fresh 1 24)
b (fresh a 24)
c (mreturn (- 24 (+ a b)))
_ (guard (= (* a a) (+ (* b b) (* c c))))
(mreturn (list a b c)))
Each var expr line binds var to each alternative produced by expr. The
final expression is the body for each combination. _ discards the value when
you only care about side-effects (e.g., guard).
Use run-backtrack-depth when you only need the first N results:
;; Take only the first 5 solutions
(bt-print (run-backtrack-depth all-solutions 5))
You can also pass --backtrack-depth N to the compiler as a flag, which emits
#define BACKTRACK_DEPTH_DEFAULT N in the generated C preamble — useful for
runtime dispatch when the stdlib is extended to check this constant.
;; Count solutions to N-Queens for N=6 using backtrack-do
(defn safe? [col row packed n] :bool
;; check if placing at col,row is safe given previous placements in packed
...)
(defn queens [n] :int
(let [result (mreturn 0)] ;; start with empty board (encoded as 0)
(let [board (mbind result (fn [packed]
;; for each row 0..n-1, expand the board
...))]
board)))
See tests/fixtures/backtrack-n-queens/input.tur for the full self-contained
implementation.
The backtracking monad can be combined with algebraic effects. Effects run
outside the monad boundary and the results are wrapped with mreturn:
(defeffect Choose [a :int b :int] :int)
(defn make-choice [] :int
(let [x (perform (Choose 10 20))]
(mreturn (* x 2))))
(defn main []
(let [result (handle (make-choice)
(Choose [a b] k)
(resume k a))] ;; always pick 'a'
(bt-print result)))
See tests/fixtures/backtrack-integration-effects/input.tur for a complete
working example.
The file tests/fixtures/backtrack-sudoku/input.tur demonstrates a 4×4
mini-Sudoku solver using iterative backtracking within inline C code. It
produces the unique solution to the given puzzle.
Cell { int64_t value; int64_t next; }.run-backtrack-depth to bound result count and avoid building large lists.benchmarks/bench-backtrack-n-queens.tur as a baseline.--dump-clone-plan compiler flag prints a summary of every
cloneable-shift site with its captured bindings and resolved Clone
instance methods — useful for debugging continuation capture overhead.| Flag | Description |
|---|---|
--backtrack-depth N |
Emit #define BACKTRACK_DEPTH_DEFAULT N in the C preamble |
--dump-clone-plan |
After the CPS pass, print each cloneable-shift site to stderr |
stdlib/backtrack.tur — standard library implementationtests/fixtures/backtrack-*/ — test fixtures for all backtracking featuresbenchmarks/bench-backtrack-*.tur — performance benchmarksdocs/archive/backtracking-cloneable-continuations-plan.md — design history