From 34176f6dcf0ab73e0406853c34e95b2d285d9d2a Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 20 Mar 2016 11:36:37 -0400 Subject: [PATCH] [scribblings] update with new applications --- trivial/scribblings/Makefile | 2 +- trivial/scribblings/trivial.scrbl | 168 ++++++++++++++++-------------- 2 files changed, 92 insertions(+), 78 deletions(-) diff --git a/trivial/scribblings/Makefile b/trivial/scribblings/Makefile index 249dbf4..585ade9 100644 --- a/trivial/scribblings/Makefile +++ b/trivial/scribblings/Makefile @@ -1,5 +1,5 @@ all: - raco6.3 scribble \ + raco scribble \ --html \ ++main-xref-in \ --redirect-main http://docs.racket-lang.org/ \ diff --git a/trivial/scribblings/trivial.scrbl b/trivial/scribblings/trivial.scrbl index aacda0a..d46d525 100644 --- a/trivial/scribblings/trivial.scrbl +++ b/trivial/scribblings/trivial.scrbl @@ -9,16 +9,16 @@ @defmodule[trivial] @(define trivial-eval (make-base-eval #:lang 'typed/racket/base '(begin (require trivial)))) -This library exports a collection of @hyperlink["http://www.greghendershott.com/fear-of-macros/"]{macros} that implement statically-checked interfaces to standard library functions. +This library exports a collection of @hyperlink["http://www.greghendershott.com/fear-of-macros/"]{macros} for improved standard library functions. All exported macros are named with a trailing colon (meant as a hint that some extra type-checking may happen at the call site).@note{ - Not a fan of the colon convention? @racket[trivial/no-colon] provides the same identifiers, colon-free. + Not happy with the colon convention? @racket[trivial/no-colon] provides the same identifiers, colon-free. Same goes for each sub-collection, for instance you can require @racket[trivial/math/no-colon]. } @emph{Hidden Agenda:} Macros are good for both (1) @emph{syntax transformations} and (2) @emph{syntax analysis}. -We use these powers to (1) derive type constraints and (2) convince Typed Racket that the end result is well-typed. +We use these powers to derive type constraints and convince Typed Racket that the end result is well-typed. Each section below describes one source file. @@ -31,11 +31,11 @@ These files may be @racket[require]-d individually, or all at once using @racket Racket's @racket[format] function expects a template string and a few extra arguments. The extra arguments must match the sequence of @hyperlink["http://docs.racket-lang.org/reference/Writing.html#%28def._%28%28quote._~23~25kernel%29._fprintf%29%29"]{formatting escapes} in the template string. -Normally, the arguments are validated at runtime. -We can do (slightly) better! +The arguments are normally validated at runtime, but our @racket[format:] macro + will check arity and argument types at compile-time, if the format string is a constant. @defform[(format: s e* ...)]{ - If @racket[s] is a string literal, parse the format escapes from @racket[s] and use them to generate constraints on the extra arguments @racket[e*]. + If the sequence of format directives in @racket[s] are known, generate constraints on the extra arguments @racket[e*]. There must be one element in @racket[e*] for each format escape, and argument types must satisfy the type constraint of their matching format escape. If @racket[s] is not a string literal, do not perform static checks. @@ -46,6 +46,8 @@ We can do (slightly) better! (format: "hello, ~a" 'world) (format: "hello, ~a" 'too "many" 'args) (format: "binary = ~b" 3.14) + (let: ([fmt "non-binary = ~b"]) + (format: fmt "Type Error!")) ] @@ -75,6 +77,7 @@ In other words, the result is one string for the matched substring and an unknow @emph{Note:} the regular expression @racket{|} operator is not currently supported because it can nullify some groups. Using the @racket{|} will result is a less-precise type. + Same goes for groups followed by a Kleene star. @examples[#:eval trivial-eval @@ -82,85 +85,33 @@ In other words, the result is one string for the matched substring and an unknow (U #f (List String String))) (ann (regexp-match: #rx"left|ri(g*)ht" "riggght") (U #f (List String String))) + (ann (regexp-match: #rx"(a)*(b)" "bbb") + (U #f (List String String String))) ] Large regular expression patterns are expensive to compile, so we offer a definition form that remembers the number of groups in a pattern for later calls to @racket[regexp-match:]. -@defform*[((define-regexp: id pattern) - (define-pregexp: id pattern) - (define-byte-regexp: id pattern) - (define-byte-pregexp: id pattern))]{} +@defform*[((regexp: id pattern) + (pregexp: id pattern) + (byte-regexp: id pattern) + (byte-pregexp: id pattern))]{} -@examples[#:eval trivial-eval - (let () - (define-regexp: rx-address "([0-9]+) ([a-z]+) ") - (ann (regexp-match: rx-address "1 main street") - (U #f (List String String String)))) - (let () - (define-pregexp: px-age "([0-9][0-9][0-9]) years old$") - (ann (regexp-match: px-age "forever young") - (U #f (List String String)))) - (let () - (define-byte-regexp: bx-logmsg #rx#"^LOG: (.*)$") - (ann (regexp-match: bx-logmsg #"LOG: full steam ahead") - (U #f (List Bytes Bytes)))) - (let () - (define-byte-pregexp: bpx-red #px#"red") - (ann (regexp-match: bpx-red #"blue") - (U #f (List Bytes)))) -] - -Similarly, @racket[let-regexp:] and friends are expression-binding forms. -For both the @racket[define-] and @racket[let-] forms, the @racket[pattern] may be any expression. -The only difference is that regular expression metadata is checked and preserved if present. - -@defform*[((let-regexp: ([id expr] ...) expr ...) +@defform*[((define-regexp: ([id expr] ...) expr ...) + (define-pregexp: ([id expr] ...) expr ...) + (define-byte-regexp: ([id expr] ...) expr ...) + (define-byte-pregexp: ([id expr] ...) expr ...) + (let-regexp: ([id expr] ...) expr ...) (let-pregexp: ([id expr] ...) expr ...) (let-byte-regexp: ([id expr] ...) expr ...) (let-byte-pregexp: ([id expr] ...) expr ...))]{} -@examples[#:eval trivial-eval - (let-regexp: ([hamburger (regexp: "^{{.*beef.*}}$")]) - (ann (regexp-match: hamburger "{{ lettuce tomato cheese }}") - (U #f (List String)))) - (let-pregexp: ([bigmac (pregexp: "(bun).*(bun).*(bun)")]) - (ann (regexp-match: bigmac "bun tomato bun meat bun") - (U #f (List String String String String)))) - (let-byte-regexp: ([low-carb (byte-regexp: #"^([^bun]*)meat([^bun]*)$")]) - (ann (regexp-match: low-carb "meat meat meat") - (U #f (List Bytes Bytes Bytes)))) - (let-byte-pregexp: ([double-diet (byte-pregexp: #" {2}")]) - (ann (regexp-match: double-diet " ") - (U #f (List Bytes)))) -] - - -The following expression forms also cache the number of pattern groups. -Use these instead of the standard defaults. - -@defform*[((regexp: pattern) - (pregexp: pattern) - (byte-regexp: pattern) - (byte-pregexp: pattern))]{} - -@examples[#:eval trivial-eval - (let () - (define-regexp: rx-email (regexp: "^(.*)@(.*)\\.com$")) - (ann (regexp-match: rx-email "admin@internet.com") - (U #f (List String String String)))) - (let () - (define-regexp: rxp (regexp "missing(.*)colon")) - (ann (regexp-match: rxp "miss") - (U #f (List String String)))) -] - @section{Mathematical Operators} @defmodule[trivial/math] Typed Racket has special types for @racket[Zero] and @racket[One]. -The special cases are often convenient, but can lead to confusing behavior. +The special cases are often convenient, but can lead to confusing behavior.@note{See @hyperlink["https://groups.google.com/forum/#!searchin/racket-users/klaus/racket-users/BfA0jsXrioo/yFhMLkl_AQAJ"]{this email thread} for some confusion.} @examples[#:eval trivial-eval (ann (- 1 1) Zero) @@ -172,7 +123,8 @@ The following operators special-case all numeric constants, uniformly sweeping t @defform*[((+: e* ...) (-: e* ...) (*: e* ...) - (/: e* ...))]{ + (/: e* ...) + (expt: e1 e2))]{ Similar to the corresponding @racket[racket/base] operators, but reduces every pair of numeric constants in @racket[e* ...] during @emph{macro expansion}. } @@ -181,18 +133,80 @@ The following operators special-case all numeric constants, uniformly sweeping t (ann (-: 2 2) Zero) (ann (-: 99 (+: 9 (*: 9 10))) Zero) (ann (+: 1 (/: 1 3)) 4/3) - ((lambda ([f : (-> Natural Natural Natural)]) (f 1 1)) +:) ] -Again, only constants are reduced. +Only constants are reduced. Any non-constants will upset the static analysis. @examples[#:eval trivial-eval - (let ([n 2]) + (lambda ([n : Integer]) (ann (-: n 2) Zero)) ] -We'll see if this is ever useful! +However, an expression such as @racket[(+ 1 2 n)] will compile to @racket[(+ 3 n)]. + + +@section{Arity Awareness} +@defmodule[trivial/function] + +When the arity of a function @racket[f] is known, we replace calls + to generic functions like @racket[curry] with specialized versions. + +@defform*[((curry: f) + (map: f e* ...))]{ + If @racket[f] accepts 2 arguments, @racket[(curry f)] returns a function + of one argument @racket[x] which returns a function of one argument @racket[y] + which returns @racket[(f x y)]. + + Likewise, @racket[map f e* ...] expects 2 lists in place of @racket[e*]. +} + + +@section{Sized Vectors} +@defmodule[trivial/vector] + +These vector operations store and update a vector's length information. + +@defform*[((vector: e* ...) + (make-vector: i e) + (build-vector: i e) + (vector-length: v) + (vector-ref: v i) + (vector-set!: v i k) + (vector-map f v* ...) + (vector-map! f v* ...) + (vector-append: v1 v2) + (vector->list: v) + (vector->immutable-vector: v) + (vector-fill: v k) + (vector-take: v i) + (vector-take-right: v i) + (vector-drop: v i) + (vector-drop-right: v i))]{} + +@examples[#:eval trivial-eval + (vector-ref: (vector-append: '#(A) '#(B)) 0) + (vector-ref: (vector-append: '#(A) '#(B)) 2) +] + + +@section{Binding forms} +@defmodule[trivial/define] + +The @racket[let:] and @racket[define:] forms infer and propagate data about their + bindings. + +@examples[#:eval trivial-eval + (let: ([n 0]) + (ann (/ 3 3) One)) + (let ([n 0]) + (ann (/ 3 3) One)) +] + + +@section{Further Reading} + +Short essay: @hyperlink["http://www.ccs.neu.edu/home/types/publications/letter-of-values/macro-goggles.pdf"]{here}. @section{Credits} @@ -200,9 +214,9 @@ We'll see if this is ever useful! Inspired by: @itemlist[ @item{@hyperlink["http://www.ccs.neu.edu/home/samth/"]{Sam Tobin-Hochstadt}} +@item{@hyperlink["http://www.ccs.neu.edu/home/stchang/"]{Stephen Chang}} @item{@hyperlink["http://www.ccs.neu.edu/home/matthias/"]{Matthias Felleisen}} @item{@hyperlink["http://www.ccs.neu.edu/home/dherman/research/papers/icfp04-dsl-analysis.pdf"]{David Herman and Philippe Meunier}} -@item{The @hyperlink["http://fsl.cs.illinois.edu/images/5/5e/Cayenne.pdf"]{Cayenne} programming language} ] -The views and code expressed in this library do not necessarily reflect the good taste and decency of the aforementioned sources of inspiration. +The views/code expressed in this library do not necessarily reflect the good taste and decency of the aforementioned sources of inspiration.