The standard boolean objects for true and false are written as #t
and #f
.
Alternatively, they may be written #true
and #false
,
respectively.
What really matters,
though, are the objects that the Scheme conditional expressions (if
,
cond
, and
, or
, when
, unless
, do
)
treat as true or
false. The phrase “a true value” (or sometimes just “true”)
means any object treated as true by the conditional expressions, and the phrase “a false value” (or “false”) means any
object treated as false by the conditional expressions.
Of all the Scheme values, only #f
counts as false in conditional
expressions. All other Scheme values, including #t
,
count as true. A test-expression
is an expression evaluated
in this manner for whether it is true or false.
Note: There are plans to also treat as false
the Java values #!null
, and also all java.lang.Boolean
instances for which booleanValue()
return false
(in addition to the special Boolean:FALSE
instance which is already false). However, this is not yet implemented.
Note: Unlike some other dialects of Lisp, Scheme distinguishes
#f
and the empty list from each other and from the symbol
nil
.
Boolean constants evaluate to themselves, so they do not need to be quoted in programs.
#t ⇒ #t #true ⇒ #t #f ⇒ #f #false ⇒ #f '#f ⇒ #f
The type of boolean values. As a type conversion, a true value is converted to
#t
, while a false value is converted to#f
. Represented as a primitive Javaboolean
orkava.lang.Boolean
when converted to an object.
The
boolean?
predicate returns#t
ifobj
is either#t
or#f
, and returns#f
otherwise.(boolean? #f) ⇒ #t (boolean? 0) ⇒ #f (boolean? '()) ⇒ #f
The not procedure returns
#t
ifobj
is false, and returns#f
otherwise.(not #t) ⇒ #f (not 3) ⇒ #f (not (list 3)) ⇒ #f (not #f) ⇒ #t (not ’()) ⇒ #f (not (list)) ⇒ #f (not ’nil) ⇒ #f
test-expression
::=
expression
consequent
::=
expression
alternate
::=
expression
if
test-expression
consequent
alternate
An
if
expression is evaluated as follows: first,test-expression
is evaluated. If it yields a true value, thenconsequent
is evaluated and its values are returned. Otherwisealternate
is evaluated and its values are returned. Iftest
yields#f
and noalternate
is specified, then the result of the expression is unspecified.(if (> 3 2) 'yes 'no) ⇒ yes (if (> 2 3) 'yes 'no) ⇒ no (if (> 3 2) (- 3 2) (+ 3 2)) ⇒ 1 (if #f #f) ⇒ unspecifiedThe
consequent
andalternate
expressions are in tail context if theif
expression itself is.
cond
cond-clause
^+
cond
cond-clause
^*
(else
expression
…
)
cond-clause
::=
(
test-expression
expression
^*)
|(
test
=>
expression
)
A
cond
expression is evaluated by evaluating thetest-expression
s of successivecond-clause
s in order until one of them evaluates to a true value. When atest-expression
evaluates to a true value, then the remainingexpression
s in itscond-clause
are evaluated in order, and the results of the lastexpression
in thecond-clause
are returned as the results of the entirecond
expression. If the selectedcond-clause
contains only thetest-expression
and noexpression
s, then the value of thetest-expression
is returned as the result. If the selectedcond-clause
uses the=>
alternate form, then theexpression
is evaluated. Its value must be a procedure. This procedure should accept one argument; it is called on the value of thetest-expression
and the values returned by this procedure are returned by thecond
expression.If all
test-expression
s evaluate to#f
, and there is noelse
clause, then the conditional expression returns unspecified values; if there is anelse
clause, then itsexpression
s are evaluated, and the values of the last one are returned.(cond ((> 3 2) 'greater) ((< 3 2) 'less)) ⇒ greater (cond ((> 3 3) 'greater) ((< 3 3) 'less) (else 'equal)) ⇒ equal (cond ('(1 2 3) => cadr) (else #f)) ⇒ 2For a
cond-clause
of one of the following forms:(test
expression
^*) (elseexpression
expression
^*)the last
expression
is in tail context if thecond
form itself is. For acond clause
of the form:(test
=>expression
)the (implied) call to the procedure that results from the evaluation of
expression
is in a tail context if thecond
form itself is.
case
case-key
case-clause
^+
case
case-key
case-clause
^*
case-else-clause
case-key
::=
expression
case-clause
::=
((
datum
^*)
expression
^+)
|((
datum
^*)
=>
expression
)
case-else-clause
::=
(else
expression
^+)
|(else =>
expression
)
Each
datum
is an external representation of some object. Eachdatum
in the entirecase
expression should be distinct.A
case
expression is evaluated as follows.
The
case-key
is evaluated and its result is compared usingeqv?
against the data represented by thedatum
s of eachcase-clause
in turn, proceeding in order from left to right through the set of clauses.If the result of evaluating
case-key
is equivalent to a datum of acase-clause
, the correspondingexpression
s are evaluated from left to right and the results of the last expression in thecase-clause
are returned as the results of thecase
expression. Otherwise, the comparison process continues.If the result of evaluating
key
is different from every datum in each set, then if there is ancase-else-clause
its expressions are evaluated and the results of the last are the results of thecase
expression; otherwise the result ofcase
expression is unspecified.If the selected
case-clause
orcase-else-clause
uses the=>
alternate form, then theexpression
is evaluated. It is an error if its value is not a procedure accepting one argument. This procedure is then called on the value of thekey
and the values returned by this procedure are returned by thecase
expression.(case (* 2 3) ((2 3 5 7) 'prime) ((1 4 6 8 9) 'composite)) ⇒ composite (case (car '(c d)) ((a) 'a) ((b) 'b)) ⇒ unspecified (case (car '(c d)) ((a e i o u) 'vowel) ((w y) 'semivowel) (else => (lambda (x) x))) ⇒ cThe last
expression
of acase clause
is in tail context if thecase
expression itself is.
and
test-expression
…
If there are no
test-expression
s,#t
is returned. Otherwise, thetest-expression
are evaluated from left to right until atest-expression
returns#f
or the lasttest-expression
is reached. In the former case, theand
expression returns#f
without evaluating the remaining expressions. In the latter case, the last expression is evaluated and its values are returned.(and (= 2 2) (> 2 1)) ⇒ #t (and (= 2 2) (< 2 1)) ⇒ #f (and 1 2 'c '(f g)) ⇒ (f g) (and) ⇒ #tThe
and
keyword could be defined in terms ofif
usingsyntax-rules
as follows:(define-syntax and (syntax-rules () ((and) #t) ((and test) test) ((and test1 test2 ...) (if test1 (and test2 ...) #t))))The last
test-expression
is in tail context if theand
expression itself is.
or
test-expression
…
If there are no
test-expression
s,#f
is returned. Otherwise, thetest-expression
s are evaluated from left to right until atest-expression
returns a true valueval
or the lasttest-expression
is reached. In the former case, theor
expression returnsval
without evaluating the remaining expressions. In the latter case, the last expression is evaluated and its values are returned.(or (= 2 2) (> 2 1)) ⇒ #t (or (= 2 2) (< 2 1)) ⇒ #t (or #f #f #f) ⇒ #f (or '(b c) (/ 3 0)) ⇒ (b c)The
or
keyword could be defined in terms ofif
usingsyntax-rules
as follows:(define-syntax or (syntax-rules () ((or) #f) ((or test) test) ((or test1 test2 ...) (let ((x test1)) (if x x (or test2 ...))))))The last
test-expression
is in tail context if theor
expression itself is.
when
test-expression
form...
If
test-expression
is true, evaluate eachform
in order, returning the value of the last one.
unless
test-expression
form...
If
test-expression
is false, evaluate eachform
in order, returning the value of the last one.
define
name
[::
]type
value
In addition to define
(which can take an optional type specifier),
Kawa has some extra definition forms.
define-private
name
[::
]type
value
define-private
(name
formals
) body
Same as
define
, except thatname
is not exported.
define-constant
name
[::
]type
value
define-early-constant
name
[::
type
] value
Defines
name
to have the givenvalue
. The value is readonly, and you cannot assign to it. (This is not fully enforced.)If
define-early-constant
is used or thevalue
is a compile-time constant, then the compiler will create afinal
field with the given name and type, and evaluatevalue
in the module's class initializer (if the definition is static) or constructor (if the definition is non-static), before other definitions and expressions. Othewise, thevalue
is evaluated in the module body where it appears.If the
value
is a compile-time constant, then the definition defaults to being static.
If
init
is specified andname
does not have a global variable binding, theninit
is evaluated, andname
bound to the result. Otherwise, the value bound toname
does not change. (Note thatinit
is not evaluated ifname
does have a global variable binding.)Also, declares to the compiler that
name
will be looked up in the dynamic environment. This can be useful for shutting up warnings from--warn-undefined-variable
.This is similar to the Common Lisp
defvar
form. However, the Kawa version is (currently) only allowed at module level.
For define-namespace
and define-private-namespace
see the section called “Namespaces and compound symbols”.
The binding constructs let
, let*
, letrec
,
and letrec*
give Scheme a block structure, like Algol 60.
The syntax of these four constructs
is identical, but they differ in the regions they establish
for their variable bindings. In a let
expression, the initial
values are computed before any of the variables become
bound; in a let*
expression, the bindings and evaluations
are performed sequentially; while in letrec
and letrec*
expressions, all the bindings are in effect while their initial
values are being computed, thus allowing mutually recursive definitions.
let
((
variable
[::
]type
init
)
...
)
body
Declare new local variables with the given
name
, initial valueinit
, and optional type specificationtype
. Theinit
s are evaluated in the current environment (in left-to-right onder), thevariable
s are bound to fresh locations holding the results, thebody
is evaluated in the extended environment, and the values of the last expression of body are returned. Each binding of a variable hasbody
as its region. Iftype
is specified, then after the expressioninit
is evaluated, the result coerced totype
, and then assigned to the variable. Iftype
is not specified, it defaults toObject
.(let ((x 2) (y 3)) (* x y)) ⇒ 6(let ((x 2) (y 3)) (let ((x 7) (z (+ x y))) (* z x))) ⇒ 35
let*
((
variable
[::
]type
init
)
...
)
body
The
let*
binding construct is similar tolet
, but the bindings are performed sequentially from left to right, and the region of avariable
is that part of thelet*
expression to the right of the binding. Thus the second binding is done in an environment in which the first binding is visible, and so on. Thevariable
s need not be distinct.(let ((x 2) (y 3)) (let* ((x 7) (z (+ x y))) (* z x))) ⇒ 70
letrec
((
variable
[::
]type
init
)
...
)
body
letrec*
((
variable
[::
]type
init
)
...
)
body
The
variable
s are bound to fresh locations, eachvariable
is assigned in left-to-right order to the result of the correspondinginit
, thebody
is evaluated in the resulting environment, and the values of the last expression in body are returned. Despite the left-to-right evaluation and assignment order, each binding of avariable
has the entireletrec
orletrec*
expression as its region, making it possible to define mutually recursive procedures.In Kawa
letrec
is defined as the same asletrec*
. In standard Scheme the order of evaluation of theinit
s is undefined, as is the order of assignments. If the order matters, you should useletrec*
.If it is not possible to evaluate each
init
without assigning or referring to the value of the correspondingvariable
or the variables that follow it , it is an error.(letrec ((even? (lambda (n) (if (zero? n) #t (odd? (- n 1))))) (odd? (lambda (n) (if (zero? n) #f (even? (- n 1)))))) (even? 88)) ⇒ #t
Lazy evaluation (or call-by-need) delays evaluating an expression until it is actually needed; when it is evaluated, the result is saved so repeated evaluation is not needed. Lazy evaluation is a technique that can make some algorithms easier to express compactly or much more efficiently, or both. It is the normal evaluation mechanism for strict functional (side-effect-free) languages such as Haskell. However, automatic lazy evaluation is awkward to combine with side-effects such as input-output. It can also be difficult to implement lazy evaluation efficiently, as it requires more book-keeping.
Kawa, like other Schemes, uses “eager evaluation” - an expression
is normally evaluated immediately, unless it is wrapped in a special form.
Standard Scheme has some basic building blocks for “manual”
lazy evaluation, using an explicit delay
operator to
indicate that an expression is to be evaluated lazily,
yielding a promise,
and a force
function to force evaluation of a promise.
This functionality is enhanced in
SRFI 45,
in R7RS-draft (based on SRFI 45),
and SRFI 41 (lazy lists aka streams).
Kawa makes lazy evaluation easier to use, by implicit forcing: The promise is automatically evaluated (forced) when used in a context that requires a normal value, such as arithmetic needing a number. Kawa enhances lazy evaluation in other ways, including support for safe multi-threaded programming.
delay
expression
The
delay
construct is used together with the procedureforce
to implement lazy evaluation or call by need.
(delay
returns an object called a promise which at some point in the future may be asked (by theexpression
)force
procedure) to evaluateexpression
, and deliver the resulting value. The effect ofexpression
returning multiple values is unspecified.
lazy
expression
The
lazy
construct is similar to delay, but it is expected that its argument evaluates to a promise. (Kawa treats a non-promise value as if it were a forced promise.) The returned promise, when forced, will evaluate to whatever the original promise would have evaluated to if it had been forced.
Returns a promise that when forced will return
obj
. It is similar todelay
, but does not delay its argument; it is a procedure rather than syntax.As a Kawa optimization, if the
obj
is not a promise (does not implementgnu.mapping.Lazy
), it is returned as-is. (This is because Kawa generally minimizes the difference bwteen a value and forced promise evaluating to the value.)
The
force
procedure forces the value ofpromise
. As a Kawa extension, if thepromise
is not a promise (a value that does not implementgnu.mapping.Lazy
) then the argument is returned unchanged. If no value has been computed for the promise, then a value is computed and returned. The value of the promise is cached (or “memoized”) so that if it is forced a second time, the previously computed value is returned.(force (delay (+ 1 2))) ⇒ 3 (let ((p (delay (+ 1 2)))) (list (force p) (force p))) ⇒ (3 3) (define integers (letrec ((next (lambda (n) (cons n (delay (next (+ n 1))))))) (next 0))) (define head (lambda (stream) (car (force stream)))) (define tail (lambda (stream) (cdr (force stream)))) (head (tail (tail integers))) ⇒ 2The following example is a mechanical transformation of a lazy stream-filtering algorithm into Scheme. Each call to a constructor is wrapped in
delay
, and each argument passed to a deconstructor is wrapped inforce
. The use of(lazy ...)
instead of(delay (force ...))
around the body of the procedure ensures that an ever-growing sequence of pending promises does not exhaust the heap.(define (stream-filter p? s) (lazy (if (null? (force s)) (delay ’()) (let ((h (car (force s))) (t (cdr (force s)))) (if (p? h) (delay (cons h (stream-filter p? t))) (stream-filter p? t)))))) (head (tail (tail (stream-filter odd? integers)))) ⇒ 5
Does
force
as many times as necessary to produce a non-promise. (A non-promise is a value that does not implementgnu.mapping.Lazy
, or if it does implementgnu.mapping.Lazy
then forcing the value using thegetValue
method yields the receiver.)The
force*
function is a Kawa extension. Kawa will add implicit calls toforce*
in most contexts that need it, but you can also call it explicitly.
The following examples are not intended to illustrate good
programming style, as delay
, lazy
, and force
are mainly
intended for programs written in the functional style.
However, they do illustrate the property that only one value is
computed for a promise, no matter how many times it is
forced.
(define count 0) (define p (delay (begin (set! count (+ count 1)) (if (> count x) count (force p))))) (define x 5) p ⇒ a promise (force p) ⇒ 6 p ⇒ a promise, still (begin (set! x 10) (force p)) ⇒ 6
If you pass a promise as an argument to a function like sqrt
if must first be forced to a number. In general, Kawa does this
automatically (implicitly) as needed, depending on the context.
For example:
(+ (delay (* 3 7)) 13) ⇒ 34
Other functions,
like cons
have no problems with promises, and automatic forcing
would be undesirable.
Generally, implicit forcing happens for arguments that require a
specific type, and does not happen for arguments that work on
any type (or Object
).
Implicit forcing happens for:
arguments to arithmetic functions;
the sequence and the index in indexing operations, like
string-ref
;the operands to
eqv?
andequal?
are forced, though the operands toeq?
are not;port operands to port functions;
the value to be emitted by a
display
but not the value to be emitted by awrite
;the function in an application.
Type membership tests, such as the instance?
operation,
generally do not force their values.
The exact behavior for when implicit forcing is a work-in-progress: There are certainly places where implicit forcing doesn't happen while it should; there are also likely to be places where implicit forcing happens while it is undesirable.
Most Scheme implementations are such that a forced promise behaves differently from its forced value, but some Scheme implementions are such that there is no means by which a promise can be operationally distingished from its forced value. Kawa is a hybrid: Kawa tries to minimize the difference between a forced promise and its forced value, and may freely optimize and replace a forced promise with its value.
A blank promise is a promise that doesn't (yet) have a value or a rule for calculating the value. Forcing a blank promise will wait forever, until some other thread makes the promise non-blank.
Blank promises are useful as a synchronization mechanism - you can use it to safely pass data from one thread (the producer) to another thread (the consumer). Note that you can only pass one value for a given promise: To pass multiple values, you need multiple promises.
(define p (promise)) (future ;; Consumer thread (begin (do-stuff) (define v (force promise)) ; waits until promise-set-value! (do-stuff-with v))) ;; Producer thread ... do stuff ... (promise-set-value! p (calculate-value))
Calling
promise
as a zero-argument constructor creates a new blank promise.This calls the constructor for
gnu.mapping.Promise
. You can also create a non-blank promise, by setting one of thevalue
,alias
,thunk
, orexception
properties. Doing so is equivalent to callingpromise-set-value!
,promise-set-alias!
,promise-set-thunk!
, orpromise-set-exception!
on the resulting promise. For example:(delay exp)
is equivalent to:(promise thunk: (lambda() exp))
The following four procedures require that their first arguments be blank promises. When the procedure returns, the promise is no longer blank, and cannot be changed. This is because a promise is conceptually placeholder for a single “not-yet-known” value; it is not a location that can be assigned multiple times. The former enables clean and safe (“declarative") use of multiple threads; the latter is much trickier.
promise-set-value!
promise
value
Sets the value of the
promise
tovalue
, which makes thepromise
forced.
promise-set-exception!
promise
exception
Associate
exception
with thepromise
. When thepromise
is forced theexception
gets thrown.
This parameterized type is the type of promises that evaluate to an value of type
T
. It is equivalent to the Java interfacegnu.mapping.Lazy<T>
. The implementation class for promises is usuallygnu.mapping.Promise
, though there are other classes that implementLazy
, most notablygnu.mapping.Future
, used for futures, which are promises evaluated in a separate thread.
Note the distinction between the types integer
(the type of actual (eager) integer values), and promise[integer]
(the type of (lazy) promises that evaluate to integer).
The two are compatible: if a promise[integer]
value is provided
in a context requiring an integer
then it is automatically
evaluated (forced). If an integer
value is provided
in context requiring a promise[integer]
, that conversion is basically
a no-op (though the compiler may wrap the integer
in a pre-forced promise).
If a fully-lazy language there would be no distinction, or at least the promise type would be the default. However, Kawa is mostly-eager language, so the eager type is the default. This makes efficient code-generation easier: If an expression has an eager type, then the compiler can generate code that works on its values directly, without having to check for laziness.
There is a very preliminary interface to create parallel threads.
The interface is similar to the standard delay
/force
,
where a thread is basically the same as a promise, except that
evaluation may be in parallel.
Creates a new thread that evaluates
expression
.(The result extends
java.lang.Thread
and implementsgnu.mapping.Lazy
.)
The standard
force
function has generalized to also work on threads. If waits for the thread'sexpression
to finish executing, and returns the result.
Creates a new
Runnable
instance from a function. Useful for passing to Java code that expects aRunnable
. You can get the result (a value or a thrown exception) using thegetResult
method.
Synchronize on the given
object
. (This means getting an exclusive lock on the object, by acquiring its monitor.) Then execute theform
s while holding the lock. When theform
s finish (normally or abnormally by throwing an exception), the lock is released. Returns the result of the lastform
. Equivalent to the Javasynchronized
statement, except that it may return a result.
An exception is an object used to signal an error or other exceptional situation. The program or run-time system can throw the exception when an error is discovered. An exception handler is a program construct that registers an action to handle exceptions when the handler is active.
If an exception is thrown and not handled then the read-eval-print-loop will print a stack trace, and bring you back to the top level prompt. When not running interactively, an unhandled exception will normally cause Kawa to be exited.
In the Scheme exception model (as of R6RS and R7RS) exception handlers are one-argument procedures that determine the action the program takes when an exceptional situation is signaled. The system implicitly maintains a current exception handler in the dynamic environment. The program raises an exception by invoking the current exception handler, passing it an object encapsulating information about the exception. Any procedure accepting one argument can serve as an exception handler and any object can be used to represent an exception.
The Scheme exception model is implemented on top of the Java VM's
native exception model where the only objects that
can be thrown are instances of java.lang.Throwable
.
Kawa also provides direct access to this native model,
as well as older Scheme exception models.
with-exception-handler
handler
thunk
It is an error if
handler
does not accept one argument. It is also an error ifthunk
does not accept zero arguments. Thewith-exception-handler
procedure returns the results of invokingthunk
. Thehandler
is installed as the current exception handler in the dynamic environment used for the invocation ofthunk
.(call-with-current-continuation (lambda (k) (with-exception-handler (lambda (x) (display "condition: ") (write x) (newline) (k 'exception)) (lambda () (+ 1 (raise ’an-error)))))) ⇒ exception and prints condition: an-error(with-exception-handler (lambda (x) (display "something went wrong\n")) (lambda () (+ 1 (raise ’an-error)))) prints something went wrongAfter printing, the second example then raises another exception.
Performance note: The
thunk
is inlined if it is a lambda expression. However, thehandler
cannot be inlined even if it is a lambda expression, because it could be called byraise-continuable
. Using theguard
form is usually more efficient.
Raises an exception by invoking the current exception handler on
obj
. The handler is called with the same dynamic environment as that of the call to raise, except that the current exception handler is the one that was in place when the handler being called was installed. If the handler returns, thenobj
is re-raised in the same dynamic environment as the handler.If
obj
is an instance ofjava.lang.Throwable
, thenraise
has the same effect asprimitive-throw
.
Raises an exception by invoking the current exception handler on
obj
. The handler is called with the same dynamic environment as the call toraise-continuable
, except that: (1) the current exception handler is the one that was in place when the handler being called was installed, and (2) if the handler being called returns, then it will again become the current exception handler. If the handler returns, the values it returns become the values returned by the call toraise-continuable
.(with-exception-handler (lambda (con) (cond ((string? con) (display con)) (else (display "a warning has been issued"))) 42) (lambda () (+ (raise-continuable "should be a number") 23))) prints: should be a number ⇒ 65
guard
variable
cond-clause
^+
body
The
body
is evaluated with an exception handler that binds the raised object tovariable
and, within the scope of that binding, evaluates the clauses as if they were the clauses of acond
expression. That implicitcond
expression is evaluated with the continuation and dynamic environment of theguard
expression. If every cond-clause’s test evaluates to#f
and there is noelse
clause, thenraise-continuable
is invoked on the raised object within the dynamic environment of the original call toraise
orraise-continuable
, except that the current exception handler is that of theguard
expression.(guard (condition ((assq 'a condition) => cdr) ((assq 'b condition))) (raise (list (cons 'a 42)))) ⇒ 42(guard (condition ((assq 'a condition) => cdr) ((assq 'b condition))) (raise (list (cons 'b 23)))) ⇒ (b . 23)Performance note: Using
guard
is moderately efficient: there is some overhead compared to using native exception handling, but both thebody
and the handlers in thecond-clause
are inlined.
dynamic-wind
in-guard
thunk
out-guard
All three arguments must be 0-argument procedures. First calls
in-guard
, thenthunk
, thenout-guard
. The result of the expression is that ofthunk
. Ifthunk
is exited abnormally (by throwing an exception or invoking a continuation),out-guard
is called.If the continuation of the dynamic-wind is re-entered (which is not yet possible in Kawa), the
in-guard
is called again.This function was added in R5RS.
Returns #t if
obj
is an object raised by theread
procedure. (That is ifobj
is agnu.text.SyntaxException
.)
Returns #t if
obj
is an object raised by inability to open an input or output port on a file. (This includesjava.io.FileNotFoundException
as well as certain other exceptions.)
Raises an exception as if by calling
raise
on a newly allocated simple error object, which encapsulates the information provided bymessage
(which should a string), as well as anyobj
arguments, known as the irritants.The string representation of a simple error object is as if calling
(format "#<ERROR ~a~{ ~w~}>"
. (That is themessage
irritants
)message
is formatted as if withdisplay
while each irritantobj
is formatted as if withwrite
.)This procedure is part of SRFI-23, and R7RS. It differs from (and is incompatible with) R6RS's
error
procedure.
Returns
#t
ifobj
is a simple error object. Specifically, thatobj
is an instance ofkawa.lang.NamedException
. Otherwise, it returns#f
.
These functions associate a symbol with exceptions and handlers: A handler catches an exception if the symbol matches.
Invoke
thunk
in the dynamic context ofhandler
for exceptions matchingkey
. If thunk throws to the symbolkey
, thenhandler
is invoked this way:(handler key args ...)
key
may be a symbol. Thethunk
takes no arguments. Ifthunk
returns normally, that is the return value ofcatch
.Handler is invoked outside the scope of its own
catch
. Ifhandler
again throws to the same key, a new handler from further up the call chain is invoked.If the key is
#t
, then a throw to any symbol will match this call tocatch
.
Throws the
exception
, which must be an instance of a sub-class ofjava.lang.Throwable
.
Evaluate
body
, and return its result. However, before it returns, evaluatehandler
. Even ifbody
returns abnormally (by throwing an exception),handler
is evaluated.(This is implemented just like Java's
try
-finally
. However, the current implementation does not duplicate thehandler
.)
Evaluate
body
, in the context of the givenhandler
specifications. Eachhandler
has the form:var
type
exp
...If an exception is thrown in
body
, the firsthandler
is selected such that the thrown exception is an instance of thehandler
'stype
. If nohandler
is selected, the exception is propagated through the dynamic execution context until a matchinghandler
is found. (If no matchinghandler
is found, then an error message is printed, and the computation terminated.)Once a
handler
is selected, thevar
is bound to the thrown exception, and theexp
in thehandler
are executed. The result of thetry-catch
is the result ofbody
if no exception is thrown, or the value of the lastexp
in the selectedhandler
if an exception is thrown.(This is implemented just like Java's
try
-catch
.)