Cookbook
Basics
Core language features: values, bindings, functions, recursion, higher-order programming, closures, composition, argument parsing, and exception handling.
Run any example with:
etai cookbook/basics/<file>.eta
Hello, World!
cookbook/basics/hello.eta · source
A minimal program that prints a greeting and demonstrates a simple recursive factorial.
(module hello
(import std.io)
(begin
(println "Hello, world!")
(defun factorial (n)
(if (= n 0) 1
(* n (factorial (- n 1)))))
(println (factorial 20))))
Language Fundamentals
cookbook/basics/basics.eta · source
Arithmetic, booleans, conditionals, variables, let / let*, strings, pairs,
lists, and record types.
(module basics
(import std.io)
(begin
;; -- Arithmetic --
(println (+ 1 2 3)) ; => 6
(println (* 2 3 4)) ; => 24
(println (- 10 3)) ; => 7
;; -- Booleans & Conditionals --
(println (and #t #f)) ; => #f
(println (or #f #t)) ; => #t
(println (if (> 3 2) "yes" "no")) ; => "yes"
(println (cond
((= 1 2) "nope")
((= 1 1) "match!")
(#t "default"))) ; => "match!"
;; -- Variables & Let --
(define x 42)
(println x) ; => 42
(let ((a 10) (b 20))
(println (+ a b))) ; => 30
;; let* -- sequential bindings
(let* ((a 1)
(b (+ a 1))
(c (+ a b)))
(println c)) ; => 4
;; -- Strings --
(println (string-append "hello" ", " "world!"))
(println (string-length "eta")) ; => 3
;; -- Pairs & Lists --
(define xs (list 1 2 3 4 5))
(println xs) ; => (1 2 3 4 5)
(println (length xs)) ; => 5
(println (append '(a b) '(c d))) ; => (a b c d)
;; -- Records --
(define-record-type <point>
(make-point x y)
point?
(x point-x)
(y point-y))
(define p (make-point 3 4))
(println (point? p)) ; => #t
(println (point-x p)) ; => 3
;; Mutable field
(define-record-type <counter>
(make-counter value)
counter?
(value counter-value set-counter-value!))
(define c (make-counter 0))
(set-counter-value! c (+ (counter-value c) 1))
(println (counter-value c)) ; => 1
;; -- Symbols & Quoting --
(println 'hello) ; => hello
(println '(one two three)) ; => (one two three)
))
Functions & Closures
cookbook/basics/functions.eta · source
Named functions, tail recursion, lambda expressions, closures, variadic
arguments, and mutual recursion via letrec.
(module functions
(import std.io std.math)
(begin
;; Named function
(defun sq (x) (* x x))
(println (sq 7)) ; => 49
;; Recursive factorial
(defun factorial (n)
(if (= n 0) 1
(* n (factorial (- n 1)))))
(println (factorial 10)) ; => 3628800
;; Tail-recursive with accumulator
(defun factorial-tr (n)
(letrec ((go (lambda (n acc)
(if (= n 0) acc
(go (- n 1) (* acc n))))))
(go n 1)))
(println (factorial-tr 20)) ; => 2432902008176640000
;; Lambda
(define double (lambda (x) (* 2 x)))
(println (double 21)) ; => 42
;; Closure: captures its environment
(defun make-adder (n)
(lambda (x) (+ n x)))
(define add5 (make-adder 5))
(define add10 (make-adder 10))
(println (add5 3)) ; => 8
(println (add10 3)) ; => 13
;; Variadic (rest args)
(defun sum-all (first . rest)
(letrec ((go (lambda (xs acc)
(if (null? xs) acc
(go (cdr xs) (+ acc (car xs)))))))
(go rest first)))
(println (sum-all 1 2 3 4 5)) ; => 15
;; Mutual recursion
(letrec
((is-even? (lambda (n)
(if (= n 0) #t (is-odd? (- n 1)))))
(is-odd? (lambda (n)
(if (= n 0) #f (is-even? (- n 1))))))
(println (is-even? 42)) ; => #t
(println (is-odd? 13))) ; => #t
))
Higher-Order Functions
cookbook/basics/higher-order.eta · source
map*, filter, foldl, foldr, reduce, any?, every?, count,
zip, take, drop, range, flatten, and sort.
(module higher-order
(import std.core std.io std.collections std.math)
(begin
(define nums (list 1 2 3 4 5 6 7 8 9 10))
;; map* -- transform every element
(println (map* square nums))
;; => (1 4 9 16 25 36 49 64 81 100)
;; filter -- keep matching elements
(println (filter even? nums)) ; => (2 4 6 8 10)
;; foldl -- left fold (sum)
(println (foldl + 0 nums)) ; => 55
;; foldr -- right fold (copy a list)
(println (foldr cons '() '(a b c))) ; => (a b c)
;; reduce -- fold without initial value
(println (reduce max '(3 7 2 9 1))) ; => 9
;; Combine: sum of squares of even numbers
(define result
(foldl + 0 (map* square (filter even? nums))))
(println result) ; => 220
;; any? / every?
(println (any? negative? '(1 -2 3))) ; => #t
(println (every? positive? '(1 2 3))) ; => #t
;; count
(println (count even? nums)) ; => 5
;; zip
(println (zip '(a b c) '(1 2 3)))
;; => ((a . 1) (b . 2) (c . 3))
;; take / drop
(println (take 3 nums)) ; => (1 2 3)
(println (drop 7 nums)) ; => (8 9 10)
;; range
(println (range 1 6)) ; => (1 2 3 4 5)
;; flatten
(println (flatten '((1 2) (3 4) (5)))) ; => (1 2 3 4 5)
;; sort
(println (sort < '(5 3 8 1 4 2 7 6))) ; => (1 2 3 4 5 6 7 8)
))
Recursion
cookbook/basics/recursion.eta · source
Classic recursive algorithms: Fibonacci (naive and tail-recursive), list reversal, depth-first flatten, Ackermann function, and Tower of Hanoi.
(module recursion
(import std.io)
(begin
;; Fibonacci (naive)
(defun fib (n)
(cond
((= n 0) 0)
((= n 1) 1)
(#t (+ (fib (- n 1)) (fib (- n 2))))))
(println (fib 10)) ; => 55
;; Fibonacci (tail-recursive)
(defun fib-fast (n)
(letrec ((go (lambda (i a b)
(if (= i 0) a
(go (- i 1) b (+ a b))))))
(go n 0 1)))
(println (fib-fast 30)) ; => 832040
;; List reversal
(defun my-reverse (xs)
(letrec ((go (lambda (xs acc)
(if (null? xs) acc
(go (cdr xs) (cons (car xs) acc))))))
(go xs '())))
(println (my-reverse '(1 2 3 4 5))) ; => (5 4 3 2 1)
;; Deep flatten
(defun deep-flatten (xs)
(cond
((null? xs) '())
((pair? (car xs))
(append (deep-flatten (car xs))
(deep-flatten (cdr xs))))
(#t (cons (car xs) (deep-flatten (cdr xs))))))
(println (deep-flatten '((1 (2 3)) (4) 5))) ; => (1 2 3 4 5)
;; Ackermann function
(defun ackermann (m n)
(cond
((= m 0) (+ n 1))
((= n 0) (ackermann (- m 1) 1))
(#t (ackermann (- m 1) (ackermann m (- n 1))))))
(println (ackermann 3 4)) ; => 125
;; Tower of Hanoi
(defun hanoi (n from to via)
(when (> n 0)
(hanoi (- n 1) from via to)
(display "Move disk ") (display n)
(display " from ") (display from)
(display " to ") (display to) (newline)
(hanoi (- n 1) via to from)))
(hanoi 3 'A 'C 'B)
))
Composition & Currying
cookbook/basics/composition.eta · source
compose, flip, constantly, manual currying, negate, and functional
data pipelines.
(module composition
(import std.core std.io std.collections std.math)
(begin
;; compose: (f o g)(x) = f(g(x))
(define inc-then-double
(compose (lambda (x) (* 2 x))
(lambda (x) (+ x 1))))
(println (inc-then-double 5)) ; => 12 ((5+1)*2)
;; flip -- swap argument order
(define rcons (flip cons))
(println (rcons '(1 2 3) 0)) ; => (0 1 2 3)
;; constantly -- always returns the same value
(define always-42 (constantly 42))
(println (always-42)) ; => 42
(println (always-42 'ignored)) ; => 42
;; Manual currying
(defun add (a) (lambda (b) (+ a b)))
(define inc (add 1))
(define add10 (add 10))
(println (inc 99)) ; => 100
(println (add10 32)) ; => 42
;; Curried multiply
(defun mul (a) (lambda (b) (* a b)))
(define double (mul 2))
(define triple (mul 3))
(println (map* double '(1 2 3 4 5))) ; => (2 4 6 8 10)
(println (map* triple '(1 2 3 4 5))) ; => (3 6 9 12 15)
;; Compose curried functions
(define double-then-inc (compose (add 1) (mul 2)))
(println (map* double-then-inc '(1 2 3 4 5)))
;; => (3 5 7 9 11)
;; negate -- flip a predicate
(define not-even? (negate even?))
(println (filter not-even? (range 1 11)))
;; => (1 3 5 7 9)
;; Pipeline: first 5 odd squares from 1..20
(define odds (filter odd? (range 1 21)))
(define squares (map* square odds))
(define top5 (take 5 squares))
(println top5)
;; => (1 9 25 49 81)
))
Argument Parsing
cookbook/basics/args.eta · source
Declarative command-line argument parsing via std.args with flags, strings,
integers, lists, custom parsers, and validators.
(module args-example
(import std.io std.args)
(begin
(define parse-level
(lambda (raw)
(let ((n (string->number raw)))
(if (and (number? n) (integer? n))
n
(error "level must be an integer")))))
(define level-in-range?
(lambda (n) (and (>= n 0) (<= n 5))))
;; Spec: (name flags type default [validators])
(define spec
(list
'(v (--verbose -v) flag)
'(o (--out -o) string "report.txt")
'(n (--num) int 0)
'(t (--tag) list)
(list 'level
'(--level)
'int
1
(list (cons 'parse parse-level)
(cons 'validate level-in-range?)))))
(defun run-demo ()
(args:parse
spec
'("-v" "--out=demo.txt" "--num" "3"
"--tag" "risk" "--tag" "stress"
"--level=4"
"--" "-z")))
(defun summarize (r)
(list
(cons 'verbose (args:get r 'v))
(cons 'out (args:get r 'o))
(cons 'num (args:get r 'n))
(cons 'tag (args:get r 't))
(cons 'level (args:get r 'level))
(cons 'positional (args:get r 'positional))))
(println (summarize (run-demo)))))
Exception Handling
cookbook/basics/exceptions.eta · source
Eta uses (raise 'tag value) and (catch 'tag body) for structured
non-local exits. A tag-less (catch body) catches any exception.
(module exceptions
(import std.io std.core)
(begin
;; Basic catch / raise
(println (catch 'err 42)) ; => 42
(println (catch 'err (raise 'err "oops!"))) ; => oops!
;; Catch-all (no tag)
(let ((result (catch (raise 'anything "caught by catch-all"))))
(println result)) ; => caught by catch-all
;; Tag specificity
(let ((result
(catch 'outer-err
(+ 10 (catch 'inner-err
(raise 'inner-err 5))))))
(println result)) ; => 15
;; Propagation through calls
(defun safe-divide (a b)
(if (= b 0)
(raise 'division-by-zero 'undefined)
(/ a b)))
(println (catch 'division-by-zero (safe-divide 10 2))) ; => 5
(println (catch 'division-by-zero (safe-divide 10 0))) ; => undefined
;; Structured payloads
(defun validate-age (age)
(cond
((< age 0) (raise 'validation-error (cons 'negative age)))
((> age 150) (raise 'validation-error (cons 'too-large age)))
(#t age)))
(let ((err (catch 'validation-error (validate-age -5))))
(println (car err)) ; => negative
(println (cdr err))) ; => -5
;; Early exit from search
(defun first-negative (lst)
(catch 'found
(letrec ((loop (lambda (xs)
(cond
((null? xs) #f)
((< (car xs) 0) (raise 'found (car xs)))
(#t (loop (cdr xs)))))))
(loop lst))))
(println (first-negative '(3 1 4 1 5))) ; => #f
(println (first-negative '(3 1 -4 2 -1))) ; => -4
;; Cleanup on exception using dynamic-wind
(define cleanup-called #f)
(catch 'resource-error
(dynamic-wind
(lambda () (set! cleanup-called #f))
(lambda () (raise 'resource-error "boom"))
(lambda () (set! cleanup-called #t))))
(println cleanup-called) ; => #t
))