On this page:
2.1 Lens Operations
2.1.1 Core Lens Forms
2.1.1.1 Lens Construction
lens?
make-lens
let-lens
2.1.1.2 Viewing and Setting
lens-view
lens-set
lens-view/  list
lens-set/  list
2.1.1.3 Lens Laws
2.1.1.4 Transforming Values With Lenses
lens-transform
lens-transform/  list
2.1.1.5 Lens Contracts
lens/  c
2.1.2 Joining and Composing Lenses
lens-compose
identity-lens
lens-thrush
2.2 Lenses for different types of data
2.2.1 Pair and List Lenses
2.2.1.1 Pair lenses
car-lens
cdr-lens
caar-lens
cadr-lens
cdar-lens
cddr-lens
caaar-lens
caadr-lens
cadar-lens
caddr-lens
cdaar-lens
cdadr-lens
cddar-lens
cdddr-lens
caaaar-lens
caaadr-lens
caadar-lens
caaddr-lens
cadaar-lens
cadadr-lens
caddar-lens
cadddr-lens
cdaaar-lens
cdaadr-lens
cdadar-lens
cdaddr-lens
cddaar-lens
cddadr-lens
cdddar-lens
cddddr-lens
2.2.1.2 List lenses
list-ref-lens
first-lens
second-lens
third-lens
fourth-lens
fifth-lens
sixth-lens
seventh-lens
eighth-lens
ninth-lens
tenth-lens
list-ref-nested-lens
list-refs-lens
2.2.1.3 Joining lenses to view lists
lens-join/  list
2.2.1.4 Association List Lenses
assoc-lens
assv-lens
assq-lens
2.2.2 Hash Lenses
hash-ref-lens
hash-ref-nested-lens
hash-pick-lens
lens-join/  hash
2.2.3 Struct Lenses
struct-lens
define-struct-lenses
struct/  lens
2.2.4 Vector lenses
vector-ref-lens
vector-ref-nested-lens
vector-pick-lens
lens-join/  vector
2.2.5 String Lenses
string-ref-lens
string-pick-lens
lens-join/  string
2.2.6 Stream Lenses
stream-first-lens
stream-rest-lens
stream-ref-lens
2.2.7 Dict lenses
dict-ref-lens
2.3 Applicable lenses
7.7

2 The Lens Reference

    2.1 Lens Operations

      2.1.1 Core Lens Forms

        2.1.1.1 Lens Construction

        2.1.1.2 Viewing and Setting

        2.1.1.3 Lens Laws

        2.1.1.4 Transforming Values With Lenses

        2.1.1.5 Lens Contracts

      2.1.2 Joining and Composing Lenses

    2.2 Lenses for different types of data

      2.2.1 Pair and List Lenses

        2.2.1.1 Pair lenses

        2.2.1.2 List lenses

        2.2.1.3 Joining lenses to view lists

        2.2.1.4 Association List Lenses

      2.2.2 Hash Lenses

      2.2.3 Struct Lenses

      2.2.4 Vector lenses

      2.2.5 String Lenses

      2.2.6 Stream Lenses

      2.2.7 Dict lenses

    2.3 Applicable lenses

2.1 Lens Operations

Provides core lens functions and other non-data-specific lens operations.

2.1.1 Core Lens Forms
2.1.1.1 Lens Construction

procedure

(lens? v)  boolean?

  v : any/c
Predicate for lenses.

procedure

(make-lens getter setter)  lens?

  getter : (-> target/c view/c)
  setter : (-> target/c view/c target/c)
Given a getter and a setter, constructs a lens defined on values satisfying target/c and viewing values satisfying view/c. The getter must accept a target and return the lens’s view. The setter must accept a target and a new view, and return a new target with its view replaced with the new view.

Examples:
> (define (set-first lst v)
    (list* v (rest lst)))
> (set-first '(1 2 3) 'a)

'(a 2 3)

