The Lens Reference has additional information on struct lenses.
Racket’s structure system is an extremely useful tool to help model your problem domain, but using them in a functional style can be laborious. To make this easier, lens provides helper macros to automatically generate lenses for structure fields, which can be composed just like any other lenses to allow easy functional programming with nested structs.
> (struct/lens point (x y) #:transparent)
This will define a struct called point, and it will also produce two lenses, point-x-lens and point-y-lens. It’s also possible to define lenses for an existing structure type using define-struct-lenses.
> (struct point (x y) #:transparent) > (define-struct-lenses point)
> (struct point (x y) #:transparent) > (struct-lens point x)
One created, structure lenses work just like any other lenses: they can be used with functions like lens-view, lens-set, and lens-transform, and they can be composed with other lenses to produce new ones.
> (lens-view point-x-lens (point 4 10))
> (lens-set point-y-lens (point 4 10) 15)
(point 4 15)
> (lens-transform point-x-lens (point 4 10) (λ (x) (* x 3)))
(point 12 10)
Composition of struct lenses can make it much easier to write purely functional state transformations, such as the “world programs” of How to Design Programs. For example, given some state:
> (struct/lens world (play-time player-stats) #:transparent) > (struct/lens player-stats (health attack) #:transparent) > (struct/lens monster (attack) #:transparent)
It’s possible to write updater functions that manipulate nested state without completely destructuring and rebuilding the structure each time:
> (define (perform-monster-attack world monster) (lens-transform (lens-compose player-stats-health-lens world-player-stats-lens) world (λ (hp) (- hp (monster-attack monster)))))
> (let ([w (world 0 (player-stats 15 6))] [m (monster 2)]) (perform-monster-attack w m))
(world 0 (player-stats 13 6))