Guard Statements
(require guard) | package: guard |
This package provides a Racket syntax for guard statements. A guard statement ensures that condition is true before executing statements after the guard. Boolean conditions can be checked with guard, and pattern matching conditions can be checked with guard-match. Guard statements can only be used within guarded blocks, which are either uses of the guarded-block macro or bodies of functions defined with define/guard.
Guard statements allow writing code linearly when it would ordinarily require deep nesting of forms like cond and match. Compare the following two function definitions, one of which uses traditional Racket forms and the other of which uses guard statements.
(define (lists-equal? xs ys) (cond [(empty? xs) (empty? ys)] [(empty? ys) #false] [else (match-define (cons x rest-xs) xs) (match-define (cons y rest-ys) ys) (and (equal? x y) (lists-equal? rest-xs rest-ys))]))
(define/guard (lists-equal? xs ys) (guard-match (cons x rest-xs) xs #:else (empty? ys)) (guard-match (cons y rest-ys) ys #:else #false) (and (equal? x y) (lists-equal? rest-xs rest-ys)))
Guard statements cooperate with macro expansion. Macros can expand into uses of guard and guard-match. Such user-defined guard statements are recognized by guarded-block and define/guard via local expansion.
syntax
(guard condition-expr #:else fail-body ...+)
condition-expr : any/c
(define/guard (add-positive x y) (guard (positive? x) #:else 'nonpositive-x) (guard (positive? y) #:else 'nonpositive-y) (+ x y))
> (add-positive -4 7) 'nonpositive-x
> (add-positive 4 -7) 'nonpositive-y
> (add-positive 4 7) 11
syntax
(guard-match match-pattern expr #:else fail-body ...+)
expr : any/c
(define/guard (zip-lists zipper xs ys) (guard-match (cons x rest-xs) xs #:else '()) (guard-match (cons y rest-ys) ys #:else '()) (cons (zipper x y) (zip-lists zipper rest-xs rest-ys)))
> (zip-lists (λ (action animal) (format "~a, ~a!" action animal)) (list "jump" "dig" "hop") (list "dog" "mole" "rabbit")) '("jump, dog!" "dig, mole!" "hop, rabbit!")
syntax
(define/guard (head args) body ...+)
head = id | (head args) args = arg ... | arg ... . rest-id arg = arg-id | [arg-id default-expr] | keyword arg-id | keyword [arg-id default-expr]
(define/guard (filter-and-double-numbers xs) (guard-match (cons x rest-xs) xs #:else '()) (guard (number? x) #:else (filter-and-double-numbers rest-xs)) (cons (* x 2) (filter-and-double-numbers rest-xs)))
> (filter-and-double-numbers (list 1 2 'apple 3 'banana 4 5)) '(2 4 6 8 10)
syntax
(guarded-block body ...+)