> (define first-lens (make-lens first set-first))
> (lens-view first-lens '(1 2 3))

1

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

'(a 2 3)

syntax

(let-lens (view-id context-id) lens-expr target-expr body ...)

Given a lens and a target, constructs the view and the context of the target through the lens and binds them to view-id and context-id respectively. The context is a function that accepts a new view and sets the target’s view to the new view. The context is conceptually a function representing the "hole" formed by abstracting the view of the target.

Example:
> (let-lens (view context) first-lens '(1 2 3)
    (printf "View is ~a\n" view)
    (context 'a))

View is 1

'(a 2 3)

2.1.1.2 Viewing and Setting

procedure

(lens-view lens target)  view/c

  lens : lens?
  target : target/c
Extracts the view of target with lens. Essentially a getter function.

Example:
> (lens-view first-lens '(1 2 3))

1

procedure

(lens-set lens target new-view)  target/c

  lens : lens?
  target : target/c
  new-view : view/c
Sets the view of target to new-view using lens. Essentially a setter function.

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

'(a 2 3)

procedure

(lens-view/list target lens ...)  view/c

  target : target/c
  lens : lens?
Like lens-view, except that it takes multiple lenses and returns a list of views.

Example:
> (lens-view/list '(a b c d e f g)
                  first-lens fourth-lens fifth-lens)

'(a d e)

procedure

(lens-set/list target lens new-view ... ...)  target/c

  target : target/c
  lens : lens?
  new-view : view/c
Like lens-set, except that it can take multiple lenses-value pairs. If the view of two of the lenses overlap, the later views overwrite the earlier ones.

Examples:
> (lens-set/list '(1 2 3 4 5)
                 first-lens 10
                 third-lens 300)

'(10 2 300 4 5)

> (lens-set/list '(1 2 3)
                 first-lens 'a
                 first-lens 'b)

'(b 2 3)

2.1.1.3 Lens Laws

While make-lens allows lenses to be constructed from arbitrary getters and setters, these getters and setters should obey some algebraic laws in order for a lens to be a proper lens. A lens that does not obey the lens laws for all values it can focus on is an improper lens. The lens laws formalize some standard intuitions for how getters and setters "ought" to work. The laws for lenses are:

For those familiar with getters and setters in OO languages, none of these should be surprising other than the requirement that lenses be pure. The purity of lenses allows them to be composed more effectively and reasoned about more easily than an impure equivalent of lenses.

All lenses provided by this library are proper unless otherwise stated. There is no enforcement or contract that lenses constructed with functions from this library will always be proper, but individual functions may provide conditional guarantees about their interactions with improper lenses and the lens laws.

2.1.1.4 Transforming Values With Lenses

procedure

(lens-transform lens target transformer)  target/c

  lens : lens?
  target : target/c
  transformer : (-> view/c view/c)
Transforms the view of target through the given lens with the transformer function. Equivalent to getting the view of target through lens, passing that value to transformer, then setting the view of target to the return value of calling transformer with the old view.

Example:
> (lens-transform first-lens '(1 2 3) number->string)

'("1" 2 3)

procedure

(lens-transform/list target    
  lens    
  transformer ...    
  ...)  target/c
  target : target/c
  lens : lens?
  transformer : (-> view/c view/c)
Like lens-transform, except that it can take multiple lenses-transformer pairs in the same way as lens-set* and later transformations overwrite earlier ones in the same way.

Example:
> (lens-transform/list '(1 2 3 4 5)
                       first-lens number->string
                       third-lens (λ (x) (* 100 x)))

'("1" 2 300 4 5)

2.1.1.5 Lens Contracts

procedure

(lens/c target/c view/c)  contract?

  target/c : contract?
  view/c : contract?
A contract constructor for lenses. The target/c contract is used for any target given to or returned by the lens, while the view/c contract is used for any view given to or returned by the lens. For example, the view/c contract is used for the return value of (lens-view lens target) and the third argument of (lens-set lens target view), as well as other places where targets or views of the lens are used as inputs or outputs.

Examples:
> (define contracted-car-lens
    (invariant-assertion (lens/c pair? number?) car-lens))
> (lens-view contracted-car-lens (cons 1 2))

1

> (lens-view contracted-car-lens 'not-a-pair)

contracted-car-lens: assertion violation

  expected: pair?

  given: 'not-a-pair

  in: the 2nd argument of

      a part of the or/c of

      method lens-view

      (lens/c pair? number?)

  contract from: invariant-assertion

  at: eval:7.0

> (lens-view contracted-car-lens (cons 'not-a-number 2))

contracted-car-lens: assertion violation

  expected: number?

  given: 'not-a-number

  in: the range of

      a part of the or/c of

      method lens-view

      (lens/c pair? number?)

  contract from: invariant-assertion

  at: eval:7.0

> (lens-set contracted-car-lens (cons 1 2) 'not-a-number)

contracted-car-lens: assertion violation

  expected: number?

  given: 'not-a-number

  in: the 3rd argument of

      a part of the or/c of

      method lens-set

      (lens/c pair? number?)

  contract from: invariant-assertion

  at: eval:7.0

2.1.2 Joining and Composing Lenses

procedure

(lens-compose lens ...)  lens?

  lens : lens?
Composes the given lenses together into one compound lens. The compound lens operates similarly to composed functions do in that the last lens is the first lens the compound lens’s target is viewed through. Each successive lens "zooms in" to a more detailed view. When called with no arguments, lens-compose produces the identity lens.

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

2

> (lens-set first-of-second-lens '((1 a) (2 b) (3 c)) 200)

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

The identity lens. Performs no destructuring at all - it’s view is the target itself. For all lenses, both (lens-compose lens identity-lens) and (lens-compose identity-lens lens) are equivalent to lens.

Examples:

procedure

(lens-thrush lens ...)  lens?

  lens : lens?
Like lens-compose, but each lens is combined in the opposite order. That is, the first lens is the first lens that the compound lens’s target is viewed through.

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

2

> (lens-set first-of-second-lens '((1 a) (2 b) (3 c)) 200)

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

2.2 Lenses for different types of data

Provides lenses and lens operations for different types of data, such as lists and structs.

2.2.1 Pair and List Lenses

 (require lens/data/list) package: lens-data

The Lens Guide has additional examples of pair and list lenses.

2.2.1.1 Pair lenses

value

car-lens : lens?

value

cdr-lens : lens?

Lenses for examining the car and cdr of a pair.

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

'a

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

'b

Lenses for accessing nested pairs. Each lens’s view is the equivalently named pair-accessor function.

Examples:
> (cdaddr '(9 8 (6 5 4 3 2 1) 7))

'(5 4 3 2 1)

> (lens-view cdaddr-lens '(9 8 (6 5 4 3 2 1) 7))

'(5 4 3 2 1)

> (lens-transform cdaddr-lens '(9 8 (6 5 4 3 2 1) 7) list->vector)

'(9 8 (6 . #(5 4 3 2 1)) 7)

2.2.1.2 List lenses

procedure

(list-ref-lens n)  lens?

  n : exact-nonnegative-integer?
Returns a lens for viewing the nth item of a list, with indexing starting from zero.

Examples:
> (lens-view (list-ref-lens 3) '(a b c d e f g h))

'd

> (lens-set (list-ref-lens 1) '(a b c d e f g h) 'FOO)

'(a FOO c d e f g h)

Lenses for examining specific items of lists. Shorthands for the common use cases of list-ref-lens.

Examples:
> (lens-view third-lens '(a b c d))

'c

> (lens-view (lens-compose second-lens fourth-lens)
             '((a 1) (b 2) (c 3) (d 4)))

4

procedure

(list-ref-nested-lens index ...)  lens?

  index : exact-nonnegative-integer?
Constructs a lens that views into a tree made from nested lists. Indexing starts from zero in the same was as list-ref-lens.

Examples:
> (define first-of-second-lens (list-ref-nested-lens 1 0))
> (lens-view first-of-second-lens '(1 (a b c) 2 3))

'a

> (lens-set first-of-second-lens '(1 (a b c) 2 3) 'foo)

'(1 (foo b c) 2 3)

procedure

(list-refs-lens index ...)  lens?

  index : exact-nonnegative-integer?
Constructs a lens that views each index item in a list. Indexing starts from zero in the same was as list-ref-lens.

Examples:
> (define 1-5-6-lens (list-refs-lens 1 5 6))
> (lens-view 1-5-6-lens '(a b c d e f g))

'(b f g)

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

'(a 1 c d e 2 3)

2.2.1.3 Joining lenses to view lists

procedure

(lens-join/list lens ...)  lens?

  lens : lens?
Constructs a lens that combines the view of each lens into a list of views. This lens can be used to view and set a list of values in a single target.

The joined lens only follows the lens laws if the views of the argument lenses don’t overlap. Views of the lenses overlap when setting one can change the view of another lens.

Examples:
> (define first-third-fifth-lens
    (lens-join/list first-lens
                    third-lens
                    fifth-lens))
> (lens-view first-third-fifth-lens '(a b c d e f))

'(a c e)

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

'(1 b 2 d 3 f)

2.2.1.4 Association List Lenses

procedure

(assoc-lens key [#:is-equal? key-equal?])  lens?

  key : any/c
  key-equal? : (-> any/c any/c any/c) = equal?
Constructs a lens for examining association lists. Specifically, for a given association list the returned lens examines the second value of the first pair that has a key that is key-equal? to key.

Examples:
> (define assoc-a-lens (assoc-lens 'a))
> (define some-assoc-list '((a . 1) (b . 2) (c . 3)))
> (lens-view assoc-a-lens some-assoc-list)

1

> (lens-set assoc-a-lens some-assoc-list 100)

'((a . 100) (b . 2) (c . 3))

The key-equal? procedure is useful for datatypes that have their own definition of equality, such as strings.

Examples:
> (define assoc-foo-lens (assoc-lens "foo" #:is-equal? string=?))
> (lens-view assoc-foo-lens '(("bar" . 1) ("foo" . 2) ("baz" . 3)))

2

procedure

(assv-lens key)  lens?

  key : any/c
Equivalent to (assoc-lens key #:is-equal? eqv?).

procedure

(assq-lens key)  lens?

  key : any/c
Equivalent to (assoc-lens key #:is-equal? eq?).

2.2.2 Hash Lenses

 (require lens/data/hash) package: lens-data

The Lens Guide has additional examples of hash lenses.

procedure

(hash-ref-lens key)  lens?

  key : any/c
Constructs a lens that targets hashes and views the value of key.

Examples:
> (define foo-lens (hash-ref-lens 'foo))
> (lens-view foo-lens (hash 'foo 10 'bar 20))

10

> (lens-set foo-lens (hash 'foo 10 'bar 20) 1000)

'#hash((bar . 20) (foo . 1000))

procedure

(hash-ref-nested-lens key ...)  lens?

  key : any/c
Contructs a lens that targets hashes with nested hashes as values and views the value obtained by using each key in order.

Examples:
> (define foo-bar-lens (hash-ref-nested-lens 'foo 'bar))
> (lens-view foo-bar-lens (hash 'foo (hash 'bar 1)))

1

> (lens-set foo-bar-lens (hash 'foo (hash 'bar 1)) 1000)

'#hash((foo . #hash((bar . 1000))))

procedure

(hash-pick-lens key ...)  lens?

  key : any/c
Creates a lens that views a subset of the target hash-table with the given keys. The view, is another hash-table with only the given keys and their corrosponding values in the target hash-table.

Examples:
> (lens-view (hash-pick-lens 'a 'c) (hash 'a 1 'b 2 'c 3))

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

> (lens-set (hash-pick-lens 'a 'c) (hash 'a 1 'b 2 'c 3) (hash 'a 4 'c 5))

'#hash((a . 4) (b . 2) (c . 5))

procedure

(lens-join/hash key lens ... ...)  lens?

  key : any/c
  lens : lens?
Constructs a lens that combines the view of each lens into a hash of views with keys as the hash keys. In the same manner as lens-join/list, if lenses share views later lenses take precedence when setting.

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

'#hash((first . 1) (third . 3))

> (lens-set first-third-hash-lens '(1 2 3) (hash 'first 100 'third 200))

'(100 2 200)

2.2.3 Struct Lenses

 (require lens/data/struct) package: lens-data

The Lens Guide has additional examples of struct lenses.

syntax

(struct-lens struct-id field-id)

Returns a lens for viewing the field-id field of a struct-id instance.

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

1

> (lens-set (struct-lens foo a) (foo 1 2 3) 100)

(foo 100 2 3)

syntax

(define-struct-lenses struct-id)

Given a struct-id, defines a lens for each of its fields.

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

1

> (lens-set foo-a-lens (foo 1 2 3) 100)

(foo 100 2 3)

syntax

(struct/lens struct-id (field-spec ...) struct-option ...)

Equivalent to struct and define-struct-lenses combined.

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

1

> (lens-set foo-a-lens (foo 1 2 3) 100)

(foo 100 2 3)

2.2.4 Vector lenses

 (require lens/data/vector) package: lens-data

procedure

(vector-ref-lens i)  lens?

  i : exact-nonnegative-integer?
Returns a lens that views an element of a vector.

Examples:
> (lens-view (vector-ref-lens 2) #(a b c d))

'c

> (lens-set (vector-ref-lens 2) #(a b c d) "sea")

'#(a b "sea" d)

procedure

(vector-ref-nested-lens i ...)  lens?

  i : exact-nonnegative-integer?
Like list-ref-nested-lens, but for vectors. Equivalent to (lens-thrush (vector-ref-lens i) ...).

Examples:
> (lens-view (vector-ref-nested-lens 2 1) #(a b #(s i) d))

'i

> (lens-set (vector-ref-nested-lens 2 1) #(a b #(s i) d) "eye")

'#(a b #(s "eye") d)

procedure

(vector-pick-lens i ...)  lens?

  i : exact-nonnegative-integer?
Like list-refs-lens, but for vectors. Equivalent to (lens-join/vector (vector-ref-lens i) ...).

Examples:
> (define 1-5-6-lens (vector-pick-lens 1 5 6))
> (lens-view 1-5-6-lens #(a b c d e f g))

'#(b f g)

> (lens-set 1-5-6-lens #(a b c d e f g) #(1 2 3))

'#(a 1 c d e 2 3)

procedure

(lens-join/vector lens ...)  lens?

  lens : lens?
Like lens-join/list, except the view is a vector, not a list.

Examples:
> (define vector-first-third-fifth-lens
    (lens-join/vector first-lens
                      third-lens
                      fifth-lens))
> (lens-view vector-first-third-fifth-lens '(a b c d e f))

'#(a c e)

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

'(1 b 2 d 3 f)

2.2.5 String Lenses

 (require lens/data/string) package: lens-data

procedure

(string-ref-lens i)  lens?

  i : exact-nonnegative-integer?
Returns a lens for viewing the ith character of a string.

Examples:
> (lens-view (string-ref-lens 2) "abcdef")

#\c

> (lens-set (string-ref-lens 2) "abcdef" #\C)

"abCdef"

Like list-refs-lens, but for strings. Equivalent to (lens-join/string (string-ref-lens i) ...).

Examples:
> (define 1-5-6-lens (string-pick-lens 1 5 6))
> (lens-view 1-5-6-lens "abcdefg")

"bfg"

> (lens-set 1-5-6-lens "abcdefg" "BFG")

"aBcdeFG"

procedure

(lens-join/string lens ...)  lens?

  lens : lens?
Like lens-join/list, except the view is a string, not a list. Each lens argument must return a char? as a view.

Examples:
> (define string-first-third-fifth-lens
    (lens-join/string first-lens
                      third-lens
                      fifth-lens))
> (lens-view string-first-third-fifth-lens '(#\a #\b #\c #\d #\e #\f))

"ace"

> (lens-set string-first-third-fifth-lens '(#\a #\b #\c #\d #\e #\f) "ACE")

'(#\A #\b #\C #\d #\E #\f)

2.2.6 Stream Lenses

 (require lens/data/stream) package: lens-data

The Lens Guide has additional examples of stream lenses.

A lens for viewing the first element of a stream.

Examples:
> (lens-view stream-first-lens (stream 1 2 3))

1

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

'(a 2 3)

A lens for viewing the rest of a stream after the first element.

Examples:
> (stream->list (lens-view stream-rest-lens (stream 1 2 3)))

'(2 3)

> (stream->list (lens-set stream-rest-lens (stream 1 2 3) (stream 200 300 400 500)))

'(1 200 300 400 500)

procedure

(stream-ref-lens i)  lens?

  i : exact-nonnegative-integer?
A lens for viewing the ith element of a stream.

Examples:
> (lens-view (stream-ref-lens 2) (stream 1 2 3 4 5 6))

3

> (stream->list (lens-set (stream-ref-lens 2) (stream 1 2 3 4 5 6) 'a))

'(1 2 a 4 5 6)

2.2.7 Dict lenses

 (require lens/data/dict) package: lens-data

The Lens Guide has additional examples of dictionary lenses.

procedure

(dict-ref-lens key)  lens?

  key : any/c
Returns a lens for viewing the value mapped to key in a dict.

Examples:
> (define dict '((a . 1) (b . 2) (c . 3)))
> (lens-view (dict-ref-lens 'a) dict)

1

> (lens-set (dict-ref-lens 'a) dict 100)

'((a . 100) (b . 2) (c . 3))

2.3 Applicable lenses

 (require lens/applicable) package: lens-lib

This module provides the same functions as lens, but enables the use of applicable lenses. Applicable lenses may be used directly as getter functions, removing the need to use lens-view.

Examples:
> (require lens/applicable)
> (first-lens '(a b c))

'a

> (map first-lens '((1 2 3) (a b c) (100 200 300)))

'(1 a 100)

Attempting to use non-applicable lenses as functions is an error.

Examples:
> (require lens)
> (first-lens '(a b c))

cannot apply a non-applicable lens as a function