8.12
1.3.1 Structures🔗ℹ

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.

To start using lenses with structs, use the struct/lens form when defining a structure instead of struct:

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

If you don’t want to use the auto-definition behavior of struct/lens or define-struct-lenses, you can also use struct-lens to create one-off lenses for particular fields.

> (struct point (x y) #:transparent)
> (struct-lens point x)

#<lens>

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

4

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