doc.txt

Unlib : A Utility Library by Untyped

_Unlib_ : A Utility Library by Untyped
=======================

By   Dave Gurnell (djg at untyped dot com)
and Noel Welsh   (noel at untyped dot com)

This manual documents Unlib version 1.0
Time-stamp: <2006-07-12 13:11:00 noel>

Keywords: _unlib_


Introduction
========

Unlib is a library of utility functions that have arisen
from work within Untyped (www.untyped.com).  It is available
under the LGPL (same licence as PLT Scheme).  In addition to
releases via PLaneT the source may be checked out of
Subversion.  The Subversion URL is:

  https://ssl.untyped.com/svn/repos/untyped.com/unlib/


Unlib API
======


base.ss
------

> exn:unlib

Basic exception thrown when Unlib encounters an error

> exn:fail:unlib

Basic exception thrown when Unlib encounters an
unrecoverable error


exn.ss
-----

> raise-exn :: (raise-exn exn message)
Syntax

Raises the given exception with the given message.  Handles
the conversion of message to an immutable string and
capturing continuation marks.

Example:

  (raise-exn exn:unlib "Something wicked this way comes")

> raise-exn/append :: (raise-exn/append exn message messages ...)
Syntax

As raise-exn excepts appends together the messages

> raise-exn/format :: (raise-exn/format exn template params ...)
Syntax 

As raise-exn, except template is a format string, and params
the parameters to format.

Example:

  (raise-exn/format exn:fail:unlib
    "This is an exception. ~a Test number ~a."
    "Oh yes it is."
    69)


contract.ss
---------

> arity/c :: (arity/c number)

A contract that ensures a procedure accepts the given number
of arguments


debug.ss
-------

> (debug message value)
debug : string 'a -> 'a

Prints a debug message consisting of message and value, and
returns value.  You can wrap it around an expression to
debug it without affecting the semantics of the code.

Example

   (debug "Message" (+ 1 2 3))

> (let-debug ((var val) ...) expr ...)
Syntax

Evaluates as a let, printing the values of var

Example

  (let-debug ([a 1]
              [b 2])
    (+ a b))

> (let*-debug ((var val) ...) expr ...)
Syntax

Evaluates as a let*, printing the values of var

Example

  (let*-debug ([a 1]
               [b (+ a 1)])
    (+ a b))

> (letrec-debug ((var val) ...) expr ...)
Syntax

Evaluates as a letrec, printing the values of var

Example

  (letrec-debug ([odd?
                  (lambda (n)
                    (if (zero? n)
                        #t
                        (even? (sub1 n))))]
                 [even?
                  (lambda (n)
                    (if (zero? n)
                        #f
                        (odd? (sub1 n))))])
    (odd? 7))

> (define-debug var val)
Syntax

Evaluates as a define, printing the value of var

Example

  (define-debug a 2)


date.ss
-------

