# COND-THUNK License: Apache-2.0 `cond-thunk` is a library that abstracts conditionals into procedures called *conditional procedues* (ct). This allows the programmer to abstract conditional branches into procedures or macros, allowing for multiple value binding or backtracking. Documentation for this library is in the format of index.scheme.org and is located in `doc`. ## Tutorial -- `cond-thunk` This tutorial is to get you aquainted with the basic `cond-thunk` form. The `cond-thunk` macro (cond-thunk expr1 expr2 ... (else body ...)) evaluates each expression in order. If the expression is `#f`, it evaluates the next expression. If it is a thunk, then it tail-calls the thunk. If no expression returns a thunk, then the `body ...` is evaluated for its values. We will start by rewriting `map` using `cond-thunk`. `map` in regular Scheme is (define (map f lst) (cond ((null? lst) '()) ((pair? lst) (cons (f (car lst)) (map f (cdr lst)))) (else (error "not a list" lst)))) With `cond-thunk`, this becomes (define (map f lst) (cond-thunk (if (null? lst) (lambda () '()) #f) (if (pair? lst) (lambda () (cons (f (car lst)) (map f (cdr lst)))) #f) (else (error "not a list" lst)))) This is very verbose. We can use the helper macro `when-ct` which will handle the conditional and wrapping the body in a lambda: (define (map f lst) (cond-thunk (when-ct (null? lst) '()) (when-ct (pair? lst) (cons (f (car lst)) (map f (cdr lst)))) (else (error "not a list" lst)))) At this point, we seem to have just re-made the usual `map`, but with more macro invocations. But now each branch is it's own expression, which are not limited by the definition of the `cond` macro. Let's implement `filter`: (define (filter predicate? lst) (cond-thunk (when-ct (null? lst) '()) (when-ct (not (pair? lst)) (error "not a list" lst)) (when-ct (predicate? (car lst)) (cons (car lst) (filter predicate? (cdr lst)))) (else (filter predicate? (cdr lst))))) There is a common case between `filter` and `map`: the empty list and the error check. We can abstract them into their own procedures: (define (null-on-null lst) (when-ct (null? lst) '())) (define (not-a-list-error lst) (when-ct (not (pair? lst)) (error "not a list" lst))) (define (map f lst) (cond-thunk (null-on-null lst) (not-a-list-error lst) (else (cons (f (car lst)) (map f (cdr lst)))))) (define (filter predicate? lst) (cond-thunk (null-on-null lst) (not-a-list-error lst) (when-ct (predicate? (car lst)) (cons (car lst) (filter predicate? (cdr lst)))) (else (filter predicate? (cdr lst))))) This makes the clauses more declarative, and easier to modify. ## Tutorial -- `after` TODO