Creates `provide-def-syntax' as a syntax that is the same as
`orig-def-syntax' together with an automatic `provide' form for the
defined symbol, which should be either the first argument or the first
identifier in a list (it does not work for recursive nesting). The
convention when this is used is to use a "*" suffix for the second
identifier.
These are just shorthands for `define-syntax', `define-syntax*', and
`let-syntax'. This naming scheme is consistent with other definitions
in this module (and the rest of Swindle).
These are convenient ways of defining simple pattern transformer
syntaxes (simple meaning they're much like inlined functions). In
each of these forms, the `name' can be either a `(name arg ...)' which
will define a simple macro or an identifier which will define a
symbol-macro. For example:
=> (defsubst (my-if cond then else)
(if (and cond (not (eq? 0 cond))) then else))
=> (my-if 1 (echo 2) (echo 3))
2
=> (my-if 0 (echo 2) (echo 3))
3
=> (define x (list 1 2 3))
=> (defsubst car-x (car x))
=> car-x
1
=> (set! car-x 11)
=> x
(11 2 3)
Actually, if a `(name arg ...)' is used, then the body can have more
pattern/expansions following -- but since this form translates to a
usage of `syntax-rules', the `name' identifier should normally be `_'
in subsequent patterns. For example:
=> (defsubst (my-if cond then else)
(if (and cond (not (eq? 0 cond))) then else)
(_ cond then)
(and cond (not (eq? 0 cond)) then))
=> (my-if 0 1)
#f
Finally, note that since these are just patterns that get handled by
syntax-rules, all the usual pattern stuff applies, like using `...'.
These are just like MzScheme's define-macro (from mzlib/defmacro) with
two major extensions:
* If `name' is a simple identifier then a symbol-macro is defined (as
with `defsubst' above).
* A `letmacro' form for local macros is provided.
Creates a `syntax parameter'. Syntax parameters are things that you
can use just like normal parameters, but they are syntax transformers,
and the information they store can be used by other syntax
transformers. The purpose of having them around is to parameterize
the way syntax transformation is used -- so they should be used as
global option changes, not for frequent side effect: they change their
value at syntax expansion time. Note that using it stores the literal
syntax that is passed to them -- there is no way to evaluate the given
argument, for example, if some parameter expects a boolean -- then
`(not #t)' will not work! The syntax parameter itself is invoked
wither with no arguments to retrieve its value, or with an argument to
set it. Retrieving or setting the value in this way is meaningful
only in an interactive context since using it in a function just
expands to the current value:
=> (define-syntax-parameter -foo- 1)
=> (-foo-)
1
=> (define (foo) (-foo-))
=> (-foo- 2)
=> (-foo-)
2
=> (foo)
1
Defined to allow `setf!' with these mzlib/list functions. Note that
there is no error checking (unlike the accessor functions which are
provided by mzlib/list).
Allow using `setf!' with `unbox'. Note: this is an alias for
`set-box!' which is an inconsistent name with other Scheme `set-foo!'
functions -- the result is that you can also do `(setf! (box foo) x)'
and bogusly get the same effect.
This is defined to be able to `setf!' into a `hash-table-get'
accessor. The form that `setf!' assembles always puts the new value
last, but it is still useful to have a default thunk which results in
an optional argument in an unusual place (and this argument is ignored
by this, which is why it is defined as a macro). For example:
=> (define t (make-hash-table))
=> (inc! (hash-table-get t 'foo))
hash-table-get: no value found for key: foo
=> (inc! (hash-table-get t 'foo (thunk 0)))
=> (hash-table-get t 'foo)
1
These functions are similar to MzScheme's hash-table functions, except
that they work with a list of keys (compared with `eq?'). If it was
possible to use a custom equality hash-table, then then would use
something like
(lambda (x y) (and (= (length x) (length y)) (andmap eq? x y))).
The implementation uses a hash-table of hash-tables, all of them weak,
since it is supposed to be used for memoization.
`set-l-hash-table-get!' is defined to work with `setf!'.
Return a memoized version of `func'. Note that if `func' is
recursive, it should be arranged for it to call the memoized version
rather then call itself directly.
Sophisticated iteration syntax. The iteration is specified by the
given clauses, where `var' serves as an accumulator variable that
collects a value beginning with `base' and continuing with `expr' --
similar to a single binding in a `do' form with a variable, an initial
value and an update expression. But there are much more iteration
options than a `do' form: this form supports a generic
list-comprehension and related constructs. Forms that use this
construct are:
Use when no value collection is needed, and the default for
expressions is to do them instead of using them as a filter.
Implemented as:
(collect => (acc (void) acc) do clause ...)
Only count matching cases, implemented as:
(sum-of 1 clause ...)
Each clause is either:
* (v <- ...): a binding generator clause;
* (v <- ... and v <- ...): parallel generator clauses;
* (v is is-expr): bind `v' to the result of `is-expr';
* while expr: a `while' keyword followed by an expression will
abort the whole loop if that expression evaluates to
#f;
* until expr: an `until' keyword followed by an expression will
abort the whole loop if that expression evaluates to
a non-#f value;
* when ...: filter by the following expressions -- if an
expression evaluates to #f, stop processing this
iteration (default for all macros except for
`loop-for');
* unless ...: filter by the negation of the following expressions;
* do ...: execute the following expressions, used for side
effects (default for the `loop-for' macro);
* expr: expression is used according to the current mode set
by a `when', `unless', or `do', keyword that
precedes it.
The effect of this form is to iterate each generator variable
according to generating `<-' clauses (see below for these) and
parallel clauses, and evaluate the `expr' with each combination, which
composes a result out of iteration-bound values and an accumulated
result. Generation is done in a nested fashion, where the rightmost
generator spin fastest. Parallel generators (specified with an infix
`and') make all iterations happen simultaneously, ending as soon as
the first one ends. An `is' clause is used for binding arbitrary
variables, a `do' clause is used to execute code for general
side-effects, and other clauses are used to filter results before
continuing down the clause list. Each clause can use variables bound
by previous clauses, and the `expr' can use all bound variables as
well as the given accumulator variable.
An optional first token can be used to specify the direction which is
used to accumulate the result. It can be one of these two tokens:
`<=': A "backward" collection, the default (similar to `foldl');
`=>': A "forward" collection (similar to `foldr').
The default "backward" direction works by generating an accumulator
carrying loop, as in this code (this code is for demonstration, not
what `collect' creates):
(let loop ([x foo] [acc '()])
(if (done? x) acc (loop (next x) (cons (value x) acc))))
which is a common Scheme idiom for such operations. The problem is
that this accumulation happens in reverse -- requiring reversing the
final result (which is done by the `list-of' macro). A "forward"
direction does a naive recursive loop:
(let loop ([x foo])
(if (done? x) '() (cons (value x) (loop (next x)))))
collecting values in the correct order, but the problem is that it
keeps a computation context which makes memory consumption
inefficient. The default style is usually preferred, since reversing
a list is a cheap operation, but it is not possible when infinite
lists (streams) are used since it is impossible to reverse them. In
these cases, the "forward" style should be used, but the `expr' must
take care not to evaluate the iteration "variable" immediately, using
`delay' or a similar mechanism (this "variable" is not bound to a
value but substituted with an expression (a symbol macro)). For
example, here's a quick lazy list usage:
=> (defsubst (lcons x y) (delay (cons x y)))
=> (define (lcar s) (car (force s)))
=> (define (lcdr s) (cdr (force s)))
=> (define x (collect (_ '() (lcons x _)) (x <- 0 ..)))
; loops indefinitely
=> (define x (collect => (_ '() (lcons x _)) (x <- 0 ..)))
=> (lcar (lcdr (lcdr x)))
2
Note that the `loop-for' macro uses a "forward" direction, but this is
only because it is slightly faster since it doesn't require an extra
binding.
[The direction can be changed for a single part by using a "<-!"
keyword instead of "<-", but this is an experimental feature since I
don't know if it's actually useful for anything. Do not try to mix
this with the `while' and `until' keywords which are implemented
differently based on the direction.]
Generator forms are one of the following ("..", "then", "until",
"while" are literal tokens), see below for what values are generated:
* (v <- sequence):
iterate `v' on values from `sequence';
* (v <- 1st [2nd] .. [last]):
iterate on an enumerated range, including last element of range;
* (v <- 1st [2nd] ..< last):
iterate on an enumerated range, excluding last element of range;
* (v <- 1st [2nd] .. while last):
iterate on an enumerated range, excluding last element of range;
* (v <- 1st [2nd] .. until last):
iterate on an enumerated range, excluding last element of range;
* (v <- x then next-e [{while|until} cond-e]):
start with the `x' expression, continue with the `next-e' expression
(which can use `v'), do this while/until `cond-e' is true if a
condition is given;
* (v <- x {while|until} cond-e):
repeat using the `x' expression while/until `cond-e' is true;
* (v <- func arg ...):
applies `func' to `arg ...', the result is expected to be some
"iterator value" which is used to do the iteration -- iteration
values are created by `collect-iterator' and `collect-numerator',
see below for their description and return values.
* (v <- gen1 <- gen2 <- ...):
generator clauses can have multiple parts specified by more `<-'s,
all of them will run sequentially;
* (v1 <- gen1 ... and v2 <- gen2 ...):
finally, an infix `and' specifies parallel generators, binding
several variables.
Iteration is possible on one of the following sequence values:
* list: iterate over the list's element;
* vector: iterate over the vector's elements;
* string: iterate over characters in the string;
* integer n: iterate on values from 0 to n-1;
* procedure f:
- if f accepts zero arguments, begin with (f) and iterate by
re-applying (f) over and over, so the only way to end this
iteration is by returning `collect-final' (see below);
- otherwise, if f accepts one argument, it is taken as a generator
function: it is passed a one-argument procedure `yield' which can
be used to suspend its execution returning the given value, and it
will be continued when more values are required (see
`function-iterator' below);
* hash-table: iterate over key-value pairs -- this is done with a
generator function:
(lambda (yield)
(hash-table-for-each seq (lambda (k v) (yield (cons k v)))))
* other values: repeated infinitely.
Note that iteration over non-lists is done efficiently, iterating over
a vector `v' is better than iterating over `(vector->list v)'.
Enumeration is used whenever a ".." token is used to specify a range.
There are different enumeration types based on different input types,
and all are modified by the token used:
* "..": a normal inclusive range;
* "..<": a range that does not include the last element;
* ".. while": a range that continues while a predicate is true;
* ".. until": a range that continues until a predicate is true.
The "..<" token extends to predicates in the expected way: the element
that satisfies the predicate is the last one and it is not included in
the enumeration -- unlike "..".
These are the possible types that can be used with an enumeration:
* num1 [num2] .. [num3]: go from num1 to num3 in num3 in num2-num1
steps, if num2 is not given then use +1/-1 steps, if num3 is not
given don't stop;
* num1 [num2] .. pred: go from num1 by num2-num1 steps (defaults to
1), up to the number that satisfies the given predicate;
* char1 [char2] .. [char3/pred]: the same as with numbers, but on
character ranges;
* func .. [pred/x]: use `func' the same way as in an iterator above,
use `pred' to identify the last element, if `pred' is omitted repeat
indefinitely;
* fst [next] .. [pred]: start with `fst', continue by repeated
applications of the `next' function on it, and use `pred' to
identify the last element, if `pred' is omitted repeat indefinitely,
if `next' is omitted repeat `fst', and if both `fst' and `next' are
numbers or characters then use their difference for stepping. (Note
that to repeat a function value you should use `identity' as for
`next' or the function will be used as described above.)
Here is a long list of examples for clarification, all using
`list-of', but the generalization should be obvious:
=> (list-of x [x <- '(1 2 3)])
(1 2 3)
=> (list-of (list x y) [x <- '(1 2 3)] [y <- 1 .. 2])
((1 1) (1 2) (2 1) (2 2) (3 1) (3 2))
=> (list-of (format "~a~a~a" x y z)
[x <- '(1 2)] [y <- #(a b)] [z <- "xy"])
("1ax" "1ay" "1bx" "1by" "2ax" "2ay" "2bx" "2by")
=> (list-of (+ x y) [x <- '(1 2 3)] [y <- 20 40 .. 100])
(21 41 61 81 101 22 42 62 82 102 23 43 63 83 103)
=> (list-of (+ x y) [x <- '(1 2 3) and y <- 20 40 .. 100])
(21 42 63)
=> (list-of y [x <- 0 .. and y <- '(a b c d e f g h i)] (even? x))
(a c e g i)
=> (list-of y [x <- 0 .. and y <- '(a b c d e f g h i)]
when (even? x) do (echo y))
a
c
e
g
i
(a c e g i)
=> (list-of (list x y) [x <- 3 and y <- 'x])
((0 x) (1 x) (2 x))
=> (list-of (list x y) [x <- 3 and y <- 'x ..])
((0 x) (1 x) (2 x))
=> (list-of (list x y) [x <- #\0 .. and y <- '(a b c d)])
((#\0 a) (#\1 b) (#\2 c) (#\3 d))
=> (list-of x [x <- '(1 2 3) then (cdr x) until (null? x)])
((1 2 3) (2 3) (3))
=> (list-of (list x y)
[x <- '(1 2 3) then (cdr y) until (null? x) and
y <- '(10 20 30) then (cdr x) until (null? y)])
(((1 2 3) (10 20 30)) ((20 30) (2 3)) ((3) (30)))
=> (list-of x [x <- (lambda (yield) 42)])
()
=> (list-of x [x <- (lambda (yield) (yield 42))])
(42)
=> (list-of x [x <- (lambda (yield) (yield (yield 42)))])
(42 42)
=> (list-of x [x <- (lambda (yield)
(for-each (lambda (x) (echo x) (yield x))
'(3 2 1 0)))])
3
2
1
0
(3 2 1 0)
=> (list-of x [x <- (lambda (yield)
(for-each (lambda (x) (echo x) (yield (/ x)))
'(3 2 1 0)))])
3
2
1
0
/: division by zero
=> (list-of x
[c <- 3 and
x <- (lambda (yield)
(for-each (lambda (x) (echo x) (yield (/ x)))
'(3 2 1 0)))])
3
2
1
(1/3 1/2 1)
=> (define h (make-hash-table))
=> (set! (hash-table-get h 'x) 1
(hash-table-get h 'y) 2
(hash-table-get h 'z) 3)
=> (list-of x [x <- h])
((y . 2) (z . 3) (x . 1))
=> (list-of x [x <- 4 <- 4 .. 0 <- '(1 2 3)])
(0 1 2 3 4 3 2 1 0 1 2 3)
=> (list-of (list x y)
[x <- 1 .. 3 <- '(a b c) and
y <- (lambda (y) (y 'x) (y 'y)) <- "abcd"])
((1 x) (2 y) (3 #\a) (a #\b) (b #\c) (c #\d))
Note that parallel iteration is useful both for enumerating results,
and for walking over a finite prefix of an infinite iteration.
The following is an extensive list of various ranges:
=> (list-of x [x <- 0 .. 6])
(0 1 2 3 4 5 6)
=> (list-of x [x <- 0 ..< 6])
(0 1 2 3 4 5)
=> (list-of x [x <- 0 .. -6])
(0 -1 -2 -3 -4 -5 -6)
=> (list-of x [x <- 0 ..< -6])
(0 -1 -2 -3 -4 -5)
=> (list-of x [x <- 0 2 .. 6])
(0 2 4 6)
=> (list-of x [x <- 0 2 ..< 6])
(0 2 4)
=> (list-of x [x <- 0 -2 ..< -6])
(0 -2 -4)
=> (list-of x [x <- #\a .. #\g])
(#\a #\b #\c #\d #\e #\f #\g)
=> (list-of x [x <- #\a ..< #\g])
(#\a #\b #\c #\d #\e #\f)
=> (list-of x [x <- #\a #\c .. #\g])
(#\a #\c #\e #\g)
=> (list-of x [x <- #\a #\c ..< #\g])
(#\a #\c #\e)
=> (list-of x [x <- #\g #\e ..< #\a])
(#\g #\e #\c)
=> (list-of x [x <- 6 5 .. zero?])
(6 5 4 3 2 1 0)
=> (list-of x [x <- 6 5 ..< zero?])
(6 5 4 3 2 1)
=> (list-of x [x <- 6 5 .. until zero?])
(6 5 4 3 2 1)
=> (list-of x [x <- 6 5 .. while positive?])
(6 5 4 3 2 1)
=> (list-of x [x <- '(1 2 3) cdr .. null?])
((1 2 3) (2 3) (3) ())
=> (list-of x [x <- '(1 2 3) cdr ..< null?])
((1 2 3) (2 3) (3))
=> (list-of x [x <- '(1 2 3) cdr .. until null?])
((1 2 3) (2 3) (3))
=> (list-of x [x <- '(1 2 3) cdr .. while pair?])
((1 2 3) (2 3) (3))
=> (list-of x [x <- #\a #\d .. while char-alphabetic?])
(#\a #\d #\g #\j #\m #\p #\s #\v #\y)
=> (list-of x [x <- #\a #\d .. char-alphabetic?])
(#\a)
=> (list-of x [x <- #\a #\d ..< char-alphabetic?])
()
=> (list-of x [x <- 0 1 .. positive?])
(0 1)
=> (list-of x [x <- 1 2 .. positive?])
(1)
=> (list-of x [x <- 1 2 ..< positive?])
()
=> (list-of x [x <- '(a b c) ..< pair?])
()
=> (list-of x [x <- '(a b c) .. pair?])
((a b c))
=> (list-of x [x <- '(a b c) cdr .. pair?])
((a b c))
=> (list-of x [x <- read-line .. eof-object?])
...list of remaining input lines, including #<eof>...
=> (list-of x [x <- read-line ..< eof-object?])
...list of remaining input lines, excluding #<eof>...
=> (list-of x [x <- read-line ..< eof])
...the same...
This value can be used to terminate iterations: when it is returned as
the iteration value (not the state), the iteration will terminate
without using it.
`f' is expected to be a function that can accept a single input value.
It is applied on a `yield' function that can be used to return a value
at any point. The return value is a function of no argument, which
returns on every application values that were passed to `yield'. When
`f' terminates, the final result of the iterated return value depends
on the optional argument -- if none was supplied, the actual return
value is returned, if a thunk was supplied it is applied for a return
value, and if any other value was given it is returned. After
termination, calling the iterated function again results in an error.
(The supplied `yield' function returns its supplied value to the
calling context when resumed.)
=> (define (foo yield) (yield 1) (yield 2) (yield 3))
=> (define bar (function-iterator foo))
=> (list (bar) (bar) (bar))
(1 2 3)
=> (bar)
3
=> (bar)
function-iterator: iterated function #<procedure:foo> exhausted.
=> (define bar (function-iterator foo 'done))
=> (list (bar) (bar) (bar) (bar))
(1 2 3 done)
=> (bar)
function-iterator: iterated function #<procedure:foo> exhausted.
=> (define bar (function-iterator foo (thunk (error 'foo "done"))))
=> (list (bar) (bar) (bar))
(1 2 3)
=> (bar)
foo: done
These functions are used to construct iterations. `collect-iterator'
is the function used to create iteration over a sequence object and it
is used by `(x <- sequence)' forms of `collect'. `collect-numerator'
create range iterations specified with `(x <- from second to)' forms,
where unspecified values are passed as `#f', and the flag argument is
a `<', `while', or `until' symbol for ranges specified with "..<",
".. while" and ".. until". These functions are available for
implementing new iteration constructs, for example:
=> (define (in-values producer)
(collect-iterator (call-with-values producer list)))
=> (list-of x [x <- in-values (thunk (values 1 2 3))])
(1 2 3)
The return value that specifies an iteration is a list of four items:
1. the initial state value;
2. a `step' function that gets a state and returns the next one;
3. a predicate for the end state (#f for none);
4. a function that computes a value from the state variable.
But usually the functions are more convenient.
Finally, remember that you can return `collect-final' as the value to
terminate any iteration.
This is a handy printout utility that offers an alternative approach
to `printf'-like output (it's a syntax, but it can be used as a
regular function too, see below). When applied, it simply prints its
arguments one by one, using certain keywords to control its behavior:
* :>e - output on the current-error-port;
* :>o - output on the current-output-port (default);
* :>s - accumulate output in a string which is the return value
(string output sets `:n-' as default (unless
pre-specified));
* :> p - output on the given port `p', or a string if `#f';
* :>> o - use `o', a procedure that gets a value and a port, as the
output handler (the procedure can take one value and
display it on the current output port);
* :d - use `display' output (default);
* :w - use `write' output;
* :d1 :w1 - change to a `display' or `write' output just for the next
argument;
* :s- - no spaces between arguments;
* :s+ - add spaces between arguments (default);
* :n- - do not print a final newline;
* :n+ - terminate the output with a newline (default);
* :n - output a newline now;
* : or :: - avoid a space at this point;
* :\{ - begin a list construct (see below).
Keywords that require additional argument are ignored if no argument
is given.
Recursive processing of a list begins with a `:\{' and ends with a
`:\}' (which can be simpler if `read-curly-brace-as-paren' is off).
Inside a list context, values are inspected and any lists cause
iteration for all elements. In each iteration, all non-list arguments
are treated normally, but lists are dissected and a single element is
printed in each step, terminating when the shortest list ends (and
repeating a last `dotted' element of a list):
=> (define abc '(a b c))
=> (echo :\{ "X" abc :\})
X a X b X c
=> (echo :\{ "X" abc '(1 2 3 4) :\})
X a 1 X b 2 X c 3
=> (echo :\{ "X" abc '(1 . 2) :\})
X a 1 X b 2 X c 2
Inside a list context, the `:^' keyword can be used to stop this
iteration if it is the last:
=> (echo :s- :\{ abc :^ ", " :\})
a, b, c
Nesting of lists is also simple, following these simple rules, by
nesting the `:\{' ... `:\}' construct:
=> (echo :s- :\{ "<" :\{ '((1 2) (3 4 5) 6 ()) :^ "," :\} ">"
:^ "-" :\})
<1,2>-<3,4,5>-<6>-<>
Note that this example is similar to the CL `format':
(format t "~{<~{~a~^,~}>~^-~}" '((1 2) (3 4 5) 6 ()))
except that `echo' treats a dotted element (a non-list in this case)
as repeating as needed.
There are two additional special keywords that are needed only in
uncommon situations:
* :k- - turn off keyword processing
* :k+ - turn keyword processing on
Usually, when `echo' is used, it is processed by a macro that detects
all keywords, even if there is a locally bound variable with a keyword
name. This means that keywords are only ones that are syntactically
so, not expressions that evaluate to keywords. The two cases where
this matters are -- when `echo' is used for its value (using it as a
value, not in a head position) no processing is done so all keywords
will just get printed; and when `echo' is used in a context where a
variable has a keyword name and you want to use its value (which not a
great idea anyway, so there is no way around it). The first case is
probably more common, so the variable `echo:' is bound to a special
value that will force treating the next value as a keyword (if it
evaluates to one) -- it can also be used to turn keyword processing on
(which means that all keyword values will have an effect). Here is a
likely examples where `echo:' should be used:
=> (define (echo-values vals)
(apply echo "The given values are:" echo: :w vals))
=> (echo-values '("a" "b" "c"))
The given values are: "a" "b" "c"
=> (echo-values '(:a :b :c))
The given values are: :a :b :c
And here are some tricky examples:
=> (echo :>s 2)
"2"
=> (define e echo) ; `e' is the real `echo' function
=> (e :>s 2) ; no processing done here
:>s 2
=> (e echo: :>s 2) ; explicit key
"2"
=> (e echo: :k+ :>s 2) ; turn on keywords
"2"
=> (let ([:>s 1]) (echo :>s 2)) ; `:>s' was processed by `echo'
"2"
=> (let ([:>s 1]) (e :>s 2)) ; `:>s' was not processed
1 2
=> (let ([:>s 1]) (e echo: :>s 2)) ; `:>s' is not a keyword here!
1 2
=> (let ([:>s 1]) (echo echo: :>s 2)) ; `echo:' not needed
"2"
Finally, it is possible to introduce new keywords to `echo'. This is
done by calling it with the `:set-user' keyword, which expects a
keyword to attach a handler to, and the handler itself. The handler
can be a simple value or a keyword that will be used instead:
=> (echo :set-user :foo "foo")
=> (echo 1 :foo 2)
1 foo 2
=> (echo :set-user :foo :n)
=> (echo 1 :foo 2)
1
2
The `:set-user' keyword can appear with other arguments, it has a
global effect in any case:
=> (echo 1 :foo :set-user :foo "FOO" 2 :foo 3
:set-user :foo "bar" :foo 4)
1
2 FOO 3 bar 4
=> (echo 1 :foo 2)
1 bar 2
If the handler is a function, then when this keyword is used, the
function is applied on arguments pulled from the remaining `echo'
arguments that follow (if the function can get any number of
arguments, then all remaining arguments are taken). The function can
work in two ways: (1) when it is called, the `current-output-port'
will be the one that `echo' currently prints to, so it can just print
stuff; (2) if the function returns a list (or a single value which is
not `#f' or `void'), then these values will be used instead of the
taken arguments. Some examples:
=> (echo :set-user :foo (thunk "FOO") 1 :foo 2)
1 FOO 2
=> (echo :set-user :add1 add1 1 :add1 2)
1 3
=> (echo :set-user :+1 (lambda (n) (list n '+1= (add1 n))) :+1 2)
2 +1= 3
=> (echo :set-user :<> (lambda args (append '("<") args '(">")))
:<> 1 2 3)
< 1 2 3 >
Care should be taken when user keywords are supposed to handle other
keywords -- the `echo:' tag will usually be among the arguments except
when `:k+' was used and an argument value was received. This exposes
the keyword treatment hack and might change in the future.
To allow user handlers to change settings temporarily, there are
`:push' and `:pop' keywords that will save and restore the current
state (space and newline flags, output type and port etc). For
example:
=> (echo :set-user :@
(lambda (l)
(echo-quote
list :push :s- :\{ "\"" l "\"" :^ ", " :\} :pop)))
=> (echo 1 :@ '(2 3 4) 5)
1 "2", "3", "4" 5
The above example shows another helper tool -- the `echo-quote'
syntax: `(echo-quote head arg ...)' will transform into `(head ...)',
where keyword arguments are prefix with the `echo:' tag. Without it,
things would look much worse.
In addition to `:set-user' there is an `:unset-user' keyword which
cancels a keyword handler. Note that built-in keywords cannot be
overridden or unset.
Try to match the given `string' against several regexps. Each clause
has one of the following forms:
* (re => function): if `string' matches `re', apply `function' on the
result list.
* ((re args ...) body ...): if `string' matches `re', bind the tail of
results (i.e, excluding the whole match result) to the given
arguments and evaluate the body. The whole match result (the first
element of `regexp-match') is bound to `match'.
* (re body ...): if `string' matches `re', evaluate the body -- no
match results are available.
* (else body ...): should be the last clause which is evaluated if all
previous cases failed.
Sorting
The following section defines functions for sorting. They are taken
directly from slib since they are more convenient and faster than the
functions in mzlib/list. See the source for more details.
True when `sequence' is a list (x0 x1 ... xm) or a vector #(x0 ... xm)
such that its elements are sorted according to `less?':
(not (less? (list-ref list i) (list-ref list (- i 1)))).
Takes two lists `a' and `b' such that both (sorted? a less?) and
(sorted? b less?) are true, and returns a new list in which the
elements of `a' and `b' have been stably interleaved so that (sorted?
(merge a b less?) less?) is true. Note: this does not accept vectors.
Takes two sorted lists `a' and `b' and smashes their cdr fields to
form a single sorted list including the elements of both. Note: this
does not accept vectors.