On this page:
3.1 lens-view/  thrush, lens-set/  thrush, and lens-transform/  thrush
lens-view/  thrush
lens-view~>
lens-set/  thrush
lens-set~>
lens-transform/  thrush
lens-transform~>
3.2 Lenses for nested data
define-nested-lenses
3.3 Lenses for nested dictionaries
dict-ref-nested-lens
3.4 Flattening and unflattening lists
append*-lens
flatten/  depth-lens
flatten/  depth
unflatten/  depth
append*n-lens
3.5 Filtering hash-tables
hash-filterer-lens/  key
hash-filterer-lens/  value
hash-filterer-lens
3.6 Conditional Lenses
lens-if
lens-cond
lens-match
3.7 Isomorphisms
make-isomorphism-lens
isomorphism-lens?
isomorphism-lens-inverse
make-isomorphism-lenses
isomorphism-compose
isomorphism-thrush
string->symbol-lens
symbol->string-lens
number->string-lens
string->number-lens
list->vector-lens
vector->list-lens
list->string-lens
string->list-lens
3.8 Joining lenses with an association list
lens-join/  assoc
3.9 Lazy lenses and recursive lenses
lazy-lens
rec-lens
3.10 Lenses that map over lists and vectors
map-lens
vector-map-lens
3.11 Lenses based on match patterns
match-lens
3.12 Filtering sets
set-filterer-lens
3.13 Lenses for membership of a set
set-member-lens
3.14 Splitting Strings
string-split-lens
3.15 Joining lenses with structs
lens-join/  struct
3.16 Converting between structs and lists
struct->list-lens
list->struct-lens
3.17 Nested struct lenses
struct-nested-lens
struct-nested-lens*
3.18 Struct-lens provide forms
struct-lenses-out
struct+  lenses-out
3.19 Sublist lenses
sublist-lens
3.20 Substring Lenses
substring-lens
3.21 Syntax Lenses
syntax-lens
syntax-keyword-seq-lens
3.22 Syntax object lenses based on syntax/  stx
stx->list-lens
stx-map-lens
stx-car-lens
stx-cdr-lens
stx-caar-lens
stx-cdar-lens
stx-cadr-lens
stx-cddr-lens
stx-caaar-lens
stx-cdaar-lens
stx-cadar-lens
stx-cddar-lens
stx-caadr-lens
stx-cdadr-lens
stx-caddr-lens
stx-cdddr-lens
stx-append*-lens
stx-flatten/  depth-lens
stx-append*n-lens
3.23 Syntax object source locations
syntax-srcloc-lens
syntax-source-lens
syntax-line-lens
syntax-column-lens
syntax-position-lens
syntax-span-lens
source-location->srcloc-lens
source-location->list-lens
source-location->vector-lens
source-location-source-lens
source-location-line-lens
source-location-column-lens
source-location-position-lens
source-location-span-lens
3.24 More Viewing and Setting
lens-set-all
3.25 Lenses that transform subpieces
lens-zoom
lens-zoom*
7.7

3 Unstable Lenses

 (require unstable/lens) package: lens-unstable

This library provides additional features for the lens library that are non-final and may change in future releases. Do not depend on this library being backwards-compatible.

3.1 lens-view/thrush, lens-set/thrush, and lens-transform/thrush

procedure

(lens-view/thrush target lens ...)  any/c

  target : any/c
  lens : lens?

procedure

(lens-view~> target lens ...)  any/c

  target : any/c
  lens : lens?
Like lens-view, except that it can take multiple lenses, which are combined into a nested lens. The argument order is switched, so that the target comes first and the lens arguments come after it. (lens-view/thrush target lens ...) produces the same value as (lens-view (lens-thrush lens ...) target), but can be more efficient. The function lens-view~> is provided as a shorter version.

