From 2b5b59dc47bcc39727803dd537fb6b6bcb1af0df Mon Sep 17 00:00:00 2001 From: Alexis King Date: Fri, 13 Nov 2015 23:14:06 -0800 Subject: [PATCH] Edit some of the guide with minor improvements in wording and style --- lens/private/scribblings/guide.scrbl | 4 +- lens/private/scribblings/guide/built-in.scrbl | 4 +- .../guide/built-in/key-value.scrbl | 6 +- .../scribblings/guide/built-in/ordered.scrbl | 7 ++- .../scribblings/guide/introduction.scrbl | 56 ++++++++++--------- .../guide/user-defined/custom.scrbl | 2 +- 6 files changed, 42 insertions(+), 37 deletions(-) diff --git a/lens/private/scribblings/guide.scrbl b/lens/private/scribblings/guide.scrbl index 7ceb1d0..7f0f8eb 100644 --- a/lens/private/scribblings/guide.scrbl +++ b/lens/private/scribblings/guide.scrbl @@ -4,8 +4,8 @@ This guide is intended for programmers who are familiar with Racket but new to working with lenses or a certain part of this lens library. It contains a non-authorative introduction to lenses, including -examples of usage and recipies for solving certain kinds of problems. It does not describe all -features this library provides; for a complete API reference, see @secref{lens-reference}. +examples of usage and recipes for solving certain kinds of problems. It does not describe all features +this library provides; for a complete API reference, see @secref{lens-reference}. @local-table-of-contents[] diff --git a/lens/private/scribblings/guide/built-in.scrbl b/lens/private/scribblings/guide/built-in.scrbl index 0b154d9..8a675fc 100644 --- a/lens/private/scribblings/guide/built-in.scrbl +++ b/lens/private/scribblings/guide/built-in.scrbl @@ -4,8 +4,8 @@ @title[#:tag "built-in-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. +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]. diff --git a/lens/private/scribblings/guide/built-in/key-value.scrbl b/lens/private/scribblings/guide/built-in/key-value.scrbl index 3d4118f..fffc7c4 100644 --- a/lens/private/scribblings/guide/built-in/key-value.scrbl +++ b/lens/private/scribblings/guide/built-in/key-value.scrbl @@ -20,14 +20,14 @@ of these structures by their keys are provided. 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: +associated with the key: @(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 +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 @@ -56,7 +56,7 @@ define our own hash lens that accepts a default value: (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: +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")] diff --git a/lens/private/scribblings/guide/built-in/ordered.scrbl b/lens/private/scribblings/guide/built-in/ordered.scrbl index bb59452..fb34dd4 100644 --- a/lens/private/scribblings/guide/built-in/ordered.scrbl +++ b/lens/private/scribblings/guide/built-in/ordered.scrbl @@ -1,3 +1,4 @@ + #lang scribble/manual @(require scribble/eval @@ -33,8 +34,8 @@ produces a new lens given an index to look up. Abbreviation lenses such as @rack (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: +This is useful, but it only works for flat lists. However, using lens composition, it is possible to +create a lens that performs indexed lookups for nested lists using only @racket[list-ref-lens]: @(lens-interaction (define (2d-list-ref-lens x y) @@ -57,7 +58,7 @@ This can also be generalized to @emph{n}-dimensional lists: 'z)) This function is actually provided by @racketmodname[lens] under the name -@racket[list-ref-nested-lens], but it's easy to implement yourself. +@racket[list-ref-nested-lens], but the above example demonstrates that it's really a derived concept. @subsection{Fetching multiple list values at once} diff --git a/lens/private/scribblings/guide/introduction.scrbl b/lens/private/scribblings/guide/introduction.scrbl index 59e1ce3..5ecc032 100644 --- a/lens/private/scribblings/guide/introduction.scrbl +++ b/lens/private/scribblings/guide/introduction.scrbl @@ -8,19 +8,19 @@ @title[#:tag "lens-intro"]{Introduction to Lenses} The @racketmodname[lens] library defines @lens-tech{lenses}, tools for extracting values from -potentially-nested data structures. Lenses are mostly useful when writing in a functional style, such -as the style employed by @emph{How to Design Programs}, in which data structures are immutable and +potentially-nested data structures. Lenses are most useful when writing in a functional style, such as +the style employed by @italic{How to Design Programs}, in which data structures are immutable and side-effects are kept to a minimum. @section{What are lenses?} -A @deftech[#:key "lens" #:normalize? #f]{lens} is a value that composes getter and setter functions to -produce a bidirectional view into a data structure. This definition is intentionally broad---lenses +A @deftech[#:key "lens" #:normalize? #f]{lens} is a value that composes a getter and a setter function +to produce a bidirectional view into a data structure. This definition is intentionally broad---lenses are a very general concept, and they can be applied to almost any kind of value that encapsulates data. To make the concept more concrete, consider one of Racket's most primitive datatypes, the @tech[#:doc '(lib "scribblings/guide/guide.scrbl")]{pair}. Pairs are constructed from two values using -@racket[cons]: the first value is retrieved using @racket[car], and the second is retrieved using -@racket[cdr]. +the @racket[cons] function; the first value can then be retrieved using @racket[car], and the second +can be retrieved using @racket[cdr]. @(interaction (define p (cons 1 2)) @@ -28,11 +28,11 @@ data. To make the concept more concrete, consider one of Racket's most primitive (car p) (cdr p)) -With these three primitives, it's very easy to create new pairs and extract values from existing -pairs. However, it's a little bit harder to update a single field from an existing pair. In a -traditional Scheme, this could be accomplished by using @racket[set-car!] or @racket[set-cdr!], but -these @emph{mutate} the original pair. To remain functional, we want to produce an @emph{entirely new} -pair with one of the fields updated. +With these three primitives, it's very easy to create new pairs and subsequently extract values from +them. However, it's a little bit harder to update a single field in an existing pair. In a traditional +Scheme, this could be accomplished by using @racket[set-car!] or @racket[set-cdr!], but these +@emph{mutate} the original pair. To remain functional, we want to produce an @emph{entirely new} pair +with one of the fields updated. Fortunately, this is quite easy to implement in Racket: @@ -50,8 +50,8 @@ Fortunately, this is quite easy to implement in Racket: This means that each field now has a pair of getters and setters: @racket[car]/@racket[set-car] and @racket[cdr]/@racket[set-cdr]. A lens just wraps up each of these pairs of functions into a single -value, so instead of having four functions, we would just have @racket[car-lens] and -@racket[cdr-lens]. And in fact, using the functions we've just written, we can implement these lenses +value, so instead of having four functions, we would just have two lenses: @racket[car-lens] and +@racket[cdr-lens]. In fact, using the functions we've just written, we can implement these lenses ourselves. @(interaction #:eval introduction-eval @@ -65,21 +65,25 @@ To use a lens's getter function, use @racket[lens-view]. To use the setter funct (lens-view car-lens (cons 1 2)) (lens-set car-lens (cons 1 2) 'x)) -This, of course, isn't very useful, since we could just use the functions on their own. One thing we -do get when using lenses is @racket[lens-transform]. This allows you to provide a procedure which will -update the “view” based on its existing value. For example, we could increment one element in a pair: +This, of course, isn't very useful, since we could just use the functions on their own. One extra +thing we @emph{do} get for free when using lenses is @racket[lens-transform]. This allows you to +provide a procedure which will update the “view” based on its existing value. For example, we could +increment one element in a pair: @(interaction #:eval introduction-eval (lens-transform cdr-lens (cons 1 2) add1)) +While that's kind of cool, it still probably isn't enough to justify using lenses instead of just +using functions. + @section[#:style 'quiet]{Why use lenses?} -So far, lenses just seem like a way to group getters and setters, and as we've seen, that's all they -really are. However, on their own, this wouldn't be very useful. Using @racket[(car _p)] is a lot -easier than using @racket[(lens-view car-lens _p)]. +So far, lenses just seem like a way to group getters and setters, and as we've seen, that's really all +they are. However, on their own, this wouldn't be very useful. Using @racket[(car _p)] is a lot easier +than using @racket[(lens-view car-lens _p)]. Using plain functions starts to get a lot harder, though, once you start nesting data structures. For -example, consider a tree structure with pairs nested inside of pairs: +example, consider a tree constructed by nesting pairs inside of pairs: @(interaction #:eval introduction-eval (define tree (cons (cons 'a 'b) @@ -113,8 +117,8 @@ than that. How can we solve it? @other-reference-note{For more ways to construct compound lenses, see @secref{composing-lenses}.} In order to solve this problem, we can use @emph{lens composition}, which is similar to function -composition but extended to lenses. Just as we can create a compound getter with the expression -@racket[(compose cdr car)], we can create a compound lens with the expression +composition but extended to lenses. Just as we can create a compound getter function with the +expression @racket[(compose cdr car)], we can create a compound lens with the expression @racket[(lens-compose cdr-lens car-lens)]. With this, we produce an entirely new lens that can be used with @racket[lens-view], @racket[lens-set], and @racket[lens-transform], all of which do what you would expect: @@ -125,7 +129,7 @@ would expect: (lens-set cdar-lens tree 'x) (lens-transform cdar-lens tree symbol->string)) -Now it may begin to crystallize why lenses are useful: they make it possible to not just get at but -to actually functionally update and transform values within deeply-nested data structures. Since they -are composable, it is easy to create lenses that can traverse any set of structures with nothing but -a small set of primitives. This library provides those primitives. +Now the reason lenses are useful may begin to crystallize: they make it possible to not just get at +but to actually functionally update and transform values within deeply-nested data structures. Since +they are composable, it is easy to create lenses that can traverse any set of structures with nothing +but a small set of primitives. This library provides those primitives. diff --git a/lens/private/scribblings/guide/user-defined/custom.scrbl b/lens/private/scribblings/guide/user-defined/custom.scrbl index 6b4cc65..e0d9a78 100644 --- a/lens/private/scribblings/guide/user-defined/custom.scrbl +++ b/lens/private/scribblings/guide/user-defined/custom.scrbl @@ -16,7 +16,7 @@ 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 aren't good enough. In that case, it's always possible to +custom data structures, and struct lenses are insufficient. In that case, it's always possible to fall back on the primitive lens constructor, @racket[make-lens]. The @racket[make-lens] constructor is simple---it creates a new lens from a getter function and a