Guard Statements
guard
guard-match
define/  guard
guarded-block
8.15.0.2

Guard Statements🔗ℹ

jackfirth

 (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.

Traditional approach:
(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))]))

Using guard statements:
(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
A guard statement that ensures that condition-expr evaluates to a non-false value. If it’s false, the surrounding guarded block short circuits evaluation with the result of executing fail-body ....

Examples:
(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
A guard statement that evaluates expr and ensures that its result matches match-pattern. If the result does not match the given pattern, the surrounding guarded block short circuits evaluation with the result of executing fail-body .... Upon successful matching, the bindings in match-pattern are made available to the surrounding definition context. This form is similar to match-define, except that the fail-body ... branch is taken if the match fails.

Examples:
(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]
Defines a function like define, but where the function body is wrapped in guarded-block.

Examples:
(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 ...+)

Evaluates each body, except that any guard statements are treated specially. When a guard statement fails, it short circuits the evaluation of the entire enclosing guarded-block.

Examples:
(define (double-numbers-only xs)
  (for/list ([x (in-list xs)])
    (guarded-block
     (guard (number? x) #:else x)
     (* x 2))))

 

> (double-numbers-only (list 1 2 'apple 3 'banana 4 5))

'(2 4 apple 6 banana 8 10)