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 Javabooleanorkava.lang.Booleanwhen converted to an object.
The
boolean?predicate returns#tifobjis either#tor#f, and returns#fotherwise.(boolean? #f) ⇒ #t (boolean? 0) ⇒ #f (boolean? '()) ⇒ #f
The not procedure returns
#tifobjis false, and returns#fotherwise.(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
ifexpression is evaluated as follows: first,test-expressionis evaluated. If it yields a true value, thenconsequentis evaluated and its values are returned. Otherwisealternateis evaluated and its values are returned. Iftestyields#fand noalternateis 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
consequentandalternateexpressions are in tail context if theifexpression itself is.
cond cond-clause^+
cond cond-clause^* (elseexpression…)
cond-clause::=(test-expressionexpression^*)
|(test=>expression)
A
condexpression is evaluated by evaluating thetest-expressions of successivecond-clauses in order until one of them evaluates to a true value. When atest-expressionevaluates to a true value, then the remainingexpressions in itscond-clauseare evaluated in order, and the results of the lastexpressionin thecond-clauseare returned as the results of the entirecondexpression. If the selectedcond-clausecontains only thetest-expressionand noexpressions, then the value of thetest-expressionis returned as the result. If the selectedcond-clauseuses the=>alternate form, then theexpressionis evaluated. Its value must be a procedure. This procedure should accept one argument; it is called on the value of thetest-expressionand the values returned by this procedure are returned by thecondexpression.If all
test-expressions evaluate to#f, and there is noelseclause, then the conditional expression returns unspecified values; if there is anelseclause, then itsexpressions 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-clauseof one of the following forms:(testexpression^*) (elseexpressionexpression^*)the last
expressionis in tail context if thecondform itself is. For acond clauseof the form:(test=>expression)the (implied) call to the procedure that results from the evaluation of
expressionis in a tail context if thecondform 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::=(elseexpression^+)
|(else =>expression)
Each
datumis an external representation of some object. Eachdatumin the entirecaseexpression should be distinct.A
caseexpression is evaluated as follows.
The
case-keyis evaluated and its result is compared usingeqv?against the data represented by thedatums of eachcase-clausein turn, proceeding in order from left to right through the set of clauses.If the result of evaluating
case-keyis equivalent to a datum of acase-clause, the correspondingexpressions are evaluated from left to right and the results of the last expression in thecase-clauseare returned as the results of thecaseexpression. Otherwise, the comparison process continues.If the result of evaluating
keyis different from every datum in each set, then if there is ancase-else-clauseits expressions are evaluated and the results of the last are the results of thecaseexpression; otherwise the result ofcaseexpression is unspecified.If the selected
case-clauseorcase-else-clauseuses the=>alternate form, then theexpressionis evaluated. It is an error if its value is not a procedure accepting one argument. This procedure is then called on the value of thekeyand the values returned by this procedure are returned by thecaseexpression.(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
expressionof acase clauseis in tail context if thecaseexpression itself is.
and test-expression …
If there are no
test-expressions,#tis returned. Otherwise, thetest-expressionare evaluated from left to right until atest-expressionreturns#for the lasttest-expressionis reached. In the former case, theandexpression returns#fwithout 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
andkeyword could be defined in terms ofifusingsyntax-rulesas follows:(define-syntax and (syntax-rules () ((and) #t) ((and test) test) ((and test1 test2 ...) (if test1 (and test2 ...) #t))))The last
test-expressionis in tail context if theandexpression itself is.
or test-expression …
If there are no
test-expressions,#fis returned. Otherwise, thetest-expressions are evaluated from left to right until atest-expressionreturns a true valuevalor the lasttest-expressionis reached. In the former case, theorexpression returnsvalwithout 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
orkeyword could be defined in terms ofifusingsyntax-rulesas 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-expressionis in tail context if theorexpression itself is.
when test-expression form...
If
test-expressionis true, evaluate eachformin order, returning the value of the last one.
unless test-expression form...
If
test-expressionis false, evaluate eachformin 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 thatnameis not exported.
define-constant name [:: ]type value
define-early-constant name [:: type] value
Defines
nameto have the givenvalue. The value is readonly, and you cannot assign to it. (This is not fully enforced.)If
define-early-constantis used or thevalueis a compile-time constant, then the compiler will create afinalfield with the given name and type, and evaluatevaluein the module's class initializer (if the definition is static) or constructor (if the definition is non-static), before other definitions and expressions. Othewise, thevalueis evaluated in the module body where it appears.If the
valueis a compile-time constant, then the definition defaults to being static.
If
initis specified andnamedoes not have a global variable binding, theninitis evaluated, andnamebound to the result. Otherwise, the value bound tonamedoes not change. (Note thatinitis not evaluated ifnamedoes have a global variable binding.)Also, declares to the compiler that
namewill 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
defvarform. 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. Theinits are evaluated in the current environment (in left-to-right onder), thevariables are bound to fresh locations holding the results, thebodyis evaluated in the extended environment, and the values of the last expression of body are returned. Each binding of a variable hasbodyas its region. Iftypeis specified, then after the expressioninitis evaluated, the result coerced totype, and then assigned to the variable. Iftypeis 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 avariableis 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. Thevariables 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
variables are bound to fresh locations, eachvariableis assigned in left-to-right order to the result of the correspondinginit, thebodyis 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 avariablehas the entireletrecorletrec*expression as its region, making it possible to define mutually recursive procedures.In Kawa
letrecis defined as the same asletrec*. In standard Scheme the order of evaluation of theinits is undefined, as is the order of assignments. If the order matters, you should useletrec*.If it is not possible to evaluate each
initwithout assigning or referring to the value of the correspondingvariableor 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
delayconstruct is used together with the procedureforceto implement lazy evaluation or call by need.
(delayreturns an object called a promise which at some point in the future may be asked (by theexpression)forceprocedure) to evaluateexpression, and deliver the resulting value. The effect ofexpressionreturning multiple values is unspecified.
lazy expression
The
lazyconstruct 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
objis 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
forceprocedure forces the value ofpromise. As a Kawa extension, if thepromiseis 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
forceas 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.Lazythen forcing the value using thegetValuemethod 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
displaybut 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
promiseas 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, orexceptionproperties. 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
promisetovalue, which makes thepromiseforced.
promise-set-exception! promise exception
Associate
exceptionwith thepromise. When thepromiseis forced theexceptiongets 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.Threadand implementsgnu.mapping.Lazy.)
The standard
forcefunction has generalized to also work on threads. If waits for the thread'sexpressionto finish executing, and returns the result.
Creates a new
Runnableinstance from a function. Useful for passing to Java code that expects aRunnable. You can get the result (a value or a thrown exception) using thegetResultmethod.
Synchronize on the given
object. (This means getting an exclusive lock on the object, by acquiring its monitor.) Then execute theforms while holding the lock. When theforms finish (normally or abnormally by throwing an exception), the lock is released. Returns the result of the lastform. Equivalent to the Javasynchronizedstatement, 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
handlerdoes not accept one argument. It is also an error ifthunkdoes not accept zero arguments. Thewith-exception-handlerprocedure returns the results of invokingthunk. Thehandleris 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
thunkis inlined if it is a lambda expression. However, thehandlercannot be inlined even if it is a lambda expression, because it could be called byraise-continuable. Using theguardform 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, thenobjis re-raised in the same dynamic environment as the handler.If
objis an instance ofjava.lang.Throwable, thenraisehas 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
bodyis evaluated with an exception handler that binds the raised object tovariableand, within the scope of that binding, evaluates the clauses as if they were the clauses of acondexpression. That implicitcondexpression is evaluated with the continuation and dynamic environment of theguardexpression. If every cond-clause’s test evaluates to#fand there is noelseclause, thenraise-continuableis invoked on the raised object within the dynamic environment of the original call toraiseorraise-continuable, except that the current exception handler is that of theguardexpression.(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
guardis moderately efficient: there is some overhead compared to using native exception handling, but both thebodyand the handlers in thecond-clauseare 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. Ifthunkis exited abnormally (by throwing an exception or invoking a continuation),out-guardis called.If the continuation of the dynamic-wind is re-entered (which is not yet possible in Kawa), the
in-guardis called again.This function was added in R5RS.
Returns #t if
objis an object raised by thereadprocedure. (That is ifobjis agnu.text.SyntaxException.)
Returns #t if
objis an object raised by inability to open an input or output port on a file. (This includesjava.io.FileNotFoundExceptionas well as certain other exceptions.)
Raises an exception as if by calling
raiseon a newly allocated simple error object, which encapsulates the information provided bymessage(which should a string), as well as anyobjarguments, known as the irritants.The string representation of a simple error object is as if calling
(format "#<ERROR ~a~{ ~w~}>". (That is themessageirritants)messageis formatted as if withdisplaywhile each irritantobjis formatted as if withwrite.)This procedure is part of SRFI-23, and R7RS. It differs from (and is incompatible with) R6RS's
errorprocedure.
Returns
#tifobjis a simple error object. Specifically, thatobjis 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
thunkin the dynamic context ofhandlerfor exceptions matchingkey. If thunk throws to the symbolkey, thenhandleris invoked this way:(handler key args ...)
keymay be a symbol. Thethunktakes no arguments. Ifthunkreturns normally, that is the return value ofcatch.Handler is invoked outside the scope of its own
catch. Ifhandleragain 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 ifbodyreturns abnormally (by throwing an exception),handleris 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 givenhandlerspecifications. Eachhandlerhas the form:vartypeexp...If an exception is thrown in
body, the firsthandleris selected such that the thrown exception is an instance of thehandler'stype. If nohandleris selected, the exception is propagated through the dynamic execution context until a matchinghandleris found. (If no matchinghandleris found, then an error message is printed, and the computation terminated.)Once a
handleris selected, thevaris bound to the thrown exception, and theexpin thehandlerare executed. The result of thetry-catchis the result ofbodyif no exception is thrown, or the value of the lastexpin the selectedhandlerif an exception is thrown.(This is implemented just like Java's
try-catch.)
