On this page:
update
2.6.1 Patterns
_
id
and
cons
list
list-of
struct-field
iso
optic
2.6.2 Getter and Updater Utilities
current-update-target
get
optic-set!
modify!
fold
2.6.3 Extending Update
define-update-syntax
8.15.0.2

2.6 Pattern-based Updating🔗ℹ

 (require ocular-patdown/update) package: ocular-patdown

syntax

(update target-expr clause ...)

 
clause = [pat body ...+]

Like match for performing immutable updates. Also useful for creating composed optics.

Example:
> (update (list 1 2 3) [(list a b c) (set! a #t) (modify! b -)])

'(#t -2 3)

Patterns act like trees of optic compositions (see optic-compose) with variables as leaves, binding composed optics.

Example:
> (update (list 1 2 3) [(list a b c) b])

#<make-lens>

Some more examples
> (struct posn [x y] #:transparent)
> (update (posn 1 2)
    [(struct-field posn x)
     (set! x 3)])

(posn 3 2)

> (update (list (posn 1 2) (posn 3 4))
    [(list (struct-field posn x) p)
     (modify! x -)
     (set! p (posn 5 6))])

(list (posn -1 2) (posn 5 6))

> (update (list (posn 1 2) (posn 3 4) (posn 5 6))
    [(list-of (struct-field posn x))
     (modify! x -)])

(list (posn -1 2) (posn -3 4) (posn -5 6))

> (update (list 1 2 3)
    [(list a b)
    (error "boom")]
    [(list a b c)
     (set! c 4)])

'(1 2 4)

> (update (list 1 2)
    [(list a b)
     (get a)])

1

> (update (list 1 2)
    [(list a b)
     (set! a (+ 4 (get b)))])

'(6 2)

2.6.1 Patterns🔗ℹ

The grammar for patterns is as follows:

  pat = _
  | id
  | (and pat ...)
  | (cons pat pat)
  | (list pat ...)
  | (list-of pat)
  | (struct-field struct-id id pat)
  | (struct-field struct-id id)
  | (iso expr expr expr pat)
  | (optic expr expr pat)

More details on the different patterns:

syntax

_

Matches any value and binds nothing.

Example:
> (update 1 [_ 2])

2

syntax

id

Matches any value and binds the id to the optic focusing on the value(s) specified by this pattern.

Example:
> (update 1 [a (get a)])

1

syntax

(and pat ...)

Match all the patterns.

Example:
> (update (list 1 2)
    [(and a (list b c))
     (set! b (get a))])

'((1 2) 2)

syntax

(cons car-pat cdr-pat)

Matches pairs. car-pat gets matched on the car (composes the car-lens optic), and cdr-pat gets matched on the cdr (composes the cdr-lens optic).

Examples:
> (update (cons 1 2)
    [(cons a b)
     (set! a 3)])

'(3 . 2)

> (update (list #t #f)
    [(cons a (cons b _))
     (set! b 'true)])

'(#t true)

syntax

(list pat ...)

Matches a list with as many elements as pats. Matches each element against its corresponding pattern. Composes car-lens and cdr-lens appropriately.

Example:
> (update (list 1 2)
    [(list a b)
     (modify! b -)])

'(1 -2)

syntax

(list-of pat)

Matches a list. Matches each element against pat, but optics bounds by pat focus on all elements, not just one element. Composes list-traversal.

Examples:
> (update (list 1 2 3 4)
    [(list-of n)
     (modify! n sqr)])

'(1 4 9 16)

> (update '((1 2 3) (4 5 6) () (7))
    [(list-of (list-of n))
     (modify! n sqr)])

'((1 4 9) (16 25 36) () (49))

> (update (list (posn 1 2) (posn 3 4))
    [(list-of (struct-field posn x))
     (modify! x -)])

(list (posn -1 2) (posn -3 4))

syntax

(struct-field struct-name field-name pat)

(struct-field struct-name field-name)
Matches a struct which is an instance of the struct type named struct-id. Focuses on the field field-name and matches pat against its value. Composes a struct-lens.

The second form is shorthand for (struct-field struct-name field-name field-name).

Cannot be used on fields from a struct’s super type.

Examples:
> (update (posn 1 2)
    [(struct-field posn x x-value)
     (set! x-value 3)])

(posn 3 2)

> (update (posn 1 2)
    [(struct-field posn x)
     (set! x 3)])

(posn 3 2)

Be careful with struct subtypes:
> (struct posn3 posn [z] #:transparent)
> (update (posn3 3 4 5)
    [(struct-field posn3 z)
     (set! z 9)])

(posn3 3 4 9)

> (update (posn3 3 4 5)
    [(struct-field posn x)
     (set! x 9)])

(posn 9 4)

Naively trying to use a super type’s struct field to perform an update on an instance of the subtype will yield an instance of the super type.

syntax

(iso target? forward backward pat)

 
  target? : (-> any/c boolean?)
  forward : (-> any/c any/c)
  backward : (-> any/c any/c)
Matches a value that satisfies target?. Matches pat against the result of forward applied to the target. Composes the isomorphism (make-iso forward backward). As such, forward and backward should be inverse functions of each other. Useful for treating values of one type as values of another, equivalent type.

Example:
> (update 'foo
    [(iso symbol? symbol->string string->symbol str)
     (modify! str string-upcase)])

'FOO

syntax

(optic target? optic-expr pat)

 
  target? : (-> any/c boolean?)
  optic-expr : optic?
Matches a value that satisfies target?. Matches pat against the focus or foci of optic-expr, where optic-expr is an optic. Composes optic-expr. This is useful for using optics as patterns and defining new patterns using optics and define-update-syntax. In fact, most of the standard patterns are defined this way.

Example:
> (update (cons 1 2)
    [(optic cons? car-lens a)
     (modify! a sub1)])

'(0 . 2)

2.6.2 Getter and Updater Utilities🔗ℹ

get, optic-set!, and modify! are just ordinary procedures that operate on optics. The only thing that is special about them is that they know about the current target of an update.

parameter

(current-update-target)  any/c

(current-update-target target)  void?
  target : any/c
A parameter that is equal to the current target of an update expression. Used in functions like get and modify!.

Examples:
> (update (list 1 2)
    [(list a b)
     (current-update-target)])

'(1 2)

> (update (list 1 2)
    [(list a b)
     (set! a 4)
     (current-update-target)])

'(4 2)

procedure

(get optic)  any/c

  optic : lens?
Gets the focus of optic using current-update-target.

Examples:
> (update (cons 1 2)
    [(cons a _)
     (get a)])

1

> (parameterize ([current-update-target (cons 1 2)])
    (get car-lens))

1

procedure

(optic-set! optic value)  any/c

  optic : lens?
  value : any/c
Sets the focus of optic to value using current-update-target. Also mutates current-update-target and returns its new value.

Using set! on a variable bound by update will use optic-set!, so you can just use set! instead.

Examples:
> (update (cons 1 2)
    [(cons a _)
     (optic-set! a #t)])

'(#t . 2)

> (update (cons 1 2)
    [(cons a _)
     (set! a #t)])

'(#t . 2)

> (current-update-target (cons 1 2))
> (optic-set! car-lens -1)

'(-1 . 2)

> (current-update-target)

'(-1 . 2)

procedure

(modify! optic proc)  any/c

  optic : optic?
  proc : (-> any/c any/c)
Updates the focus of optic by applying proc, using current-update-target. Also mutates current-update-target and returns its new value.

Examples:
> (update (cons 1 2)
    [(cons a _)
     (modify! a sub1)])

'(0 . 2)

> (current-update-target (cons 1 2))
> (modify! cdr-lens sqr)

'(1 . 4)

> (current-update-target)

'(1 . 4)

procedure

(fold optic proc init)  any/c

  optic : optic?
  proc : (-> any/c any/c any/c)
  init : any/c
Folds (foldl) over the foci of optic using current-update-target.

Examples:
> (update (list 1 2 3)
    [(list-of a)
     (fold a + 0)])

6

> (parameterize ([current-update-target (list 1 2 3)])
    (fold list-traversal cons '()))

'(3 2 1)

2.6.3 Extending Update🔗ℹ

syntax

(define-update-syntax id transformer-expr)

(define-update-syntax (id arg-id ...) body-expr ...+)
 
  transformer-expr : (-> syntax? syntax?)
Like define-syntax, but defines a macro for patterns in update. Macro names are only visible within update expressions, so they will not shadow names provided by Racket.

The first form defines a macro named id bound to transformer-expr. The second form is shorthand for
(define-update-syntax id
  (lambda (arg-id ...)
    (begin body-expr ...)))

Examples:
> (define-update-syntax posn
    (syntax-rules ()
      [(posn x-pat y-pat)
       (and (struct-field posn x x-pat) (struct-field posn y y-pat))]))
> (update (posn 1 2)
    [(posn a b)
     (set! a 4)
     (modify! b -)])

(posn 4 -2)