On this page:
optic?
optic-get
optic-set
optic-modify
optic-compose
8.15.0.2

2.1 Optics🔗ℹ

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

The ocular-patdown/optics module provides all bindings of ocular-patdown/optics/lens, ocular-patdown/optics/traversal, ocular-patdown/optics/isomorphism, and ocular-patdown/optics/prism, in addition to the bindings documented here.

An optic is a first class getter and (immutable) setter for some target type and a focus or several foci within it. For example, car-lens is an optic that targets pairs and focuses on a pair’s car. This optic can be used to get the car of a pair, or return a new pair with an updated car.

Examples:
> (define pair (cons 1 2))
> (optic-get car-lens pair)

1

> (optic-set car-lens pair 3)

'(3 . 2)

> pair

'(1 . 2)

Optics can be composed to focus on values deep within a structure.

Examples:
> (define pair (cons 1 (cons 2 3)))
> (optic-set (optic-compose cdr-lens car-lens) pair #t)

'(1 #t . 3)

procedure

(optic? val)  boolean?

  val : any/c
Predicate for optics.

Examples:
> (optic? car-lens)

#t

> (optic? list-traversal)

#t

> (optic? (cons 1 2))

#f

procedure

(optic-get optic target)  any/c

  optic : lens?
  target : any/c
Gets the focus of target under optic. The optic must be a lens.

Examples:
> (optic-get car-lens (cons 1 2))

1

> (optic-get cdr-lens (cons 1 2))

2

procedure

(optic-set optic target focus)  any/c

  optic : lens?
  target : any/c
  focus : any/c
Sets the focus of target to focus under optic. Does not mutate target, but rather, returns an updated copy of it. The optic must be a lens.

Example:
> (optic-set car-lens (cons 1 2) #t)

'(#t . 2)

procedure

(optic-modify optic target proc)  any/c

  optic : optic?
  target : any/c
  proc : (-> any/c any/c)
Like optic-set, but applies a function to the current value of the focus to update it. For multiple foci, each focus is updated individually. The optic does not have to be a lens.

Examples:
> (optic-modify car-lens (cons 2 3) sqr)

'(4 . 3)

> (optic-modify list-traversal (list #t #t #f) not)

'(#f #f #t)

procedure

(optic-compose optic ...)  optic?

  optic : optic?
Compose multiple optics where the focus of the first optic is the target of the next. In other words, the shallow, top-level optic comes first and the deep, bottom-level optic comes last.

Examples:
> (struct tree [val children] #:transparent)
> (define tree-first-child-lens (optic-compose (struct-lens tree children) car-lens))
> (optic-set tree-first-child-lens
             (tree 1 (list (tree 2 '()) (tree 3 '())))
             (tree #t '()))

(tree 1 (list (tree #t '()) (tree 3 '())))

> (define second-lens (optic-compose cdr-lens car-lens))
> (optic-set second-lens (list 1 2) #t)

'(1 #t)

> (define first-of-second-lens (optic-compose second-lens car-lens))
> (optic-set first-of-second-lens (list 1 (list 2 3)) #t)

'(1 (#t 3))

> (define first-of-each-traversal (optic-compose list-traversal car-lens))
> (optic-modify first-of-each-traversal
             (list (list 1 2 3) (list 4 5) (list 6))
             number->string)

'(("1" 2 3) ("4" 5) ("6"))

When composing optics of different types, the result is an instance of the most general optic type among the arguments.