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

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. However, pattern-bound variables have special behavior when used in a clause body. Variable references retrieve the value of the focus of its optic. The variable must be "single-valued" (correspond to a lens). Using set! on a pattern-bound variable will update the current copy of the target using the optic to set the focus to a new value (see optic-set!). Note that we are only mutating a parameter tracking the current copy of the target, not the target itself. To retrieve a variable’s optic itself, use opticb.

More examples
> (struct posn [x y] #:transparent)
; get focus
> (update (posn 1 2)
    [(posn x)
     x])

1

; set focus
> (update (posn 1 2)
    [(posn x)
     (set! x 3)])

(posn 3 2)

; no mutation of the original target
> (define p (posn 1 2))
> (update p
    [(posn x)
     (set! x 3)])

(posn 3 2)

> p

(posn 1 2)

; multiple independent updates
> (update (list (posn 1 2) (posn 3 4))
    [(list (posn x) p)
     (modify! x -)
     (set! p (posn 5 6))])

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

; 'multi-valued' update
> (update (list (posn 1 2) (posn 3 4) (posn 5 6))
    [(list-of (posn x))
     (modify! x -)])

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

; cannot use references or set! on a 'multi-valued' variable
> (update (list 1 2 3)
    [(list-of x)
     x])

lens-get: contract violation

  expected: lens?

  given: #<make-traversal>

  in: the 1st argument of

      (-> lens? any/c any/c)

  contract from:

      <pkgs>/ocular-patdown/optics/lens.rkt

  blaming: <pkgs>/ocular-patdown/optics/optic.rkt

   (assuming the contract is correct)

  at: <pkgs>/ocular-patdown/optics/lens.rkt:35:3

> (update (list 1 2 3)
    [(list-of x)
     (set! x 0)])

lens-set: contract violation

  expected: lens?

  given: #<make-traversal>

  in: the 1st argument of

      (-> lens? any/c any/c any/c)

  contract from:

      <pkgs>/ocular-patdown/optics/lens.rkt

  blaming: <pkgs>/ocular-patdown/optics/optic.rkt

   (assuming the contract is correct)

  at: <pkgs>/ocular-patdown/optics/lens.rkt:38:3

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

'(1 2 4)

; get and set
> (update (list 1 2)
    [(list a b)
     (set! a (+ 4 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)
  | (struct-id field-spec ...)
  | (iso expr expr expr pat)
  | (optic expr expr pat)

  field-spec = [field-id pat]
  | field-id

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 a])

1

syntax

(and pat ...)

Match all the patterns on the same value.

Example:
> (update (list 1 2)
    [(and a (list b c))
     (set! b 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)

The last pattern can be ellipsized, which will give it the behavior of list-of on the tail of the list.

Example:
> (update (list 1 2 3 4 5)
    [(list a b ...)
     (modify! b add1)])

'(1 3 4 5 6)

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

(struct-id field-spec ...)

A wrapper around struct-field for syntactic convenience. The order of fields doesn’t matter and it is not necessary to supply all fields.

Example:
> (update (posn 1 2)
    [(posn y [x a])
     (set! y 3)
     (set! a 4)])

(posn 4 3)

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

One of
  • opticp for use inside of a pattern.

  • opticb for use inside of the body of a clause.

(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)
     (set! a 0)])

'(0 . 2)

2.6.2 Getter and Updater Utilities🔗ℹ

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 by forms like set! 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)

(optic optic-id)

Gets the optic corresponding to optic-id.

Examples:
> (define op
    (update (cons 1 2)
      [(cons a _)
       (optic a)]))
> op

#<make-lens>

> (lens-get op (cons 1 2))

1

procedure

(optic-set! op value)  any/c

  op : lens?
  value : any/c
Sets the focus of op to value using current-update-target. Also mutates current-update-target and returns its new value. Note that op must be a lens, not a traversal like from list-of.

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! (optic 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)

syntax

(modify! optic-var proc)

Updates the focus of the optic corresponding to optic-var by applying proc, using current-update-target. Also mutates current-update-target and returns its new value. optic-var need not refer to a lens. It can also refer to a traversal.

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

'(1 4 9 16)

syntax

(fold optic-var proc init)

Folds (like foldl) over the foci of optic-var using current-update-target.

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

6

> (update (list 1 2 3)
    [(list-of a)
     (fold a cons (list))])

'(3 2 1)

2.6.3 Extending Update🔗ℹ

syntax

(define-update-syntax id transformer-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.

Defines a pattern macro named id bound to transformer-expr. id is defined in a separate binding space, so it is safe to shadow names that are already defined by Racket. This macro is only usable in the pattern language of update.

Examples:
> (define-update-syntax list*
    (syntax-rules ()
      [(list* p) p]
      [(list* p0 p ...) (cons p0 (list* p ...))]))
> (update (list 1 2 3 4 5 6 7)
    [(list* a b c (list-of n))
     (set! b 0)
     (modify! n sqr)])

'(1 0 3 16 25 36 49)