1.3.2 Constructing Entirely New Lenses
Sometimes the existing set of lenses isn’t enough. Perhaps you have a particularly unique data structure, and you want to create a lens for it. Perhaps you just want to provide lenses for your custom data structures, and struct lenses are insufficient. In that case, it’s always possible to fall back on the primitive lens constructor, make-lens.
The make-lens constructor is simple—
As an example, it would actually be possible to implement lenses for complex numbers: one lens for the
real part and a second lens for the imaginary part. Implementing these lenses is fairly simple—
> (define real-lens (make-lens real-part (λ (n r) (make-rectangular (real-part r) (imag-part n)))))
> (define imag-lens (make-lens imag-part (λ (n i) (make-rectangular (real-part n) (real-part i)))))
In this case, Racket already provides the getters for us: real-part and imag-part. We need to implement the setters ourselves, which we can do using make-rectangular. Now we can actually do math on separate components of numbers using lens-transform:
> (lens-transform real-lens 2+3i (λ (x) (* x 2))) 4+3i
> (lens-transform imag-lens 2+3i (λ (x) (* x 2))) 2+6i
When creating a lens with make-lens, it’s important to make sure it also follows the lens laws. These are simple requirements to ensure that your custom lens behaves intuitively. Lenses that do not adhere to these laws will most likely cause unexpected behavior. However, as long as your lens plays by the rules, it will automatically work with all the other lens functions, including lens combinators like lens-compose.