#lang scribble/doc @(require scribble/manual scribble/eval scheme/sandbox "private/planet.ss") @(require (for-label scheme "main.ss" "class.ss" "function.ss" "syntax.ss" "text.ss" "values.ss")) @(define (evaluator) (parameterize ([sandbox-output 'string] [sandbox-error-output 'string] [sandbox-path-permissions '([read #rx#""])] [sandbox-eval-limits (list 10 40)]) (make-evaluator 'scheme #:requires (list "main.ss")))) @title{Scheme Utilities in @scheme/this-package[]} This is my personal library of PLT Scheme programming utilities. Feel free to use it, copy my code, or ask me questions. @table-of-contents[] @section[#:tag "main"]{Main Module} @defmodule/this-package[] This module provides all the bindings described below under the individual modules. @section[#:tag "class"]{Classes and Objects} @defmodule/this-package[class] This module provides tools for classes, objects, and mixins. @defthing[class-or-interface/c flat-contract?]{ Recognizes classes and interfaces. } @defproc[(object/c [spec class-or-interface/c] ...) flat-contract?]{ Recognizes objects which are instances of all the given classes and interfaces. } @defproc[(class/c [spec class-or-interface/c] ...) flat-contract?]{ Recognizes classes which are subclasses (not strictly) and implementations, respectively, of all the given classes and interfaces. } @defform[(mixin/c [super-expr ...] [arg-expr ...] [sub-expr ...])]{ Function contract for a mixin whose first argument is the parent class @var[c%] matching @scheme[(class/c super-expr ...)], whose remaining arguments match @scheme[arg-expr ...], and whose result matches @scheme[(class/c #,(var c%) sub-expr ...)]. } @defproc[(ensure-interface [i<%> interface?] [mx (mixin/c [] [] i<%>)] [c% class?]) (class/c c% i<%>)]{ Returns @scheme[c%] if it implements @scheme[i<%>]; otherwise, returns @scheme[(mx c%)]. } @defform[(send+ obj [message arg ...] ...)]{ Sends each message (with arguments) to @scheme[obj], then returns @scheme[obj]. @defexamples[ #:eval (evaluator) (define c% (class object% (super-new) (define/public (say msg) (printf "~a!\n" msg)))) (send+ (new c%) [say 'Hello] [say 'Good-bye]) ] } @defform[(send-each objs message arg ...)]{ Sends the message to each object in the list @scheme[objs], returning @scheme[(void)]. @defexamples[ #:eval (evaluator) (define c% (class object% (super-new) (init-field msg) (define/public (say to) (printf "~a, ~a!\n" msg to)))) (send-each (list (new c% [msg 'Hello]) (new c% [msg 'Good-bye])) say 'World) ] } @section[#:tag "function"]{Functions} @defmodule/this-package[function] This module provides tools for higher-order programming and creating functions. @defproc[(identity [v any/c]) (one-of/c v)]{ This function returns its argument. } @defproc[(constant [v any/c]) (unconstrained-> (one-of/c v))]{ Produces a function that returns @scheme[v] regardless of input. @defexamples[ #:eval (evaluator) (define f (constant (gensym))) (f) (f '(4 5 6)) (f #:x 7 #:y 8 #:z 9) ] } @defform[(thunk body ...)]{ Creates a function that ignores its inputs and evaluates the given body. Useful for creating event handlers with no (or irrelevant) arguments. @defexamples[ #:eval (evaluator) (define f (thunk (define x 1) (printf "~a\n" x))) (f) (f 'x) (f #:y 'z) ] } @defproc[(call [f (-> A ... B)] [arg A] ...) B]{ Calls its first argument, passing on any remaining arguments. Useful for invoking thunks in a higher-order context (for instance, as an argument to @scheme[map] or @scheme[for-each]). @defexamples[ #:eval (evaluator) (define (say [msg "Hello"] #:to [to "World"]) (printf "~a, ~a!\n" msg to)) (call say) (call say "Good night" #:to "Moon") ] } @defproc[((conjoin [f (-> A ... boolean?)] ...) [arg A] ...) boolean?]{ Combines calls to each function with @scheme[and]. @defexamples[ #:eval (evaluator) (define f (conjoin exact? integer?)) (f 1) (f 1.0) (f 1/2) (f 0.5) ] } @defproc[((disjoin [f (-> A ... boolean?)] ...) [arg A] ...) boolean?]{ Combines calls to each function with @scheme[or]. @defexamples[ #:eval (evaluator) (define f (disjoin exact? integer?)) (f 1) (f 1.0) (f 1/2) (f 0.5) ] } @defform/subs[ (lambda/parameter (param-arg ...) body ...) ([param-arg param-arg-spec (code:line keyword param-spec)] [param-arg-spec id [id default-expr] [id #:param param-expr]]) ]{ Constructs a function much like @scheme[lambda], except that some optional arguments correspond to the value of a parameter. For each clause @scheme[[id #:param param-expr]] in which @scheme[param-expr] evalutes to a value @scheme[param] satisfying @scheme[parameter?], the default value of @scheme[id] is @scheme[(param)]; conversely, when @scheme[id] is provided, @scheme[param] is bound to @scheme[id] via @scheme[parameterize] during the function call. @defexamples[ #:eval (evaluator) (define p (open-output-string)) (define hello-world (lambda/parameter ([port #:param current-output-port]) (display "Hello, World!") (newline port))) (hello-world p) (get-output-string p) ] } @section[#:tag "syntax"]{Syntax Objects} @defmodule/this-package[syntax] This module provides tools for macro transformers. @defproc[(syntax-datum/c [datum/c flat-contract/predicate?]) flat-contract?]{ Recognizes syntax objects @scheme[stx] such that @scheme[(syntax->datum stx)] satisfies @scheme[datum/c]. } @defproc[(syntax-listof/c [elem/c flat-contract/predicate?]) flat-contract?]{ Recognizes syntax objects @scheme[stx] such that @scheme[(syntax->list stx)] satisfies @scheme[(listof elem/c)]. } @defproc[(syntax-list/c [elem/c flat-contract/predicate?] ...) flat-contract?]{ Recognizes syntax objects @scheme[stx] such that @scheme[(syntax->list stx)] satisfies @scheme[(list/c elem/c ...)]. } @defproc[(syntax-map [f (-> syntax? A)] [stx syntax?]) (listof A)]{ Performs @scheme[(map f (syntax->list stx))]. @defexamples[ #:eval (evaluator) (syntax-map syntax-e #'(a (b c) d)) ] } @defproc[(to-syntax [datum any/c] [#:stx stx (or/c false/c syntax?) #f] [#:src src (or/c false/c syntax?) #f] [#:ctxt ctxt (or/c false/c syntax?) #f] [#:prop prop (or/c false/c syntax?) #f] [#:cert cert (or/c false/c syntax?) #f]) syntax?]{ A wrapper for @scheme[datum->syntax] with keyword arguments for the syntax properties. Users may leave them all off for unadorned syntax objects, or use the "master" keyword @scheme[#:stx] to set all properties from one syntax object, or use the other keywords for individual kinds of properties, or some combination thereof. @defexamples[ #:eval (evaluator) (define blank-stx (to-syntax 'car)) blank-stx (syntax-e blank-stx) (free-identifier=? blank-stx #'car) (define full-stx (to-syntax 'car #:stx #'here)) full-stx (syntax-e full-stx) (free-identifier=? full-stx #'car) (define partial-stx (to-syntax 'car #:ctxt #'here)) partial-stx (syntax-e partial-stx) (free-identifier=? partial-stx #'car) ] } @defproc[(to-datum [v any/c]) (not/c syntax?)]{ A deeply-traversing version of @scheme[syntax->datum]; this copies @scheme[v], descending into pairs, vectors, and prefab structures, converting syntax objects to the data they contain along the way. @defexamples[ #:eval (evaluator) (to-datum '(a b c d)) (to-datum #'(a b c d)) (to-datum (list 'a #'(b c) 'd)) ] } @defform[(with-syntax* ([pattern expr] ...) body ...+)]{ Like @scheme[with-syntax], but with nested scope. @defexamples[ #:eval (evaluator) (with-syntax* ([a #'id] [b #'a]) (syntax-e #'b)) ] } @section[#:tag "text"]{Text Representations} @defmodule/this-package[text] This module provides tools for manipulating and converting textual data. @deftogether[( @defthing[text/c flat-contract?]{} @defproc[(text? [v any/c]) boolean?]{} )]{ This contract and predicate recognize text values: strings, byte strings, symbols, and keywords, as well as syntax objects containing them. @defexamples[ #:eval (evaluator) (text? "text") (text? #"text") (text? 'text) (text? '#:text) (text? #'"text") (text? #'#"text") (text? #'text) (text? #'#:text) (text? '(not text)) ] } @deftogether[( @defproc[(string-literal? [v any/c]) boolean?]{} @defproc[(bytes-literal? [v any/c]) boolean?]{} @defproc[(keyword-literal? [v any/c]) boolean?]{} )]{ These predicates recognize specific text types stored in syntax objects. @defexamples[ #:eval (evaluator) (string-literal? #'"literal") (string-literal? "not literal") (bytes-literal? #'#"literal") (bytes-literal? #"not literal") (keyword-literal? #'#:literal) (keyword-literal? '#:not-literal) ] } @deftogether[( @defproc[(text->string [text text/c] ...) string?]{} @defproc[(text->bytes [text text/c] ...) bytes?]{} @defproc[(text->symbol [text text/c] ...) symbol?]{} @defproc[(text->keyword [text text/c] ...) keyword?]{} )]{ These functions convert text values to specific types, retaining their textual content and concatenating text when necessary. @defexamples[ #:eval (evaluator) (text->string #"concat" #'enate) (text->bytes 'concat #'#:enate) (text->symbol '#:concat #'"enate") (text->keyword "concat" #'#"enate") ] } @deftogether[( @defproc[(text->string-literal [text text/c] ... [#:stx stx (or/c syntax? false/c) #f]) string-literal?]{} @defproc[(text->bytes-literal [text text/c] ... [#:stx stx (or/c syntax? false/c) #f]) bytes-literal?]{} @defproc[(text->identifier [text text/c] ... [#:stx stx (or/c syntax? false/c) #f]) identifier?]{} @defproc[(text->keyword-literal [text text/c] ... [#:stx stx (or/c syntax? false/c) #f]) keyword-literal?]{} )]{ These functions convert text values to specific syntax object types, retaining their textual value, concatenating text when necessary, and deriving syntax object properties from the @scheme[stx] argument. @defexamples[ #:eval (evaluator) (define (show stx) (values stx (syntax-e stx))) (show (text->string-literal #"concat" #'enate)) (show (text->bytes-literal 'concat #'#:enate)) (show (text->identifier '#:concatenate #:stx #'props)) (show (text->keyword-literal "concatenate" #:stx #'props)) ] } @defproc[(text-append [text text/c] ...) text/c]{ This function appends multiple text values, producing a text value of arbitrary type with the concatenated content. @defexamples[ #:eval (evaluator) (text-append "a" #'"b" #"c" #'#"d" 'e #'f '#:g #'#:h) ] } @defproc[(text=? [one text/c] [two text/c]) boolean?]{ Compares the character content of two text values. @defexamples[ #:eval (evaluator) (text=? 'a "b") (text=? #'x (datum->syntax #f 'x)) ] } @section[#:tag "values"]{Multiple Values} @defmodule/this-package[values] This module provides tools for functions producing multiple values. @defform[(values->list expr)]{ Produces a list of the values returned by @scheme[expr]. @defexamples[ #:eval (evaluator) (values->list (values 1 2 3)) ] } @defproc[(map2 [f (-> A ... (values B C))] [lst (listof A)] ...) (values (listof B) (listof C))]{ Produces a pair of lists of the respective values of @scheme[f] applied to the elements in @scheme[lst ...] sequentially. @defexamples[ #:eval (evaluator) (map2 (lambda (x) (values (+ x 1) (- x 1))) (list 1 2 3)) ] } @defproc[(map/values [n natural-number/c] [f (-> A ... (values B_1 ... B_n))] [lst (listof A)] ...) (values (listof B_1) ... (listof B_n))]{ Produces lists of the respective values of @scheme[f] applied to the elements in @scheme[lst ...] sequentially. @defexamples[ #:eval (evaluator) (map/values 3 (lambda (x) (values (+ x 1) x (- x 1))) (list 1 2 3)) ] } @deftogether[( @defproc[(foldr/values [f (-> A ... B ... (values B ...))] [vs (list/c B ...)] [lst (listof A)] ...) (values B ...)] @defproc[(foldl/values [f (-> A ... B ... (values B ...))] [vs (list/c B ...)] [lst (listof A)] ...) (values B ...)] )]{ These functions combine the values in the lists @scheme[lst ...] using the multiple-valued function @scheme[f]; @scheme[foldr/values] traverses the lists right to left and @scheme[foldl/values] traverses left to right. @defexamples[ #:eval (evaluator) (define (add/cons a b c d) (values (+ a c) (cons b d))) (foldr/values add/cons (list 0 null) (list 1 2 3 4) (list 5 6 7 8)) (foldl/values add/cons (list 0 null) (list 1 2 3 4) (list 5 6 7 8)) ] }