#lang scribble/doc @(require scribble/manual scribble/eval scheme/sandbox "private/planet.ss") @(require (for-label scheme/base "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 #f #f)]) (make-evaluator 'scheme/base #:requires (list 'scheme/class "main.ss")))) @title{@bold{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[] @defmodule/this-package[] The default 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) ] } @deftogether[( @defproc[((curry* [f procedure?] [#:i a any/c] ... [x any/c] ...) [#:j b any/c] ... [y any/c] ...) any/c] @defproc[((curryr* [f procedure?] [#:i a any/c] ... [x any/c] ...) [#:j b any/c] ... [y any/c] ...) any/c] )]{ Partial applications of a function; @scheme[curry*] provides the left arguments first, while @scheme[curryr*] provides the right arguments first. @schemeblock[((curry* f x ...) y ...) = (f x ... y ...)] @schemeblock[((curryr* f x ...) y ...) = (f y ... x ...)] @defexamples[ #:eval (evaluator) (define (f a [b 0] #:x x #:y [y 0]) (list a b x y)) ((curry* f 1) #:x 2) ((curry* f 1 #:x 3) 2 #:y 4) ((curryr* f 1) #:x 2) ((curryr* f 1 #:x 3) 2 #:y 4) ] } @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 "queue"]{Imperative Queues} @defmodule/this-package[queue] This module provides a mutable queue representation. @defproc[(make-queue) queue/c]{ Produces an empty queue. } @defproc[(enqueue! [q queue/c] [v any/c]) void?]{ Adds an element to the back of a queue. } @defproc[(dequeue! [q nonempty-queue/c]) any/c]{ Removes an element from the front of a nonempty queue, and returns that element. @defexamples[ #:eval (evaluator) (define q (make-queue)) (enqueue! q 1) (dequeue! q) (dequeue! q) (enqueue! q 2) (enqueue! q 3) (dequeue! q) (dequeue! q) (dequeue! q) ] } @defproc[(queue-empty? [q queue/c]) boolean?]{ Recognizes whether a queue is empty or not. @defexamples[ #:eval (evaluator) (define q (make-queue)) (queue-empty? q) (enqueue! q 1) (queue-empty? q) (dequeue! q) (queue-empty? q) ] } @defproc[(queue? [v any/c]) boolean?]{ This predicate recognizes queues. @defexamples[ #:eval (evaluator) (queue? (make-queue)) (queue? 'not-a-queue) ] } @deftogether[( @defthing[queue/c flat-contract?] @defthing[nonempty-queue/c flat-contract?] )]{ These contracts recognize queues; the latter requires the queue to contain at least one value. } @section[#:tag "syntax"]{Syntax Objects} @defmodule/this-package[syntax] This module provides tools for macro transformers. @defproc[(syntax-datum/c [datum/c any/c]) flat-contract?]{ Recognizes syntax objects @scheme[stx] such that @scheme[(syntax->datum stx)] satisfies @scheme[datum/c]. } @defproc[(syntax-listof/c [elem/c any/c]) flat-contract?]{ Recognizes syntax objects @scheme[stx] such that @scheme[(syntax->list stx)] satisfies @scheme[(listof elem/c)]. } @defproc[(syntax-list/c [elem/c any/c] ...) 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)) ] } @defthing[current-syntax (parameter/c (or/c syntax? false/c))]{ A parameter that may be used to store the current syntax object being transformed. It is not used by the expander; you have to assign to it yourself. This parameter is used by @scheme[syntax-error], below. It defaults to @scheme[#f]. } @defproc[(syntax-error [stx syntax?] [fmt string?] [arg any/c] ...) none/c]{ Raises a syntax error based on the locations of @scheme[(current-syntax)] and @scheme[stx], with @scheme[(format fmt arg ...)] as its message. @defexamples[ #:eval (evaluator) (define stx #'(a b c)) (parameterize ([current-syntax #f]) (syntax-error stx "~s location" 'general)) (parameterize ([current-syntax stx]) (syntax-error (car (syntax-e stx)) "~s location" 'specific)) ] } @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)) (text=? '#:z #"y") ] } @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)) (text>? '#:z #"y") ] } @defproc[(textsyntax #f 'x)) (textlist 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)) ] }