etc.ss: Useful Procedures and Syntax

To load: (require (lib "etc.ss"))

(begin-lifted expr ···1)      SYNTAX

Lifts the exprs so that they are evaluated once at the ``top level'' of the current context, and the result of the last expr is used for every evaluation of the begin-lifted form.

When this form is used as a run-time expression within a module, the ``top level'' corresponds to the module's top level, so that each expr is evaluated once for each invocation of the module. When it is used as a run-time expression outside of a module, the ``top level'' corresponds to the true top level. When this form is used in a define-syntax, letrec-syntax, etc. binding, the ``top level'' corresponds to the beginning of the binding's right-hand side. Other forms may redefine ``top level'' (using local-expand/capture-lifts) for the expressions that they enclose.

(begin-with-definitions defn-or-expr ···)      SYNTAX

Supports a mixture of expressions and mutually recursive definitions, much like a module body. Unlike in a module, however, syntax definitions cannot be used to generate other immediate definitions (though they can be used for expressions).

The result of the begin-with-definitions form is the result of the last defn-or-expr if it is an expression, void otherwise. If no defn-or-expr is provided (after flattening begin forms), the result is void.

(boolean=? bool1 bool2)      PROCEDURE

Returns #t if bool1 and bool2 are both #t or both #f, and returns #f otherwise. If either bool1 or bool2 is not a Boolean, the exn:fail:contract exception is raised.

(build-list n f)      PROCEDURE

Creates a list of n elements by applying f to the integers from 0 to n - 1 in order, where n is a non-negative integer. If r is the resulting list, (list-ref r i) is (f i).

(build-string n f)      PROCEDURE

Creates a string of length n by applying f to the integers from 0 to n - 1 in order, where n is a non-negative integer and f returns a character for the n invocations. If r is the resulting string, (string-ref r i) is (f i).

(build-vector n f)      PROCEDURE

Creates a vector of n elements by applying f to the integers from 0 to n - 1 in order, where n is a non-negative integer. If r is the resulting vector, (vector-ref r i) is (f i).

(compose f ···1)      PROCEDURE

Returns a procedure that composes the given functions, applying the last f first and the first f last. The composed functions can consume and produce any number of values, as long as each function produces as many values as the preceding function consumes.

For example, (compose f g) returns the equivalent of (lambda l (call-with-values (lambda () (apply g l)) f)).

(define-syntax-set (identifier ···) defn ···)      SYNTAX

This form is similar to define-syntaxes, but instead of a single body expression, a sequence of definitions follows the sequence of defined identifiers. For each identifier, the defns should include a definition for identifier/proc. The value for identifier/proc is used as the (expansion-time) value for identifier.

The define-syntax-set form is especially useful for defining a set of syntax transformers that share helper functions.

Example:

(define-syntax-set (let-current-continuation let-current-escape-continuation)
  (define (mk call-id)
     (lambda (stx)
       (syntax-case stx ()
         [(_ id body1 body ...) 
          (with-syntax ([call call-id])
            (syntax (call (lambda (id) body1 body ...))))])))
  (define let-current-continuation/proc (mk (quote-syntax call/cc)))
  (define let-current-escape-continuation/proc (mk (quote-syntax call/ec))))

(evcase key-expr (value-expr body-expr ···) ···1)      SYNTAX

The evcase form is similar to case, except that expressions are provided in each clause instead of a sequence of data. After key-expr is evaluated, each value-expr is evaluated until a value is found that is eqv? to the key value; when a matching value is found, the corresponding body-exprs are evaluated and the value(s) for the last is the result of the entire evcase expression.

A value-expr can be the special identifier else. This identifier is recognized as in case (see section 2.3 in PLT MzScheme: Language Manual).

false      BOOLEAN

Boolean false.

(identity v)      PROCEDURE

Returns v.

(let+ clause body-expr ···1)      SYNTAX

A new binding construct that specifies scoping on a per-binding basis instead of a per-expression basis. It helps eliminate rightward-drift in programs. It looks similar to let, except each clause has an additional keyword tag before the binding variables.

Each clause has one of the following forms:

The clauses bind left-to-right. Each target above can either be an identifier or (values variable ···). In the latter case, multiple values returned by the corresponding expression are bound to the multiple variables.

