Add guide entries for most of the built-in datatype lenses
This commit is contained in:
parent
e0ab371d2f
commit
a361070ee0
|
@ -2,7 +2,7 @@
|
|||
|
||||
@(require "../doc-util/main.rkt")
|
||||
|
||||
@title{Lens Laws}
|
||||
@title[#:tag "laws"]{Lens Laws}
|
||||
|
||||
While @racket[make-lens] allows lenses to be constructed
|
||||
from arbitrary getters and setters, these getters and setters
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
@(require "doc-util/main.rkt")
|
||||
|
||||
|
||||
@title{Dict lenses}
|
||||
@title[#:tag "dict-reference"]{Dict lenses}
|
||||
|
||||
@see-guide-note["dict-guide"]{dictionary lenses}
|
||||
|
||||
@defproc[(dict-ref-lens [key any/c]) lens?]{
|
||||
Returns a lens for viewing the value mapped to @racket[key] in a dict.
|
||||
|
|
|
@ -16,6 +16,7 @@ for-label
|
|||
racket/stream
|
||||
racket/set
|
||||
racket/contract
|
||||
racket/function
|
||||
for-syntax
|
||||
racket/base
|
||||
syntax/parse
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
(only-in scribble/core style)
|
||||
setup/collects)
|
||||
|
||||
(provide other-reference-note)
|
||||
(provide other-reference-note see-guide-note see-reference-note)
|
||||
|
||||
(define css-resource
|
||||
(make-css-addition
|
||||
|
@ -23,3 +23,11 @@
|
|||
|
||||
(define (other-reference-note . pre-content)
|
||||
(margin-note (flexible-container finger (apply flexible-element pre-content))))
|
||||
|
||||
(define (see-guide-note tag . pre-content)
|
||||
@other-reference-note{
|
||||
@seclink[tag]{The Lens Guide} has additional examples of @|pre-content|.})
|
||||
|
||||
(define (see-reference-note tag . pre-content)
|
||||
@other-reference-note{
|
||||
@seclink[tag]{The Lens Reference} has additional information on @|pre-content|.})
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
#lang scribble/manual
|
||||
|
||||
@(require "../doc-util/scribble-include-no-subsection.rkt")
|
||||
@(require "../doc-util/main.rkt"
|
||||
"../doc-util/scribble-include-no-subsection.rkt")
|
||||
|
||||
@title{Hash Lenses}
|
||||
@title[#:tag "hash-reference"]{Hash Lenses}
|
||||
|
||||
@see-guide-note["hash-guide"]{hash lenses}
|
||||
|
||||
@scribble-include/no-subsection["ref.scrbl"]
|
||||
@scribble-include/no-subsection["nested.scrbl"]
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
#lang scribble/manual
|
||||
|
||||
@title{Pair and List Lenses}
|
||||
@(require "../doc-util/main.rkt")
|
||||
|
||||
@title[#:tag "pair-list-reference"]{Pair and List Lenses}
|
||||
|
||||
@see-guide-note["pair-list-guide"]{pair and list lenses}
|
||||
|
||||
@include-section["car-cdr.scrbl"]
|
||||
@include-section["list-ref-take-drop.scrbl"]
|
||||
|
|
|
@ -10,3 +10,4 @@ features this library provides; for a complete API reference, see @secref{lens-r
|
|||
@local-table-of-contents[]
|
||||
|
||||
@include-section["guide/introduction.scrbl"]
|
||||
@include-section["guide/built-in.scrbl"]
|
||||
|
|
|
@ -9,6 +9,14 @@
|
|||
@(define-syntax-rule (lens-interaction expr ...)
|
||||
(interaction #:eval (make-lens-eval) expr ...))
|
||||
|
||||
@title[#:tag "builtin-lenses"]{Lenses on Built-In Datatypes}
|
||||
@title[#:tag "builtin-lenses" #:style 'toc]{Lenses on Built-In Datatypes}
|
||||
|
||||
This library provides @lens-tech{lenses} for most built-in Racket datatypes. In general the name of
|
||||
each lens corresponds to the name of its accessor function, with @racket[-lens] appended to the end.
|
||||
For example, the lens for accessing the first element of a pair is @racket[car-lens], and the lens for
|
||||
accessing an element of a hash is called @racket[hash-ref-lens].
|
||||
|
||||
@local-table-of-contents[]
|
||||
|
||||
@include-section["built-in/ordered.scrbl"]
|
||||
@include-section["built-in/key-value.scrbl"]
|
||||
|
|
90
lens/private/scribblings/guide/built-in/key-value.scrbl
Normal file
90
lens/private/scribblings/guide/built-in/key-value.scrbl
Normal file
|
@ -0,0 +1,90 @@
|
|||
#lang scribble/manual
|
||||
|
||||
@(require scribble/eval
|
||||
"../../../doc-util/main.rkt")
|
||||
|
||||
@(define make-lens-eval
|
||||
(make-eval-factory '(racket/base lens)))
|
||||
|
||||
@(define-syntax-rule (lens-interaction expr ...)
|
||||
(interaction #:eval (make-lens-eval) expr ...))
|
||||
|
||||
@title[#:tag "key-value-lenses"]{Lenses on Key-Value Data}
|
||||
|
||||
Many Racket data structures hold values that correspond to a given key. Lenses for accessing elements
|
||||
of these structures by their keys are provided.
|
||||
|
||||
@section[#:tag "hash-guide"]{Hash Tables}
|
||||
|
||||
@see-reference-note["hash-reference"]{hash lenses}
|
||||
|
||||
Racket hash tables are simple key-value associations, and as a result, they only have one primitive
|
||||
lens constructor, @racket[hash-ref-lens]. Given a key, it produces a lens which views the value
|
||||
associated with the lens:
|
||||
|
||||
@(lens-interaction
|
||||
(lens-transform (hash-ref-lens 'a) (hash 'a "Hello")
|
||||
(λ (s) (string-append s ", world!"))))
|
||||
|
||||
Note that @racket[hash-ref-lens]'s signature differs from that of @racket[hash-ref] in an important
|
||||
way: it does not accept a "failure result" if the key is missing from the hash. Instead, the lens
|
||||
always throws an error:
|
||||
|
||||
@(lens-interaction
|
||||
(lens-view (hash-ref-lens 'not-a-key) (hash)))
|
||||
|
||||
This may seem inconvenient, but this limitation is by design---supporting a failure result would
|
||||
violate one of the @seclink["laws"]{lens laws}. Specifically, “get-set consistency” would no longer
|
||||
hold. Consider this example:
|
||||
|
||||
@(racketblock
|
||||
(let ([l (hash-ref-lens 'not-a-key "default")]
|
||||
[h (hash)])
|
||||
(lens-set l h (lens-view l h))))
|
||||
|
||||
If @racket[hash-ref-lens] accepted a default value, then the above expression would produce a new hash
|
||||
that was not @racket[equal?] to the original target. Enforcing this property makes lenses easier to
|
||||
reason about, just as ensuring purity makes functions easier to reason about.
|
||||
|
||||
Of course, sometimes breaking purity is the easiest way to solve a problem, and similarly, sometimes
|
||||
breaking the lens laws is okay (though it should be avoided if possible). We could, if we wished,
|
||||
define our own hash lens that accepts a default value:
|
||||
|
||||
@(define ref-default-eval (make-lens-eval))
|
||||
@(interaction #:eval ref-default-eval
|
||||
(define (hash-ref-lens/default key failure-result)
|
||||
(make-lens (λ (h) (hash-ref h key failure-result))
|
||||
(λ (h v) (hash-set h key v)))))
|
||||
|
||||
With this custom, "naughty" lens, we can actually perform the example from above:
|
||||
|
||||
@(interaction #:eval ref-default-eval
|
||||
(let ([l (hash-ref-lens/default 'not-a-key "default")]
|
||||
[h (hash)])
|
||||
(lens-set l h (lens-view l h))))
|
||||
|
||||
In addition to @racket[hash-ref-lens], @racket[hash-ref-nested-lens] is provided, which assists in
|
||||
fetching values from nested hashes. It is defined in terms of @racket[hash-ref-lens] and
|
||||
@racket[lens-compose], so it is just a shorter way of expressing the same concept:
|
||||
|
||||
@(lens-interaction
|
||||
(lens-set (hash-ref-nested-lens 'a 'b 'c)
|
||||
(hash 'a (hash 'b (hash 'c "foo")))
|
||||
"bar"))
|
||||
|
||||
@section[#:tag "dict-guide"]{Dictionaries}
|
||||
|
||||
@see-reference-note["dict-reference"]{dictionary lenses}
|
||||
|
||||
Racket @tech[#:doc '(lib "scribblings/reference/reference.scrbl")]{dictionaries} provide a generic
|
||||
interface for many kinds of key-value data-structures. They encompass hash tables, association lists,
|
||||
user-defined dictionaries, and even integer-keyed structures like vectors.
|
||||
|
||||
In practice, dictionary lenses work identically to lenses on hashes. The @racket[dict-ref-lens]
|
||||
lens constructor creates a lens with a view that is the value associated with the lens's key.
|
||||
|
||||
@(lens-interaction
|
||||
(lens-transform (dict-ref-lens 'b)
|
||||
'((a . 1)
|
||||
(b . 2))
|
||||
(λ (x) (* x 2))))
|
107
lens/private/scribblings/guide/built-in/ordered.scrbl
Normal file
107
lens/private/scribblings/guide/built-in/ordered.scrbl
Normal file
|
@ -0,0 +1,107 @@
|
|||
#lang scribble/manual
|
||||
|
||||
@(require scribble/eval
|
||||
"../../../doc-util/main.rkt")
|
||||
|
||||
@(define make-lens-eval
|
||||
(make-eval-factory '(racket/base racket/function racket/list racket/stream lens)))
|
||||
|
||||
@(define-syntax-rule (lens-interaction expr ...)
|
||||
(interaction #:eval (make-lens-eval) expr ...))
|
||||
|
||||
@title[#:tag "ordered-data-lenses"]{Lenses on Ordered Data}
|
||||
|
||||
Many Racket data structures hold @emph{ordered} or @emph{sequential} values. Lenses for accessing
|
||||
elements of these structures by index are provided.
|
||||
|
||||
@section[#:tag "pair-list-guide"]{Pairs and Lists}
|
||||
|
||||
@see-reference-note["pair-list-reference"]{pair and list lenses}
|
||||
|
||||
The two primitive pair lenses are @racket[car-lens] and @racket[cdr-lens]:
|
||||
|
||||
@(lens-interaction
|
||||
(lens-transform car-lens '(1 . 2) (curry * 2))
|
||||
(lens-transform cdr-lens '(1 . 2) (curry * 2)))
|
||||
|
||||
Obviously, these also work with lists, but most of the time, it's easier to use list-specific lenses.
|
||||
For arbitrary access to elements within a list, use the @racket[list-ref-lens] lens constructor, which
|
||||
produces a new lens given an index to look up. Abbreviation lenses such as @racket[first-lens] and
|
||||
@racket[second-lens] are provided for common use-cases:
|
||||
|
||||
@(lens-interaction
|
||||
(lens-transform (list-ref-lens 3) (range 10) sub1)
|
||||
(lens-transform third-lens (range 10) sub1))
|
||||
|
||||
Using @racket[list-ref-lens], it is possible to create a lens that performs indexed lookups for nested
|
||||
lists:
|
||||
|
||||
@(lens-interaction
|
||||
(define (2d-list-ref-lens x y)
|
||||
(lens-compose (list-ref-lens x)
|
||||
(list-ref-lens y)))
|
||||
(lens-set (2d-list-ref-lens 1 2)
|
||||
'((1 2 3)
|
||||
(4 5 6)
|
||||
(7 8 9))
|
||||
0))
|
||||
|
||||
This can also be generalized to @emph{n}-dimensional lists:
|
||||
|
||||
@(lens-interaction
|
||||
(define (list-ref-lens* . indicies)
|
||||
(apply lens-compose (map list-ref-lens indicies)))
|
||||
(lens-set (list-ref-lens* 0 1 0)
|
||||
'(((a b) (c d))
|
||||
((e f) (g h)))
|
||||
'z))
|
||||
|
||||
This function is actually provided by @racketmodname[lens] under the name
|
||||
@racket[list-ref-nested-lens], but it's easy to implement yourself.
|
||||
|
||||
@subsection{Fetching multiple list values at once}
|
||||
|
||||
Sometimes it can be useful to fetch multiple values from a list with a single lens. This can be done
|
||||
with @racket[lens-join/list], which combines multiple lenses whose target is a single value and
|
||||
produces a new lens whose view is all of those values.
|
||||
|
||||
@(lens-interaction
|
||||
(define first-two-lens (lens-join/list first-lens second-lens))
|
||||
(lens-view first-two-lens '(1 2 3 4))
|
||||
(lens-set first-two-lens '(1 2 3 4) '(a b))
|
||||
(lens-transform first-two-lens '(1 2 3 4) (curry map sub1)))
|
||||
|
||||
This can be useful to implement a form of information hiding, in which only a portion of a list is
|
||||
provided to client code, but the result can still be used to update the original list.
|
||||
|
||||
@section[#:tag "vectors-strings-guide"]{Vectors and Strings}
|
||||
|
||||
@other-reference-note{
|
||||
The @secref["vectors-reference"] and @secref["strings-reference"] sections in The Lens Reference
|
||||
have additional information on vector and string lenses, respectively.}
|
||||
|
||||
Lenses for random-access retrieval and functional update on vectors and strings are similar to the
|
||||
lenses provided for lists, but unlike lists, they are truly random-access. The
|
||||
@racket[vector-ref-lens] and @racket[string-ref-lens] lens constructors produce random-access lenses,
|
||||
and @racket[lens-join/vector] and @racket[lens-join/string] combine multiple lenses with vector or
|
||||
string targets.
|
||||
|
||||
@(lens-interaction
|
||||
(lens-transform (vector-ref-lens 1) #("a" "b" "c") string->symbol)
|
||||
(lens-transform (string-ref-lens 3) "Hello!" char-upcase))
|
||||
|
||||
@section[#:tag "streams-guide"]{Streams}
|
||||
|
||||
@see-reference-note["streams-reference"]{stream lenses}
|
||||
|
||||
Racket's @tech[#:doc '(lib "scribblings/reference/reference.scrbl")]{streams} contain ordered data,
|
||||
much like lists, but unlike lists, they are @emph{lazy}. Lenses on streams are similarly lazy, only
|
||||
forcing the stream up to what is necessary. This allows stream lenses to successfully operate on
|
||||
infinite streams.
|
||||
|
||||
@(lens-interaction
|
||||
(lens-view (stream-ref-lens 10)
|
||||
(stream-map (curry expt 2) (in-naturals))))
|
||||
|
||||
Keep in mind that since @racket[lens-transform] is strict, using it to update a value within a stream
|
||||
will force the stream up to the position of the element being modified.
|
|
@ -3,7 +3,9 @@
|
|||
@(require "doc-util/main.rkt")
|
||||
|
||||
|
||||
@title{Stream Lenses}
|
||||
@title[#:tag "streams-reference"]{Stream Lenses}
|
||||
|
||||
@see-guide-note["streams-guide"]{stream lenses}
|
||||
|
||||
@defthing[stream-first-lens lens?]{
|
||||
A lens for viewing the first element of a stream.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
@(require "doc-util/main.rkt")
|
||||
|
||||
@title{String Lenses}
|
||||
@title[#:tag "strings-reference"]{String Lenses}
|
||||
|
||||
@defproc[(string-ref-lens [i exact-nonnegative-integer?]) lens?]{
|
||||
Returns a lens for viewing the @racket[i]th character of a string.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
@(require "../doc-util/scribble-include-no-subsection.rkt")
|
||||
|
||||
@title{Vector lenses}
|
||||
@title[#:tag "vectors-reference"]{Vector lenses}
|
||||
|
||||
@scribble-include/no-subsection["ref.scrbl"]
|
||||
@scribble-include/no-subsection["nested.scrbl"]
|
||||
|
|
Loading…
Reference in New Issue
Block a user