--- title: "Standard Library: Control Flow and Macros" output: arl::arl_html_vignette pkgdown: as_is: true vignette: > %\VignetteIndexEntry{Standard Library: Control Flow and Macros} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") arl::register_knitr_engine() ``` *Examples on this page may reference functions from other stdlib modules without showing explicit `(import ...)` statements. In the REPL or your own code, you will need to import non-prelude modules before using their exports — see [Importing modules](lang-reference.html#importing-modules).* ## Control Flow Macros {#section-control-flow-macros} ### when {#when} Evaluate body forms when test is truthy. **Signature:** `(when test body...)` **Parameters:** - **`test`** — Condition to evaluate - **`body`** — Expressions to evaluate when test is truthy **Examples:** ```{arl} (when #t "yes") ; => "yes" (when #f "yes") ; => #nil (when (> 3 2) (+ 1 1)) ; => 2 ``` ```{arl, include=FALSE} (assert-equal "yes" (when #t "yes")) (assert-equal #nil (when #f "yes")) (assert-equal 2 (when (> 3 2) (+ 1 1))) ``` **See also:** [unless](#unless), [if](lang-core.html#if), [cond](#cond) --- ### unless {#unless} Evaluate body forms when test is falsy. **Signature:** `(unless test body...)` **Parameters:** - **`test`** — Condition to evaluate - **`body`** — Expressions to evaluate when test is falsy **Examples:** ```{arl} (unless #f "fallback") ; => "fallback" (unless #t "fallback") ; => #nil (unless (= 1 2) "nope") ; => "nope" ``` ```{arl, include=FALSE} (assert-equal "fallback" (unless #f "fallback")) (assert-equal #nil (unless #t "fallback")) (assert-equal "nope" (unless (= 1 2) "nope")) ``` **See also:** [when](#when), [if](lang-core.html#if) --- ## Conditional Macros {#section-conditional-macros} ### cond {#cond} Multi-branch conditional. **Signature:** `(cond clause rest...)` **Parameters:** - **`clause`** — First (test body...) clause - **`rest`** — Additional clauses; last may be (else body...) **Examples:** ```{arl} (cond ((= 1 2) "a") ((= 1 1) "b") (else "c")) ; => "b" (cond ((> 10 20) "big") (else "small")) ; => "small" ``` ```{arl, include=FALSE} (assert-equal "b" (cond ((= 1 2) "a") ((= 1 1) "b") (else "c"))) (assert-equal "small" (cond ((> 10 20) "big") (else "small"))) ``` **See also:** [case](#case), [if](lang-core.html#if), [when](#when) --- ### case {#case} Branch on key equality. Each clause: ((datum ...) body ...) or (else body ...). Key expression is evaluated only once. **Signature:** `(case key clause rest...)` **Parameters:** - **`key`** — Expression to match (evaluated once) - **`clause`** — First match clause: ((datum...) body...) - **`rest`** — Additional clauses; last may be (else body...) **Examples:** ```{arl} (case (+ 1 1) ((1) "one") ((2) "two") ((3) "three") (else "other")) ; => "two" (case 'x ((a b) "ab") ((x y) "xy") (else "?")) ; => "xy" ``` ```{arl, include=FALSE} (assert-equal "two" (case (+ 1 1) ((1) "one") ((2) "two") ((3) "three") (else "other"))) (assert-equal "xy" (case 'x ((a b) "ab") ((x y) "xy") (else "?"))) ``` **Note:** The key expression is evaluated only once. Each clause datum list can contain multiple values. **See also:** [cond](#cond), [if](lang-core.html#if) --- ## Error Handling {#section-error-handling} ### try {#try} Evaluate thunk with error/finally handlers. **Signature:** `(try thunk [error_handler] [finally_handler])` **Parameters:** - **`thunk`** — Zero-argument function to execute - **`error_handler`** — Function receiving error condition (default #f) - **`finally_handler`** — Zero-argument cleanup function (default #f) **Examples:** ```{arl} (try (lambda () 42)) ; => 42 (try (lambda () (stop "oops")) :error_handler (lambda (e) "caught")) ; => "caught" (try (lambda () 1) :finally_handler (lambda () (display "done"))) ; => 1 ``` ```{arl, include=FALSE} (assert-equal 42 (try (lambda () 42))) (assert-equal "caught" (try (lambda () (stop "oops")) :error_handler (lambda (e) "caught"))) ``` **Note:** Low-level functional interface to R's tryCatch. **See also:** [try-catch](#try-catch) --- ### try-catch {#try-catch} Macro wrapper around try with catch/finally. **Signature:** `(try-catch body clauses...)` **Parameters:** - **`body`** — Expression to evaluate - **`clauses`** — Optional (catch var body...) and/or (finally body...) clauses **Examples:** ```{arl} ;; Basic try with catch (try-catch (/ 1 0) (catch e (string-append "caught: " ($ e "message")))) ;; try with finally (always runs) (try-catch (+ 1 2) (finally (display "cleanup"))) ; => 3 ;; try with both catch and finally (try-catch (stop "oops") (catch e (string-append "error: " ($ e "message"))) (finally (display "done"))) ; => "error: oops" ;; try with no handlers (just evaluates body) (try-catch (+ 1 2)) ; => 3 ``` ```{arl, include=FALSE} (assert-equal 3 (try-catch (+ 1 2))) (assert-equal "error: oops" (try-catch (stop "oops") (catch e (string-append "error: " ($ e "message"))))) ``` **Note:** Macro wrapper around `try` that provides familiar catch/finally syntax. The catch clause binds the error condition to the given variable. The finally clause runs regardless of success or failure. **See also:** [try](#try) --- ## Continuations {#section-continuations} ### call-cc {#call-cc} Invoke receiver with the current continuation (escape procedure). **Signature:** `(call-cc receiver)` **Parameters:** - **`receiver`** — Function that receives the current continuation as its argument **Examples:** ```{arl} (call-cc (lambda (k) (k 42))) ; => 42 ``` ```{arl, include=FALSE} (assert-equal 42 (call-cc (lambda (k) (k 42)))) ``` **Note:** Alias for R's `callCC`. The receiver is passed the current continuation. **See also:** [call-with-current-continuation](#call-with-current-continuation) --- ### call-with-current-continuation {#call-with-current-continuation} Full name alias for call-cc. **Signature:** `(call-with-current-continuation receiver)` **Parameters:** - **`receiver`** — Function that receives the current continuation as its argument **Examples:** ```{arl} (call-with-current-continuation (lambda (k) (k 42))) ; => 42 ``` ```{arl, include=FALSE} (assert-equal 42 (call-with-current-continuation (lambda (k) (k 42)))) ``` **Note:** Full name alias for `call-cc`. **See also:** [call-cc](#call-cc) --- ## Binding Macros {#section-binding-macros} ### pattern-symbols {#pattern-symbols} Collect all symbols from a destructuring pattern, ignoring dots. **Signature:** `(pattern-symbols pattern)` **Parameters:** - **`pattern`** — Nested list/symbol pattern to extract symbol names from **See also:** [destructuring-bind](#destructuring-bind) --- ### destructuring-bind {#destructuring-bind} Bind a destructuring pattern to a value, then evaluate body forms. **Signature:** `(destructuring-bind pattern value body...)` **Parameters:** - **`pattern`** — Destructuring pattern (possibly nested, with . for rest) - **`value`** — Expression whose result is destructured - **`body`** — Expressions evaluated with the pattern bindings in scope **Examples:** ```{arl} (destructuring-bind (a b c) (list 1 2 3) (+ a b c)) ; => 6 (destructuring-bind (x . rest) (list 10 20 30) rest) ; => (20 30) ``` ```{arl, include=FALSE} (assert-equal 6 (destructuring-bind (a b c) (list 1 2 3) (+ a b c))) (assert-equal (list 20 30) (destructuring-bind (x . rest) (list 10 20 30) rest)) ``` **See also:** [let](#let), [let*](#let-star), [pattern-symbols](#pattern-symbols) --- ### let {#let} Bind names to values within body. **Signature:** `(let bindings body...)` **Parameters:** - **`bindings`** — List of (pattern value) pairs, evaluated in parallel - **`body`** — Expressions evaluated with the bindings in scope **Examples:** ```{arl} (let ((x 1) (y 2)) (+ x y)) ; => 3 (let ((x 10)) (* x x)) ; => 100 ``` ```{arl, include=FALSE} (assert-equal 3 (let ((x 1) (y 2)) (+ x y))) (assert-equal 100 (let ((x 10)) (* x x))) ``` **Note:** Bindings are evaluated in parallel: earlier bindings are NOT visible to later ones. Use `let*` for sequential binding. **See also:** [let*](#let-star), [letrec](#letrec) --- ### let* {#let-star} Sequential let bindings. **Parameters:** - **`bindings`** — List of (pattern value) pairs, evaluated sequentially - **`body`** — Expressions evaluated with the bindings in scope **Examples:** ```{arl} (let* ((x 1) (y (+ x 1))) (+ x y)) ; => 3 (let* ((a 10) (b (* a 2)) (c (+ a b))) c) ; => 30 ``` ```{arl, include=FALSE} (assert-equal 3 (let* ((x 1) (y (+ x 1))) (+ x y))) (assert-equal 30 (let* ((a 10) (b (* a 2)) (c (+ a b))) c)) ``` **Note:** Bindings are evaluated sequentially: each binding can refer to previously bound names. Supports destructuring patterns. **See also:** [let](#let), [letrec](#letrec) --- ### letrec {#letrec} Recursive bindings. **Signature:** `(letrec bindings body...)` **Parameters:** - **`bindings`** — List of (name value) pairs, all mutually visible - **`body`** — Expressions evaluated with the bindings in scope **Examples:** ```{arl} (letrec ((even? (lambda (n) (if (= n 0) #t (odd? (- n 1))))) (odd? (lambda (n) (if (= n 0) #f (even? (- n 1)))))) (even? 10)) ; => #t ``` ```{arl, include=FALSE} (assert-true (letrec ((ev? (lambda (n) (if (= n 0) #t (od? (- n 1))))) (od? (lambda (n) (if (= n 0) #f (ev? (- n 1)))))) (ev? 10))) ``` **Note:** All bindings are visible to all init expressions, enabling mutual recursion. Init values are assigned via `set!` into pre-allocated slots. **See also:** [let](#let), [let*](#let-star) --- ### when-let {#when-let} Bind pattern to value and evaluate body when value is truthy. **Signature:** `(when-let binding body...)` **Parameters:** - **`binding`** — Single (pattern value) pair - **`body`** — Expressions evaluated when value is truthy, with pattern bound **Examples:** ```{arl} (when-let (x (assoc 'a (list (list 'a 1) (list 'b 2)))) (cadr x)) ; => 1 (when-let (x #f) "never reached") ; => #nil ``` ```{arl, include=FALSE} (assert-equal 1 (when-let (x (assoc 'a (list (list 'a 1) (list 'b 2)))) (cadr x))) (assert-equal #nil (when-let (x #f) "never reached")) ``` **See also:** [if-let](#if-let), [when](#when), [let](#let) --- ### if-let {#if-let} Bind pattern to value and choose branch based on its truthiness. **Signature:** `(if-let binding then rest...)` **Parameters:** - **`binding`** — Single (pattern value) pair - **`then`** — Expression evaluated when value is truthy, with pattern bound - **`rest`** — Optional else expression when value is falsy **Examples:** ```{arl} (if-let (x 42) (+ x 1) "nothing") ; => 43 (if-let (x #f) "truthy" "falsy") ; => "falsy" ``` ```{arl, include=FALSE} (assert-equal 43 (if-let (x 42) (+ x 1) "nothing")) (assert-equal "falsy" (if-let (x #f) "truthy" "falsy")) ``` **See also:** [when-let](#when-let), [if](lang-core.html#if), [let](#let) --- ## Looping Macros {#section-looping-macros} ### do-list {#do-list} Loop over seq binding var each iteration. Expands to while. **Signature:** `(do-list binding body...)` **Parameters:** - **`binding`** — Pair of (var sequence-expr) - **`body`** — Expressions evaluated for each element with var bound **Examples:** ```{arl} (define total 0) (do-list (x '(1 2 3)) (set! total (+ total x))) total ; => 6 (define result (list)) (do-list (ch '("a" "b" "c")) (set! result (append result (list ch)))) result ; => ("a" "b" "c") ``` ```{arl, include=FALSE} (define _test-total 0) (do-list (_x '(1 2 3)) (set! _test-total (+ _test-total _x))) (assert-equal 6 _test-total) ``` **Note:** Expands to a `while` loop over the sequence converted to a list. **See also:** [while](lang-core.html#while), [map](lang-functional.html#map), [loop](#loop) --- ### recur {#recur} Signal a recur back to the enclosing `loop`. Only valid inside a `loop` body. **Signature:** `(recur args...)` **Parameters:** - **`args`** — New values for each loop variable (must match loop binding count) **Examples:** ```{arl} (loop ((i 5) (acc 0)) (if (= i 0) acc (recur (- i 1) (+ acc i)))) ; => 15 ``` ```{arl, include=FALSE} (assert-equal 15 (loop ((i 5) (acc 0)) (if (= i 0) acc (recur (- i 1) (+ acc i))))) ``` **See also:** [loop](#loop) --- ### loop {#loop} Clojure-style loop/recur that expands to an R while loop. **Signature:** `(loop bindings body...)` **Parameters:** - **`bindings`** — List of (var init-value) pairs for loop variables - **`body`** — Loop body; use (recur ...) to iterate with new values **Examples:** ```{arl} ;; Factorial via loop/recur (loop ((n 5) (acc 1)) (if (= n 0) acc (recur (- n 1) (* acc n)))) ; => 120 ;; Sum of a list via loop/recur (loop ((xs '(1 2 3 4)) (total 0)) (if (null? xs) total (recur (cdr xs) (+ total (car xs))))) ; => 10 ``` ```{arl, include=FALSE} (assert-equal 120 (loop ((n 5) (acc 1)) (if (= n 0) acc (recur (- n 1) (* acc n))))) (assert-equal 10 (loop ((xs '(1 2 3 4)) (total 0)) (if (null? xs) total (recur (cdr xs) (+ total (car xs)))))) ``` **Note:** The compiler auto-optimizes self-tail-calls in `(define name (lambda ...))` patterns, so `loop`/`recur` is primarily useful for anonymous iteration and mutual recursion patterns. Expands to an R `while` loop internally. **See also:** [recur](#recur), [while](lang-core.html#while), [do-list](#do-list) --- ### until {#until} Repeat body until test is truthy. **Signature:** `(until test body...)` **Parameters:** - **`test`** — Condition checked before each iteration; loop stops when truthy - **`body`** — Expressions evaluated each iteration **Examples:** ```{arl} (define i 0) (until (= i 5) (set! i (+ i 1))) i ; => 5 ``` ```{arl, include=FALSE} (define _test-i 0) (until (= _test-i 5) (set! _test-i (+ _test-i 1))) (assert-equal 5 _test-i) ``` **See also:** [while](lang-core.html#while) --- ## Threading Macros {#section-threading-macros} ### -> {#thread-first} Thread value through forms (first argument). **Signature:** `(-> value forms...)` **Parameters:** - **`value`** — Initial value to thread - **`forms`** — Sequence of forms to thread through **Examples:** ```{arl} (-> 5 (+ 3) (* 2)) ; => 16 ; (5+3)*2 (-> (list 1 2 3) reverse) ; => (3 2 1) (-> "hello" nchar) ; => 5 ``` ```{arl, include=FALSE} (assert-equal 16 (-> 5 (+ 3) (* 2))) (assert-equal (list 3 2 1) (-> (list 1 2 3) reverse)) (assert-equal 5 (-> "hello" nchar)) ``` **Note:** Threads the value as the FIRST argument to each successive form. If a form is a bare symbol, it is called as a one-argument function. **See also:** [->>](#thread-last) --- ### ->> {#thread-last} Thread value through forms (last argument). **Signature:** `(->> value forms...)` **Parameters:** - **`value`** — Initial value to thread - **`forms`** — Sequence of forms to thread through **Examples:** ```{arl} (->> '(1 2 3) (map (lambda (x) (* x x)))) ; => (1 4 9) (->> 5 (- 10)) ; => 5 ; (- 10 5) (->> '(1 2 3) (map (lambda (x) (+ x 10))) (car)) ; => 11 ``` ```{arl, include=FALSE} (assert-equal (list 1 4 9) (->> '(1 2 3) (map (lambda (x) (* x x))))) (assert-equal 5 (->> 5 (- 10))) (assert-equal 11 (->> '(1 2 3) (map (lambda (x) (+ x 10))) (car))) ``` **Note:** Threads the value as the LAST argument to each successive form. Compare with `->` which threads as the first argument. **See also:** [->](#thread-first) --- ### as-> {#as-to} Thread with a named binding. The value is bound to name in each form, so it can appear in any position. **Signature:** `(as-> expr name forms...)` **Parameters:** - **`expr`** — Initial value - **`name`** — Symbol to bind the threaded value to - **`forms`** — Sequence of forms using name **Examples:** ```{arl} (as-> 1 x (+ x 1) (* x 3)) ; => 6 ``` ```{arl, include=FALSE} (assert-equal 6 (as-> 1 x (+ x 1) (* x 3))) ``` **See also:** [->](#thread-first), [->>](#thread-last) --- ### some-> {#some-to} Thread-first with short-circuit on falsy values. Like -> but stops and returns #nil or #f if any step produces a falsy value. **Signature:** `(some-> value forms...)` **Parameters:** - **`value`** — Initial value - **`forms`** — Sequence of forms to thread through **Examples:** ```{arl} (some-> 5 (+ 3) (* 2)) ; => 16 (some-> #nil (+ 3)) ; => #nil ``` ```{arl, include=FALSE} (assert-equal 16 (some-> 5 (+ 3) (* 2))) (assert-equal #nil (some-> #nil (+ 3))) ``` **See also:** [->](#thread-first), [some->>](#some-to-gt) --- ### some->> {#some-to-gt} Thread-last with short-circuit on falsy values. Like ->> but stops and returns #nil or #f if any step produces a falsy value. **Signature:** `(some->> value forms...)` **Parameters:** - **`value`** — Initial value - **`forms`** — Sequence of forms to thread through **Examples:** ```{arl} (some->> 5 (- 10)) ; => 5 (some->> #nil (- 10)) ; => #nil ``` ```{arl, include=FALSE} (assert-equal 5 (some->> 5 (- 10))) (assert-equal #nil (some->> #nil (- 10))) ``` **See also:** [->>](#thread-last), [some->](#some-to) --- ### cond-> {#cond-to} Conditionally thread value through forms (first argument). Each clause is (test form). If test is truthy, threads value through form; otherwise passes value unchanged. **Signature:** `(cond-> value clauses...)` **Parameters:** - **`value`** — Initial value - **`clauses`** — Pairs of (test form) to conditionally apply **Examples:** ```{arl} (cond-> 1 (#t (+ 1)) (#f (* 3))) ; => 2 ``` ```{arl, include=FALSE} (assert-equal 2 (cond-> 1 (#t (+ 1)) (#f (* 3)))) ``` **See also:** [->](#thread-first), [cond->>](#cond-to-gt) --- ### cond->> {#cond-to-gt} Conditionally thread value through forms (last argument). Thread-last variant of cond->. **Signature:** `(cond->> value clauses...)` **Parameters:** - **`value`** — Initial value - **`clauses`** — Pairs of (test form) to conditionally apply **Examples:** ```{arl} (cond->> 5 (#t (- 10)) (#f (* 100))) ; => 5 ``` ```{arl, include=FALSE} (assert-equal 5 (cond->> 5 (#t (- 10)) (#f (* 100)))) ``` **See also:** [->>](#thread-last), [cond->](#cond-to) ---