> (leap-year? year)
leap-year? : number -> (U #t #f)

True if the number represents a leap year, false otherwise

Example

  (leap-year? 2000) -> #t

> (days-in-month month [year])
days-in-month : number [number] -> number

Returns the number of days in the given month and year.  The
year defaults to 2001 (a non-leap year) if not specified.

Example

  (days-in-month 2 2000) -> 29


file.ss
-------

> (make-directory-tree tree)
make-directory-tree : (tree = (U (list-of tree) string)) -> void

Creates a directory tree in the current directory that
matches tree.  Existing directories are ignored.

Example

  (make-directory-tree '("a" ("b" "c" ("d"))))

  Creates the directory tree:
    a / b
      / c / d
    

hash-table.ss
-------------

> (make-hash-table/pairs . pairs)
make-hash-table/pairs : (cons 'a 'b) ... -> (hash-of 'a 'b)

Makes a hash table from the given pairs

Example

  (make-hash-table/pairs
   '(a . 1)
   '(b . 2)
   '(c . 3))

> (hash-table-mapped? table key)
hash-table-mapped? : (hash-of 'a 'b) 'a -> (U #t #f)

Returns true is the given hash table contains a mapping for
key.

> (hash-table-get/default table key default)
hash-table-get/default : (hash-of 'a 'b) "a 'b -> 'b

Gets the value that the hash table maps to the given key, or
default if there is no mapping

> (hash-table-accessor table)
hash-table-accessor : (hash-of 'a 'b) -> ('a -> 'b)

Given a hash table, makes a function that takes a key and
returns the values mapped to that key, or exn:fail:unlib if
there is no mapping

> (hash-table-accessor/default table default)
hash-table-accessor/default : (hash-of 'a 'b) 'b -> ('a -> 'b)

Given a hash table, makes a function that takes a key and
returns the values mapped to that key, or default if there
is no mapping

> (hash-table-put/append! table key value)
hash-table-put/append! : (hash-of 'a (list-of 'b)) 'a 'b -> void

Appends value to the value mapped to key in table, creating
a list of one element if there is no mapping.  If the mapped
value is not a list exn:fail:unlib is raised.

> (hash-table-mutator table)
hash-table-mutator : (hash-of 'a 'b) -> ('a 'b -> void)

Given a hash table, creates a procedure that maps the key to
the value.

> (hash-table-mutator/append table)
hash-table-mutator/append : (hash-of 'a 'b) -> ('a 'b ->
void)

Given a hash table, creates a procedure that appends the
value to the values mapped to key, as given by
hash-table-put/append!

> (hash-table-find table selector [default])
hash-table-find : (hash-of 'a 'b) ('a 'b -> (U 'c #f)) [(()
-> 'd)] -> (U 'c 'd)

Applies selector to every key and value in table, returning
the first non-#f value returned by selector, or the value of
default if no non-#f value is returned.  The default value
of default is #f

> (any-keys-have-values? table)
any-keys-have-values? : (hash-of 'a 'b) -> (U #t #f)

Returns to #t if every value in table is a list, otherwise
raises exn:fail:unlib

> (key-has-values? table key)
key-has-values? : (hash-of 'a 'b) 'a -> (U #t #f)

Return #t if table maps key to a non-null list.  Raises
exn:fail:unlib otherwise.

> (hash-table->string table [delimiter])
hash-table->string : (hash-of 'a 'b) [string] -> string

Returns a string representation of table, of the form
key=value separated by delimiter.  Delimiter defaults to ,


list.ss
-------

type (tree-of 'a) = (U 'a (cons tree tree))

type (alist-of 'a 'b) = (list-of (cons 'a 'b))

> (list-delimit list delimiter)
list-delimit : (list-of 'a) 'b -> (list-of (U 'a 'b))

Inserts delimiter between each element of list

Example:

  (list-delimit '("a" "b" "c") " ")
  -> '("a" " " "b" " " "c")

> (char-iota count [start])
char-iota : number char

Generates count characters starting at start

Example 

  (char-iota 10 #\0)
  ->(list #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)

> (tree-map fn tree)
tree-map : ('a -> 'b)  (tree-of 'a) -> (tree-of 'b)

Returns the tree that is the result of applying fn to each
leaf of tree

> (tree-for-each fn tree)
tree-for-each : ('a -> 'b) (tree-of 'a) -> void

Applies fn to each leaf of tree

> (assoc-value key alist)
assoc-value : 'a (alist-of 'a 'b) -> 'b

Return the value in the alist mapped to key, raising
exn:fail:unlib if no mapping is found

> (assoc-value/default key alist default)
assoc-value/default : 'a (alist-of 'a 'b) 'b -> 'b

Return the value in the alist mapped to key, of default if
no mapping is found

> (alist-accessor alist)
Syntax

Expands to a function of a key that calls assoc-value on
alist

> (alist-accessor/default alist)
Syntax

Expands to a function of a key that calls
assoc-value/default on alist

> (alist-set key value alist)
alist-set : 'a 'b (alist-of 'a 'b) -> (alist-of 'a 'b)

Returns an alist with the value mapped to key replaced with
value, if one exists, otherwise adding a new mapping from
key to value.

> (alist-mutator alist)
Syntax

Expands to a function that accepts a key and a value and
destructively the modifies alist to containing a mapping
from key to value

Example

  (define alist null)
  (define mutate! (alist-mutator alist))
  alist ;; null
  (mutate! 'a 1)
  alist :: '((a . 1))

> (alist-mutator/append alist)
Syntax

As alist-mutator, except values are appended to the end of
existing values

> (alist-map fn alist)
alist-map : ('a -> 'c) (alist-of 'a 'b) -> (alist-of 'a 'c)

Applies fn to every key and value in alist, returning a list
of the results

> (alist-for-each fn alist)
alist-for-each : ('a -> 'c) (alist-of 'a 'b) -> void

Applies fn to every key and value in alist


log.ss
------

> current-log-preamble
parameter current-log-preamble : (parameter-of () -> (list-of any))

Parameter that stores a thunk returning a list of values to
be included at the begining of each log message.

> (with-log-preamble thunk expr ...)
Syntax

Evaluates expr ... in a dynamic environment with
current-log-preamble parameterised to thunk.

> current-log-port
parameter current-log-port : (parameter-of (U (parameter-of
output-port) output-port))

Parameter that stores either a paramter storing an
output-port (such as current-output-port or
current-error-port) or just an output-port.  Defaults to
current-output-port

> (with-log-port port expr ...)
Syntax

Evaluates expr ... in a dynamic environment with
current-log-port parameterised to port.

> (log-message msg ...)
log-message : any ... -> integer

Logs msg and returns a timestamp as a unique identifier

> (log-warning msg ...)
log-warning : any ... -> integer

Logs msg as a warning and returns a timestamp as a unique
identifier

> (log-error msg ...)
log-error : any ... -> integer

Logs msg as a error and returns a timestamp as a unique
identifier

> (log-generic msg-type msg)
log-generic symbol (list-of any) -> integer

Logs msg as type msg-type, returning a timestamp as a unique
identifier


number.ss
---------

> (number->symbol number)
number->symbol : number -> symbol

Converts number to a symbol


parameter.ss
------------

> (make-guard predicate message)
make-guard : ('a -> (U #t #f)) string -> ('a -> 'a)

Make a guard for a paramter.  Given a predicate and a string
documenting the expected type, returns a function that
raises exn:fail:unlib if its argument does not match the
predicate, and otherwise returns the argument

> (define-parameter name initial-value guard with-form)
Syntax

Expands to two definitions. The first binds name to a
parameter with initial value of initial-value, and guard of
guard.  The second binds with-form to syntax (with-form
new-value expr ...) that expands to a parameterisation of
name with new-value in the dynamic environment of
expression.

Example

  (define-parameter foo 
    1 
    (make-guard (lambda (x) (or (integer? x) (not x)))
                "(U integer #f)")
    with-foo)

  Expands to

  (define foo
    (make-parameter 1 
      (make-guard (lambda (x) (or (integer? x) (not x)))
                  "(U integer #f)")))

  (define-syntax (with-foo stx)
    (syntax-case stx ()
      [(with-foo new-value exp (... ...))
       (syntax
         (parameterize ([foo new-value])
           exp (... ...)))]))

  And we can use as such:

  (with-foo 10 (foo))

  Evaluates to 10

  (with-foo "bar" 10)

  Raises exn:fail:unlib


pipeline.ss
---------

A "pipeline" allows a programmer to wrap a procedure in one
or more pieces of useful functionality. Pipelines are lists
of "stages", each of which performs some function and calls
the next stage. The last stage calls the target procedure.

An example of this (and the original reason for creating
pipelines) is request processing in a web server. The server
may consist of a number of controller procedures, each of
which serves a different page. All of these procedures may
have one or more bits of functionality in common.  For
example:

  - set up cookies
  - identify the user's browser
  - check the user's security privileges

Note that, while many of these functions will be common
across many controllers, there will be occasions where one
controller will need to do things differently from the
others.

The items above can be implemented as stages in a request
processing pipeline. A standard pipeline can be offered
site-wide, and controllers can choose to customise it where
appropriate by adding, removing or changing stages.

Stages are named so they can be uniquely referred to when
manipulating pipelines in this way. This has the added
advantage that single stages can be extracted and run out of
context with the rest of the pipeline.

More formally, given a target procedure:

     target : any ... -> any

a pipeline is a list of stages:

     pipeline : (list-of stage)

where a stage is a name and a body procedure:

     struct stage : symbol ((any ... -> any) any ... -> any)

The body procedure takes at least one argument: a
"continuation procedure" that is called to continue the
pipeline. The arguments passed to the continuation procedure
are passed on to the next stage in the pipeline. The target
procedure is considered a "pseudo stage" that is called
after all other stages.

Any stage can abort the pipeline simply by failing to call
the continuation procedure.  It is also perfectly reasonable
for stages to set up parameters, install exception handlers,
change the arguments to subsequent stages and so on.

> (call-with-pipeline pipeline procedure . args)
call-with-pipeline : pipeline (any ... -> any) any ... -> any

Calls a procedure via a pipeline. The result returned is
either the result of the procedure or that of the last stage
invoked.

> (make-stage name procedure)
struct stage : symbol ((any ... -> any) any ... -> any)

Constructs a stage with the given name and body procedure.
The first argument to the body procedure  is *always* a
continuation procedure that passes control to the next stage
in the pipeline.

The definition of stage takes advantage of MzScheme's
"structures as procedures" functionality such that stages
can be called directly as if they are procedures. For
example:

  (define my-stage
    (make-stage
     'my-stage
     (lambda (continue name age)
       (printf "Hello ~a, " name)
       (continue age))))

  (my-stage
   (lambda (age)
     (printf "you are ~a years old!" age))
   "Dave" 27))

would print:

     Hello Dave, you are 27 years old!

> (stage? val)
stage? : any -> (U #t #f)

Returns #t if val is stage, #f otherwise.

> (stage-name stage)
stage-name : stage -> symbol

Returns the name of the stage.

> (find-stage pipeline name)
find-stage : (list-of stage) symbol -> (U stage #f)

Returns the appropriately named stage in the specified
pipeline, or #f if such a stage cannot be found.

> (replace-stage pipeline name)
replace-stage : (list-of stage) stage -> (list-of stage)

Replaces the equivalently named stage in the supplied
pipeline (if such a stage can be found).

> (delete-stage pipeline name)
delete-stage : (list-of stage) symbol -> (list-of stage)

Deletes the appropriately named stage from the supplied
pipeline (if such a stage can be found).


preprocess.ss
-------------

> (apply-template template bindings)
apply-template : (U string port) (alist-of symbol any) -> string

Applies template to the given bindings, returning a string
of the results.  Template is a port or string representing
an mzpp template (see (lib "mzpp.ss" "preprocessor")).

Examples:

  (define tmpl (open-input-string "<< (+ 1 2) >>"))
  (apply-template tmpl '()) ;; evaluates to "3\n"

  (define tmpl (open-input-string "<< dummy >>"))
  (apply-template tmpl '((dummy . 3))) ;; evaluates to "3\n"

Note that the bindings are eval'ed, so they should be
s-expressions that evaluate to the values you want in the
template.  Confused?  Here's an example:

Say you want the binding foo to be the value '(1 2 3)
If you call

  (apply-template tmpl '(foo . (1 2 3)))

you will get an error, as the s-expression '(1 2 3) evals to
an application of 1 to the arguments 2 3.  As 1 is not a
function this is an error.  What you want is

  (apply-template tmpl '(foo . (list 1 2 3)))

as the s-expression '(list 1 2 3) evaluates to the list '(1
2 3)

See the print-convert library in MzLib for a generic way of
converting values to their eval-able representation.


string.ss
---------

> (string-namecase string)
string-namecase : string -> string

Similar to string-titlecase buts deals with various special
cases common in European names

> (ensure-string string-or-bytes)
ensure-string : (U string bytes) -> string

If the input is bytes it is converted to a string using the
UTF-8 conversion.  Useful in servlets where the web server
may provide bytes or strings depending on the encoding


symbol.ss
---------

> (symbol-append symbol ...)
symbol-append : symbol ... -> symbol

Appends the symbols together, creating a new symbol


syntax.ss
---------

> (syntax-map fn stx)
syntax-map : (stx -> 'a) stx -> (list-of 'a)

Applies fn to every element in stx, returning a list of the results

> (syntax-append-map fn stx)
syntax-append-map : (stx -> (list-of 'a)) stx -> (list-of 'a)

Applies fn to every element in stx. appending the results.

> (symbolic-identifier=? stx1 stx2)
symbolic-identifier=? : stx stx -> (U #t #f)

Returns #t if the datums that stx1 and stx2 represent are eq?

> (atom->string atom)
atom->string : (U string symbol number stx) -> string

Converts atom to a string

> (make-syntax-symbol stx . args)
make-syntax-symbol : stx (U string symbol number stx) ... -> stx

Concatentates the string representations of args and
converts to syntax in the lexical context of stx.  Useful
for constructing identifiers.  For example:

  (make-syntax-symbol stx 'make- 'foo)

creates syntax of make-foo in the lexical context of stx


trace.ss
--------

> (define-traced (name arg ...) expr1 expr2 ...)
> (define-traced name (lambda (arg1 arg2 ...) expr1 expr2 ...))
> (define-traced name (opt-lambda (arg1 arg2 ...) expr1 expr2 ...))
Syntax

Define name in the usual way, except procedure entry and
exit is logged to the current-output-port.  

> (lambda-traced (arg ...) expr ...)

Like define-traced above, but expands to a lambda, not a
define, and output goes via the same system as debug.ss
above.