Examples:
> (lens-view/thrush '(a b ((c d) e f) g) third-lens first-lens second-lens)

'd

> (lens-view~> '(a b ((c d) e f) g) third-lens first-lens second-lens)

'd

procedure

(lens-set/thrush target    
  lens ...    
  #:-> new-view)  any/c
  target : any/c
  lens : lens?
  new-view : any/c

procedure

(lens-set~> target lens ... #:-> new-view)  any/c

  target : any/c
  lens : lens?
  new-view : any/c
Like lens-set, except that it can take multiple lenses, which again are combined into a nested lens. (lens-set/thrush target lens ... #:-> new-view) is equivalent to (lens-set (lens-thrush lens ...) target new-view), and lens-set~> is the shorter version.

Examples:
> (lens-set/thrush '(a b ((c d) e f) g) third-lens first-lens second-lens #:-> "sea")

'(a b ((c "sea") e f) g)

> (lens-set~> '(a b ((c d) e f) g) third-lens first-lens second-lens #:-> "sea")

'(a b ((c "sea") e f) g)

procedure

(lens-transform/thrush target    
  lens ...    
  #:-> transformer)  any/c
  target : any/c
  lens : lens?
  transformer : (-> any/c any/c)

procedure

(lens-transform~> target    
  lens ...    
  #:-> transformer)  any/c
  target : any/c
  lens : lens?
  transformer : (-> any/c any/c)
Like lens-transform, except that it can take multiple lenses, just like lens-set/thrush. (lens-transform/thrush target lens ... #:-> transformer) is equivalent to (lens-transform (lens-thrush lens ...) target transformer), and lens-transform~> is the shorter verison.

Examples:
> (lens-transform/thrush '(a b ((c d) e f) g) third-lens first-lens second-lens #:-> symbol->string)

'(a b ((c "d") e f) g)

> (lens-transform~> '(a b ((c d) e f) g) third-lens first-lens second-lens #:-> symbol->string)

'(a b ((c "d") e f) g)

3.2 Lenses for nested data

syntax

(define-nested-lenses [base-id base-lens-expr] clause ...)

 
clause = 
[sub-id sub-lens-expr
  clause
  ...]
A shorthand for defining composed lenses for nested data structures.

For example, if there is a top struct containing a middle struct, which contains an x field and a y field, a form like:
(define-nested-lenses [top-middle top-middle-lens]
  [x middle-x-lens]
  [y middle-y-lens])
Will define top-middle-x-lens and top-middle-y-lens as (lens-thrush top-middle-lens middle-x-lens) and (lens-thrush top-middle-lens middle-y-lens).

Clauses can be nested within other clauses as well:

Examples:
> (struct/lens game (player1 player2) #:transparent)
> (struct/lens player (position score) #:transparent)
> (struct/lens position (x y) #:transparent)
> (define-nested-lenses [game-player1 game-player1-lens]
    [score player-score-lens]
    [position player-position-lens
      [x position-x-lens]
      [y position-y-lens]])
> (define-nested-lenses [game-player2 game-player2-lens]
    [score player-score-lens]
    [position player-position-lens
      [x position-x-lens]
      [y position-y-lens]])
> (define the-game (game (player (position 1 2) 5) (player (position 3 4) 6)))
> (lens-view game-player1-score-lens the-game)

5

> (lens-view game-player1-position-lens the-game)

(position 1 2)

> (lens-view game-player1-position-x-lens the-game)

1

> (lens-set game-player1-score-lens the-game 9005)

(game (player (position 1 2) 9005) (player (position 3 4) 6))

> (lens-set game-player1-position-lens the-game (position 2 0))

(game (player (position 2 0) 5) (player (position 3 4) 6))

> (lens-set game-player1-position-x-lens the-game 3)

(game (player (position 3 2) 5) (player (position 3 4) 6))

3.3 Lenses for nested dictionaries

procedure

(dict-ref-nested-lens k ...)  (lens/c functional-dict? any/c)

  k : any/c
Similar to hash-ref-nested-lens, but for dicts.

Examples:
> (define a-x (dict-ref-nested-lens 'a 'x))
> (lens-view a-x '([a    [x . 1] [y . 2]]  '[b    [z . 3]]))

1

> (lens-set a-x '([a    [x . 1] [y . 2]]  '[b    [z . 3]])  100)

'((a (x . 100) (y . 2)) '(b (z . 3)))

3.4 Flattening and unflattening lists

A lens that flattens a list one-level down when viewing, and restores the original structure when setting. The target list must be a nested list at least 2 levels deep, or a list of lists. Viewing with this lens is equivalent to using append*, and setting with this lens restores the structure of the original nested list.

When setting, the new view must have the same length as the old view, so it must have the same length as (append* target).

This is equivalent to (flatten/depth-lens 2), since it flattens lists of depth 2.

Examples:
> (lens-view append*-lens '((a b c) (1 2 3)))

'(a b c 1 2 3)

> (lens-set append*-lens '((a b c) (1 2 3)) '("do" "re" "mi" "re" "mi" "do"))

'(("do" "re" "mi") ("re" "mi" "do"))

> (lens-view append*-lens '((a) (b c) () (d e f)))

'(a b c d e f)

> (lens-set append*-lens '((a) (b c) () (d e f)) '(1 2 3 4 5 6))

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

The further nested elements don’t have to be atomic, they could be other lists. append*-lens doesn’t recur into those.

Examples:
> (lens-view append*-lens '(((a) (b) (c)) ((1) (2) (3))))

'((a) (b) (c) (1) (2) (3))

> (lens-set append*-lens '(((a) (b) (c)) ((1) (2) (3))) '("mi" "re" "do" "re" "re" "mi"))

'(("mi" "re" "do") ("re" "re" "mi"))

Creates a lens that flattens a list of depth n when viewing, and restores the original structure when setting.

A list of depth 0 is a single element, a list of depth 1 is a list, a list of depth 2 is a list of lists, and so on.

This is a generalization of append*-lens, with that being equivalent to (flatten/depth-lens 2). It uses flatten/depth to view, and unflatten/depth to set.

When setting, the new view must have the same length as the old view, so it must have the same length as (flatten/depth n target).

Examples:
> (lens-view (flatten/depth-lens 0) 'a)

'(a)

> (lens-set (flatten/depth-lens 0) 'a '(1))

1

> (lens-view (flatten/depth-lens 1) '(a b c))

'(a b c)

> (lens-set (flatten/depth-lens 1) '(a b c) '(1 2 3))

'(1 2 3)

> (lens-view (flatten/depth-lens 2) '((a) (b c) () (d e f)))

'(a b c d e f)

> (lens-set (flatten/depth-lens 2) '((a) (b c) () (d e f)) '(1 2 3 4 5 6))

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

> (lens-view (flatten/depth-lens 3) '(((a) ()) (() (b) (c)) () ((d e) () (f))))

'(a b c d e f)

> (lens-set (flatten/depth-lens 3) '(((a) ()) (() (b) (c)) () ((d e) () (f))) '(1 2 3 4 5 6))

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

procedure

(flatten/depth n structure)  list?

  n : exact-nonnegative-integer?
  structure : any/c
Flattens a list of depth n. For depth n = 0, it returns (list structure). For a depth of 1, it returns structure. For a depth of 2, it returns (append* structure). For a depth of 3, it returns (append* (append* structure)), and so on for higher depths.

This is what flatten/depth-lens uses for viewing.

Examples:
> (flatten/depth 0 'a)

'(a)

> (flatten/depth 1 '(a b c))

'(a b c)

> (flatten/depth 2 '((a) (b c) () (d e f)))

'(a b c d e f)

> (flatten/depth 3 '(((a) ()) (() (b) (c)) () ((d e) () (f))))

'(a b c d e f)

procedure

(unflatten/depth n structure flattened)  any/c

  n : exact-nonnegative-integer?
  structure : any/c
  flattened : list?
Un-does the work done by flatten/depth, to return an un-flattened version of flattened, with the structure restored based on structure.

This is what flatten/depth-lens uses for setting.

Examples:
> (unflatten/depth 0 'a '(1))

1

> (unflatten/depth 1 '(a b c) '(1 2 3))

'(1 2 3)

> (unflatten/depth 2 '((a) (b c) () (d e f)) '(1 2 3 4 5 6))

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

> (unflatten/depth 3 '(((a) ()) (() (b) (c)) () ((d e) () (f))) '(1 2 3 4 5 6))

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

procedure

(append*n-lens n)  lens?

  n : exact-nonnegative-integer?
This is deprecated. Use (flatten/depth-lens (add1 n)) instead.

3.5 Filtering hash-tables

procedure

(hash-filterer-lens/key keep?)

  (lens/c immutable-hash? immutable?)
  keep? : (-> any/c boolean?)
Creates a lens that filters a hash-table to keys that pass the predicate keep?.

Examples:
> (lens-view (hash-filterer-lens/key symbol?) (hash 'a 1 "b" 2 'c 3))

'#hash((a . 1) (c . 3))

> (lens-set (hash-filterer-lens/key symbol?) (hash 'a 1 "b" 2 'c 3) (hash 'd 4 'e 5))

'#hash((d . 4) (e . 5) ("b" . 2))

procedure

(hash-filterer-lens/value keep?)

  (lens/c immutable-hash? immutable?)
  keep? : (-> any/c boolean?)
Like hash-filterer-lens/value, but filters based on values that pass keep?, not keys.

Examples:
> (lens-view (hash-filterer-lens/value number?) (hash 'a 1 'b "two" 'c 3))

'#hash((a . 1) (c . 3))

> (lens-set (hash-filterer-lens/value number?) (hash 'a 1 'b "two" 'c 3) (hash 'd 4))

'#hash((b . "two") (d . 4))

procedure

(hash-filterer-lens keep?)

  (lens/c immutable-hash? immutable?)
  keep? : (-> any/c any/c boolean?)
Creates a lens that filters a hash-table by the predicate keep?, which takes the key and the value as its two arguments.

Examples:
> (lens-view (hash-filterer-lens =) (hash 1 1.0 2 45 3 3))

'#hash((1 . 1.0) (3 . 3))

> (lens-set (hash-filterer-lens =) (hash 1 1.0 2 45 3 3) (hash 4 4.0 5.0 5))

'#hash((2 . 45) (4 . 4.0) (5.0 . 5))

3.6 Conditional Lenses

procedure

(lens-if pred lens1 lens2)  (lens/c target/c view/c)

  pred : (-> target/c any/c)
  lens1 : (lens/c target/c view/c)
  lens2 : (lens/c target/c view/c)
Creates a lens that uses lens1 when the target satisfies pred, and uses lens2 when the target doesn’t satisfy pred.

Examples:
> (define if-lens (lens-if list? first-lens (vector-ref-lens 0)))
> (lens-view if-lens '(1 2 3))

1

> (lens-view if-lens '#(1 2 3))

1

> (lens-set if-lens '(1 2 3) 'a)

'(a 2 3)

> (lens-set if-lens '#(1 2 3) 'a)

'#(a 2 3)

syntax

(lens-cond [pred-expr lens-expr] ... [else else-lens-expr])

(lens-cond [pred-expr lens-expr] ...)
Like lens-if, but based on cond instead of if. It creates a lens that uses the first lens if the target matches the first predicate, the second lens if the target matches the second predicate, and so on. If the target matches none of the predicates, an error is raised.

Examples:
> (define cond-lens (lens-cond [list? first-lens]
                               [vector? (vector-ref-lens 0)]
                               [string? (string-ref-lens 0)]))
> (lens-view cond-lens '(1 2 3))

1

> (lens-view cond-lens '#(1 2 3))

1

> (lens-view cond-lens "123")

#\1

> (lens-set cond-lens '(1 2 3) 'a)

'(a 2 3)

> (lens-set cond-lens '#(1 2 3) 'a)

'#(a 2 3)

> (lens-set cond-lens "123" #\a)

"a23"

> (lens-view cond-lens 'none-of-the-above)

lens-cond: no matching clause for target

  target: 'none-of-the-above

  expected: '(or/c list? vector? string?)

syntax

(lens-match [pat lens-expr] ...)

Like lens-if and lens-cond, but based on pattern matching the target against each pat with match. It creates a lens that uses the first lens if the target matches the first pat, the second lens if it matches the second pat, and so on.

Examples:
> (define lens (lens-match [(list a) first-lens]
                           [(list a b) second-lens]))
> (lens-view lens '(1))

1

> (lens-view lens '(1 2))

2

> (lens-set lens '(1) 'a)

'(a)

> (lens-set lens '(1 2) 'a)

'(1 a)

3.7 Isomorphisms

procedure

(make-isomorphism-lens f inv)  lens?

  f : (a/c . -> . b/c)
  inv : (b/c . -> . a/c)
Creates a lens for an isomorphism. The f argument should be a function with an inverse, and the inv argument should be its inverse. The f function converts targets to views, and the inv function converts views to targets.

So for instance a symbol->string-lens could be defined with:

Examples:
> (lens-view symbol->string-lens 'something)

"something"

> (lens-transform symbol->string-lens 'something (λ (s) (string-append "make-" s)))

'make-something

procedure

(isomorphism-lens? v)  boolean?

  v : any/c
A predicate that returns true when v is a lens constructed with make-isomorphism-lens, isomorphism-lens-inverse, or make-isomorphism-lenses, and returns false otherwise. All isomorphism lenses are also lenses according to lens?.

procedure

(isomorphism-lens-inverse iso-lens)  isomorphism-lens?

  iso-lens : isomorphism-lens?
Returns the inverse of iso-lens.

procedure

(make-isomorphism-lenses f inv)

  
isomorphism-lens? isomorphism-lens?
  f : (a/c . -> . b/c)
  inv : (b/c . -> . a/c)
Returns two values. The first value is the result of (make-isomorphism-lens f inv), and the second value is the inverse of that lens.

The lenses symbol->string-lens and string->symbol-lens, for example, are defined like this:

procedure

(isomorphism-compose lens ...)  isomorphism-lens?

  lens : isomorphism-lens?
Like lens-compose, but works only on isomorphism lenses, and returns an isomorphism lens. It is also more efficient than lens-compose.

procedure

(isomorphism-thrush lens ...)  isomorphism-lens?

  lens : isomorphism-lens?
Like lens-thrush, but works only on isomorphism lenses, and returns an isomorphism lens. It is also more efficient than lens-thrush.

Isomorphim lenses for string->symbol, number->string, and so on.

3.8 Joining lenses with an association list

procedure

(lens-join/assoc key lens ... ...)

  (lens/c target/c (listof (cons/c key/c value/c)))
  key : key/c
  lens : (lens/c target/c value/c)
Like lens-join/hash, except joins the keys and values into an association list instead of a hash-table.

Examples:
> (define a-b-lens (lens-join/assoc 'a first-lens
                                    'b third-lens))
> (lens-view a-b-lens '(1 2 3))

'((a . 1) (b . 3))

> (lens-set a-b-lens '(1 2 3) '((a . 100) (b . 200)))

'(100 2 200)

3.9 Lazy lenses and recursive lenses

syntax

(lazy-lens lens-expr)

Creates a lazy lens that lazily evaluates lens-expr and uses it to view and set the target.

Examples:
> (define lazy-first-lens
    (lazy-lens (begin (displayln "evaluating") first-lens)))
> lazy-first-lens

#<lens>

> (lens-view lazy-first-lens '(1 2 3))

evaluating

1

> (lens-set lazy-first-lens '(1 2 3) 'a)

'(a 2 3)

syntax

(rec-lens rec-id lens-expr)

Creates a potentially recursive lens, where lens-expr can refer to rec-id as a lazy version of itself.

Examples:
> (define (tree-map-lens item-lens)
    (rec-lens the-tree-lens
      (lens-cond [list? (map-lens the-tree-lens)]
                 [else item-lens])))
> (lens-view (tree-map-lens symbol->string-lens) '(a (b (() c)) (d)))

'("a" ("b" (() "c")) ("d"))

> (lens-set (tree-map-lens symbol->string-lens)
            '(a (b (() c)) (d))
            '("hay" ("bee" (() "sea")) ("deep")))

'(hay (bee (() sea)) (deep))

3.10 Lenses that map over lists and vectors

procedure

(map-lens lens)  lens?

  lens : lens?
Creates a lens that maps lens over a target list.

Examples:
> (lens-view (map-lens first-lens) '((a b) (c d) (e f)))

'(a c e)

> (lens-set (map-lens first-lens) '((a b) (c d) (e f)) '(1 2 3))

'((1 b) (2 d) (3 f))

> (lens-transform (map-lens first-lens) '((a b) (c d) (e f)) (λ (xs) (map symbol->string xs)))

'(("a" b) ("c" d) ("e" f))

procedure

(vector-map-lens lens)  lens?

  lens : lens?
Creates a lens that maps lens over a target vector with vector-map.

Examples:
> (lens-view (vector-map-lens first-lens) '#((a b) (c d) (e f)))

'#(a c e)

> (lens-set (vector-map-lens first-lens) '#((a b) (c d) (e f)) '#(1 2 3))

'#((1 b) (2 d) (3 f))

> (lens-transform (vector-map-lens first-lens) '#((a b) (c d) (e f))
                  (λ (xs) (vector->immutable-vector (vector-map symbol->string xs))))

'#(("a" b) ("c" d) ("e" f))

3.11 Lenses based on match patterns

syntax

(match-lens id pattern replacement)

Creates a lens for viewing the id within the pattern.

The replacement expression should be an expression such that (match target [pattern replacement]) produces a value equivalent to target, and should use id as the view.

Examples:
> (define car-lens (match-lens a (cons a b) (cons a b)))
> (define cdr-lens (match-lens b (cons a b) (cons a b)))
> (define third-lens (match-lens c (list a b c d ...) (list* a b c d)))
> (define vector-second-lens (match-lens b (vector a b c ...) (apply vector a b c)))
> (define v2-of-l3-lens (match-lens d
                          (list a b (vector c d e ...) f ...)
                          (list* a b (apply vector c d e) f)))

3.12 Filtering sets

procedure

(set-filterer-lens pred)

  (lens/c functional-set? functional-set?)
  pred : (-> any/c any/c)
Creates a lens that filters a set by the predicate pred.

Examples:
> (lens-view (set-filterer-lens number?) (set 1 'a 2 'b 'c 3 'd 'e))

(set 1 3 2)

> (lens-set (set-filterer-lens number?) (set 1 'a 2 'b 'c 3 'd 'e) (set 4 5 6 7))

(set 5 7 'a 'e 'd 6 4 'c 'b)

Lists are also sets, so set-filterer-lens works for lists too, but it does not preserve ordering. It follows the lens laws only if you compare using set=?, not equal?.

Examples:
> (lens-view (set-filterer-lens number?) '(a 1 2 3))

'(1 2 3)

> (lens-set (set-filterer-lens number?) '(a 1 2 3) '(1 2 3))

'(3 2 1 a)

; this breaks the lens laws according to equal?
> (equal? '(a 1 2 3) '(3 2 1 a))

#f

; but not according to set=?
> (set=? '(a 1 2 3) '(3 2 1 a))

#t

3.13 Lenses for membership of a set

procedure

(set-member-lens v)  (lens/c functional-set? boolean?)

  v : any/c
Creates a lens for telling whether v is a member of the target set.

Examples:
> (define 2-lens (set-member-lens 2))
> (lens-view 2-lens (set 1 2 3))

#t

> (lens-view 2-lens (set 1 3))

#f

> (lens-set 2-lens (set 1 2 3) #t)

(set 1 3 2)

> (lens-set 2-lens (set 1 2 3) #f)

(set 1 3)

> (lens-set 2-lens (set 1 3) #t)

(set 1 3 2)

> (lens-set 2-lens (set 1 3) #f)

(set 1 3)

3.14 Splitting Strings

procedure

(string-split-lens sep)  lens?

  sep : (or/c string? char? regexp?)
Creates a lens that splits a string into multiple pieces like regexp-split or string-split.

Examples:
> (lens-view (string-split-lens ",") "a,b,c")

'("a" "b" "c")

> (lens-set (string-split-lens ",") "a,b,c" '("1" "2" "3"))

"1,2,3"

Lenses created by string-split-lens do not trim strings first, so that when viewing a target that either starts or ends with something matching sep, the view will include empty strings as the first or last element, which is consistant with regexp-split or string-split with #:trim? #f. This is also more useful when using lens-set.

Examples:
> (lens-view (string-split-lens ",") ",b,c")

'("" "b" "c")

> (lens-set (string-split-lens ",") ",b,c" '("a" "b" "c"))

"a,b,c"

> (lens-view (string-split-lens ",") "a,b,c,")

'("a" "b" "c" "")

> (lens-set (string-split-lens ",") "a,b,c," '("a" "b" "c" "d"))

"a,b,c,d"

3.15 Joining lenses with structs

syntax

(lens-join/struct struct-id field-lens ...)

 
field-lens = lens-expr
  | field-keyword lens-expr
Like lens-join/list, except that the views of the given lenses are put in an instance of the struct-id struct instead of in a list.

Examples:
> (struct foo (a b) #:transparent)
> (define lens (lens-join/struct foo first-lens third-lens))
> (lens-view lens '(1 2 3))

(foo 1 3)

> (lens-set lens '(1 2 3) (foo 'a 'b))

'(a 2 b)

Struct fields in a lens-join/struct form can also be specified by keywords, in any order, and even with some fields specied by position and some by keywords:

Examples:
> (struct foo (a b) #:transparent)
> (lens-view (lens-join/struct foo first-lens third-lens) '(1 2 3))

(foo 1 3)

> (lens-view (lens-join/struct foo #:a first-lens #:b third-lens) '(1 2 3))

(foo 1 3)

> (lens-view (lens-join/struct foo #:b third-lens #:a first-lens) '(1 2 3))

(foo 1 3)

> (lens-view (lens-join/struct foo first-lens #:b third-lens) '(1 2 3))

(foo 1 3)

3.16 Converting between structs and lists

syntax

(struct->list-lens struct-id)

syntax

(list->struct-lens struct-id)

Lenses that convert between structs and lists.

Examples:
> (struct foo (a b c) #:transparent)
> (lens-view (struct->list-lens foo) (foo 1 2 3))

'(1 2 3)

> (lens-set (struct->list-lens foo) (foo 1 2 3) '(4 5 6))

(foo 4 5 6)

> (lens-view (list->struct-lens foo) '(1 2 3))

(foo 1 2 3)

> (lens-set (list->struct-lens foo) '(1 2 3) (foo 4 5 6))

'(4 5 6)

For structs that inherit from other structs, it puts the inherited fields first, so that it matches the arguments to the constructor:

Examples:
> (struct foo (a b c) #:transparent)
> (struct bar foo (d e))
> (lens-view (struct->list-lens bar) (bar 1 2 3 4 5))

'(1 2 3 4 5)

> (lens-set (struct->list-lens bar) (bar 1 2 3 4 5) '(6 7 8 9 10))

(bar 6 7 8 ...)

> (lens-view (list->struct-lens bar) '(1 2 3 4 5))

(bar 1 2 3 ...)

> (lens-set (list->struct-lens bar) '(1 2 3 4 4) (bar 6 7 8 9 10))

'(6 7 8 9 10)

3.17 Nested struct lenses

syntax

(struct-nested-lens [struct-id field-id] ...)

Constructs a lens that views nested structures. Each struct-id and field-id pair is paired into a lens for viewing that field of that struct, then the list of lenses are lens-thrushed together.

For example, given a complicated nested tree of state representing a game:

Examples:
> (struct game (player level) #:transparent)
> (struct player (posn stats) #:transparent)
> (struct posn (x y) #:transparent)
> (struct combat-stats (health attack) #:transparent)
> (define the-game (game (player (posn 0 0) (combat-stats 10 1)) 'foo-level))
> the-game

(game (player (posn 0 0) (combat-stats 10 1)) 'foo-level)

We can create a lens for traversing the nested structures of the game state. At each step, we provide the name of the struct we’re examining and the name of the field we wish to traverse into.

Examples:
> (define game-player-health-lens
    (struct-nested-lens [game player]
                        [player stats]
                        [combat-stats health]))
> (lens-view game-player-health-lens the-game)

10

> (lens-set game-player-health-lens the-game 20)

(game (player (posn 0 0) (combat-stats 20 1)) 'foo-level)

syntax

(struct-nested-lens* struct-id both-id ... field-id)

Like struct-nested-lens, but for the case where each nested field is named the same as its struct type. For example, given the game state defined in the examples for struct-nested-lens:

Examples:
> (struct game (player level) #:transparent)
> (struct player (posn stats) #:transparent)
> (struct posn (x y) #:transparent)
> (struct combat-stats (health attack) #:transparent)
> (define the-game (game (player (posn 0 0) (combat-stats 10 1)) 'foo-level))
> the-game

(game (player (posn 0 0) (combat-stats 10 1)) 'foo-level)

Because each field is named the same as its struct type, we can create a lens for viewing the player’s x coordinate more succinctly than with struct-nested-examples:

Examples:
> (define game-player-x-lens
    (struct-nested-lens* game player posn x))
> (lens-view game-player-x-lens the-game)

0

> (lens-set game-player-x-lens the-game 5)

(game (player (posn 5 0) (combat-stats 10 1)) 'foo-level)

3.18 Struct-lens provide forms

syntax

(struct-lenses-out struct-id)

A provide sub-form that provides the lenses defined by define-struct-lenses or struct/lens.

syntax

(struct+lenses-out struct-id)

A provide sub-form short for using both struct-out and struct-lenses-out.

3.19 Sublist lenses

Examples:
> (lens-view (sublist-lens 1 4) '(0 1 2 3 4 5))

'(1 2 3)

> (lens-set (sublist-lens 1 4) '(0 1 2 3 4 5) '(a b c))

'(0 a b c 4 5)

3.20 Substring Lenses

procedure

(substring-lens start end)  (lens/c string? string?)

  start : exact-nonnegative-integer?
  end : exact-nonnegative-integer?
Creates a lens that views a substring from start to end of a given string. start is inclusive and end is exclusive, in the same way as for substring.

Examples:
> (lens-view (substring-lens 1 4) "abcdefg")

"bcd"

> (lens-set (substring-lens 1 4) "abcdefg" "FOO")

"aFOOefg"

When setting a new view, the replacement string has to be the same length as the span of the substring lens to uphold the lens laws.

Example:
> (lens-set (substring-lens 1 4) "kitten" "this string is too long!")

substring-lens: contract violation

  expected: (string-length-=/c 3)

  given: "this string is too long!"

  in: an and/c case of

      the 3rd argument of

      a part of the or/c of

      method lens-set

      the result result of

      (->i

       ((start natural?)

        (end

         (start)

         (and/c

          exact-nonnegative-integer?

          (>=/c start))))

       (result

        (start end)

        (lens/c

         (string-length->=/c end)

         (string-length-=/c (- end start)))))

  contract from:

      <pkgs>/lens-data/lens/private/string/substring.rkt

  blaming: top-level

   (assuming the contract is correct)

  at: <pkgs>/lens-data/lens/private/string/substring.rkt:7.3

3.21 Syntax Lenses

syntax

(syntax-lens target-id structure)

Constructs a lens that parses a syntax object and returns a piece of that syntax object as determined by where target-id appears in structure.

Examples:
> (define first-of-second-stx-lens
    (syntax-lens A
      (_ (A _ ...) _ ...)))
> (lens-view first-of-second-stx-lens
             #'(foo (1 2 3) bar baz (blah blah)))

#<syntax:eval:9:0 1>

> (lens-set first-of-second-stx-lens
            #'(define (f a) a)
            #'g)

#<syntax:eval:8:0 (define (g a) a)>

procedure

(syntax-keyword-seq-lens kw)  lens?

  kw : keyword?
Constructs a lens that examines a non-flat syntax object and views a syntax object containing all the terms in the target syntax that appear after kw but before any other keyword.

Examples:
> (define foo-kw-seq-lens (syntax-keyword-seq-lens '#:foo))
> (lens-view foo-kw-seq-lens #'(a #:foo c d #:bar f))

#<syntax:/home/root/user/.racket/7.7/pkgs/lens-data/lens/private/syntax/syntax-keyword.rkt:33:7 (c d)>

> (lens-set foo-kw-seq-lens #'(a #:foo c d #:bar f) #'(1 2 3 4 5 6))

#<syntax:/home/root/user/.racket/7.7/pkgs/lens-data/lens/private/syntax/syntax-keyword.rkt:41:11 (a #:foo 1 2 3 4 5 6 #:bar f)>

If the target syntax object has no occurence of kw, or if the occurence of kw is at the end of the syntax object or immediately followed by another keyword, then viewing produces the empty list syntax object #'(). In the case where kw is not present, setting is a no-op.

Examples:
> (define foo-kw-seq-lens (syntax-keyword-seq-lens '#:foo))
> (lens-view foo-kw-seq-lens #'(a b f g))

#<syntax:/home/root/user/.racket/7.7/pkgs/lens-data/lens/private/syntax/syntax-keyword.rkt:27:43 ()>

> (lens-view foo-kw-seq-lens #'(a #:foo #:bar f))

#<syntax:/home/root/user/.racket/7.7/pkgs/lens-data/lens/private/syntax/syntax-keyword.rkt:33:7 ()>

> (lens-set foo-kw-seq-lens #'(a #:foo #:bar f) #'(1 2 3 4 5 6))

#<syntax:/home/root/user/.racket/7.7/pkgs/lens-data/lens/private/syntax/syntax-keyword.rkt:41:11 (a #:foo 1 2 3 4 5 6 #:bar f)>

> (lens-set foo-kw-seq-lens #'(a b f g) #'(these are ignored))

#<syntax:/home/root/user/.racket/7.7/pkgs/lens-data/lens/private/syntax/syntax-keyword.rkt:41:11 (a b f g)>

3.22 Syntax object lenses based on syntax/stx

The lenses in this section preserve the distinction between syntax pairs and syntax lists, e.g.

(lens-set stx->list-lens #'(a . (b c)) '(1 2 3))

will have the same structure as

#'(1 . (2 3))

If the distinction between syntax pairs and syntax lists were not preserved, the result would instead be:

#'(1 2 3)

A lens that views a stx-list as a list. Viewing with this lens is equivalent to using stx->list, and if the target is a syntax object, setting it with this lens preserves the lexical context, source location, and syntax properties of the outer syntax object.

Examples:
> (lens-view stx->list-lens #'(a b c))

'(#<syntax:eval:8:0 a> #<syntax:eval:8:0 b> #<syntax:eval:8:0 c>)

> (lens-set stx->list-lens #'(a b c) '(1 2 3))

#<syntax:eval:9:0 (1 2 3)>

procedure

(stx-map-lens lens)  lens?

  lens : lens?
Creates a lens that maps lens over a target stx-list. Like stx->list-lens, setting with a syntax object target preserves lexical context, location, and properties.

This is the syntax version of map-lens.

Examples:
> (lens-view (stx-map-lens stx-car-lens) #'((a b) (c d) (e f)))

'(#<syntax:eval:8:0 a> #<syntax:eval:8:0 c> #<syntax:eval:8:0 e>)

> (lens-set (stx-map-lens stx-car-lens) #'((a b) (c d) (e f)) #'(1 2 3))

#<syntax:eval:9:0 ((1 b) (2 d) (3 f))>

Lenses for looking at the car and cdr of syntax-pairs.

These are the syntax versions of car-lens and cdr-lens.

Examples:
> (lens-view stx-car-lens #'(a . b))

#<syntax:eval:8:0 a>

> (lens-view stx-cdr-lens #'(a . b))

#<syntax:eval:9:0 b>

> (lens-set stx-car-lens #'(a . b) #'1)

#<syntax:eval:10:0 (1 . b)>

> (lens-set stx-cdr-lens #'(a . b) #'1)

#<syntax:eval:11:0 (a . 1)>

Lenses for accessing nested syntax-pairs.

Examples:
> (lens-view stx-caddr-lens #'(a b c d))

#<syntax:eval:8:0 c>

> (lens-set stx-caddr-lens #'(a b c d) #'1)

#<syntax:eval:9:0 (a b 1 d)>

A lens like that flattens a stx-list one-level down when viewing, and restores the original structure when setting.

This is the syntax version of append*-lens.

Examples:
> (lens-view stx-append*-lens #'((a) (b c) () (d e f)))

'(#<syntax:eval:8:0 a>

  #<syntax:eval:8:0 b>

  #<syntax:eval:8:0 c>

  #<syntax:eval:8:0 d>

  #<syntax:eval:8:0 e>

  #<syntax:eval:8:0 f>)

> (lens-set stx-append*-lens #'((a) (b c) () (d e f)) #'(1 2 3 4 5 6))

#<syntax:eval:9:0 ((1) (2 3) () (4 5 6))>

Creates a lens that flattens a stx-list of depth n when viewing, and restores the original structure when setting.

This is the syntax version of flatten/depth-lens.

Examples:
> (lens-view (stx-flatten/depth-lens 0) #'42)

'(#<syntax:eval:8:0 42>)

> (lens-set (stx-flatten/depth-lens 0) #'42 #'(43))

#<syntax:eval:9:0 43>

> (lens-view (stx-flatten/depth-lens 1) #'(a b c))

#<syntax:eval:10:0 (a b c)>

> (lens-set (stx-flatten/depth-lens 1) #'(a b c) #'(1 2 3))

#<syntax:eval:11:0 (1 2 3)>

> (lens-view (stx-flatten/depth-lens 2) #'((a) (b c) () (d e f)))

'(#<syntax:eval:12:0 a>

  #<syntax:eval:12:0 b>

  #<syntax:eval:12:0 c>

  #<syntax:eval:12:0 d>

  #<syntax:eval:12:0 e>

  #<syntax:eval:12:0 f>)

> (lens-set (stx-flatten/depth-lens 2) #'((a) (b c) () (d e f)) #'(1 2 3 4 5 6))

#<syntax:eval:13:0 ((1) (2 3) () (4 5 6))>

> (lens-view (stx-flatten/depth-lens 3) #'(((a) ()) (() (b) (c)) () ((d e) () (f))))

'(#<syntax:eval:14:0 a>

  #<syntax:eval:14:0 b>

  #<syntax:eval:14:0 c>

  #<syntax:eval:14:0 d>

  #<syntax:eval:14:0 e>

  #<syntax:eval:14:0 f>)

> (lens-set (stx-flatten/depth-lens 3) #'(((a) ()) (() (b) (c)) () ((d e) () (f))) #'(1 2 3 4 5 6))

#<syntax:eval:15:0 (((1) ()) (() (2) (3)) () ((4 5) () (6)))>

This is deprecated. Use (stx-flatten/depth-lens (add1 n)) instead.

3.23 Syntax object source locations

A lens that views the source location of a syntax object as a srcloc structure.

Examples:
> (lens-view syntax-srcloc-lens #'here)

(srcloc 'eval 8 0 8 1)

> (lens-set syntax-srcloc-lens #'here (srcloc "a.rkt" 5 8 55 13))

#<syntax:a.rkt:5:8 here>

> (syntax-source (lens-set syntax-srcloc-lens #'here (srcloc "a.rkt" 5 8 55 13)))

"a.rkt"

> (syntax-position (lens-set syntax-srcloc-lens #'here (srcloc "a.rkt" 5 8 55 13)))

55

A lens that views the source field of a syntax object.

Examples:
> (lens-view syntax-source-lens #'here)

'eval

> (lens-set syntax-source-lens #'here "a.rkt")

#<syntax:a.rkt:9:0 here>

> (syntax-source (lens-set syntax-source-lens #'here "a.rkt"))

"a.rkt"

A lens that views the line number of a syntax object.

Examples:
> (lens-view syntax-line-lens #'here)

8

> (lens-set syntax-line-lens #'here 8)

#<syntax:eval:8:0 here>

> (syntax-line (lens-set syntax-line-lens #'here 8))

8

A lens that views the column number of a syntax object within its line.

Examples:
> (lens-view syntax-column-lens #'here)

0

> (lens-set syntax-column-lens #'here 13)

#<syntax:eval:9:13 here>

> (syntax-column (lens-set syntax-column-lens #'here 13))

13

A lens that views the source position a syntax object.

Examples:
> (lens-view syntax-position-lens #'here)

8

> (lens-set syntax-position-lens #'here 21)

#<syntax:eval:9:0 here>

> (syntax-position (lens-set syntax-position-lens #'here 21))

21

A lens that views the source span a syntax object.

Examples:
> (lens-view syntax-span-lens #'here)

1

> (lens-set syntax-span-lens #'here 34)

#<syntax:eval:9:0 here>

> (syntax-span (lens-set syntax-span-lens #'here 34))

34

Lenses for converting from all the common types of source locations into srcloc structures, lists, and vectors.

Like syntax-source-lens, syntax-line-lens, etc, but for all the common types of source locations.

3.24 More Viewing and Setting

procedure

(lens-set-all target new-view lens ...)  any/c

  target : any/c
  new-view : any/c
  lens : lens?
Sets the view of target through each lens to new--view

Example:
> (lens-set-all '(1 2 3 4 5) 'a
                first-lens
                third-lens
                fourth-lens)

'(a 2 a a 5)

3.25 Lenses that transform subpieces

procedure

(lens-zoom zoom-lens transform-lens)  lens?

  zoom-lens : lens?
  transform-lens : lens?
Creates a lens that transforms the subpiece of the target that zoom-lens views with transform-lens.

(lens-view (lens-zoom zoom-lens transform-lens) target)

is equivalent to:

(lens-transform zoom-lens target (λ (v) (lens-view transform-lens v)))

Examples:
> (define first-zoom-second-lens
    (lens-zoom first-lens second-lens))
> (lens-view first-zoom-second-lens '((1 2 3) b c))

'(2 b c)

> (lens-set first-zoom-second-lens '((1 2 3) b c) '(2000 b FOO))

'((1 2000 3) b FOO)

procedure

(lens-zoom* zoom-lens transform-lens ... ...)  lens?

  zoom-lens : lens?
  transform-lens : lens?
A multi-arg version of lens-zoom, analogous to lens-transform/list. It is equivalent to (lens-thrush (lens-zoom zoom-lens transform-lens) ...).

Examples:
> (define first-zoom-second/third-zoom-first-lens
    (lens-zoom* first-lens second-lens
                third-lens first-lens))
> (lens-view first-zoom-second/third-zoom-first-lens '((1 2 3) foo (a b c)))

'(2 foo a)

> (lens-set first-zoom-second/third-zoom-first-lens '((1 2 3) foo (a b c)) '(200 FOO asdf))

'((1 200 3) FOO (asdf b c))