Compare commits

..

No commits in common. "master" and "2.0" have entirely different histories.
master ... 2.0

247 changed files with 1047 additions and 5174 deletions

2
.gitignore vendored
View File

@ -1,7 +1,7 @@
**/compiled/*
doc/
**/*.bak
**/*.html
**/*.css
**/*.js
*~
**.rktd

View File

@ -1,36 +1,22 @@
language: c
langauge: c
sudo: false
env:
global:
- RACKET_DIR=~/racket
- VERSION_TO_COVER="6.5"
matrix:
- RACKET_VERSION="6.0.1" CATALOG_VERSION="6.0.1"
- RACKET_VERSION="6.1" CATALOG_VERSION="6.1"
- RACKET_VERSION="6.1.1" CATALOG_VERSION="6.1.1"
- RACKET_VERSION="6.2" CATALOG_VERSION="6.2"
- RACKET_VERSION="6.2.1" CATALOG_VERSION="6.2.1"
- RACKET_VERSION="6.3" CATALOG_VERSION="6.3"
- RACKET_VERSION="6.4" CATALOG_VERSION="6.4"
- RACKET_VERSION="6.5" CATALOG_VERSION="6.5"
- RACKET_VERSION="6.6" CATALOG_VERSION="6.6"
- RACKET_VERSION="HEAD" CATALOG_VERSION="6.6"
matrix:
allow_failures:
- env: RACKET_VERSION="HEAD" CATALOG_VERSION="6.6"
- RACKET_VERSION=6.1.1
- RACKET_VERSION=6.2
- RACKET_VERSION=HEAD
before_install:
- git clone https://github.com/greghendershott/travis-racket.git ../travis-racket
- cat ../travis-racket/install-racket.sh | bash
- export PATH="${RACKET_DIR}/bin:${PATH}"
- export VERSION_SPECIFIC_CATALOG="http://download.racket-lang.org/releases/${CATALOG_VERSION}/catalog/"
- raco pkg config --set catalogs $VERSION_SPECIFIC_CATALOG http://pkgs.racket-lang.org http://planet-compats.racket-lang.org
- if [ "${VERSION_TO_COVER}" == "${RACKET_VERSION}" ]; then raco pkg install --auto cover cover-coveralls; fi
install:
- raco pkg install --auto lens-common/ lens-data/ lens-lib/ lens-unstable/ lens-doc/ lens/
install: raco pkg install --deps search-auto $TRAVIS_BUILD_DIR # install dependencies
script:
- raco test -p lens-common lens-data lens-lib lens-unstable lens-doc lens
- if [ "${VERSION_TO_COVER}" == "${RACKET_VERSION}" ]; then raco cover -f coveralls -d $TRAVIS_BUILD_DIR/coverage -p lens-common lens-data lens-lib lens-unstable lens-doc lens; fi
- raco setup
- raco test $TRAVIS_BUILD_DIR # run tests. you wrote tests, right?
after_success:
- raco cover -f coveralls -d $TRAVIS_BUILD_DIR/coverage . # generate coverage information for coveralls

23
LICENSE
View File

@ -1,23 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Jack Firth
Modified work Copyright 2015 Alex Knauth
Modified work Copyright 2015 Google Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,78 +1,15 @@
# lens ![Version](https://img.shields.io/badge/Version-2.0-green.svg) [![Build Status](https://travis-ci.org/jackfirth/lens.svg?branch=master)](https://travis-ci.org/jackfirth/lens) [![Coverage Status](https://coveralls.io/repos/jackfirth/lens/badge.svg)](https://coveralls.io/r/jackfirth/lens) [![Stories in Ready](https://badge.waffle.io/jackfirth/lens.png?label=ready&title=Ready)](https://waffle.io/jackfirth/lens) [![Scribble Docs](https://img.shields.io/badge/Docs-Scribble%20-blue.svg)](http://pkg-build.racket-lang.org/doc/lens/index.html) [![Join the chat at https://gitter.im/jackfirth/lens](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jackfirth/lens?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
lens [![Build Status](https://travis-ci.org/jackfirth/lens.svg?branch=master)](https://travis-ci.org/jackfirth/lens) [![Coverage Status](https://coveralls.io/repos/jackfirth/lens/badge.svg)](https://coveralls.io/r/jackfirth/lens) [![Stories in Ready](https://badge.waffle.io/jackfirth/lens.png?label=ready&title=Ready)](https://waffle.io/jackfirth/lens)
===================================
Documentation: [`lens`](http://pkg-build.racket-lang.org/doc/lens/index.html) [`unstable/lens`](http://pkg-build.racket-lang.org/doc/unstable-lens/index.html)
Latest Version: `1.2`
A Racket package for creating and composing pure functional lenses.
`raco pkg install lens`
`(require lens)`
### What on earth are lenses?
A lens is a value that can be used to focus on a small subpiece of some larger structure. A lens splits some data structure into two pieces - a *view*, which is some small isolated component of the data structure, and a *context*, which is everything else. The context can have a new view placed into it. This makes a lens act like a pure functional getter and setter:
```racket
> (lens-view first-lens '(1 2 3))
1
> (lens-set first-lens '(1 2 3) 'a)
'(a 2 3)
```
Lenses are first class values and pure functional, so they can be abstracted over and functions that operate on lenses can be created. For instance, given a lens its view can be "updated":
```racket
> (lens-transform first-lens '(1 2 3) number->string)
'("1" 2 3)
```
Additionaly, lenses are separate values from the objects they operate on, so they can be manipulated independently of any specific data. Functions can construct lenses, and operations can combine lenses. This allows for *lens composition*:
```racket
> (define first-of-b-key-lens (lens-compose first-lens (hash-ref-lens 'b)))
> (define a-hash (hash 'a '(1 2 3) 'b '(10 20 30) 'c '(100 200 300)))
> (lens-view first-of-b-key-lens a-hash)
10
> (lens-set first-of-b-key-lens a-hash 'foo)
#hash((a . (1 2 3)) (b . (foo 20 30)) (c . (100 200 300)))
```
Lenses can also be joined together to form compound lenses that view many things:
```racket
> (define first-third-fifth-lens (lens-join/list first-lens third-lens fifth-lens))
> (lens-view first-third-fifth-lens '(1 2 3 4 5 6))
'(1 3 5)
> (lens-set first-third-fifth-lens '(1 2 3 4 5 6) '(a b c))
'(a 2 b 4 c 6)
```
Lenses can also be extended to operate on some new data structure:
```racket
> (define first-of-each-lens (map-lens first-lens))
> (lens-view first-of-each-lens '((1 2) (3 4) (5 6)))
'(1 3 5)
> (lens-set first-of-each-lens '((1 2) (3 4) (5 6)) '(a b c))
'((a 2) (b 4) (c 6))
```
See [the documentation](http://pkg-build.racket-lang.org/doc/lens/index.html) for a full API reference
#### So when would I want to use lenses?
Lenses are most effective when you're dealing with the "giant ball of state" problem. When you
have a large amount of state you need to pass around between code written in a functional
style, it's difficult to update and manage it due to the lack of mutation "magically" updating
your entire object graph when a function changes a small part of it. Lenses allow code to
break down and manipulate portions of this state, simplifying interactions and updates.
In particular, consider using lenses if you find yourself doing any of the following:
- Using a giant complex piece of state that most pieces of code only care about a small part of
- Writing `struct-copy` a lot
- Converting some hairy data structure into another one, manipulating it, then turning it back
- Wishing you could treat data X as if it were a Y, i.e. "I wish this struct was a list so I could `map` over it easily"
- Creating structs that have nested struct instances inside them.
For a more in depth introduction, see [The Lens Guide](http://pkg-build.racket-lang.org/doc/lens/lens-guide.html). For detailed API documentation, see [The Lens Reference](http://pkg-build.racket-lang.org/doc/lens/lens-reference.html).
A lens is a value that can be used to focus on a small subpiece of some larger structure. Given a lens and a value of that larger structure, two values can be dervied: a *view* value, which is the subpiece, and a *context* function, which accepts a new view value and returns a new structure with the old view replaced by the new view. Think of them as composable, pure functional getters and setters. Examples can be found in the documentation.
#### Contributions

68
info.rkt Normal file
View File

@ -0,0 +1,68 @@
#lang info
(define collection 'multi)
(define version "2.0")
(define deps
'("base"
"rackunit-lib"
"unstable-lib"
"fancy-app"
"alexis-util"
"scribble-lib"))
(define build-deps
'("cover"
"rackunit-lib"
"racket-doc"
"doc-coverage"))
(define test-omit-paths
'("info.rkt"
"lens/base/base.scrbl"
"lens/base/contract.scrbl"
"lens/base/laws.scrbl"
"lens/base/main.scrbl"
"lens/base/transform.scrbl"
"lens/base/view-set.scrbl"
"lens/compound/compose.scrbl"
"lens/compound/join-hash.scrbl"
"lens/compound/join-list.scrbl"
"lens/compound/join-string.scrbl"
"lens/compound/join-vector.scrbl"
"lens/compound/main.scrbl"
"lens/compound/thrush.scrbl"
"lens/doc-util"
"lens/hash/main.scrbl"
"lens/hash/nested.scrbl"
"lens/hash/pick.scrbl"
"lens/hash/ref.scrbl"
"lens/list/assoc.scrbl"
"lens/list/car-cdr.scrbl"
"lens/list/list-ref-take-drop.scrbl"
"lens/list/main.scrbl"
"lens/list/multi.scrbl"
"lens/struct/field.scrbl"
"lens/struct/main.scrbl"
"lens/struct/struct.scrbl"
"lens/test-util"
"lens/vector/main.scrbl"
"lens/vector/nested.scrbl"
"lens/vector/pick.scrbl"
"lens/vector/ref.scrbl"
"lens/applicable.scrbl"
"lens/dict.scrbl"
"lens/info.rkt"
"lens/main.scrbl"
"lens/stream.scrbl"
"lens/string.scrbl"
"unstable/lens/arrow.scrbl"
"unstable/lens/main.scrbl"
"unstable/lens/sublist.scrbl"
"unstable/lens/syntax.scrbl"
"unstable/lens/view-set.scrbl"))

View File

@ -1,26 +0,0 @@
#lang info
(define collection 'multi)
(define deps
'("base"
"fancy-app"
"rackunit-lib"
"reprovide-lang"
"unstable-contract-lib"
"unstable-lib"
))
(define build-deps
'("lens-data"
"sweet-exp-lib"
))
(define cover-omit-paths
'(#rx"info\\.rkt"
#rx"main\\.rkt"
"lens/common.rkt"
"lens/private/test-util"
"lens/private/util"
))

View File

@ -1,8 +0,0 @@
#lang sweet-exp reprovide
except-in
combine-in
lens/private/base/main
lens/private/compound/main
gen:lens
focus-lens
use-applicable-lenses!

View File

@ -1,11 +0,0 @@
#lang racket/base
(require reprovide/reprovide)
(reprovide (except-in "gen-lens.rkt" gen-lens/c) "make-lens.rkt" "contract.rkt")
(module+ test
(require rackunit racket/list)
(define (set-first l v)
(list* v (rest l)))
(define first-lens (make-lens first set-first))
(let-lens (view-first setter-first) first-lens '(1 2 3 4 5)
(check-eqv? view-first 1)
(check-equal? (setter-first 'a) '(a 2 3 4 5))))

View File

@ -1,44 +0,0 @@
#lang racket/base
(provide lens/c)
(require racket/contract/base
unstable/contract
"gen-lens.rkt"
)
(module+ test
(require rackunit
racket/contract/region
fancy-app
"make-lens.rkt"
))
(define (lens/c target/c view/c)
(rename-contract
(gen-lens/c
[lens-view (or/c #f [lens? target/c . -> . view/c])]
[lens-set (or/c #f [lens? target/c view/c . -> . target/c])]
[focus-lens (or/c #f [lens? target/c . -> . (values view/c [view/c . -> . target/c])])])
`(lens/c ,(contract-name target/c) ,(contract-name view/c))))
(module+ test
(check-exn exn:fail:contract?
(λ ()
(define/contract lns (lens/c any/c any/c) #f)
(void)))
(define/contract lns (lens/c hash? string?)
(make-lens (hash-ref _ 'a) (hash-set _ 'a _)))
(check-equal? (lens-view lns (hash 'a "alpha" 'b "bet"))
"alpha")
(check-equal? (lens-set lns (hash 'a "alpha" 'b "bet") "alfa")
(hash 'a "alfa" 'b "bet"))
(let-lens [tgt ctxt] lns (hash 'a "alpha" 'b "bet")
(check-equal? tgt "alpha")
(check-equal? (ctxt "alfa") (hash 'a "alfa" 'b "bet"))
(check-exn exn:fail:contract?
(λ () (ctxt 'alpha))))
(check-exn exn:fail:contract?
(λ () (lens-view lns (hash 'a 'alpha 'b 'bet))))
(check-exn exn:fail:contract?
(λ () (lens-set lns (hash 'a "alpha" 'b "bet") 'alpha)))
)

View File

@ -1,56 +0,0 @@
#lang racket/base
(require racket/contract/base)
(provide gen:lens
let-lens
(rename-out [lens/c gen-lens/c])
(contract-out
[lens? predicate/c]
[lens-view (-> lens? any/c any/c)]
[lens-set (-> lens? any/c any/c any/c)]
[focus-lens (-> lens? any/c
(values any/c (-> any/c any/c)))]
[use-applicable-lenses! (-> void?)]
))
(require racket/generic fancy-app)
(define-generics lens
(lens-view lens target)
(lens-set lens target x)
(focus-lens lens target)
#:defined-predicate lens-implements?
#:fallbacks
[(define/generic gen-lens-view lens-view)
(define/generic gen-lens-set lens-set)
(define/generic gen-focus-lens focus-lens)
(define (lens-view lens target)
(unless (lens-implements? lens 'focus-lens)
(error 'lens-view "not implemented for ~v" lens))
(let-values ([(view _) (gen-focus-lens lens target)])
view))
(define (lens-set lens target x)
(unless (lens-implements? lens 'focus-lens)
(error 'lens-set "not implemented for ~v" lens))
(let-values ([(_ setter) (gen-focus-lens lens target)])
(setter x)))
(define (focus-lens lens target)
(unless (lens-implements? lens 'lens-view 'lens-set)
(error 'focus-lens "not implemented for ~v" lens))
(values (gen-lens-view lens target)
(gen-lens-set lens target _)))]
#:derive-property prop:procedure
(lambda (this target)
(if (lenses-applicable?)
(lens-view this target)
(error "cannot apply a non-applicable lens as a function"))))
(define lenses-applicable? (make-parameter #f))
(define (use-applicable-lenses!)
(lenses-applicable? #t))
(define-syntax-rule (let-lens (view context) lens-expr target-expr body ...)
(let-values ([(view context) (focus-lens lens-expr target-expr)])
body ...))

View File

@ -1,4 +0,0 @@
#lang reprovide
lens/private/base/base
"view-set.rkt"
"transform.rkt"

View File

@ -1,34 +0,0 @@
#lang racket/base
(require racket/contract/base)
(provide (contract-out [make-lens (-> (-> any/c any/c)
(-> any/c any/c any/c)
lens?)]))
(require "gen-lens.rkt")
(module+ test
(require rackunit racket/list racket/function))
(struct lens-struct (get set)
#:methods gen:lens
[(define (lens-view this target)
((lens-struct-get this) target))
(define (lens-set this target x)
((lens-struct-set this) target x))]
#:methods gen:custom-write
[(define (write-proc this out mode)
(write-string "#<lens>" out))])
(define (make-lens getter setter)
(lens-struct getter setter))
(module+ test
(define (set-first l v)
(list* v (rest l)))
(define first-lens (make-lens first set-first))
(check-exn exn:fail? (thunk (first-lens '(a b c))))
(let-lens (view-first setter-first) first-lens '(1 2 3 4 5)
(check-eqv? view-first 1)
(check-equal? (setter-first 'a) '(a 2 3 4 5)))
(check-equal? (format "~v" first-lens) "#<lens>"))

View File

@ -1,19 +0,0 @@
#lang racket/base
(require reprovide/reprovide)
(reprovide lens/common)
(require (only-in lens/private/base/base use-applicable-lenses!))
(module+ test
(require rackunit))
(use-applicable-lenses!)
(module+ test
(define (set-car p a)
(cons a (cdr p)))
(define car-lens (make-lens car set-car))
(check-equal? (car-lens '(a b c)) 'a)
(check-equal? (lens-view car-lens '(a b c)) 'a)
(check-equal? (lens-set car-lens '(a b c) 97) '(97 b c)))

View File

@ -1,74 +0,0 @@
#lang sweet-exp racket/base
provide define-nested-lenses
require lens/private/compound/thrush
for-syntax racket/base
racket/syntax
syntax/parse
syntax/srcloc
"../util/id-append.rkt"
module+ test
require lens/private/base/base
lens/private/list/main
rackunit
begin-for-syntax
(define (with-sub-range-binders stx prop)
(syntax-property stx 'sub-range-binders prop))
(define -- (update-source-location (datum->syntax #f '-)
#:span 1))
(define -lens (update-source-location (datum->syntax #f '-lens)
#:span 5))
;; helper syntax-class for define-nested-lenses
(define-syntax-class (clause base-id base-lens-tmp)
#:attributes (def)
[pattern [suffix-id:id suffix-lens-expr:expr
unchecked-clause ...]
#:with base-lens:id base-lens-tmp
#:do [(define-values [base-suffix-id base-suffix-sub-range]
(id-append #:context base-id
base-id -- #'suffix-id))
(define-values [base-suffix-lens-id base-suffix-lens-sub-range]
(id-append #:context base-id
base-suffix-id -lens))]
#:with base-suffix
base-suffix-id
#:with base-suffix-lens
base-suffix-lens-id
#:with [(~var clause (clause #'base-suffix #'base-suffix-lens)) ...]
#'[unchecked-clause ...]
#:with def
(with-sub-range-binders
#'(begin
(define base-suffix-lens
(lens-thrush base-lens suffix-lens-expr))
clause.def
...)
base-suffix-lens-sub-range)])
(define-syntax define-nested-lenses
(syntax-parser
[(define-nested-lenses [base:id base-lens-expr:expr]
(~parse base-lens:id (generate-temporary #'base))
(~var clause (clause #'base #'base-lens))
...)
#'(begin
(define base-lens base-lens-expr)
clause.def
...)]))
module+ test
(define-nested-lenses [first first-lens]
[first first-lens]
[second second-lens]
[third third-lens
[first first-lens]
[second second-lens]])
(check-equal? (lens-view first-first-lens '((a b c d) e)) 'a)
(check-equal? (lens-view first-second-lens '((a b c d) e)) 'b)
(check-equal? (lens-view first-third-lens '((a b c d) e)) 'c)
(check-equal? (lens-view first-third-first-lens '((a b (c d) e) f)) 'c)
(check-equal? (lens-view first-third-second-lens '((a b (c d) e) f)) 'd)

View File

@ -1,23 +0,0 @@
#lang sweet-exp racket/base
require racket/function
racket/contract/base
"../base/main.rkt"
lens/private/isomorphism/base
module+ test
require rackunit
"../base/main.rkt"
"../test-util/test-lens.rkt"
provide
contract-out
identity-lens lens?
(define identity-lens
(make-isomorphism-lens identity identity))
(module+ test
(check-lens-view identity-lens 'foo 'foo)
(check-lens-set identity-lens 'foo 'bar 'bar))

View File

@ -1,92 +0,0 @@
#lang racket/base
(provide lens-if
lens-cond
lens-match
)
(require lens/private/base/main
racket/match
(for-syntax racket/base
syntax/parse
))
(module+ test
(require rackunit lens/private/list/main lens/private/vector/main lens/private/string/main))
(define (lens-if pred lens1 lens2)
(make-lens
(λ (tgt)
(if (pred tgt)
(lens-view lens1 tgt)
(lens-view lens2 tgt)))
(λ (tgt nvw)
(if (pred tgt)
(lens-set lens1 tgt nvw)
(lens-set lens2 tgt nvw)))))
(define (any? x) #t)
(define-syntax lens-cond
(syntax-parser #:literals (else)
[(lens-cond [pred-expr:expr lens-expr:expr] ... [else else-lens-expr:expr])
#'(lens-cond [pred-expr lens-expr] ... [any? else-lens-expr])]
[(lens-cond [pred-expr:expr lens-expr:expr] ...)
#:with [pred ...] (generate-temporaries #'[pred-expr ...])
#:with [lens ...] (generate-temporaries #'[lens-expr ...])
#'(let ([pred pred-expr] ... [lens lens-expr] ...)
(make-lens
(λ (tgt)
(cond [(pred tgt) (lens-view lens tgt)]
...
[else (raise-lens-cond-error tgt 'pred-expr ...)]))
(λ (tgt nvw)
(cond [(pred tgt) (lens-set lens tgt nvw)]
...
[else (raise-lens-cond-error tgt 'pred-expr ...)]))))]))
(define (raise-lens-cond-error tgt . pred-expr-syms)
(raise-arguments-error 'lens-cond "no matching clause for target"
"target" tgt
"expected" `(or/c ,@pred-expr-syms)))
(define-syntax lens-match
(syntax-parser
[(lens-match [pat:expr lens-expr:expr] ...)
#'(make-lens
(λ (tgt)
(match tgt
[pat (lens-view lens-expr tgt)]
...))
(λ (tgt nvw)
(match tgt
[pat (lens-set lens-expr tgt nvw)]
...)))]))
(module+ test
(define if-lens (lens-if list? first-lens (vector-ref-lens 0)))
(check-equal? (lens-view if-lens '(1 2 3)) 1)
(check-equal? (lens-view if-lens '#(1 2 3)) 1)
(check-equal? (lens-set if-lens '(1 2 3) 'a) '(a 2 3))
(check-equal? (lens-set if-lens '#(1 2 3) 'a) '#(a 2 3))
(define cond-lens (lens-cond [list? first-lens]
[vector? (vector-ref-lens 0)]
[string? (string-ref-lens 0)]))
(check-equal? (lens-view cond-lens '(1 2 3)) 1)
(check-equal? (lens-view cond-lens '#(1 2 3)) 1)
(check-equal? (lens-view cond-lens "123") #\1)
(check-equal? (lens-set cond-lens '(1 2 3) 'a) '(a 2 3))
(check-equal? (lens-set cond-lens '#(1 2 3) 'a) '#(a 2 3))
(check-equal? (lens-set cond-lens "123" #\a) "a23")
(define match-lens (lens-match [(list a) first-lens]
[(list a b) second-lens]
[(list a b c) third-lens]
[(list a ... b) (list-ref-lens (length a))]))
(check-equal? (lens-view match-lens '(1)) 1)
(check-equal? (lens-view match-lens '(1 2)) 2)
(check-equal? (lens-view match-lens '(1 2 3)) 3)
(check-equal? (lens-view match-lens '(1 2 3 4 5 6)) 6)
(check-equal? (lens-set match-lens '(1) 'a) '(a))
(check-equal? (lens-set match-lens '(1 2) 'a) '(1 a))
(check-equal? (lens-set match-lens '(1 2 3) 'a) '(1 2 a))
(check-equal? (lens-set match-lens '(1 2 3 4 5 6) 'a) '(1 2 3 4 5 a))
)

View File

@ -1,32 +0,0 @@
#lang sweet-exp racket/base
provide lazy-lens
rec-lens
require fancy-app lens/private/base/main racket/promise
module+ test
require rackunit
lens/private/compound/if
lens/private/isomorphism/data
lens/private/list/map
(define-syntax-rule (lazy-lens expr)
(let ([p (delay expr)])
(make-lens (lens-view (force p) _) (lens-set (force p) _ _))))
(define-syntax-rule (rec-lens name expr)
(letrec ([name (lazy-lens expr)])
name))
module+ test
(define (tree-map-lens item-lens)
(rec-lens the-tree-lens
(lens-cond [list? (map-lens the-tree-lens)]
[else item-lens])))
(check-equal? (lens-view (tree-map-lens symbol->string-lens) '(a (b (() c)) (d)))
'("a" ("b" (() "c")) ("d")))
(check-equal? (lens-set (tree-map-lens symbol->string-lens)
'(a (b (() c)) (d))
'("hay" ("bee" (() "sea")) ("deep")))
'(hay (bee (() sea)) (deep)))

View File

@ -1,4 +0,0 @@
#lang reprovide
"compose.rkt"
"identity.rkt"
"thrush.rkt"

View File

@ -1,104 +0,0 @@
#lang sweet-exp racket/base
require racket/contract/base
provide
contract-out
lens-zoom (-> lens? lens? lens?)
lens-zoom* (->* [] #:rest (listof2 lens? lens?) lens?)
require fancy-app
lens/private/base/main
lens/private/compound/thrush
lens/private/util/list-pair-contract
racket/match
unstable/sequence
lens/private/isomorphism/base
module+ test
require lens/private/list/main
rackunit
lens/private/isomorphism/data
lens/private/list/map
;; lens-zoom : (Lens (Outer Inner) Inner) (Lens A B) -> (Lens (Outer A) (Outer B))
(define (lens-zoom zoom-lens transformer-lens)
(match transformer-lens
[(make-isomorphism-lens transformer inverse)
;; transformer : A -> B
;; inverse : B -> A
(make-isomorphism-lens
(lens-transform zoom-lens _ transformer) ; (Outer A) -> (Outer B)
(lens-transform zoom-lens _ inverse))] ; (Outer B) -> (Outer A)
[transformer-lens
;; get : (Outer A) -> (Outer B)
(define (get tgt)
;; transformer : A -> B
(define (transformer a)
(lens-view transformer-lens a))
(lens-transform zoom-lens tgt transformer))
;; set : (Outer A) (Outer B) -> (Outer A)
(define (set tgt nvw)
;; a : A
(define a (lens-view zoom-lens tgt))
;; transformer : B -> A
(define (transformer b)
(lens-set transformer-lens a b))
(lens-transform zoom-lens nvw transformer))
(make-lens get set)]))
(define (lens-zoom* . lenses/transformers)
(apply lens-thrush
(for/list ([args (in-slice 2 lenses/transformers)])
(apply lens-zoom args))))
module+ test
(define first-sym->str
(lens-zoom first-lens symbol->string-lens))
(check-equal? (lens-view first-sym->str '(a b c))
'("a" b c))
(check-equal? (lens-set first-sym->str '(a b c) '("a" b c))
'(a b c))
(check-equal? (lens-set first-sym->str '(a b c) '("z" b c))
'(z b c))
(check-equal? (lens-set first-sym->str '(a b c) '("z" bee sea))
'(z bee sea))
(check-equal? (lens-view first-sym->str (lens-set first-sym->str '(a b c) '("z" bee sea)))
'("z" bee sea))
(define trans-second-first/third-second
(lens-zoom* second-lens first-lens third-lens second-lens))
(check-equal? (lens-view trans-second-first/third-second '(1 (2 3) (4 5)))
'(1 2 5))
(check-equal? (lens-set trans-second-first/third-second '(1 (2 3) (4 5)) '(1 2 5))
'(1 (2 3) (4 5)))
(check-equal? (lens-set trans-second-first/third-second '(1 (2 3) (4 5)) '(1 b 5))
'(1 (b 3) (4 5)))
(check-equal? (lens-set trans-second-first/third-second '(1 (2 3) (4 5)) '(a b c))
'(a (b 3) (4 c)))
(check-equal? (lens-view trans-second-first/third-second
(lens-set trans-second-first/third-second '(1 (2 3) (4 5)) '(a b c)))
'(a b c))
(define (rekey-alist-lens key->new-key-lens)
(map-lens (lens-zoom car-lens key->new-key-lens)))
(check-equal? (lens-view (rekey-alist-lens symbol->string-lens) '((a . 1) (b . 2) (c . 3)))
'(("a" . 1) ("b" . 2) ("c" . 3)))
(check-equal? (lens-set (rekey-alist-lens symbol->string-lens)
'((a . 1) (b . 2) (c . 3))
'(("a" . 10) ("b" . 200) ("c" . 3000)))
'((a . 10) (b . 200) (c . 3000)))
(check-equal? (lens-set (rekey-alist-lens symbol->string-lens)
'((a . 1) (b . 2) (c . 3))
'(("one" . 10) ("two" . 200) ("three" . 3000)))
'((one . 10) (two . 200) (three . 3000)))
(define (rek+v-alist-lens key->new-key-lens value->new-value-lens)
(map-lens (lens-zoom* car-lens key->new-key-lens cdr-lens value->new-value-lens)))
(check-equal? (lens-view (rek+v-alist-lens symbol->string-lens number->string-lens)
'((a . 1) (b . 2) (c . 3)))
'(("a" . "1") ("b" . "2") ("c" . "3")))
(check-equal? (lens-set (rek+v-alist-lens symbol->string-lens number->string-lens)
'((a . 1) (b . 2) (c . 3))
'(("a" . "10") ("b" . "200") ("c" . "3000")))
'((a . 10) (b . 200) (c . 3000)))
(check-equal? (lens-set (rek+v-alist-lens symbol->string-lens number->string-lens)
'((a . 1) (b . 2) (c . 3))
'(("one" . "10") ("two" . "200") ("three" . "3000")))
'((one . 10) (two . 200) (three . 3000)))

View File

@ -1,26 +0,0 @@
#lang sweet-exp racket/base
provide isomorphism-lens?
isomorphism-lens-inverse
rename-out [isomorphism-lens make-isomorphism-lens]
[isomorphism-lenses make-isomorphism-lenses]
require racket/match
lens/private/base/gen-lens
(struct isomorphism-lens (f inv) #:transparent
#:methods gen:lens
[(define (lens-view lens tgt)
((isomorphism-lens-f lens) tgt))
(define (lens-set lens tgt v)
((isomorphism-lens-inv lens) v))])
(define (isomorphism-lens-inverse lens)
(match lens
[(isomorphism-lens f inv)
(isomorphism-lens inv f)]))
(define (isomorphism-lenses f inv)
(values (isomorphism-lens f inv)
(isomorphism-lens inv f)))

View File

@ -1,33 +0,0 @@
#lang sweet-exp racket/base
require racket/contract/base
provide
contract-out
isomorphism-compose
(rest-> isomorphism-lens? isomorphism-lens?)
isomorphism-thrush
(rest-> isomorphism-lens? isomorphism-lens?)
require racket/match
lens/private/util/rest-contract
"base.rkt"
module+ test
require lens/private/base/main
lens/private/compound/identity
lens/private/isomorphism/data
rackunit
(define (isomorphism-compose . args)
(match args
[(list (make-isomorphism-lens fs invs) ...)
(make-isomorphism-lens
(apply compose1 fs)
(apply compose1 (reverse invs)))]))
(define (isomorphism-thrush . args)
(apply isomorphism-compose (reverse args)))
module+ test
(define string->vector-lens (isomorphism-thrush string->list-lens list->vector-lens))
(check-equal? (lens-view string->vector-lens "abc") #(#\a #\b #\c))
(check-equal? (lens-set string->vector-lens "abc" #(#\1 #\2 #\3)) "123")

View File

@ -1,47 +0,0 @@
#lang racket/base
(require racket/contract
rackunit
fancy-app
lens/private/base/base
"../base/view-set.rkt")
(provide
(contract-out
[check-lens-view (-> lens? any/c any/c void?)]
[check-lens-set (-> lens? any/c any/c any/c void?)]
[check-lens-view-set (-> lens? any/c void?)]
[check-lens-set-view (-> lens? any/c any/c void?)]
[check-lens-set-set (-> lens? any/c any/c any/c void?)]
[test-lens-laws (-> lens? any/c any/c any/c void?)]))
(define-check (check-lens-view lens target expected-view)
(check-equal? (lens-view lens target) expected-view))
(define-check (check-lens-set lens target new-view expected-new-target)
(check-equal? (lens-set lens target new-view) expected-new-target))
(define-check (check-lens-view-set lens target)
(check-lens-set lens target (lens-view lens target)
target
"setting target's view to its own view not equal? to itself"))
(define-check (check-lens-set-view lens target new-view)
(check-lens-view lens (lens-set lens target new-view)
new-view
"view of target after setting it's view not equal? to the set view"))
(define-check (check-lens-set-set lens target new-view1 new-view2)
(let* ([target* (lens-set lens target new-view1)]
[target** (lens-set lens target* new-view2)])
(check-lens-view lens target**
new-view2
"view of target after setting its view twice not equal? to second view")))
(define (test-lens-laws lens test-target test-view1 test-view2)
(check-lens-view-set lens test-target)
(check-lens-set-view lens test-target test-view1)
(check-lens-set-view lens test-target test-view2)
(check-lens-set-set lens test-target test-view1 test-view2))

View File

@ -1,21 +0,0 @@
#lang sweet-exp racket/base
provide test-multi*
require racket/match
racket/string
racket/format
syntax/parse/define
rackunit
for-syntax racket/base
syntax/parse
(define-simple-macro
(test-multi* ([test-id:id #:in [test-variant:expr ...]] ...)
body ...)
#:with [pair-id ...] (generate-temporaries #'[test-id ...])
#:with [which-test ...] (generate-temporaries #'[test-id ...])
(for* ([pair-id (in-list (list (cons 'test-variant test-variant) ...))] ...)
(match-define (cons which-test test-id) pair-id) ...
(test-case (string-join (list (format "~a = ~a" 'test-id which-test) ...) ", ")
body ...)))

View File

@ -1,65 +0,0 @@
#lang racket/base
(provide id-append)
(require racket/list
racket/syntax
syntax/srcloc)
;; orig : Syntax -> Syntax
(define (orig stx)
(syntax-property stx 'original-for-check-syntax #t))
;; Sub-Range-Binder-Prop = (Treeof (Vector Id Nat Nat Real Real Id Nat Nat Real Real))
;; Binder-Proc = Id -> Sub-Range-Binder-Prop
;; make-binder-proc : Id Nat -> Binder-Proc
(define ((make-binder-proc base n) id)
(vector (syntax-local-introduce id)
n (syntax-span base) 0.5 0.5
(syntax-local-introduce base)
0 (syntax-span base) 0.5 0.5))
;; get-sub-range-binders : Id (Listof Binder-Proc) -> Sub-Range-Binder-Prop
(define (get-sub-range-binders id binder-procs)
(for/list ([binder-proc (in-list binder-procs)])
(binder-proc id)))
;; empty-id : Syntax -> Id
(define (empty-id ctxt)
(datum->syntax ctxt '||))
(define appended-id-prop (gensym 'appended-id))
;; id-append : #:context Syntax Identifier ... -> (values Identifier Sub-Range-Binder-Prop)
;; a wrapper around id-append* that keeps track of identifiers that
;; are themselves appended from other identifiers
(define (id-append #:context ctxt . ids)
(define ids*
(append*
(for/list ([id (in-list ids)])
;; appended : (U #false (Listof Id))
(define appended (syntax-property id appended-id-prop))
(cond [appended appended]
[else (list id)]))))
(define-values [id sub-range-binders]
(apply id-append* #:context ctxt ids*))
(values (syntax-property id appended-id-prop ids*)
sub-range-binders))
;; id-append* : #:context Syntax Identifier ... -> (values Identifier Sub-Range-Binder-Prop)
(define (id-append* #:context ctxt . ids)
;; binder-procs : (Listof Binder-Proc)
(define-values [id n binder-procs]
(for/fold ([id1 (empty-id ctxt)] [n 0] [binder-procs '()])
([id2 (in-list ids)])
(values (format-id ctxt "~a~a" id1 id2)
(+ n (syntax-span id2))
(cons (make-binder-proc id2 n) binder-procs))))
(define id* (orig id))
(values id*
(get-sub-range-binders id* binder-procs)))

View File

@ -1,27 +0,0 @@
#lang info
(define collection 'multi)
(define deps
'("base"
"lens-common"
"rackunit-lib"
"unstable-lib"
"unstable-list-lib"
"unstable-contract-lib"
"fancy-app"
"syntax-classes-lib"
"struct-update-lib"
"kw-make-struct"
"reprovide-lang"
))
(define build-deps
'("sweet-exp-lib"
))
(define cover-omit-paths
'(#rx"info\\.rkt"
#rx"main\\.rkt"
))

View File

@ -1,8 +0,0 @@
#lang sweet-exp reprovide
"data/dict.rkt"
"data/hash.rkt"
"data/list.rkt"
"data/stream.rkt"
"data/string.rkt"
"data/struct.rkt"
"data/vector.rkt"

View File

@ -1,2 +0,0 @@
#lang reprovide
lens/private/dict/dict

View File

@ -1,2 +0,0 @@
#lang reprovide
lens/private/hash/main

View File

@ -1,2 +0,0 @@
#lang reprovide
lens/private/list/main

View File

@ -1,2 +0,0 @@
#lang reprovide
lens/private/stream/stream

View File

@ -1,2 +0,0 @@
#lang reprovide
lens/private/string/main

View File

@ -1,2 +0,0 @@
#lang reprovide
lens/private/struct/main

View File

@ -1,2 +0,0 @@
#lang reprovide
lens/private/vector/main

View File

@ -1,24 +0,0 @@
#lang sweet-exp racket/base
require racket/contract/base
provide
contract-out
dict-ref-nested-lens (->* [] #:rest (listof any/c) (lens/c functional-dict? any/c))
require lens/private/base/main
lens/private/compound/thrush
lens/private/dict/dict
lens/private/util/functional-dict
module+ test
require rackunit fancy-app
(define (dict-ref-nested-lens . ks)
(apply lens-thrush (map dict-ref-lens ks)))
module+ test
(define a-x (dict-ref-nested-lens 'a 'x))
(let-lens [val ctxt] a-x '([a . ([x . 1] [y . 2])] '[b . ([z . 3])])
(check-equal? val 1)
(check-equal? (ctxt 100) '([a . ([x . 100] [y . 2])] '[b . ([z . 3])])))
(check-equal? (lens-transform/list '([a . ([x . 1] [y . 2])] '[b . ([z . 3])]) a-x (* 10 _))
'([a . ([x . 10] [y . 2])] '[b . ([z . 3])]))

View File

@ -1,60 +0,0 @@
#lang sweet-exp racket
;; inspired by https://github.com/jackfirth/racket-auto-fix-deps/blob/master/job/src/filter-hash.rkt
provide
contract-out
hash-filterer-lens (-> (-> any/c any/c boolean?) (lens/c immutable-hash? immutable-hash?))
hash-filterer-lens/key (-> predicate/c (lens/c immutable-hash? immutable-hash?))
hash-filterer-lens/value (-> predicate/c (lens/c immutable-hash? immutable-hash?))
require fancy-app
lens/private/base/main
lens/private/util/immutable
unstable/hash
module+ test
require lens/private/test-util/test-lens
rackunit
(define (hash-filter keep? hsh)
(for/hash ([(k v) (in-hash hsh)] #:when (keep? k v))
(values k v)))
(define (hash-filter-not drop? hsh)
(hash-filter (λ (k v) (not (drop? k v))) hsh))
(define (hash-andmap f hsh)
(for/and ([(k v) (in-hash hsh)])
(f k v)))
(define (hash-filterer-lens keep?)
(make-lens
(hash-filter keep? _)
(λ (tgt nvw)
(unless (hash-andmap keep? nvw)
(raise-argument-error 'hash-filterer-lens-setter
(format "a hash where all key-value pairs pass ~v" keep?)
nvw))
(hash-union (hash-filter-not keep? tgt) nvw))))
(define (hash-filterer-lens/key keep?)
(hash-filterer-lens (λ (k v) (keep? k))))
(define (hash-filterer-lens/value keep?)
(hash-filterer-lens (λ (k v) (keep? v))))
module+ test
(check-lens-view (hash-filterer-lens/key symbol?) (hash 'a 1 "b" 2 'c 3)
(hash 'a 1 'c 3))
(check-lens-set (hash-filterer-lens/key symbol?) (hash 'a 1 "b" 2 'c 3) (hash 'd 4 'e 5)
(hash "b" 2 'd 4 'e 5))
(check-lens-view (hash-filterer-lens/value number?) (hash 'a 1 'b "two" 'c 3)
(hash 'a 1 'c 3))
(check-lens-set (hash-filterer-lens/value number?) (hash 'a 1 'b "two" 'c 3) (hash 'd 4)
(hash 'b "two" 'd 4))
(check-lens-view (hash-filterer-lens =) (hash 1 1.0 2 45 3 3)
(hash 1 1.0 3 3))
(check-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))
(check-exn exn:fail:contract?
(thunk (lens-set (hash-filterer-lens/key symbol?) (hash 'a 1) (hash "d" 4))))

View File

@ -1,42 +0,0 @@
#lang racket/base
(require racket/contract
racket/match
unstable/sequence
fancy-app
lens/private/base/main
lens/private/util/alternating-list
lens/private/util/list-pair-contract
"../util/immutable.rkt"
"../list/join-list.rkt")
(module+ test
(require rackunit
"../list/list-ref-take-drop.rkt"
lens/private/test-util/test-lens))
(provide
(contract-out
[lens-join/hash (->* () #:rest (listof2 any/c lens?) (lens/c any/c immutable-hash?))]))
(define (keys+values->hash keys vs)
(make-immutable-hash (keys+values->assoc-list keys vs)))
(define (lens-join/hash . keys/lenses)
(define-values [keys lenses] (alternating-list->keys+values keys/lenses))
(define list-lens (apply lens-join/list lenses))
(define (get target)
(keys+values->hash keys (lens-view list-lens target)))
(define (set target new-view-hash)
(lens-set list-lens target (map (hash-ref new-view-hash _) keys)))
(make-lens get set))
(module+ test
(define a-b-lens (lens-join/hash 'b third-lens
'a first-lens))
(check-lens-view a-b-lens '(1 2 3)
(hash 'a 1 'b 3))
(check-lens-set a-b-lens '(1 2 3) (hash 'a 100 'b 200)
'(100 2 200)))

View File

@ -1,5 +0,0 @@
#lang reprovide
"nested.rkt"
"pick.rkt"
"ref.rkt"
"join-hash.rkt"

View File

@ -1,47 +0,0 @@
#lang sweet-exp racket/base
provide string->symbol-lens
symbol->string-lens
number->string-lens
string->number-lens
list->vector-lens
vector->list-lens
list->string-lens
string->list-lens
require lens/private/base/main
lens/private/util/alternating-list
lens/private/isomorphism/base
module+ test
require rackunit
(define-values [string->symbol-lens symbol->string-lens]
(make-isomorphism-lenses string->symbol symbol->string))
(define-values [number->string-lens string->number-lens]
(make-isomorphism-lenses number->string string->number))
(define-values [list->vector-lens vector->list-lens]
(make-isomorphism-lenses list->vector vector->list))
(define-values [list->string-lens string->list-lens]
(make-isomorphism-lenses list->string string->list))
(define-values [alternating->assoc-list-lens assoc->alternating-list-lens]
(make-isomorphism-lenses alternating->assoc-list assoc->alternating-list))
(module+ test
(test-case "string-symbol"
(check-equal? (lens-view string->symbol-lens "a") 'a)
(check-equal? (lens-set string->symbol-lens "a" 'b) "b")
(check-equal? (lens-view symbol->string-lens 'a) "a")
(check-equal? (lens-set symbol->string-lens 'a "b") 'b))
(test-case "number-string"
(check-equal? (lens-view number->string-lens 5) "5")
(check-equal? (lens-set number->string-lens 5 "6") 6)
(check-equal? (lens-view string->number-lens "5") 5)
(check-equal? (lens-set string->number-lens "5" 6) "6"))
(test-case "inverses"
(check-equal? (isomorphism-lens-inverse string->symbol-lens) symbol->string-lens)
(check-equal? (isomorphism-lens-inverse symbol->string-lens) string->symbol-lens)
(check-equal? (isomorphism-lens-inverse number->string-lens) string->number-lens)
(check-equal? (isomorphism-lens-inverse string->number-lens) number->string-lens)))

View File

@ -1,4 +0,0 @@
#lang reprovide
lens/private/isomorphism/base
lens/private/isomorphism/compound
"data.rkt"

View File

@ -1,76 +0,0 @@
#lang racket/base
(provide append*-lens append*n-lens)
(require "flatten.rkt")
(module+ test
(require rackunit lens/common lens/private/test-util/test-lens))
(define (append*n-lens n)
(flatten/depth-lens (add1 n)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(module+ test
(test-case "append*n-lens"
(define append**-lens (append*n-lens 2))
(define append***-lens (append*n-lens 3))
(check-equal? (lens-view append**-lens
(list (list (list) (list 1))
(list (list 2 3))
(list)
(list (list 4) (list) (list 5 6))))
(list 1 2 3 4 5 6))
(check-equal? (lens-set append**-lens
(list (list (list) (list 1))
(list (list 2 3))
(list)
(list (list 4) (list) (list 5 6)))
(list 'a 'b 'c 'd 'e 'f))
(list (list (list) (list 'a))
(list (list 'b 'c))
(list)
(list (list 'd) (list) (list 'e 'f))))
(test-lens-laws append**-lens
(list (list (list) (list 1))
(list (list 2 3))
(list)
(list (list 4) (list) (list 5 6)))
(list 'a 'b 'c 'd 'e 'f)
(list "a" "b" "c" "d" "e" "f"))
(check-equal? (lens-view append***-lens
(list (list (list) (list (list 1)))
(list (list (list) (list 2 3)))
(list)
(list (list (list 4) (list)) (list) (list (list 5 6)))))
(list 1 2 3 4 5 6))
(check-equal? (lens-set append***-lens
(list (list (list) (list (list 1)))
(list (list (list) (list 2 3)))
(list)
(list (list (list 4) (list)) (list) (list (list 5 6))))
(list 'a 'b 'c 'd 'e 'f))
(list (list (list) (list (list 'a)))
(list (list (list) (list 'b 'c)))
(list)
(list (list (list 'd) (list)) (list) (list (list 'e 'f)))))
(test-lens-laws append**-lens
(list (list (list) (list 1))
(list (list 2 3))
(list)
(list (list 4) (list) (list 5 6)))
(list 'a 'b 'c 'd 'e 'f)
(list "a" "b" "c" "d" "e" "f"))
(test-lens-laws append***-lens
(list (list (list) (list (list 1)))
(list (list (list) (list 2 3)))
(list)
(list (list (list 4) (list)) (list) (list (list 5 6))))
(list 'a 'b 'c 'd 'e 'f)
(list "a" "b" "c" "d" "e" "f"))
))

View File

@ -1,217 +0,0 @@
#lang racket/base
(provide append*-lens flatten/depth-lens flatten/depth unflatten/depth)
(require fancy-app lens/common racket/list racket/match)
(module+ test
(require rackunit syntax/parse lens/private/test-util/test-lens))
;; (define-type (Listof* A n)
;; (cond [(zero? n) A]
;; [else (Listof* (Listof A) (sub1 n))]))
;; flatten/depth-lens : (Lens (Listof* Any n) (Listof Any))
;; where the only valid views are lists with the same length as the
;; result of (flatten/depth n target)
(define (flatten/depth-lens n)
(make-lens
(flatten/depth n _)
(unflatten/depth n _ _)))
;; append*-lens : (Lens (Listof (Listof Any)) (Listof Any))
;; where the only valid views are lists with the same length as the
;; result of applying append* to the target.
;; Viewing is equivalent to using append*
;; Setting restores the structure of the original nested list
(define append*-lens
(flatten/depth-lens 2))
;; flatten/depth : n (Listof* A n) -> (Listof A)
(define (flatten/depth n structure)
(check-structure-depth! n structure)
(cond [(zero? n) (list structure)]
[else (append*n (sub1 n) structure)]))
;; unflatten/depth : n (Listof* A n) (Listof B) -> (Listof* B n)
(define (unflatten/depth n structure flattened)
(check-structure-depth! n structure)
(check-flattened-length! n structure flattened)
(cond [(zero? n) (first flattened)]
[else (unappend*n (sub1 n) structure flattened)]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; restore-structure : (Listof (Listof A)) (Listof B) -> (Listof (Listof B))
;; Takes a list of lists and a list and un-flattens the flattened
;; argument according to the structure of the structure arguement.
;; The length of the flattened list must be the same as the length
;; of (append* structure).
(define (restore-structure structure flattened)
(restore-structure/acc structure flattened (list)))
;; restore-structure/acc : (Listof (Listof A)) (Listof B) (Listof (Listof B)) -> (Listof (Listof B))
;; Accumulates a reversed version of the result of restore-structure,
;; then returns an un-reversed version.
(define (restore-structure/acc structure flattened acc)
(match structure
[(list)
(reverse acc)]
[(cons s-lst s-rst)
(define-values [f-lst f-rst]
(split-at flattened (length s-lst)))
(restore-structure/acc s-rst f-rst (cons f-lst acc))]))
;; append*n : n (Listof (Listof* A n)) -> (Listof A)
(define (append*n n structure)
(cond [(zero? n) structure]
[else (append*n (sub1 n) (append* structure))]))
;; unappend*n : n (Listof (Listof* A n)) (Listof B) -> (Listof (Listof* B n))
(define (unappend*n n structure flattened)
(cond [(zero? n) flattened]
[else (restore-structure
structure
(unappend*n (sub1 n) (append* structure) flattened))]))
;; list/depth? : Natural Any -> Boolean
(define (list/depth? n structure)
(cond [(zero? n) #true]
[else (and (list? structure)
(andmap (list/depth? (sub1 n) _) structure))]))
;; check-structure-depth! : n (Listof* A n) -> Void
(define (check-structure-depth! depth structure)
(unless (list/depth? depth structure)
(raise-argument-error 'flatten/depth-lens
(format "a nested list of depth ~v" depth)
structure)))
;; check-flattened-length! : n (Listof* A n) (Listof B) -> Void
(define (check-flattened-length! depth structure flattened)
(unless (= (length (flatten/depth depth structure)) (length flattened))
(raise-argument-error 'flatten/depth-lens
(format "a list of length ~v"
(length (flatten/depth depth structure)))
1
structure
flattened)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(module+ test
(test-case "append*-lens"
(check-equal? (lens-view append*-lens (list (list 1) (list 2 3) (list)))
(list 1 2 3))
(check-equal? (lens-set append*-lens
(list (list 1) (list 2 3) (list))
(list 'a 'b 'c))
(list (list 'a) (list 'b 'c) (list)))
(check-equal? (lens-transform append*-lens
(list (list 1) (list 2 3) (list))
reverse) ; any length-preserving computation
(list (list 3) (list 2 1) (list)))
(check-exn #rx"expected: a nested list of depth 2\n given: '\\(5\\)"
(λ () (lens-view append*-lens (list 5))))
(check-exn #rx"expected: a nested list of depth 2\n given: '\\(5\\)"
(λ () (lens-set append*-lens (list 5) (list 'a))))
(check-exn #rx"expected: a list of length 3\n given: '\\(a b\\)"
(λ () (lens-set append*-lens (list (list 1) (list 2 3) (list)) (list 'a 'b))))
(test-lens-laws append*-lens
(list (list 1) (list 2 3) (list))
(list 'a 'b 'c)
(list "a" "b" "c"))
)
(test-case "(flatten/depth-lens 0) adds a list layer"
(define flat0-lens (flatten/depth-lens 0))
(check-equal? (lens-view flat0-lens 42) (list 42))
(check-equal? (lens-set flat0-lens 42 (list 'a)) 'a)
(check-equal? (lens-transform flat0-lens 42 reverse) 42)
(test-lens-laws flat0-lens
42
(list 'a)
(list "a")))
(test-case "(flatten/depth-lens 1) copies the list"
(define flat1-lens (flatten/depth-lens 1))
(check-equal? (lens-view flat1-lens (list 1 2 3)) (list 1 2 3))
(check-equal? (lens-set flat1-lens (list 1 2 3) (list 'a 'b 'c)) (list 'a 'b 'c))
(check-equal? (lens-transform flat1-lens (list 1 2 3) reverse) (list 3 2 1))
(test-lens-laws flat1-lens
(list 1 2 3)
(list 'a 'b 'c)
(list "a" "b" "c")))
(test-case "(flatten/depth-lens 2) should be equivalent to append*-lens"
(define flat2-lens (flatten/depth-lens 2))
(check-equal? (lens-view flat2-lens
(list (list 1) (list 2 3) (list)))
(list 1 2 3))
(check-equal? (lens-set flat2-lens
(list (list 1) (list 2 3) (list))
(list 'a 'b 'c))
(list (list 'a) (list 'b 'c) (list)))
(check-equal? (lens-transform flat2-lens
(list (list 1) (list 2 3) (list))
reverse)
(list (list 3) (list 2 1) (list)))
(test-lens-laws flat2-lens
(list (list 1) (list 2 3) (list))
(list 'a 'b 'c)
(list "a" "b" "c")))
(test-case "(flatten/depth-lens 3) deals with lists of depth 3"
(define flat3-lens (flatten/depth-lens 3))
(check-equal? (lens-view flat3-lens
(list (list (list) (list 1))
(list (list 2 3))
(list)
(list (list 4) (list) (list 5 6))))
(list 1 2 3 4 5 6))
(check-equal? (lens-set flat3-lens
(list (list (list) (list 1))
(list (list 2 3))
(list)
(list (list 4) (list) (list 5 6)))
(list 'a 'b 'c 'd 'e 'f))
(list (list (list) (list 'a))
(list (list 'b 'c))
(list)
(list (list 'd) (list) (list 'e 'f))))
(check-equal? (lens-transform flat3-lens
(list (list (list) (list 1))
(list (list 2 3))
(list)
(list (list 4) (list) (list 5 6)))
reverse)
(list (list (list) (list 6))
(list (list 5 4))
(list)
(list (list 3) (list) (list 2 1))))
(check-exn #rx"expected: a nested list of depth 3\n *given: '\\(5\\)"
(λ () (lens-view flat3-lens (list 5))))
(check-exn #rx"expected: a nested list of depth 3\n given: '\\(5\\)"
(λ () (lens-set flat3-lens (list 5) (list 'a))))
(check-exn #rx"expected: a list of length 6\n given: '\\(a b\\)"
(λ () (lens-set flat3-lens
(list (list (list) (list 1))
(list (list 2 3))
(list)
(list (list 4) (list) (list 5 6)))
(list 'a 'b))))
(test-lens-laws flat3-lens
(list (list (list) (list 1))
(list (list 2 3))
(list)
(list (list 4) (list) (list 5 6)))
(list 'a 'b 'c 'd 'e 'f)
(list "a" "b" "c" "d" "e" "f")))
)

View File

@ -1,35 +0,0 @@
#lang sweet-exp racket/base
require racket/contract/base
provide
contract-out
lens-join/assoc (->* [] #:rest (listof2 any/c lens?) (lens/c any/c (listof pair?)))
require lens/private/base/main
lens/private/list/join-list
lens/private/list/assoc
lens/private/util/alternating-list
lens/private/util/list-pair-contract
racket/match
unstable/sequence
module+ test
require rackunit lens/private/list/list-ref-take-drop
(define (lens-join/assoc . ks/lenses)
(define-values [keys lenses]
(alternating-list->keys+values ks/lenses))
(define key-lenses (map assoc-lens keys))
(define list-lens (apply lens-join/list lenses))
(make-lens
(λ (tgt)
(keys+values->assoc-list keys (lens-view list-lens tgt)))
(λ (tgt nvw)
(lens-set list-lens tgt (apply lens-view/list nvw key-lenses)))))
module+ test
(define a-b-lens (lens-join/assoc 'a first-lens
'b third-lens))
(check-equal? (lens-view a-b-lens '(1 2 3))
'((a . 1) (b . 3)))
(check-equal? (lens-set a-b-lens '(1 2 3) '((a . 100) (b . 200)))
'(100 2 200))

View File

@ -1,35 +0,0 @@
#lang sweet-exp racket/base
require racket/list
racket/contract
lens/private/base/main
lens/private/util/alternating-list
lens/private/util/rest-contract
module+ test
require rackunit
"../list/list-ref-take-drop.rkt"
lens/private/test-util/test-lens
provide
contract-out
lens-join/list (rest-> lens? (lens/c any/c list?))
(define (lens-join/list . lenses)
(define (get target)
(apply lens-view/list target lenses))
(define (set target new-views)
(apply lens-set/list target (keys+values->alternating-list lenses new-views)))
(make-lens get set))
(module+ test
(define first-third-fifth-lens
(lens-join/list first-lens
third-lens
fifth-lens))
(check-lens-view first-third-fifth-lens '(a b c d e f)
'(a c e))
(check-lens-set first-third-fifth-lens '(a b c d e f) '(1 2 3)
'(1 b 2 d 3 f)))

View File

@ -1,7 +0,0 @@
#lang reprovide
"car-cdr.rkt"
(except-in "list-ref-take-drop.rkt" drop-lens take-lens)
"cadr-etc.rkt"
"multi.rkt"
"join-list.rkt"
"assoc.rkt"

View File

@ -1,55 +0,0 @@
#lang racket/base
(require racket/contract/base)
(provide (contract-out
[map-lens
(-> lens? (lens/c list? list?))]
[vector-map-lens
(-> lens? (lens/c immutable-vector? immutable-vector?))]
))
(require lens/private/base/main
lens/private/util/immutable
racket/vector
fancy-app
)
(module+ test
(require rackunit lens/private/list/main))
(define (map-lens lens)
(make-lens
(lens-view/map lens _)
(lens-set/map lens _ _)))
(define (lens-view/map lens tgts)
(map (lens-view lens _) tgts))
(define (lens-set/map lens tgts new-views)
(map (lens-set lens _ _) tgts new-views))
(define (vector-map-lens lens)
(make-lens
(lens-view/vector-map lens _)
(lens-set/vector-map lens _ _)))
(define (lens-view/vector-map lens tgt)
(vector->immutable-vector (vector-map (lens-view lens _) tgt)))
(define (lens-set/vector-map lens tgt new-view)
(vector->immutable-vector (vector-map (lens-set lens _ _) tgt new-view)))
(module+ test
(check-equal? (lens-view (map-lens first-lens) '((a b) (c d) (e f)))
'(a c e))
(check-equal? (lens-set (map-lens first-lens) '((a b) (c d) (e f)) '(1 2 3))
'((1 b) (2 d) (3 f)))
(check-equal? (lens-transform (map-lens first-lens) '((a b) (c d) (e f)) (map symbol->string _))
'(("a" b) ("c" d) ("e" f)))
(check-equal? (lens-view (vector-map-lens first-lens) '#((a b) (c d) (e f)))
'#(a c e))
(check-equal? (lens-set (vector-map-lens first-lens) '#((a b) (c d) (e f)) '#(1 2 3))
'#((1 b) (2 d) (3 f)))
(check-equal? (lens-transform (vector-map-lens first-lens) '#((a b) (c d) (e f))
(immutable-vector-map symbol->string _))
'#(("a" b) ("c" d) ("e" f)))
)

View File

@ -1,32 +0,0 @@
#lang sweet-exp racket/base
require racket/contract/base
provide
contract-out
reverse-lens (lens/c list? list?)
last-lens (lens/c list? any/c)
require lens/private/base/main
lens/private/list/main
lens/private/compound/main
lens/private/isomorphism/base
module+ test
require rackunit fancy-app
(define reverse-lens
(make-isomorphism-lens reverse reverse))
module+ test
(check-equal? (lens-view reverse-lens '(1 2 3)) '(3 2 1))
(check-equal? (lens-transform reverse-lens '(1 2 3) (cons 4 _)) '(1 2 3 4))
(define last-lens
(lens-thrush reverse-lens first-lens))
module+ test
(check-equal? (lens-view last-lens '(1 2 3)) 3)
(check-equal? (lens-set last-lens '(1 2 3) 'a) '(1 2 a))

View File

@ -1,34 +0,0 @@
#lang racket/base
(provide match-lens)
(require racket/match
racket/local
syntax/parse/define
lens/private/base/main
)
(module+ test
(require rackunit lens/private/test-util/test-lens))
(define-simple-macro (match-lens a:id pat:expr replacement:expr)
(local [(define (get target)
(match target
[pat
a]))
(define (set target new-view)
(match target
[pat
(let ([a new-view])
replacement)]))]
(make-lens get set)))
(module+ test
(define car-lens (match-lens a (cons a b) (cons a b)))
(define cdr-lens (match-lens b (cons a b) (cons a b)))
(check-lens-view car-lens (cons 1 2) 1)
(check-lens-view cdr-lens (cons 1 2) 2)
(check-lens-set car-lens (cons 1 2) 'a (cons 'a 2))
(check-lens-set cdr-lens (cons 1 2) 'a (cons 1 'a))
(test-lens-laws car-lens (cons 1 2) 'a 'b)
(test-lens-laws cdr-lens (cons 1 2) 'a 'b)
)

View File

@ -1,54 +0,0 @@
#lang sweet-exp racket/base
require racket/contract/base
provide
contract-out
set-filterer-lens (-> predicate/c (lens/c functional-set? functional-set?))
require lens/private/base/main
lens/private/util/functional-set
racket/set
racket/function
fancy-app
module+ test
require rackunit
(define (set-filter pred set)
(for/fold ([set set]) ([elem (in-set set)] #:unless (pred elem))
(set-remove set elem)))
(define (set-filter-not pred set)
(for/fold ([set set]) ([elem (in-set set)] #:when (pred elem))
(set-remove set elem)))
(define (andmap-set pred set)
(andmap pred (set->list set)))
(define (check-set-filterer-lens-view pred new-view-to-check)
(unless (andmap-set pred new-view-to-check)
(raise-argument-error 'set-filterer-lens
(format "(set/c ~a)" (contract-name pred))
new-view-to-check)))
(define (set-filterer-lens pred)
(define (insert-filtered-items target new-view)
(check-set-filterer-lens-view pred new-view)
(set-union (set-filter-not pred target) new-view))
(make-lens (set-filter pred _)
insert-filtered-items))
module+ test
(check-equal? (lens-view (set-filterer-lens number?) '(1 a 2 b c 3 d e))
'(1 2 3))
(check-equal? (lens-set (set-filterer-lens number?) '(1 a 2 b c 3 d e) '(4 5 6 7))
'(7 6 5 4 a b c d e))
(check-equal? (lens-view (set-filterer-lens number?) (set 1 'a 2 'b 'c 3 'd 'e))
(set 1 2 3))
(check-equal? (lens-set (set-filterer-lens number?) (set 1 'a 2 'b 'c 3 'd 'e) (set 4 5 6 7))
(set 4 5 6 7 'a 'b 'c 'd 'e))
(check-exn exn:fail:contract?
(thunk (lens-set (set-filterer-lens number?) (set 1) (set 'a))))

View File

@ -1,30 +0,0 @@
#lang sweet-exp racket/base
require racket/contract/base
provide
contract-out
set-member-lens (-> any/c (lens/c functional-set? boolean?))
require fancy-app
lens/private/base/main
lens/private/util/functional-set
racket/set
module+ test
require rackunit
(define (set-member-lens v)
(make-lens
(set-member? _ v)
(λ (tgt nvw)
(if nvw
(set-add tgt v)
(set-remove tgt v)))))
module+ test
(define 2-lens (set-member-lens 2))
(check-equal? (lens-view 2-lens (set 1 2 3)) #t)
(check-equal? (lens-view 2-lens (set 1 3)) #f)
(check-equal? (lens-set 2-lens (set 1 2 3) #t) (set 1 2 3))
(check-equal? (lens-set 2-lens (set 1 2 3) #f) (set 1 3))
(check-equal? (lens-set 2-lens (set 1 3) #t) (set 1 2 3))
(check-equal? (lens-set 2-lens (set 1 3) #f) (set 1 3))

View File

@ -1,77 +0,0 @@
#lang sweet-exp racket/base
require racket/contract/base
provide
contract-out
stream-first-lens (lens/c stream? any/c)
stream-rest-lens (lens/c stream? stream?)
stream-ref-lens (-> exact-nonnegative-integer? (lens/c stream? any/c))
require racket/stream
fancy-app
lens/private/base/main
lens/private/compound/main
module+ test
require rackunit
lens/private/test-util/test-lens
module+ test
(define-check (check-stream-equal? stream1 stream2)
(let ([list1 (stream->list stream1)] [list2 (stream->list stream2)])
(with-check-info
(['actual-list list1] ['expected-list list2])
(check-equal? list1 list2))))
(define (stream-ref-lens i)
(lens-compose stream-first-lens (stream-tail-lens i)))
(define (stream-set-first s v)
(stream-cons v (stream-rest s)))
(define (stream-set-rest s rst)
(stream-cons (stream-first s) rst))
(define stream-first-lens
(make-lens
stream-first
stream-set-first))
(define stream-rest-lens
(make-lens
stream-rest
stream-set-rest))
(define (stream-tail-lens i)
(make-lens
(stream-tail _ i)
(stream-set-tail _ i _)))
(define (stream-set-tail s i rst)
(define rev-fst
(for/fold ([rev-fst '()]) ([v (in-stream s)] [_ (in-range i)])
(cons v rev-fst)))
(for/fold ([rst rst]) ([v (in-list rev-fst)])
(stream-cons v rst)))
module+ test
(check-lens-view stream-first-lens (stream 'a 'b 'c) 'a)
(check-lens-view (stream-ref-lens 2) (stream 'a 'b 'c) 'c)
(check-stream-equal? (lens-set stream-first-lens (stream 'a 'b 'c) 1)
(stream 1 'b 'c))
(check-stream-equal? (lens-set (stream-ref-lens 2) (stream 'a 'b 'c) 1)
(stream 'a 'b 1))
(define (stream-ref-nested-lens . is)
(apply lens-thrush (map stream-ref-lens is)))
module+ test
(check-lens-view (stream-ref-nested-lens 1 2 0)
(stream 'a (stream 1 2 (stream 'foo 'bar 'baz) 3 4) 'b 'c 'd)
'foo)
(check-lens-set-view (stream-ref-nested-lens 1 2 0)
(stream 'a (stream 1 2 (stream 'foo 'bar 'baz) 3 4) 'b 'c 'd)
'FOO)

View File

@ -1,36 +0,0 @@
#lang sweet-exp racket/base
require racket/contract
lens/private/base/main
lens/private/isomorphism/base
lens/private/compound/compose
lens/private/util/rest-contract
"../util/immutable.rkt"
"../list/join-list.rkt"
module+ test
require rackunit
lens/private/test-util/test-lens
"../list/list-ref-take-drop.rkt"
provide
contract-out
lens-join/string (rest-> (lens/c any/c char?) (lens/c any/c immutable-string?))
(define (lens-join/string . lenses)
(lens-compose list->string-lens (apply lens-join/list lenses)))
(define list->string-lens
(make-isomorphism-lens list->immutable-string string->list))
(module+ test
(define string-first-third-fifth-lens
(lens-join/string first-lens
third-lens
fifth-lens))
(check-lens-view string-first-third-fifth-lens '(#\a #\b #\c #\d #\e #\f)
"ace")
(check-pred immutable? (lens-view string-first-third-fifth-lens '(#\a #\b #\c #\d #\e #\f)))
(check-lens-set string-first-third-fifth-lens '(#\a #\b #\c #\d #\e #\f) "ACE"
'(#\A #\b #\C #\d #\E #\f)))

View File

@ -1,3 +0,0 @@
#lang reprovide
"string.rkt"
"join-string.rkt"

View File

@ -1,72 +0,0 @@
#lang racket/base
(require racket/contract/base)
(provide (contract-out
[string-split-lens
(-> (or/c immutable-string? char? regexp?)
(lens/c immutable-string? (listof immutable-string?)))]
))
(require racket/match
racket/string
lens/private/base/main
lens/private/util/immutable
)
(module+ test
(require rackunit))
(define (string-split-lens sep)
(define sep-rx
(cond
[(string? sep) (regexp (regexp-quote sep))]
[(char? sep) (regexp (regexp-quote (string sep)))]
[(regexp? sep) sep]
[else (error 'string-split-lens "expected a string, char, or regexp, given: ~v" sep)]))
(define (get str)
(map string->immutable-string (regexp-split sep-rx str)))
(define (set str lst)
(for ([s (in-list lst)])
(when (regexp-match? sep-rx s) ; this would violate the lens laws
(error 'string-split-lens "expected a string not matching ~v, given: ~v" sep s)))
(define seps (regexp-match* sep-rx str))
(match-define (cons fst rst) lst)
(string->immutable-string (string-append* fst (map string-append seps rst))))
(make-lens get set))
(module+ test
(define ws-lens (string-split-lens #px"\\s+"))
(check-equal? (lens-view ws-lens "a b c") '("a" "b" "c"))
(check-equal? (lens-set ws-lens "a b c" '("d" "e" "f")) "d e f")
(check-equal? (lens-view ws-lens " foo bar baz \r\n\t")
'("" "foo" "bar" "baz" ""))
(check-equal? (lens-set ws-lens " foo bar baz \r\n\t" '("a" "b" "c" "d" "e"))
"a b c d \r\n\te")
(check-equal? (lens-view ws-lens "a b c d \r\n\te")
'("a" "b" "c" "d" "e"))
(check-equal? (lens-set ws-lens "a b c d \r\n\te" '("" "foo" "bar" "baz" ""))
" foo bar baz \r\n\t")
;; this input would violate the lens laws
(check-exn (regexp (regexp-quote "expected a string not matching #px\"\\\\s+\", given: \"e f\""))
(λ ()
(lens-set ws-lens "a b c" '("d" "e f" "g"))))
(define newline-lens (string-split-lens "\n"))
(check-equal? (lens-view newline-lens "a,b\nc,d\ne,f,g")
'("a,b" "c,d" "e,f,g"))
(check-equal? (lens-set newline-lens "a,b\nc,d\ne,f,g" '("1" "2" "3"))
"1\n2\n3")
;; this input would violate the lens laws
(check-exn (regexp (regexp-quote "expected a string not matching \"\\n\", given: \"2\\n2.5\""))
(λ ()
(lens-set newline-lens "a,b\nc,d\ne,f,g" '("1" "2\n2.5" "3"))))
(define comma-lens (string-split-lens #\,))
(check-equal? (lens-view comma-lens "a,b,c")
'("a" "b" "c"))
(check-equal? (lens-set comma-lens "a,b,c" '("1" "2" "3"))
"1,2,3")
;; this input would violate the lens laws
(check-exn (regexp (regexp-quote "expected a string not matching #\\,, given: \"2,2.5\""))
(λ ()
(lens-set comma-lens "a,b,c" '("1" "2,2.5" "3"))))
)

View File

@ -1,65 +0,0 @@
#lang racket/base
(require racket/function racket/contract/base unstable/contract)
(provide
(contract-out
[substring-lens (->i ([start exact-nonnegative-integer?]
[end (start) (and/c exact-nonnegative-integer?
(>=/c start))])
[result (start end)
(lens/c (string-length->=/c end)
(string-length-=/c (- end start)))])]))
(define (string-length->=/c min)
(define (length>=? str)
(>= (string-length str) min))
(and/c string?
(rename-contract length>=?
`(string-length->=/c ,min))))
(define (string-length-=/c n)
(define (length=? str)
(= (string-length str) n))
(and/c string?
(rename-contract length=?
`(string-length-=/c ,n))))
(require lens/common)
(module+ test
(require rackunit))
(define (set-substring str start end replacement-str)
(string-append (substring str 0 start)
replacement-str
(substring str end)))
(module+ test
(check-equal? (set-substring "mitten" 0 4 "MITT") "MITTen")
(check-equal? (set-substring "mitten" 2 4 "ZZ") "miZZen")
(check-equal? (set-substring "mitten" 2 6 "LLER") "miLLER"))
(define (substring-lens start end)
(define (substring-lens-getter str)
(substring str start end))
(define (substring-lens-setter str replacement-str)
(set-substring str start end replacement-str))
(make-lens substring-lens-getter substring-lens-setter))
(module+ test
(check-pred lens? (substring-lens 2 4))
(check-equal? (lens-view (substring-lens 2 4) "mitten") "tt")
(check-equal? (lens-set (substring-lens 2 4) "mitten" "TT") "miTTen"))
(module+ test
(require (submod ".."))
(check-exn exn:fail:contract?
(thunk (substring-lens -1 5))) ; Improper substring boundaries
(check-exn exn:fail:contract?
(thunk (lens-set (substring-lens 2 4) "kitten" "c"))) ; Replacement string is too short
(check-exn exn:fail:contract?
(thunk (lens-set (substring-lens 2 4) "kitten" "cat"))) ; Replacement string is too long
(check-not-exn
(thunk (lens-set (substring-lens 2 4) "kitten" "ca"))) ; Replacement string is just right!
)

View File

@ -1,5 +0,0 @@
#lang sweet-exp reprovide
"field.rkt"
except-in "struct.rkt"
struct-lenses-out
struct+lenses-out

View File

@ -1,81 +0,0 @@
#lang sweet-exp racket/base
provide lens-join/struct
require racket/local
racket/match
lens/private/base/main
kw-make-struct
for-syntax racket/base
syntax/parse
module+ test
require rackunit lens/private/hash/main lens/private/test-util/test-multi
(begin-for-syntax
(define-splicing-syntax-class field-lenses
#:attributes ([lens-expr 1] [lens-id 1] [vw-id 1] [norm 1])
[pattern (~seq lens-expr:expr ...)
#:with [lens-id ...] (generate-temporaries #'[lens-expr ...])
#:with [vw-id ...] (generate-temporaries #'[lens-expr ...])
#:with [norm ...] #'[vw-id ...]]
[pattern (~seq fst-lens:expr ...+ rst:field-lenses)
#:with [fst-lens-id ...] (generate-temporaries #'[fst-lens ...])
#:with [fst-vw-id ...] (generate-temporaries #'[fst-lens ...])
#:with [lens-expr ...] #'[fst-lens ... rst.lens-expr ...]
#:with [lens-id ...] #'[fst-lens-id ... rst.lens-id ...]
#:with [vw-id ...] #'[fst-vw-id ... rst.vw-id ...]
#:with [norm ...] #'[fst-vw-id ... rst.norm ...]]
[pattern (~seq (~seq kw:keyword fst-lens:expr) ...+ rst:field-lenses)
#:with [fst-lens-id ...] (generate-temporaries #'[fst-lens ...])
#:with [fst-vw-id ...] (generate-temporaries #'[fst-lens ...])
#:with [lens-expr ...] #'[fst-lens ... rst.lens-expr ...]
#:with [lens-id ...] #'[fst-lens-id ... rst.lens-id ...]
#:with [vw-id ...] #'[fst-vw-id ... rst.vw-id ...]
#:with [[fst-kw/vw-id ...] ...] #'[[kw fst-vw-id] ...]
#:with [norm ...] #'[fst-kw/vw-id ... ... rst.norm ...]]
))
(define-syntax lens-join/struct
(lambda (stx)
(syntax-parse stx
[(lens-join/struct s:id flds:field-lenses)
#:with make/kw-form #`(make/kw/derived #,stx s flds.norm ...)
#:with [[lens-id/vw-id ...] ...] #'[[flds.lens-id flds.vw-id] ...]
#`(local [(define flds.lens-id flds.lens-expr) ...]
(make-lens
(λ (tgt)
(define flds.vw-id (lens-view flds.lens-id tgt))
...
make/kw-form)
(λ (tgt nvw)
(match-define make/kw-form nvw)
(lens-set/list tgt lens-id/vw-id ... ...))))])))
(module+ test
(struct foo (a b c) #:transparent)
(define foo-hash-lens1
(lens-join/struct foo
(hash-ref-lens 'a)
(hash-ref-lens 'b)
(hash-ref-lens 'c)))
(define foo-hash-lens2
(lens-join/struct foo
#:a (hash-ref-lens 'a)
#:b (hash-ref-lens 'b)
#:c (hash-ref-lens 'c)))
(define foo-hash-lens3
(lens-join/struct foo
#:c (hash-ref-lens 'c)
#:a (hash-ref-lens 'a)
#:b (hash-ref-lens 'b)))
(define foo-hash-lens4
(lens-join/struct foo
(hash-ref-lens 'a)
#:c (hash-ref-lens 'c)
#:b (hash-ref-lens 'b)))
(test-multi* ([foo-hash-lens #:in [foo-hash-lens1 foo-hash-lens2 foo-hash-lens3 foo-hash-lens4]])
(check-equal? (lens-view foo-hash-lens (hash 'a 1 'b 2 'c 3))
(foo 1 2 3))
(check-equal? (lens-set foo-hash-lens (hash 'a 1 'b 2 'c 3) (foo 10 20 30))
(hash 'a 10 'b 20 'c 30))
))

View File

@ -1,59 +0,0 @@
#lang sweet-exp racket/base
provide struct->list-lens list->struct-lens
require racket/local
lens/private/isomorphism/base
for-syntax racket/base
racket/list
racket/struct-info
syntax/parse
module+ test
require lens/private/base/base
lens/private/test-util/test-lens
rackunit
begin-for-syntax
(define-syntax-class struct-id
#:attributes (info constructor-id [accessor-id 1])
[pattern struct-id:id
#:attr v (syntax-local-value #'struct-id (λ () #f))
#:when (struct-info? (attribute v))
#:attr info (extract-struct-info (attribute v))
#:with descriptor-id:id (first (attribute info))
#:with constructor-id:id (syntax-property (second (attribute info))
'disappeared-use
(list (syntax-local-introduce #'struct-id)))
#:with predicate-id:id (third (attribute info))
#:with [accessor-id:id ...] (reverse (fourth (attribute info)))])
(define-syntax struct->list-lens
(syntax-parser
[(struct->list-lens s:struct-id)
#'(local [(define (struct->list struct)
(list (s.accessor-id struct) ...))
(define (list->struct list)
(apply s.constructor-id list))]
(make-isomorphism-lens struct->list list->struct))]))
(define-syntax list->struct-lens
(syntax-parser
[(list->struct-lens s:struct-id)
#'(isomorphism-lens-inverse (struct->list-lens s))]))
module+ test
(struct foo (a b c))
;; foo is opaque, so struct->vector doesn't work
(check-equal? (struct->vector (foo 1 2 3)) '#(struct:foo ...))
(test-case "without inheritance"
(check-equal? (lens-view (struct->list-lens foo) (foo 1 2 3)) '(1 2 3))
(check-match (lens-set (struct->list-lens foo) (foo 1 2 3) '(4 5 6)) (foo 4 5 6))
(check-match (lens-view (list->struct-lens foo) '(1 2 3)) (foo 1 2 3))
(check-equal? (lens-set (list->struct-lens foo) '(1 2 3) (foo 4 5 6)) '(4 5 6)))
(struct bar foo (d e))
(test-case "inheriting from foo"
(check-equal? (lens-view (struct->list-lens bar) (bar 1 2 3 4 5)) '(1 2 3 4 5))
(check-match (lens-set (struct->list-lens bar) (bar 1 2 3 4 5) '(6 7 8 9 10)) (bar 6 7 8 9 10))
(check-match (lens-view (list->struct-lens bar) '(1 2 3 4 5)) (bar 1 2 3 4 5))
(check-equal? (lens-set (list->struct-lens bar) '(1 2 3 4 4) (bar 6 7 8 9 10)) '(6 7 8 9 10)))

View File

@ -1,50 +0,0 @@
#lang racket/base
(require fancy-app
lens/common
lens/private/struct/main
(for-syntax racket/base
syntax/parse))
(module+ test
(require rackunit))
(provide struct-nested-lens
struct-nested-lens*)
(define-syntax struct-nested-lens
(syntax-parser
[(_ [struct-id:id field-id:id] ...)
#'(lens-thrush (struct-lens struct-id field-id) ...)]))
(define-syntax struct-nested-lens*
(syntax-parser
[(_ struct-id:id field-id:id)
#'(struct-lens struct-id field-id)]
[(_ struct-id:id both0:id both:id ... field-id:id)
#'(lens-thrush (struct-lens struct-id both0)
(struct-nested-lens* both0 both ... field-id))]))
(module+ test
(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))
(define game-player-health-lens
(struct-nested-lens [game player]
[player stats]
[combat-stats health]))
(check-equal? (lens-view game-player-health-lens the-game) 10)
(check-equal? (lens-set game-player-health-lens the-game 20)
(game (player (posn 0 0) (combat-stats 20 1)) 'foo-level))
(define game-player-posn-x-lens
(struct-nested-lens* game player posn x))
(check-equal? (lens-view game-player-posn-x-lens the-game) 0)
(check-equal? (lens-set game-player-posn-x-lens the-game 3)
(game (player (posn 3 0) (combat-stats 10 1)) 'foo-level)))

View File

@ -1,73 +0,0 @@
#lang racket/base
(require syntax/parse/define
struct-update
racket/provide-syntax
lens/private/base/main
(for-syntax racket/base
syntax/parse
syntax/parse/class/struct-id
racket/syntax
racket/struct-info))
(module+ test
(require rackunit
fancy-app
lens/private/test-util/test-lens))
(provide define-struct-lenses
struct/lens
struct-lenses-out
struct+lenses-out)
(define-for-syntax (get-struct-own-accessor-ids struct-id-stx)
(syntax-parse struct-id-stx
[s:struct-id
(attribute s.own-accessor-id)]))
(define-for-syntax (map-format-id lex-context format-str ids)
(define (format-one-id id)
(format-id lex-context format-str id #:source id))
(map format-one-id ids))
(define-for-syntax (struct-get-set-lens-ids struct-id-stx)
(define accessor-ids (get-struct-own-accessor-ids struct-id-stx))
(define set-ids (map-format-id struct-id-stx "~a-set" accessor-ids))
(define lens-ids (map-format-id struct-id-stx "~a-lens" accessor-ids))
(list accessor-ids set-ids lens-ids))
(define-syntax define-struct-lenses
(syntax-parser
[(define-struct-lenses s:id)
#:with [(s-fld ...)
(s-fld-set ...)
(s-fld-lens ...)] (struct-get-set-lens-ids #'s)
#'(begin
(define-struct-updaters s)
(define s-fld-lens (make-lens s-fld s-fld-set))
...)]))
(define-simple-macro (struct/lens s:id (field-spec ...) option ...)
(begin
(struct s (field-spec ...) option ...)
(define-struct-lenses s)))
(define-provide-syntax struct-lenses-out
(syntax-parser
[(struct-lenses-out struct-type:id)
#:do [(define accessor-ids (get-struct-own-accessor-ids #'struct-type))]
#:with [lens-id ...] (map-format-id #'struct-type "~a-lens" accessor-ids)
#'(combine-out lens-id ...)]))
(define-provide-syntax struct+lenses-out
(syntax-parser
[(struct+lenses-out struct-type:id)
#'(combine-out (struct-out struct-type) (struct-lenses-out struct-type))]))
(module+ test
(struct/lens foo (a b c d) #:transparent)
(check-lens-view foo-b-lens (foo 1 2 3 4) 2)
(check-lens-set foo-c-lens (foo 1 2 3 4) 'a (foo 1 2 'a 4))
(test-lens-laws foo-a-lens (foo 1 2 3 4) 'a 'b))

View File

@ -1,3 +0,0 @@
#lang reprovide
"syntax.rkt"
"syntax-keyword.rkt"

View File

@ -1,269 +0,0 @@
#lang sweet-exp racket/base
provide syntax-srcloc-lens
syntax-source-lens
syntax-line-lens
syntax-position-lens
syntax-column-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
require fancy-app
lens/common
syntax/parse/define
syntax/srcloc
module+ test
require rackunit
(define-simple-macro
(define-source-location-lenses [lens-id:id getter:expr update-kw:keyword] ...)
(begin
(define lens-id
(make-lens getter (update-source-location _ update-kw _)))
...))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Syntax
;; syntax-srcloc : Syntax -> Srcloc
(define (syntax-srcloc stx)
(source-location->srcloc stx))
;; syntax-set-source-location : Syntax Source-Location -> Syntax
(define (syntax-set-source-location stx src)
(define stx* (syntax-disarm stx #f))
(syntax-rearm
(datum->syntax stx*
(syntax-e stx*)
(source-location->list src)
stx*)
stx))
(define syntax-srcloc-lens
(make-lens
syntax-srcloc
syntax-set-source-location))
(define-source-location-lenses
[syntax-source-lens syntax-source #:source]
[syntax-line-lens syntax-line #:line]
[syntax-column-lens syntax-column #:column]
[syntax-position-lens syntax-position #:position]
[syntax-span-lens syntax-span #:span])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Source Locations
;; source-location->srcloc : Source-Location -> Srcloc
(define (source-location->srcloc src)
(build-source-location src))
;; source-location->list : Source-Location -> Source-Location-List
(define (source-location->list src)
(build-source-location-list src))
;; source-location->vector : Source-Location -> Source-Location-Vector
(define (source-location->vector src)
(build-source-location-vector src))
;; replace-source-location : Syntax Source-Location -> Syntax
;; Srcloc Source-Location -> Srcloc
;; Source-Location-List Source-Location -> Source-Location-List
;; Source-Location-Vector Source-Location -> Source-Location-Vector
;; Source-Location Source-Location -> Source-Location
(define (replace-source-location old new)
(update-source-location old
#:source (source-location-source new)
#:line (source-location-line new)
#:column (source-location-column new)
#:position (source-location-position new)
#:span (source-location-span new)))
(define source-location->srcloc-lens
(make-lens
source-location->srcloc
replace-source-location))
(define source-location->list-lens
(make-lens
source-location->list
replace-source-location))
(define source-location->vector-lens
(make-lens
source-location->vector
replace-source-location))
(define-source-location-lenses
[source-location-source-lens source-location-source #:source]
[source-location-line-lens source-location-line #:line]
[source-location-column-lens source-location-column #:column]
[source-location-position-lens source-location-position #:position]
[source-location-span-lens source-location-span #:span])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Tests
(module+ test
(define-check (check-syntax actual-stx expected-datum expected-srcloc)
(check-pred syntax? actual-stx)
(check-equal? (syntax->datum actual-stx) expected-datum)
(check-equal? (syntax-srcloc actual-stx) expected-srcloc))
(define a-src (srcloc 'a 12 5 144 9))
(define b-src (srcloc 'b 49 7 343 14))
(define a-lst (list 'a 12 5 144 9))
(define b-lst (list 'b 49 7 343 14))
(define a-vec (vector-immutable 'a 12 5 144 9))
(define b-vec (vector-immutable 'b 49 7 343 14))
(define a (datum->syntax #f (list '+ 1 2 3) a-lst))
(define b (datum->syntax #f (list 'define 'x 987) b-lst))
(test-case "syntax-srcloc-lens"
(check-equal? (lens-view syntax-srcloc-lens a) a-src)
(check-equal? (lens-view syntax-srcloc-lens b) b-src)
(check-syntax (lens-set syntax-srcloc-lens a a-src) (list '+ 1 2 3) a-src)
(check-syntax (lens-set syntax-srcloc-lens b b-src) (list 'define 'x 987) b-src)
(check-syntax (lens-set syntax-srcloc-lens a b-src) (list '+ 1 2 3) b-src)
(check-syntax (lens-set syntax-srcloc-lens b a-src) (list 'define 'x 987) a-src)
;; same thing, but with source-location->srcloc-lens instead of syntax-srcloc-lens
(check-equal? (lens-view source-location->srcloc-lens a) a-src)
(check-equal? (lens-view source-location->srcloc-lens b) b-src)
(check-syntax (lens-set source-location->srcloc-lens a a-src) (list '+ 1 2 3) a-src)
(check-syntax (lens-set source-location->srcloc-lens b b-src) (list 'define 'x 987) b-src)
(check-syntax (lens-set source-location->srcloc-lens a b-src) (list '+ 1 2 3) b-src)
(check-syntax (lens-set source-location->srcloc-lens b a-src) (list 'define 'x 987) a-src)
;; same thing, but with source-location->list-lens
(check-equal? (lens-view source-location->list-lens a) a-lst)
(check-equal? (lens-view source-location->list-lens b) b-lst)
(check-syntax (lens-set source-location->list-lens a a-lst) (list '+ 1 2 3) a-src)
(check-syntax (lens-set source-location->list-lens b b-lst) (list 'define 'x 987) b-src)
(check-syntax (lens-set source-location->list-lens a b-lst) (list '+ 1 2 3) b-src)
(check-syntax (lens-set source-location->list-lens b a-lst) (list 'define 'x 987) a-src)
;; same thing, but with source-location->vector-lens
(check-equal? (lens-view source-location->vector-lens a) a-vec)
(check-equal? (lens-view source-location->vector-lens b) b-vec)
(check-syntax (lens-set source-location->vector-lens a a-vec) (list '+ 1 2 3) a-src)
(check-syntax (lens-set source-location->vector-lens b b-vec) (list 'define 'x 987) b-src)
(check-syntax (lens-set source-location->vector-lens a b-vec) (list '+ 1 2 3) b-src)
(check-syntax (lens-set source-location->vector-lens b a-vec) (list 'define 'x 987) a-src)
;; source-location->srcloc-lens also works with other types of source-locations
(check-equal? (lens-view source-location->srcloc-lens a-src) a-src)
(check-equal? (lens-view source-location->srcloc-lens b-src) b-src)
(check-equal? (lens-view source-location->srcloc-lens a-lst) a-src)
(check-equal? (lens-view source-location->srcloc-lens b-lst) b-src)
(check-equal? (lens-view source-location->srcloc-lens a-vec) a-src)
(check-equal? (lens-view source-location->srcloc-lens b-vec) b-src)
(check-equal? (lens-set source-location->srcloc-lens a-src b-src) b-src)
(check-equal? (lens-set source-location->srcloc-lens a-lst b-src) b-lst)
(check-equal? (lens-set source-location->srcloc-lens a-vec b-src) b-vec)
(check-equal? (lens-set source-location->srcloc-lens b-src a-src) a-src)
(check-equal? (lens-set source-location->srcloc-lens b-lst a-src) a-lst)
(check-equal? (lens-set source-location->srcloc-lens b-vec a-src) a-vec)
)
(test-case "syntax-source-lens"
(check-equal? (lens-view syntax-source-lens a) 'a)
(check-equal? (lens-view syntax-source-lens b) 'b)
(check-syntax (lens-set syntax-source-lens a "bye.rkt")
(list '+ 1 2 3)
(srcloc "bye.rkt" 12 5 144 9))
(check-syntax (lens-set syntax-source-lens b "hellooo.rkt")
(list 'define 'x 987)
(srcloc "hellooo.rkt" 49 7 343 14))
;; same thing, but with source-location-source-lens instead of syntax-source-lens
(check-equal? (lens-view source-location-source-lens a) 'a)
(check-equal? (lens-view source-location-source-lens b) 'b)
(check-syntax (lens-set source-location-source-lens a "bye.rkt")
(list '+ 1 2 3)
(srcloc "bye.rkt" 12 5 144 9))
(check-syntax (lens-set source-location-source-lens b "hellooo.rkt")
(list 'define 'x 987)
(srcloc "hellooo.rkt" 49 7 343 14))
)
(test-case "syntax-line-lens"
(check-equal? (lens-view syntax-line-lens a) 12)
(check-equal? (lens-view syntax-line-lens b) 49)
(check-syntax (lens-set syntax-line-lens a 8)
(list '+ 1 2 3)
(srcloc 'a 8 5 144 9))
(check-syntax (lens-set syntax-line-lens b 11)
(list 'define 'x 987)
(srcloc 'b 11 7 343 14))
;; same thing, but with source-location-line-lens instead of syntax-line-lens
(check-equal? (lens-view source-location-line-lens a) 12)
(check-equal? (lens-view source-location-line-lens b) 49)
(check-syntax (lens-set source-location-line-lens a 8)
(list '+ 1 2 3)
(srcloc 'a 8 5 144 9))
(check-syntax (lens-set source-location-line-lens b 11)
(list 'define 'x 987)
(srcloc 'b 11 7 343 14))
)
(test-case "syntax-column-lens"
(check-equal? (lens-view syntax-column-lens a) 5)
(check-equal? (lens-view syntax-column-lens b) 7)
(check-syntax (lens-set syntax-column-lens a 8)
(list '+ 1 2 3)
(srcloc 'a 12 8 144 9))
(check-syntax (lens-set syntax-column-lens b 11)
(list 'define 'x 987)
(srcloc 'b 49 11 343 14))
;; same thing, but with source-location-column-lens instead of syntax-column-lens
(check-equal? (lens-view source-location-column-lens a) 5)
(check-equal? (lens-view source-location-column-lens b) 7)
(check-syntax (lens-set source-location-column-lens a 8)
(list '+ 1 2 3)
(srcloc 'a 12 8 144 9))
(check-syntax (lens-set source-location-column-lens b 11)
(list 'define 'x 987)
(srcloc 'b 49 11 343 14))
)
(test-case "syntax-position-lens"
(check-equal? (lens-view syntax-position-lens a) 144)
(check-equal? (lens-view syntax-position-lens b) 343)
(check-syntax (lens-set syntax-position-lens a 233)
(list '+ 1 2 3)
(srcloc 'a 12 5 233 9))
(check-syntax (lens-set syntax-position-lens b 610)
(list 'define 'x 987)
(srcloc 'b 49 7 610 14))
;; same thing, but with source-location-position-lens instead of syntax-position-lens
(check-equal? (lens-view source-location-position-lens a) 144)
(check-equal? (lens-view source-location-position-lens b) 343)
(check-syntax (lens-set source-location-position-lens a 233)
(list '+ 1 2 3)
(srcloc 'a 12 5 233 9))
(check-syntax (lens-set source-location-position-lens b 610)
(list 'define 'x 987)
(srcloc 'b 49 7 610 14))
)
(test-case "syntax-span-lens"
(check-equal? (lens-view syntax-span-lens a) 9)
(check-equal? (lens-view syntax-span-lens b) 14)
(check-syntax (lens-set syntax-span-lens a 10)
(list '+ 1 2 3)
(srcloc 'a 12 5 144 10))
(check-syntax (lens-set syntax-span-lens b 15)
(list 'define 'x 987)
(srcloc 'b 49 7 343 15))
;; same thing, but with source-location-span-lens instead of syntax-span-lens
(check-equal? (lens-view source-location-span-lens a) 9)
(check-equal? (lens-view source-location-span-lens b) 14)
(check-syntax (lens-set source-location-span-lens a 10)
(list '+ 1 2 3)
(srcloc 'a 12 5 144 10))
(check-syntax (lens-set source-location-span-lens b 15)
(list 'define 'x 987)
(srcloc 'b 49 7 343 15))
)
)

View File

@ -1,419 +0,0 @@
#lang racket/base
(provide 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
)
(require fancy-app lens/common lens/private/list/main racket/list racket/match syntax/stx)
(module+ test
(require rackunit syntax/parse lens/private/test-util/test-lens))
;; stx-e : Any -> Any
(define (stx-e stx)
(if (syntax? stx)
(syntax-e stx)
stx))
;; restore-stx : (case-> [Stx Any -> Stx]
;; [Any Any -> Any])
(define (restore-stx stx dat)
(if (syntax? stx)
(datum->syntax stx dat stx stx)
dat))
(define stx-e-lens
(make-lens
stx-e
restore-stx)) ; the target will be used as the context
;; stx->list* : (Stx-Listof Any) -> (Listof Any)
(define (stx->list* stx)
(define lst (stx->list stx))
;; lst : (U (Listof Any) False)
(unless lst (error 'stx->list* "expected a stx-list, given ~v" stx))
;; lst : (Listof Any)
lst)
(define stx->list-lens
(make-lens
stx->list*
restore-stx))
(define (stx-map-lens elt-lens)
(make-lens
(lens-view/stx-map elt-lens _)
(lens-set/stx-map elt-lens _ _)))
(define (lens-view/stx-map lens tgts)
(stx-map (lens-view lens _) tgts))
(define (lens-set/stx-map lens tgts new-views)
(restore-stx tgts
(stx-map (lens-set lens _ _) tgts new-views)))
(define stx-car-lens (lens-thrush stx-e-lens car-lens))
(define stx-cdr-lens (lens-thrush stx-e-lens cdr-lens))
(define stx-caar-lens (lens-thrush stx-car-lens stx-car-lens))
(define stx-cdar-lens (lens-thrush stx-car-lens stx-cdr-lens))
(define stx-cadr-lens (lens-thrush stx-cdr-lens stx-car-lens))
(define stx-cddr-lens (lens-thrush stx-cdr-lens stx-cdr-lens))
(define stx-caaar-lens (lens-thrush stx-caar-lens stx-car-lens))
(define stx-cdaar-lens (lens-thrush stx-caar-lens stx-cdr-lens))
(define stx-cadar-lens (lens-thrush stx-cdar-lens stx-car-lens))
(define stx-cddar-lens (lens-thrush stx-cdar-lens stx-cdr-lens))
(define stx-caadr-lens (lens-thrush stx-cadr-lens stx-car-lens))
(define stx-cdadr-lens (lens-thrush stx-cadr-lens stx-cdr-lens))
(define stx-caddr-lens (lens-thrush stx-cddr-lens stx-car-lens))
(define stx-cdddr-lens (lens-thrush stx-cddr-lens stx-cdr-lens))
;; stx-append* : (Stx-Listof (Stx-Listof A)) -> (Stx-Listof A)
(define (stx-append* lol)
(append* (stx-map stx->list* lol)))
;; restore-structure : (Stx-Listof (Stx-Listof A)) (Stx-Listof B) -> (Stx-Listof (Stx-Listof B))
;; Takes a list of lists and a list and un-flattens the flattened
;; argument according to the structure of the structure arguement.
;; The length of the flattened list must be the same as the length
;; of (stx-append* structure).
(define (restore-structure structure flattened)
(match (stx-e structure)
[(list)
(unless (stx-null? flattened)
(error 'stx-append*-lens "flattened list is too long to match the structure"))
structure]
[(cons s-lst s-rst)
(define-values [f-lst f-rst]
(stx-split-at flattened (stx-length s-lst)))
(restore-stx structure
(cons (restore-stx s-lst f-lst)
(restore-structure s-rst f-rst)))]))
;; stx-flatten/depth-lens : (Lens (Stx-Listof* Any n) (Stx-Listof Any))
;; where the only valid views are stx-lists with the same length as
;; the result of (stx-flatten/depth n target)
(define (stx-flatten/depth-lens n)
(make-lens
(stx-flatten/depth n _)
(stx-unflatten/depth n _ _)))
;; stx-append*-lens : (Lens (Stx-Listof (Stx-Listof Any)) (Stx-Listof Any))
;; where the only valid views are stx-lists with the same length as
;; the result of applying stx-append* to the target.
;; Viewing is equivalent to using stx-append*
;; Setting restores the structure of the original nested stx-list
(define stx-append*-lens
(stx-flatten/depth-lens 2))
;; stx-flatten/depth : n (Stx-Listof* A n) -> (Stx-Listof A)
(define (stx-flatten/depth n lst*)
(check-structure-depth! n lst*)
(cond [(zero? n) (list lst*)]
[else (stx-append*n (sub1 n) lst*)]))
;; stx-unflatten/depth : n (Stx-Listof* A n) (Stx-Listof B) -> (Stx-Listof* B n)
(define (stx-unflatten/depth n lst* lst)
(check-structure-depth! n lst*)
(check-flattened-length! n lst* lst)
(cond [(zero? n)
(match-define (list v) (stx->list* lst))
v]
[else
(stx-unappend*n (sub1 n) lst* lst)]))
;; stx-append*n : n (Stx-Listof (Stx-Listof* A n)) -> (Stx-Listof A)
(define (stx-append*n n lst*)
(cond [(zero? n) lst*]
[else (stx-append*n (sub1 n) (stx-append* lst*))]))
;; stx-unappend*n : n (Stx-Listof (Stx-Listof* A n)) (Stx-Listof B) -> (Stx-Listof (Stx-Listof* B n))
(define (stx-unappend*n n lst* lst)
(cond [(zero? n) lst]
[else (restore-structure
lst*
(stx-unappend*n (sub1 n) (stx-append* lst*) lst))]))
(define (stx-append*n-lens n)
(stx-flatten/depth-lens (add1 n)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; stx-list/depth? : Natural Any -> Boolean
(define (stx-list/depth? n structure)
(cond [(zero? n) #true]
[else (and (stx-list? structure)
(stx-andmap (stx-list/depth? (sub1 n) _) structure))]))
;; check-structure-depth! : n (Stx-Listof* A n) -> Void
(define (check-structure-depth! depth structure)
(unless (stx-list/depth? depth structure)
(raise-argument-error 'stx-flatten/depth-lens
(format "a nested stx-list of depth ~v" depth)
structure)))
;; check-flattened-length! : n (Stx-Listof* A n) (Stx-Listof B) -> Void
(define (check-flattened-length! depth structure flattened)
(unless (= (stx-length (stx-flatten/depth depth structure)) (stx-length flattened))
(raise-argument-error 'stx-flatten/depth-lens
(format "a stx-list of length ~v"
(stx-length (stx-flatten/depth depth structure)))
1
structure
flattened)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; stx-length : (Stx-Listof A) -> Natural
(define (stx-length lst)
(length (stx->list* lst)))
;; stx-andmap : [A -> Boolean] (Stx-Listof A) -> Boolean
(define (stx-andmap f lst)
(andmap f (stx->list* lst)))
;; stx-split-at : (Stx-Listof A) Natural -> (values (Listof A) (Stx-Listof A))
(define (stx-split-at lst* pos*)
(let loop ([acc (list)] [pos pos*] [lst lst*])
(cond [(zero? pos)
(values (reverse acc) lst)]
[(stx-null? lst)
(error 'stx-split-at "index is too large for stx-list\n index: ~v\n stx-list: ~v"
pos* lst*)]
[else
(loop (cons (stx-car lst) acc)
(sub1 pos)
(stx-cdr lst))])))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(module+ test
(define a* #'a)
(define b* #'b)
(define c* #'c)
(define 1* #'1)
(define 2* #'2)
(define 3* #'3)
(test-case "syntax-e-lens and stx-e-lens"
(check-equal? (lens-view stx-e-lens a*) 'a)
(check-equal? (syntax-e (lens-set stx-e-lens a* 1)) 1)
(check-equal? (lens-view stx-e-lens 'a) 'a)
(check-equal? (lens-set stx-e-lens 'a 1) 1)
(check-equal? (lens-view stx-e-lens #`(#,a* #,b* #,c*)) (list a* b* c*))
(check-equal? (syntax-e (lens-set stx-e-lens #`(#,a* #,b* #,c*) (list 1* 2* 3*)))
(list 1* 2* 3*))
(check-equal? (lens-view stx-e-lens (list a* b* c*)) (list a* b* c*))
(check-equal? (lens-set stx-e-lens (list a* b* c*) (list 1* 2* 3*)) (list 1* 2* 3*))
)
(test-case "stx->list-lens"
(check-equal? (lens-view stx->list-lens #`(#,a* #,b* #,c*))
(list a* b* c*))
(check-equal? (syntax->list (lens-set stx->list-lens #`(#,a* #,b* #,c*) (list 1* 2* 3*)))
(list 1* 2* 3*))
(check-exn #rx"expected a stx-list, given #<syntax.* 5>"
(λ () (lens-view stx->list-lens #'5)))
)
(test-case "(stx-map-lens stx->list-lens)"
(check-equal? (lens-view (stx-map-lens stx->list-lens) #`((#,a*) (#,b* #,c*) ()))
(list (list a*) (list b* c*) (list)))
(check-equal? (stx-map syntax->list
(lens-set (stx-map-lens stx->list-lens)
#`((#,a*) (#,b* #,c*) ())
(list (list 1*) (list 2* 3*) (list))))
(list (list 1*) (list 2* 3*) (list)))
)
(test-case "stx-car-lens, stx-cdr-lens, etc."
(check-equal? (lens-view stx-car-lens #`(#,a* . #,b*)) a*)
(check-equal? (lens-view stx-cdr-lens #`(#,a* . #,b*)) b*)
(check-equal? (lens-view stx-car-lens (cons a* b*)) a*)
(check-equal? (lens-view stx-cdr-lens (cons a* b*)) b*)
(check-equal? (syntax-e (lens-set stx-car-lens #`(#,a* . #,b*) 1*)) (cons 1* b*))
(check-equal? (syntax-e (lens-set stx-cdr-lens #`(#,a* . #,b*) 1*)) (cons a* 1*))
(check-equal? (lens-set stx-car-lens (cons a* b*) 1*) (cons 1* b*))
(check-equal? (lens-set stx-cdr-lens (cons a* b*) 1*) (cons a* 1*))
(check-equal? (lens-view stx-car-lens #`(#,a* #,b* #,c*)) a*)
(check-equal? (lens-view stx-cadr-lens #`(#,a* #,b* #,c*)) b*)
(check-equal? (lens-view stx-caddr-lens #`(#,a* #,b* #,c*)) c*)
(check-equal? (lens-view stx-car-lens (list a* b* c*)) a*)
(check-equal? (lens-view stx-cadr-lens (list a* b* c*)) b*)
(check-equal? (lens-view stx-caddr-lens (list a* b* c*)) c*)
(check-equal? (syntax-e (lens-set stx-car-lens #`(#,a* #,b* #,c*) 1*)) (list 1* b* c*))
(check-equal? (syntax-e (lens-set stx-cadr-lens #`(#,a* #,b* #,c*) 1*)) (list a* 1* c*))
(check-equal? (syntax-e (lens-set stx-caddr-lens #`(#,a* #,b* #,c*) 1*)) (list a* b* 1*))
(check-equal? (lens-set stx-car-lens (list a* b* c*) 1*) (list 1* b* c*))
(check-equal? (lens-set stx-cadr-lens (list a* b* c*) 1*) (list a* 1* c*))
(check-equal? (lens-set stx-caddr-lens (list a* b* c*) 1*) (list a* b* 1*))
)
(test-case "stx-append*-lens"
(check-equal? (lens-view stx-append*-lens (list (list 1*) (list 2* 3*) (list)))
(list 1* 2* 3*))
(check-equal? (lens-view stx-append*-lens #`((#,1*) (#,2* #,3*) ()))
(list 1* 2* 3*))
(check-equal? (lens-set stx-append*-lens
(list (list 1) (list 2 3) (list))
(list 'a 'b 'c))
(list (list 'a) (list 'b 'c) (list)))
(check-equal? (map syntax->list
(lens-set stx-append*-lens
(list #`(#,1*) #`(#,2* #,3*) #`())
(list a* b* c*)))
(list (list a*) (list b* c*) (list)))
(check-equal? (map syntax->list
(syntax-e
(lens-set stx-append*-lens
#`((#,1*) (#,2* #,3*) ())
(list a* b* c*))))
(list (list a*) (list b* c*) (list)))
(check-equal? (lens-transform stx-append*-lens
(list (list 1) (list 2 3) (list))
(lambda (lst)
;; a length-preserving computation
(let loop ([acc (list)] [sum 0] [lst lst])
(match lst
[(list) (reverse acc)]
[(cons fst rst)
(loop (cons (+ sum fst) acc)
(+ sum fst)
rst)]))))
(list (list 1) (list 3 6) (list)))
(check-equal? (map syntax->datum
(syntax-e
(lens-transform
stx-append*-lens
#'(((+ a)) ((- a b) (* c d)) ())
(lambda (lst)
;; a length-preserving computation
(syntax-parse
(expand #`(#%expression (λ (a b c d) (#%app list #,@lst))))
#:literals (#%plain-lambda #%plain-app list)
[(#%expression (#%plain-lambda (x ...) (#%plain-app list e ...)))
#'[e ...]])))))
(list (list '(#%app + a))
(list '(#%app - a b) '(#%app * c d))
(list)))
(check-exn #rx"expected: a nested stx-list of depth 2\n given: '\\(5\\)"
(λ () (lens-view stx-append*-lens (list 5))))
(check-exn #rx"expected: a nested stx-list of depth 2\n given: '\\(5\\)"
(λ () (lens-set stx-append*-lens (list 5) (list 'a))))
(check-exn #rx"expected: a stx-list of length 3\n given: '\\(a b\\)"
(λ () (lens-set stx-append*-lens (list (list 1) (list 2 3) (list)) (list 'a 'b))))
(test-lens-laws stx-append*-lens
(list (list 1) (list 2 3) (list))
(list 'a 'b 'c)
(list "a" "b" "c"))
(test-lens-laws stx-append*-lens
(list (list 1*) (list 2* 3*) (list))
(list a* b* c*)
(list "a" "b" "c"))
)
(test-case "stx-flatten/depth-lens"
(define flat0-lens (stx-flatten/depth-lens 0))
(define flat1-lens (stx-flatten/depth-lens 1))
(define flat2-lens (stx-flatten/depth-lens 2))
(define flat3-lens (stx-flatten/depth-lens 3))
(define flat4-lens (stx-flatten/depth-lens 4))
(check-equal? (lens-view flat3-lens
(list (list (list) (list 1))
(list (list 2 3))
(list)
(list (list 4) (list) (list 5 6))))
(list 1 2 3 4 5 6))
(check-equal? (lens-set flat3-lens
(list (list (list) (list 1))
(list (list 2 3))
(list)
(list (list 4) (list) (list 5 6)))
(list 'a 'b 'c 'd 'e 'f))
(list (list (list) (list 'a))
(list (list 'b 'c))
(list)
(list (list 'd) (list) (list 'e 'f))))
(test-lens-laws flat3-lens
(list (list (list) (list 1))
(list (list 2 3))
(list)
(list (list 4) (list) (list 5 6)))
(list 'a 'b 'c 'd 'e 'f)
(list "a" "b" "c" "d" "e" "f"))
(check-equal? (lens-view flat4-lens
(list (list (list) (list (list 1)))
(list (list (list) (list 2 3)))
(list)
(list (list (list 4) (list)) (list) (list (list 5 6)))))
(list 1 2 3 4 5 6))
(check-equal? (lens-set flat4-lens
(list (list (list) (list (list 1)))
(list (list (list) (list 2 3)))
(list)
(list (list (list 4) (list)) (list) (list (list 5 6))))
(list 'a 'b 'c 'd 'e 'f))
(list (list (list) (list (list 'a)))
(list (list (list) (list 'b 'c)))
(list)
(list (list (list 'd) (list)) (list) (list (list 'e 'f)))))
(check-exn #rx"expected: a nested stx-list of depth 3\n *given: '\\(5\\)"
(λ () (lens-view flat3-lens (list 5))))
(check-exn #rx"expected: a nested stx-list of depth 3\n given: '\\(5\\)"
(λ () (lens-set flat3-lens (list 5) (list 'a))))
(check-exn #rx"expected: a stx-list of length 6\n given: '\\(a b\\)"
(λ () (lens-set flat3-lens
(list (list (list) (list 1))
(list (list 2 3))
(list)
(list (list 4) (list) (list 5 6)))
(list 'a 'b))))
(test-lens-laws flat0-lens
42
(list 'a)
(list "a"))
(test-lens-laws flat1-lens
(list 1 2 3)
(list 'a 'b 'c)
(list "a" "b" "c"))
(test-lens-laws flat2-lens
(list (list 1) (list 2 3) (list))
(list 'a 'b 'c)
(list "a" "b" "c"))
(test-lens-laws flat3-lens
(list (list (list) (list 1))
(list (list 2 3))
(list)
(list (list 4) (list) (list 5 6)))
(list 'a 'b 'c 'd 'e 'f)
(list "a" "b" "c" "d" "e" "f"))
(test-lens-laws flat4-lens
(list (list (list) (list (list 1)))
(list (list (list) (list 2 3)))
(list)
(list (list (list 4) (list)) (list) (list (list 5 6))))
(list 'a 'b 'c 'd 'e 'f)
(list "a" "b" "c" "d" "e" "f"))
))

View File

@ -1,31 +0,0 @@
#lang racket/base
(require lens/private/base/gen-lens
rackunit
racket/function)
(struct bad1 ()
#:methods gen:lens [])
(check-exn #rx"lens-view: not implemented"
(thunk (lens-view (bad1) 1)))
(check-exn #rx"lens-set: not implemented"
(thunk (lens-set (bad1) 1 1)))
(check-exn #rx"focus-lens: not implemented"
(thunk (focus-lens (bad1) 1)))
(struct bad2 ()
#:methods gen:lens
[(define (lens-view this tgt) "something")])
(check-equal? (lens-view (bad2) 1) "something")
(check-exn #rx"lens-set: not implemented"
(thunk (lens-set (bad2) 1 1)))
(check-exn #rx"focus-lens: not implemented"
(thunk (focus-lens (bad2) 1)))
(struct bad3 ()
#:methods gen:lens
[(define (lens-set this tgt nvw) tgt)])
(check-equal? (lens-set (bad3) 1 2) 1)
(check-exn #rx"lens-view: not implemented"
(thunk (lens-view (bad3) 1)))
(check-exn #rx"focus-lens: not implemented"
(thunk (focus-lens (bad3) 1)))

View File

@ -1,61 +0,0 @@
#lang sweet-exp racket/base
provide alternating->assoc-list
assoc->alternating-list
keys+values->assoc-list
assoc-list->keys+values
keys+values->alternating-list
alternating-list->keys+values
require racket/list
racket/match
unstable/sequence
module+ test
require rackunit
(define (alternating->assoc-list alternating-list)
(for/list ([lst (in-slice 2 alternating-list)])
(match-define (list a b) lst)
(cons a b)))
(define (assoc->alternating-list alist)
(append*
(for/list ([(k v) (in-pairs alist)])
(list k v))))
(define (keys+values->assoc-list keys values)
(map cons keys values))
(define (assoc-list->keys+values alist)
(values (map car alist)
(map cdr alist)))
(define (keys+values->alternating-list keys values)
(append-map list keys values))
(define (alternating-list->keys+values alternating-list)
(for/lists (ks vv) ([lst (in-slice 2 alternating-list)])
(match-define (list k v) lst)
(values k v)))
module+ test
(check-equal? (alternating->assoc-list '(a 1 b 2)) '((a . 1) (b . 2)))
(check-equal? (alternating->assoc-list '(b 2 a 1)) '((b . 2) (a . 1)))
(check-equal? (assoc->alternating-list '((a . 1) (b . 2))) '(a 1 b 2))
(check-equal? (assoc->alternating-list '((b . 2) (a . 1))) '(b 2 a 1))
(check-equal? (keys+values->assoc-list '(a b) '(1 2)) '((a . 1) (b . 2)))
(check-equal? (keys+values->assoc-list '(b a) '(2 1)) '((b . 2) (a . 1)))
(check-equal? (keys+values->alternating-list '(a b) '(1 2)) '(a 1 b 2))
(check-equal? (keys+values->alternating-list '(b a) '(2 1)) '(b 2 a 1))
(let-values ([(ks vs) (assoc-list->keys+values '((a . 1) (b . 2)))])
(check-equal? ks '(a b))
(check-equal? vs '(1 2)))
(let-values ([(ks vs) (assoc-list->keys+values '((b . 2) (a . 1)))])
(check-equal? ks '(b a))
(check-equal? vs '(2 1)))
(let-values ([(ks vs) (alternating-list->keys+values '(a 1 b 2))])
(check-equal? ks '(a b))
(check-equal? vs '(1 2)))
(let-values ([(ks vs) (alternating-list->keys+values '(b 2 a 1))])
(check-equal? ks '(b a))
(check-equal? vs '(2 1)))

View File

@ -1,15 +0,0 @@
#lang sweet-exp racket/base
provide functional-dict?
require racket/dict
module+ test
require rackunit
(define (functional-dict? v)
(and (dict? v) (dict-can-functional-set? v)))
module+ test
(check-true (functional-dict? (hash 'a 1 'b 2)))
(check-true (functional-dict? '((a . 1) (b . 2))))
(check-false (functional-dict? (make-hash '((a . 1) (b . 2)))))

View File

@ -1,17 +0,0 @@
#lang sweet-exp racket/base
provide functional-set?
require racket/set
module+ test
require rackunit
(define (functional-set? st)
(and (generic-set? st)
(set-implements? st 'set-add 'set-remove)
(not (set-mutable? st))))
module+ test
(check-true (functional-set? (set 1 2 3)))
(check-true (functional-set? '(1 2 3)))
(check-false (functional-set? (mutable-set 1 2 3)))

View File

@ -1,37 +0,0 @@
#lang sweet-exp racket/base
require racket/contract
lens/private/base/main
lens/private/isomorphism/base
lens/private/compound/compose
lens/private/util/rest-contract
"../util/immutable.rkt"
"../list/join-list.rkt"
module+ test
require rackunit
lens/private/test-util/test-lens
"../list/list-ref-take-drop.rkt"
provide
contract-out
lens-join/vector (rest-> lens? (lens/c any/c immutable-vector?))
(define (lens-join/vector . lenses)
(lens-compose list->vector-lens (apply lens-join/list lenses)))
(define list->vector-lens
(make-isomorphism-lens list->immutable-vector vector->list))
(module+ test
(define vector-first-third-fifth-lens
(lens-join/vector first-lens
third-lens
fifth-lens))
(check-lens-view vector-first-third-fifth-lens '(a b c d e f)
#(a c e))
(check-pred immutable? (lens-view vector-first-third-fifth-lens '(a b c d e f)))
(check-lens-set vector-first-third-fifth-lens '(a b c d e f) #(1 2 3)
'(1 b 2 d 3 f)))

View File

@ -1,5 +0,0 @@
#lang reprovide
"nested.rkt"
"pick.rkt"
"ref.rkt"
"join-vector.rkt"

View File

@ -1,26 +0,0 @@
#lang info
(define collection 'multi)
(define deps
'("base"
"lens-lib"
"lens-unstable"
"scribble-lib"
"reprovide-lang"
"jack-scribble-example"
))
(define build-deps
'("at-exp-lib"
"doc-coverage"
"racket-doc"
"sweet-exp-lib"
))
(define cover-omit-paths
'(#rx".*\\.scrbl"
#rx"info\\.rkt"
"lens/private/doc-util"
))

View File

@ -1,4 +0,0 @@
#lang info
(define scribblings '(["main.scrbl" (multi-page) (library) "lens"]))

View File

@ -1,25 +0,0 @@
#lang scribble/manual
@(require lens/private/doc-util/main)
@title[#:style '(toc)]{Lenses}
@defmodule[lens]
This library includes functions and forms for working with
@lens-tech{lenses}. A lens is a value that operates on some small piece
of a larger structure. Think of them as a more general representation
of getters and setters in object-oriented languages.
@author[@author+email["Jack Firth" "jackhfirth@gmail.com"]
@author+email["Alex Knauth" "alexander@knauth.org"]]
source code: @url["https://github.com/jackfirth/lens"]
@stability-notice[unstable/lens]
@local-table-of-contents[]
@include-section["private/scribblings/guide.scrbl"]
@include-section["private/scribblings/reference.scrbl"]
@include-section[(lib "unstable/lens/main.scrbl")]

View File

@ -1,22 +0,0 @@
#lang scribble/manual
@(require lens/private/doc-util/main)
@title{Lens Contracts}
@defproc[(lens/c [target/c contract?] [view/c contract?]) contract?]{
A contract constructor for lenses. The @racket[target/c] contract is used for
any target given to or returned by the lens, while the @racket[view/c] contract
is used for any view given to or returned by the lens. For example, the
@racket[view/c] contract is used for the return value of
@racket[(lens-view lens target)] and the third argument of
@racket[(lens-set lens target view)], as well as other places where targets or
views of the lens are used as inputs or outputs.
@lens-examples[
(define contracted-car-lens
(invariant-assertion (lens/c pair? number?) car-lens))
(lens-view contracted-car-lens (cons 1 2))
(lens-view contracted-car-lens 'not-a-pair)
(lens-view contracted-car-lens (cons 'not-a-number 2))
(lens-set contracted-car-lens (cons 1 2) 'not-a-number)
]}

View File

@ -1,8 +0,0 @@
#lang scribble/manual
@(require lens/private/doc-util/scribble-include-no-subsection)
@title[#:tag "composing-lenses"]{Joining and Composing Lenses}
@scribble-include/no-subsection["compose.scrbl"]
@scribble-include/no-subsection["thrush.scrbl"]

View File

@ -1,8 +0,0 @@
#lang racket/base
(provide include-sections)
(require (only-in scribble/base include-section))
(define-syntax-rule (include-sections mod-path ...)
(begin (include-section mod-path) ...))

View File

@ -1,8 +0,0 @@
#lang racket/base
(require scribble/manual)
(provide lens-tech)
(define (lens-tech . pre-content)
(apply tech #:key "lens" #:normalize? #f pre-content))

View File

@ -1,21 +0,0 @@
#lang sweet-exp racket
provide lens-examples
lens-applicable-examples
lens-unstable-examples
persistent-lens-unstable-examples
require scribble-example
(define-examples-form lens-examples
lens racket/list racket/vector racket/stream racket/set racket/contract)
(define-examples-form lens-applicable-examples
lens/applicable racket/list racket/vector racket/stream racket/set racket/contract)
(define-examples-form lens-unstable-examples
lens unstable/lens racket/list racket/vector racket/stream racket/set racket/contract)
(define-persistent-examples-form persistent-lens-unstable-examples
lens unstable/lens racket/list racket/vector racket/stream racket/set racket/contract)

View File

@ -1,23 +0,0 @@
#lang sweet-exp reprovide
syntax/parse/define
lens/private/doc-util/deflenses
lens/private/doc-util/include-sections
lens/private/doc-util/lens-tech
lens/private/doc-util/lenses-examples
lens/private/doc-util/other-reference
lens/private/doc-util/stability-notice
for-label
lens
unstable/lens
racket/base
racket/match
racket/list
racket/vector
racket/stream
racket/set
racket/contract
racket/function
for-syntax
racket/base
syntax/parse
racket/syntax

View File

@ -1,19 +0,0 @@
.flexible-container {
display: -webkit-flex;
display: flex;
}
.flexible-element {
-webkit-flex: 1;
flex: 1;
}
.margin-note__image-left {
float: left;
font-size: 1.25em;
margin-right: 0.4em;
}
.margin-note__image-left--finger::before {
content: '☞';
}

View File

@ -1,33 +0,0 @@
#lang at-exp racket/base
(require racket/require
(multi-in scribble (base html-properties struct))
(only-in scribble/core style)
setup/collects)
(provide other-reference-note see-guide-note see-reference-note)
(define css-resource
(make-css-addition
(path->collects-relative
(collection-file-path "other-reference.css" "lens" "private" "doc-util"))))
(define finger (element (style "margin-note__image-left margin-note__image-left--finger"
(list css-resource))
'()))
(define (flexible-container . content)
(element (style "flexible-container" (list css-resource (alt-tag "div"))) content))
(define (flexible-element . content)
(element (style "flexible-element" (list css-resource (alt-tag "div"))) content))
(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|.})

View File

@ -1,30 +0,0 @@
#lang sweet-exp racket/base
provide scribble-include/no-subsection
require syntax/parse/define
racket/match
scribble/core
for-syntax racket/base
syntax/parse
;; scribble-include/no-subsection requires that the module to be included:
;; - has no title
;; - has no tag-prefix
;; - has exactly one (list 'part (generated-tag)) tag
;; - has no subsections
;; - has no `to-collect` content
(define-syntax scribble-include/no-subsection
(syntax-parser
[(~and stx (scribble-include/no-subsection mod))
#:with doc-from-mod (datum->syntax #'mod 'doc)
(unless (module-path? (syntax->datum #'mod))
(raise-syntax-error #f
"not a module path"
#'stx
#'mod))
#'(begin
(require (only-in mod [doc-from-mod doc]))
(match-define (part #f (list (list 'part (generated-tag))) #f style '() blocks '()) doc)
(nested-flow style blocks))]))

View File

@ -1,10 +0,0 @@
#lang sweet-exp racket/base
provide stability-notice
require scribble/manual
(define-syntax-rule (stability-notice id)
(list "This library is stable, backwards compatibility will be maintained. For experimental features see "
(racketmodname id) "."))

View File

@ -1,15 +0,0 @@
#lang scribble/manual
@(require lens/private/doc-util/main
lens/private/doc-util/scribble-include-no-subsection)
@title[#:tag "hash-reference"]{Hash Lenses}
@defmodule[lens/data/hash]
@see-guide-note["hash-guide"]{hash lenses}
@scribble-include/no-subsection["ref.scrbl"]
@scribble-include/no-subsection["nested.scrbl"]
@scribble-include/no-subsection["pick.scrbl"]
@scribble-include/no-subsection["join-hash.scrbl"]

View File

@ -1,88 +0,0 @@
#lang scribble/manual
@(require lens/private/doc-util/main)
@title{Flattening and unflattening lists}
@defthing[append*-lens lens?]{
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 @racket[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 @racket[(append* target)].
This is equivalent to @racket[(flatten/depth-lens 2)], since it
flattens lists of depth 2.
@lens-unstable-examples[
(lens-view append*-lens '((a b c) (1 2 3)))
(lens-set append*-lens '((a b c) (1 2 3)) '("do" "re" "mi" "re" "mi" "do"))
(lens-view append*-lens '((a) (b c) () (d e f)))
(lens-set append*-lens '((a) (b c) () (d e f)) '(1 2 3 4 5 6))
]
The further nested elements don't have to be atomic, they could be
other lists. @racket[append*-lens] doesn't recur into those.
@lens-unstable-examples[
(lens-view append*-lens '(((a) (b) (c)) ((1) (2) (3))))
(lens-set append*-lens '(((a) (b) (c)) ((1) (2) (3))) '("mi" "re" "do" "re" "re" "mi"))
]}
@defproc[(flatten/depth-lens [n exact-nonnegative-integer?]) lens?]{
Creates a lens that flattens a list of depth @racket[n] when
viewing, and restores the original structure when setting.
A list of depth @racket[0] is a single element, a list of depth
@racket[1] is a list, a list of depth @racket[2] is a list of lists,
and so on.
This is a generalization of @racket[append*-lens], with that being
equivalent to @racket[(flatten/depth-lens 2)]. It uses
@racket[flatten/depth] to view, and @racket[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 @racket[(flatten/depth n target)].
@lens-unstable-examples[
(lens-view (flatten/depth-lens 0) 'a)
(lens-set (flatten/depth-lens 0) 'a '(1))
(lens-view (flatten/depth-lens 1) '(a b c))
(lens-set (flatten/depth-lens 1) '(a b c) '(1 2 3))
(lens-view (flatten/depth-lens 2) '((a) (b c) () (d e f)))
(lens-set (flatten/depth-lens 2) '((a) (b c) () (d e f)) '(1 2 3 4 5 6))
(lens-view (flatten/depth-lens 3) '(((a) ()) (() (b) (c)) () ((d e) () (f))))
(lens-set (flatten/depth-lens 3) '(((a) ()) (() (b) (c)) () ((d e) () (f))) '(1 2 3 4 5 6))
]}
@defproc[(flatten/depth [n exact-nonnegative-integer?] [structure any/c]) list?]{
Flattens a list of depth @racket[n]. For depth @racket[n] = @racket[0],
it returns @racket[(list structure)]. For a depth of @racket[1], it
returns @racket[structure]. For a depth of @racket[2], it returns
@racket[(append* structure)]. For a depth of @racket[3], it returns
@racket[(append* (append* structure))], and so on for higher depths.
This is what @racket[flatten/depth-lens] uses for viewing.
@lens-unstable-examples[
(flatten/depth 0 'a)
(flatten/depth 1 '(a b c))
(flatten/depth 2 '((a) (b c) () (d e f)))
(flatten/depth 3 '(((a) ()) (() (b) (c)) () ((d e) () (f))))
]}
@defproc[(unflatten/depth [n exact-nonnegative-integer?] [structure any/c] [flattened list?]) any/c]{
Un-does the work done by @racket[flatten/depth], to return an
un-flattened version of @racket[flattened], with the structure restored based
on @racket[structure].
This is what @racket[flatten/depth-lens] uses for setting.
@lens-unstable-examples[
(unflatten/depth 0 'a '(1))
(unflatten/depth 1 '(a b c) '(1 2 3))
(unflatten/depth 2 '((a) (b c) () (d e f)) '(1 2 3 4 5 6))
(unflatten/depth 3 '(((a) ()) (() (b) (c)) () ((d e) () (f))) '(1 2 3 4 5 6))
]}
@defproc[(append*n-lens [n exact-nonnegative-integer?]) lens?]{
This is deprecated. Use @racket[(flatten/depth-lens (add1 n))] instead.
}

View File

@ -1,14 +0,0 @@
#lang scribble/manual
@(require lens/private/doc-util/main)
@title[#:tag "pair-list-reference"]{Pair and List Lenses}
@defmodule[lens/data/list]
@see-guide-note["pair-list-guide"]{pair and list lenses}
@include-section["car-cdr.scrbl"]
@include-section["list-ref-take-drop.scrbl"]
@include-section["join-list.scrbl"]
@include-section["assoc.scrbl"]

View File

@ -1,14 +0,0 @@
#lang scribble/manual
@title[#:tag "lens-guide" #:style 'toc]{The Lens Guide}
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 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[]
@include-section["guide/introduction.scrbl"]
@include-section["guide/built-in.scrbl"]
@include-section["guide/user-defined.scrbl"]

View File

@ -1,15 +0,0 @@
#lang scribble/manual
@(require lens/private/doc-util/main)
@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.
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"]

View File

@ -1,90 +0,0 @@
#lang scribble/manual
@(require scribble/eval
lens/private/doc-util/main)
@(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 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
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))))

View File

@ -1,108 +0,0 @@
#lang scribble/manual
@(require scribble/eval
lens/private/doc-util/main)
@(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" #:style 'quiet]{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))
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)
(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* . indices)
(apply lens-compose (map list-ref-lens indices)))
(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 the above example demonstrates that it's really a derived concept.
@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.

View File

@ -1,135 +0,0 @@
#lang scribble/manual
@(require scribble/eval
lens/private/doc-util/main)
@(define introduction-eval ((make-eval-factory '(racket/base lens))))
@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 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 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
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))
p
(car p)
(cdr p))
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:
@(interaction #:eval introduction-eval
(define (set-car p v)
(cons v (cdr p)))
(define (set-cdr p v)
(cons (car p) v))
(set-car (cons 1 2) 'x)
(set-cdr (cons 1 2) 'y))
@other-reference-note{
Both @racket[car-lens] and @racket[cdr-lens], are provided by @racketmodname[lens] out of the box,
along with some other shorthand lenses. For the full list, see @secref{pair-lenses}.}
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 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
(define car-lens (make-lens car set-car))
(define cdr-lens (make-lens cdr set-cdr)))
To use a lens's getter function, use @racket[lens-view]. To use the setter function, use
@racket[lens-set]:
@(interaction #:eval introduction-eval
(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 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 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 constructed by nesting pairs inside of pairs:
@(interaction #:eval introduction-eval
(define tree (cons (cons 'a 'b)
(cons 'c 'd))))
Now, getting at a nested value gets much harder. It's necessary to nest calls to get at the right
value:
@(interaction #:eval introduction-eval
(cdr (car tree)))
Still, this isn't too bad. However, what if we want to @emph{set} a value? We could use our
@racket[set-car] and @racket[set-cdr] functions from earlier, but if we try, we'll find they don't
work quite right:
@(interaction #:eval introduction-eval
(set-cdr (car tree) 'x))
Oops. We wanted to get back the whole tree, but we just got back one of the internal nodes because
we used @racket[set-cdr] on that node. In order to actually do what we want, we'd need to add a lot
more complexity:
@(interaction #:eval introduction-eval
(set-car tree (set-cdr (car tree) 'x)))
That's what we need to do just for @emph{one} level of nesting---it would be much worse for any more
than that. How can we solve it?
@subsection{Lens composition}
@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 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:
@(interaction #:eval introduction-eval
(define cdar-lens (lens-compose cdr-lens car-lens))
(lens-view cdar-lens tree)
(lens-set cdar-lens tree 'x)
(lens-transform cdar-lens tree symbol->string))
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.

View File

@ -1,12 +0,0 @@
#lang scribble/manual
@title[#:tag "user-defined-lenses" #:style 'toc]{Lenses on User-Defined Datatypes}
In addition to the built-in lenses, @racketmodname[lens] provides utilities for construcing new lenses
for user-defined datatypes. This section covers the utilities for creating lenses for Racket structs,
as well as the general lens constructors for making arbitrary lenses.
@local-table-of-contents[]
@include-section["user-defined/struct.scrbl"]
@include-section["user-defined/custom.scrbl"]

View File

@ -1,49 +0,0 @@
#lang scribble/manual
@(require scribble/eval
lens/private/doc-util/main)
@(define make-lens-eval
(make-eval-factory '(racket/base lens)))
@(define-syntax-rule (lens-interaction expr ...)
(interaction #:eval (make-lens-eval) expr ...))
@(define construct-eval (make-lens-eval))
@(define-syntax-rule (construct-interaction expr ...)
(interaction #:eval construct-eval expr ...))
@title[#:tag "construction-guide"]{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, @racket[make-lens].
The @racket[make-lens] constructor is simple---it creates a new lens from a getter function and a
(functional) setter function. That's it. A lens is nothing more than that.
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---we
just need to write getters and setters for each portion of the number:
@(construct-interaction
(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: @racket[real-part] and @racket[imag-part].
We need to implement the setters ourselves, which we can do using @racket[make-rectangular]. Now we
can actually do math on separate components of numbers using @racket[lens-transform]:
@(construct-interaction
(lens-transform real-lens 2+3i (λ (x) (* x 2)))
(lens-transform imag-lens 2+3i (λ (x) (* x 2))))
When creating a lens with @racket[make-lens], it's important to make sure it also follows the
@seclink["laws"]{lens laws}. These are simple requirements to ensure that your custom lens behaves
intuitively. Lenses that do @emph{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 @racket[lens-compose].

View File

@ -1,75 +0,0 @@
#lang scribble/manual
@(require scribble/eval
lens/private/doc-util/main)
@(define make-lens-eval
(make-eval-factory '(racket/base lens)))
@(define-syntax-rule (lens-interaction expr ...)
(interaction #:eval (make-lens-eval) expr ...))
@(define struct-eval (make-lens-eval))
@(define-syntax-rule (struct-interaction expr ...)
(interaction #:eval struct-eval expr ...))
@title[#:tag "struct-guide"]{Structures}
@see-reference-note["struct-reference"]{struct lenses}
Racket's structure system is an extremely useful tool to help model your problem domain, but using
them in a functional style can be laborious. To make this easier, @racketmodname[lens] provides helper
macros to automatically generate lenses for structure fields, which can be composed just like any
other lenses to allow easy functional programming with nested structs.
To start using lenses with structs, use the @racket[struct/lens] form when defining a structure
instead of @racket[struct]:
@(struct-interaction
(struct/lens point (x y) #:transparent))
This will define a struct called @racket[point], and it will also produce two lenses,
@racket[point-x-lens] and @racket[point-y-lens]. It's also possible to define lenses for an
@emph{existing} structure type using @racket[define-struct-lenses].
@(lens-interaction
(struct point (x y) #:transparent)
(define-struct-lenses point))
If you don't want to use the auto-definition behavior of @racket[struct/lens] or
@racket[define-struct-lenses], you can also use @racket[struct-lens] to create one-off lenses for
particular fields.
@(lens-interaction
(struct point (x y) #:transparent)
(struct-lens point x))
One created, structure lenses work just like any other lenses: they can be used with functions like
@racket[lens-view], @racket[lens-set], and @racket[lens-transform], and they can be composed with
other lenses to produce new ones.
@(struct-interaction
(lens-view point-x-lens (point 4 10))
(lens-set point-y-lens (point 4 10) 15)
(lens-transform point-x-lens (point 4 10)
(λ (x) (* x 3))))
Composition of struct lenses can make it much easier to write purely functional state transformations,
such as the “world programs” of @italic{How to Design Programs}. For example, given some state:
@(struct-interaction
(struct/lens world (play-time player-stats) #:transparent)
(struct/lens player-stats (health attack) #:transparent)
(struct/lens monster (attack) #:transparent))
It's possible to write updater functions that manipulate nested state without completely destructuring
and rebuilding the structure each time:
@(struct-interaction
(define (perform-monster-attack world monster)
(lens-transform (lens-compose player-stats-health-lens
world-player-stats-lens)
world
(λ (hp) (- hp (monster-attack monster)))))
(let ([w (world 0 (player-stats 15 6))]
[m (monster 2)])
(perform-monster-attack w m)))

View File

@ -1,9 +0,0 @@
#lang scribble/manual
@title[#:tag "lens-reference"]{The Lens Reference}
@local-table-of-contents[]
@include-section["reference/common.scrbl"]
@include-section["reference/data.scrbl"]
@include-section["../../applicable.scrbl"]

View File

@ -1,11 +0,0 @@
#lang scribble/manual
@title[#:tag "lens-common-reference"]{Lens Operations}
@defmodule[lens/common]{
Provides core lens functions and other non-data-specific lens
operations.
}
@include-section[(lib "lens/private/base/main.scrbl")]
@include-section[(lib "lens/private/compound/main.scrbl")]

View File

@ -1,16 +0,0 @@
#lang scribble/manual
@title[#:tag "lens-data-reference"]{Lenses for different types of data}
@defmodule[lens/data]{
Provides lenses and lens operations for different types of data, such
as lists and structs.
}
@include-section["../../list/main.scrbl"]
@include-section["../../hash/main.scrbl"]
@include-section["../../struct/main.scrbl"]
@include-section["../../vector/main.scrbl"]
@include-section["../../string/main.scrbl"]
@include-section["../../stream/stream.scrbl"]
@include-section["../../dict/dict.scrbl"]

View File

@ -1,10 +0,0 @@
#lang scribble/manual
@(require "../doc-util/scribble-include-no-subsection.rkt")
@title[#:tag "strings-reference"]{String Lenses}
@defmodule[lens/data/string]
@scribble-include/no-subsection["string.scrbl"]
@scribble-include/no-subsection["join-string.scrbl"]

View File

@ -1,13 +0,0 @@
#lang scribble/manual
@(require lens/private/doc-util/main
lens/private/doc-util/scribble-include-no-subsection)
@title[#:tag "struct-reference"]{Struct Lenses}
@defmodule[lens/data/struct]
@see-guide-note["struct-guide"]{struct lenses}
@scribble-include/no-subsection["field.scrbl"]
@scribble-include/no-subsection["struct.scrbl"]

View File

@ -1,82 +0,0 @@
#lang scribble/manual
@(require lens/private/doc-util/main (for-label syntax/srcloc))
@title{Syntax object source locations}
@defthing[syntax-srcloc-lens (lens/c syntax? srcloc?)]{
A lens that views the source location of a syntax object as a
@racket[srcloc] structure.
@lens-unstable-examples[
(lens-view syntax-srcloc-lens #'here)
(lens-set syntax-srcloc-lens #'here (srcloc "a.rkt" 5 8 55 13))
(syntax-source (lens-set syntax-srcloc-lens #'here (srcloc "a.rkt" 5 8 55 13)))
(syntax-position (lens-set syntax-srcloc-lens #'here (srcloc "a.rkt" 5 8 55 13)))
]}
@defthing[syntax-source-lens (lens/c syntax? any/c)]{
A lens that views the source field of a syntax object.
@lens-unstable-examples[
(lens-view syntax-source-lens #'here)
(lens-set syntax-source-lens #'here "a.rkt")
(syntax-source (lens-set syntax-source-lens #'here "a.rkt"))
]}
@defthing[syntax-line-lens (lens/c syntax? (or/c exact-positive-integer? #f))]{
A lens that views the line number of a syntax object.
@lens-unstable-examples[
(lens-view syntax-line-lens #'here)
(lens-set syntax-line-lens #'here 8)
(syntax-line (lens-set syntax-line-lens #'here 8))
]}
@defthing[syntax-column-lens (lens/c syntax? (or/c exact-nonnegative-integer? #f))]{
A lens that views the column number of a syntax object within its line.
@lens-unstable-examples[
(lens-view syntax-column-lens #'here)
(lens-set syntax-column-lens #'here 13)
(syntax-column (lens-set syntax-column-lens #'here 13))
]}
@defthing[syntax-position-lens (lens/c syntax? (or/c exact-positive-integer? #f))]{
A lens that views the source position a syntax object.
@lens-unstable-examples[
(lens-view syntax-position-lens #'here)
(lens-set syntax-position-lens #'here 21)
(syntax-position (lens-set syntax-position-lens #'here 21))
]}
@defthing[syntax-span-lens (lens/c syntax? (or/c exact-nonnegative-integer? #f))]{
A lens that views the source span a syntax object.
@lens-unstable-examples[
(lens-view syntax-span-lens #'here)
(lens-set syntax-span-lens #'here 34)
(syntax-span (lens-set syntax-span-lens #'here 34))
]}
@deftogether[[
@defthing[source-location->srcloc-lens (lens/c source-location? srcloc?)]
@defthing[source-location->list-lens (lens/c source-location? source-location-list?)]
@defthing[source-location->vector-lens (lens/c source-location? source-location-vector?)]
]]{
Lenses for converting from all the common types of source locations
into @racket[srcloc] structures, lists, and vectors.
}
@deftogether[[
@defthing[source-location-source-lens (lens/c source-location? any/c)]
@defthing[source-location-line-lens (lens/c source-location? (or/c exact-positive-integer? #f))]
@defthing[source-location-column-lens (lens/c source-location? (or/c exact-nonnegative-integer? #f))]
@defthing[source-location-position-lens (lens/c source-location? (or/c exact-positive-integer? #f))]
@defthing[source-location-span-lens (lens/c source-location? (or/c exact-nonnegative-integer? #f))]
]]{
Like @racket[syntax-source-lens], @racket[syntax-line-lens], etc, but for all
the common types of source locations.
}

View File

@ -1,97 +0,0 @@
#lang scribble/manual
@(require lens/private/doc-util/main (for-label syntax/stx))
@title{Syntax object lenses based on @racketmodname[syntax/stx]}
@defthing[stx->list-lens lens?]{
A lens that views a stx-list as a list. Viewing with this lens is
equivalent to using @racket[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.
@lens-unstable-examples[
(lens-view stx->list-lens #'(a b c))
(lens-set stx->list-lens #'(a b c) '(1 2 3))
]}
@defproc[(stx-map-lens [lens lens?]) lens?]{
Creates a lens that maps @racket[lens] over a target stx-list. Like
@racket[stx->list-lens], setting with a syntax object target preserves
lexical context, location, and properties.
This is the syntax version of @racket[map-lens].
@lens-unstable-examples[
(lens-view (stx-map-lens stx-car-lens) #'((a b) (c d) (e f)))
(lens-set (stx-map-lens stx-car-lens) #'((a b) (c d) (e f)) #'(1 2 3))
]}
@deftogether[[
@defthing[stx-car-lens lens?]
@defthing[stx-cdr-lens lens?]
]]{
Lenses for looking at the car and cdr of syntax-pairs.
These are the syntax versions of @racket[car-lens] and @racket[cdr-lens].
@lens-unstable-examples[
(lens-view stx-car-lens #'(a . b))
(lens-view stx-cdr-lens #'(a . b))
(lens-set stx-car-lens #'(a . b) #'1)
(lens-set stx-cdr-lens #'(a . b) #'1)
]}
@deftogether[[
@defthing[stx-caar-lens lens?]
@defthing[stx-cdar-lens lens?]
@defthing[stx-cadr-lens lens?]
@defthing[stx-cddr-lens lens?]
@defthing[stx-caaar-lens lens?]
@defthing[stx-cdaar-lens lens?]
@defthing[stx-cadar-lens lens?]
@defthing[stx-cddar-lens lens?]
@defthing[stx-caadr-lens lens?]
@defthing[stx-cdadr-lens lens?]
@defthing[stx-caddr-lens lens?]
@defthing[stx-cdddr-lens lens?]
]]{
Lenses for accessing nested syntax-pairs.
@lens-unstable-examples[
(lens-view stx-caddr-lens #'(a b c d))
(lens-set stx-caddr-lens #'(a b c d) #'1)
]}
@defthing[stx-append*-lens lens?]{
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 @racket[append*-lens].
@lens-unstable-examples[
(lens-view stx-append*-lens #'((a) (b c) () (d e f)))
(lens-set stx-append*-lens #'((a) (b c) () (d e f)) #'(1 2 3 4 5 6))
]}
@defproc[(stx-flatten/depth-lens [n exact-nonnegative-integer?]) lens?]{
Creates a lens that flattens a stx-list of depth @racket[n] when
viewing, and restores the original structure when setting.
This is the syntax version of @racket[flatten/depth-lens].
@lens-unstable-examples[
(lens-view (stx-flatten/depth-lens 0) #'42)
(lens-set (stx-flatten/depth-lens 0) #'42 #'(43))
(lens-view (stx-flatten/depth-lens 1) #'(a b c))
(lens-set (stx-flatten/depth-lens 1) #'(a b c) #'(1 2 3))
(lens-view (stx-flatten/depth-lens 2) #'((a) (b c) () (d e f)))
(lens-set (stx-flatten/depth-lens 2) #'((a) (b c) () (d e f)) #'(1 2 3 4 5 6))
(lens-view (stx-flatten/depth-lens 3) #'(((a) ()) (() (b) (c)) () ((d e) () (f))))
(lens-set (stx-flatten/depth-lens 3) #'(((a) ()) (() (b) (c)) () ((d e) () (f))) #'(1 2 3 4 5 6))
]}
@defproc[(stx-append*n-lens [n exact-nonnegative-integer?]) lens?]{
This is deprecated. Use @racket[(stx-flatten/depth-lens (add1 n))] instead.
}

View File

@ -1,12 +0,0 @@
#lang scribble/manual
@(require lens/private/doc-util/scribble-include-no-subsection)
@title[#:tag "vectors-reference"]{Vector lenses}
@defmodule[lens/data/vector]
@scribble-include/no-subsection["ref.scrbl"]
@scribble-include/no-subsection["nested.scrbl"]
@scribble-include/no-subsection["pick.scrbl"]
@scribble-include/no-subsection["join-vector.scrbl"]

Some files were not shown because too many files have changed in this diff Show More