Examples:

(let+ ([val (values x y) (values 1 2)])
   (list x y)) ; => '(1 2)

(let ([x 1])
   (let+ ([val x 3]
          [val y x])
      y)) ; => 3

(local (definition ···) body-expr ···1)      SYNTAX

This is a binding form similar to letrec, except that each definition is a define-values expression (after partial macro expansion). The body-exprs are evaluated in the lexical scope of these definitions.

(loop-until start done? next f)      PROCEDURE

Repeatedly invokes the f procedure until the done? procedure returns #t. The procedure is best described by its implementation:

(define loop-until
  (lambda (start done? next f)
    (let loop ([i start])
      (unless (done? i)
        (f i)
        (loop (next i))))))

(namespace-defined? symbol)      PROCEDURE

Returns #t if namespace-variable-value would return a value for symbol, #f otherwise. See section 8.2 in PLT MzScheme: Language Manual for further information.

(nand expr ···)      SYNTAX

Returns (not (and expr ···)).

(nor expr ···)      SYNTAX

Returns (not (or expr ···)).

(opt-lambda formals body-expr ···1)      SYNTAX

The opt-lambda form is like lambda, except that default values are assigned to arguments (C++-style). Default values are defined in the formals list by replacing each variable by [variable default-value-expression]. If a variable has a default value expression, then all (non-aggregate) variables after it must have default value expressions. A final aggregate variable can be used as in lambda, but it cannot be given a default value. Each default value expression is evaluated only if it is needed. The environment of each default value expression includes the preceding arguments.

For example:

(define f
  (opt-lambda (a [b (add1 a)] . c)
     ...))

In the example, f is a procedure which takes at least one argument. If a second argument is specified, it is the value of b, otherwise b is (add1 a). If more than two arguments are specified, then the extra arguments are placed in a new list that is the value of c.

See also section 25 for a library that generalizes both optional and keyword arguments.

(recur name bindings body-expr ···1)      SYNTAX

This is equivalent to a named let: (let name bindings body-expr ···1).

(rec name value-expr)      SYNTAX

This is equivalent to a letrec expression that returns its binding: (letrec ((name value-expr)) name).

(rec (name id ...) expr)      SYNTAX

(rec (name id ... . id) expr)      SYNTAX

These two are shorthands for the use of rec above, much like define allows shorthands for defining procedures. In particular the first one expands into a use of rec bound to a lambdaexpression whose body is expr and whose parameters are id .... The second is like the first, but with a rest argument.

(symbol=? symbol1 symbol2)      PROCEDURE

Returns #t if symbol1 and symbol2 are equivalent (as determined by eq?), #f otherwise. If either symbol1 or symbol2 is not a symbol, the exn:fail:contract exception is raised.

(this-expression-source-directory)      SYNTAX

Note: see section 40 for a definition form that works better when creating executables.

Expands to an expression that evaluates to the name of the directory of the file containing the source expression. The source expression's file is determined through source location information associated with the syntax, if it is present. Otherwise, current-load-relative-directory is used if it is not #f, and current-directory is used if all else fails.

If the expression has a source module, then the expansion attempts to determine the module's run-time location. This location is determined by preserving the original expression as a syntax object, extracting its source module path at run time, and then resolving the module path.

If the expression has no source, or if no directory can be determined at run time, the expansion falls back to using source-location information associated with the expression. A simple (bytes->path #"...") expression is used, unless the directory is within the result of find-collects-dir from (lib "dirs.ss" "setup"), in which case the expansion records the path relative to (find-collects-dir) and then reconstructs it using (find-collects-dir) at run time.

(this-expression-file-name)      SYNTAX

Expands to an expression that evaluates to the file name of the source expression. The source expression's file name is determined through source location information associated with the syntax if it is present. If this information is missing, or is not a path (e.g., for a standard-input expression), then #f will be used instead.

true      BOOLEAN

Boolean true.

(hash-table 'flag ...(key value) ...)      SYNTAX

This creates a new hash-table providing the quoted flags (if any) to make-hash-table, and make each of the keys map to the corresponding values. (Flags must be specified by a quoted form.)