Table of Contents
In this chapter we will examine the language that SISC interprets, which is a superset of the R5RS Scheme Standard.
The full Scheme number tower is supported:
Integers
Floating Point numbers
Rational numbers
Complex numbers
Depending on the numeric library compiled into SISC, floating point numbers have either 32 or 64 bit IEEE precision, or arbitrary [2] precision. Regardless, SISC's complex numbers have floating point components of the same precision as the reals. Integers have arbitrary precision in all numeric libraries, and rational numbers are built with arbitrary precision components.
The precision specifying exponents (S, F, L,
and D
) are
ignored in SISC, all inexact numbers are kept in the precision
of the numeric library. The exponents are read and used to scale
the real number as expected. In the case of arbitrary precision
floats, specific precision constraints are maintained to prevent
a runaway increase of precision. The constraints can be set by
minFloatPrecision
and
maxFloatPrecision
configuration parameters on
startup. See the section called “Configuration Parameters”.
All four base specifiers (#x, #o,
#d, #b
) are supported for
integers and rationals. Only decimal
(#d
), the
default, is supported for floating point and complex
numbers.
SISC will produce infinite or not-a-number quantities from
some operations. Those quantities are represented and
can be used in Scheme programs as
#!+inf
(positive infinity),
#!-inf
(negative infinity), and
#!nan
(not-a-number).
Exactness and inexactness contagion behaves as expected. Rational's are made inexact through division. Floats are made exact by conversion to a rational number. SISC attempts as accurate a conversion as possible, by converting the decimal portion of the number to a ratio with a denominator of the form 10^n, where n is the scale of the floating point number. Then the fraction is reduced as usual.
Since complex numbers must have floating point components currently, conversion to an exact merely rounds the components to integers.
SISC's characters are double-byte wide. This means that they
are capable of representing the full range of unicode characters.
Unicode characters can be created with
number->character
;
#\nnnnnn
, where
nnnnnn
is an octal number in the range
000000 -> 177777; or #\uxxxx
,
where xxxx
is a hexadecimal number in the
range 0000 -> ffff. At least two zeros must be specified
to distinguish from the '0' character when using an octal
character literal. At least one zero must be specified to
distinguish a hexadecimal character from the 'u' character.
SISC also provides additional named characters, to add to
the Scheme standard's space
and
newline
:
Table 3.1. Named character literals
Character Name | Unicode Value (hex) |
---|---|
backspace | 0008 |
newline | 000a |
nul | 0000 |
page | 000c |
return | 000d |
rubout | 007f |
space | 0020 |
tab | 0009 |
Formally, SISC's lexer modifies the R5RS grammar with the following productions for character literals:
<character> --> #\ <any character> | #\u <uinteger 16> | #\ <uinteger 8> | #\ <character name> <character name> --> backspace | newline | nul | page | return | rubout | space | tab
Characters are not compared with respect to the
locale of the running system. Character comparison is equivalent
to numeric comparison of the character value as returned by
char->integer
.
There are a number of reasons why a full Unicode system is non-trivial, especially within the framework of the R5RS string and character functions. Such a discussion is outside the scope of this document. Unicode compliant processing may be made available in the future as a library, however.
SISC's symbols are ordinarily case-insensitive. SISC maintains true pointer equality between symbols with like contents, unless the symbol is created uninterned. An uninterned symbol is one which is guaranteed to be pointer distinct from any other symbol in the Scheme system, even another with the same contents. Uninterned symbols can be generated with:
procedure:
(string->uninterned-symbol string) => symbol
Converts the provided string into an uninterned, pointer distinct symbol.
Uninterned symbols, while always pointer-distinct, may still
be equal?
to another symbol if its
representation matches another.
SISC also allows symbols to be created that
are case-sensitive. This can be done
one of two ways. The first is by setting the
caseSensitive
configuration parameter (see
the section called “Configuration Parameters”. The second method is
via a non-standard symbol syntax. If a symbol is enclosed in
pipe ('|') characters, the reader will treat that individual
symbol as cased. The syntax extends the R5RS grammar with
the following production:
<cased symbol> --> |<identifier>|
Example 3.1. Case sensitive Symbol literals
(eq? 'a '|A|) ; => #f (eq? 'a '|a|) ; => #t (eq? '|A| '|a|) ; => #f
Symbols may contain characters that are disallowed by R5RS
using symbol->string
. In such a case,
the printed representation of that symbol will contain those
characters, prefaced with the escape ('\') character.
Likewise, such symbols may be created without
symbol->string
by escaping non-standard
characters.
Symbols which contain characters that could only be present
in a case-sensitive environment will be printed in one of
two ways, depending on the value of the
case-sensitive
parameter. If true, the
symbols will be printed as is, containing the upper and
lower case letters. If false, the symbol will be printed
surrounded by pipe characters.
Strings are built from Unicode characters, and are compared lexicographically in a manner derived from character comparison. In addition to using backslash to escape the double-quote (") character and the backspace character itself, SISC provides several escape codes to ease string literal construction.
Table 3.2. String escape codes
Escape | Value |
---|---|
\f | Inserts the formfeed character (unicode 000c) |
\n | Inserts the newline character (unicode 000a) |
\r | Inserts the rubout character (unicode 007f) |
\t | Inserts the tab character (unicode 0009) |
\uxxxx | Inserts the unicode character described by the hex number 'xxxx'. All four hex digits must be specified. |
\\ | Inserts the backslash ('\') character |
\" | Inserts the double quote ('"') character |
A function is provided to determine if a given pair is a proper list.
procedure:
(proper-list? datum) => #t/#f
Returns
#t
if the given argument is a proper-list. That is, if the argument is a pair, whosecdr
is either the empty-list or also a proper-list, and which contains no references to itself (is not circular).
SISC supports the length prefix method of creating Vector
constants. For example, '#5(x)
creates a vector constant containing five identical
symbols. In addition, the length-prefix form is used when
printing vectors, and if elements repeat at the end of a
Vector, only the last unique element is printed. This form
is referred to as the compact vector
representation. The unprefixed form with all elements
displayed is called the verbose representation.
Vectors are displayed differently depending on the call
used. When called with
display
, in addition to the
ordinary R5RS rules regarding the output of values
displayed with display
,
the verbose representation is displayed. Using
write
, on the other hand
produces the compact representation.
Displaying a vector with pretty-print
may
output either the verbose or compact representation of a
vector. The behavior in this regard is controlled by the
vectorLengthPrefixing
configuration parameter
(see the section called “Configuration Parameters”). If set to #t,
pretty-print
will emit the compact
representation. If #f, the verbose representation is
produced.
SISC supports boxes, a container for a Scheme value. Boxing is often used to implement call-by-reference semantics. Boxes are created and accessed using the following three functions:
Creates a box filled with the given value.
procedure:
(unbox box) => value
Returns the value contained in the given box.
procedure:
(set-box! box value) => undefined
Replaces the value contained in the given box with the value provided.
In addition to the box
function for
creating boxes, SISC provides an external representation for
boxes and boxed values. It extends the R5RS grammar with the
following:
<boxed value> --> #&<datum>
This syntax denotes a boxed value, with
<datum>
as the contained
value.
Boxes are a distinct first class type. The
box?
predicate tests a value to see if is
a box.
Boxes, like pairs, are only equal in the sense of
eq?
and eqv?
when a
box is compared with itself. A box is equal to another in the
sense of equal?
if the value contained
within the box is equal?
to the value
contained in the other.
A parameter is a named dynamic variable that is accessed through a function. The function, when given no arguments, returns the current value of the parameter. When given an argument, the value of the parameter is set to the provided value.
SISC's parameters are fully compatible with those specified by SRFI-39. Consult the SRFI-39 specification at srfi.schemers.org for documentation on how to construct and use parameters. SRFI-39 does not specify the semantics for parameters in the presence of threads. SISC's parameters bind into the dynamic environment, which means their semantics are defined based on the semantics of dynamic environments' interactions with threads, specified in the section called “ Scheme Thread Semantics ”.
SISC's storage model maintains true pointer equality between
symbols, booleans, the end-of-file object, void, and the empty
list. Thus two instances of any of those types is guaranteed to
return #t
from eq?
if they
would have produced #t
from
equal?
.
Numbers and characters are not pointer equal ordinarily (unless actually
occupying the same storage). SISC will return
#t
from eqv?
if two numbers are both exact, or both
inexact, and are numerically equal. Two characters are
equivalent from eqv?
if they occupy the
same code-point in the unicode character set. This is the behavior
specified by R5RS.
Strings, vectors, lists, and boxes are containers for other
Scheme types. As such they are not pointer equal unless they
are referenced by two variables that point to the same storage
location (i.e. they are actually pointer equal). SISC holds
that only equal?
will return
#t
if two objects are the
same type and their contents contain equivalent values with respect to
equal?
.
In addition to the single line comments of the Scheme
standard, SISC supports both s-expression
commenting and nested, multiline comments. An s-expression comment is used to
comment out an entire s-expression. To do this, the sharp
sequence #;
is used. It extends the R5RS
grammar with the following production:
<expression-comment> --> #;<datum>
The reader, upon encountering this sharp sequence, will read and discard the next datum. The expression commented out must still be a valid s-expression, however.
Nested, multiline comments are as defined in SRFI-30.
Briefly, a multiline comment begins with the sharp sequence
#|
and ends with the sequence
|#
. The comment may contain nested
comments as well. Unfortunately, this extension cannot be
represented in a stateless grammar for the lexical structure.
SISC provides a parse-time syntax for creating data (primarily vectors and lists) that contain references to themselves or data which contains several pointer-equal elements. This can be useful to create streams, graphs, and other self-referencing structures while maintaining readability and avoiding complex construction code.
The reader syntax has two parts, defining a pointer, and later referencing the pointer to create the circular reference.
Below is an additional production in the R5RS formal syntax (specifically section 7.1.2, external representations) to support circular structures:
<pointer definition> --> #<uinteger 10>=<datum> <pointer reference> --> #<uinteger 10>#
The first form instructs the reader to create a pointer identified by the specified integer, which maps to the datum that follows, and is active during the reading of the datum on the right-hand side of the definition.
If a second definition occurs during the reading of the datum with the same integral identifier, the previous definition is overwritten for the duration of the read. The definitions are not scoped in any way. The pointer identifiers should be kept unique by the programmer to prevent any unintended effects of identifier collisions.
The second form references a previously created pointer definition. It is an error to reference an undefined pointer. The reader will handle a valid reference by placing a pointer at the current read position back to the location of the definition.
At this point some examples might be helpful:
Example 3.2. Circular Structures
(define x '#0=(1 2 . #0#)) (caddr x) ; => 1 (list-ref x 15) ; => 2 (define y '(1 2 #1=#(3 4) . #1#)) (eq? (caddr y) (cdddr y)) ; => #t
Ordinarily, the display of cyclical data would cause a
problem for a Read-Eval-Print-Loop. For this reason, the
REPL will attempt to check the structure it is about to
print for circularities before printing. If a cycle is
found in the structure, the REPL will refuse to print if the
printShared
configuration parameter,
described below, is false
. In that case
the REPL will issue a warning to the user that the structure
contains a cycle. If a circular structure is printed with
display
, write
,
etc, and the printShared
parameter is set to
false
, the environment may enter an
infinite loop which may or may not cause the Scheme system
to exit with an error.
The printShared
configuration parameter (see
the section called “Configuration Parameters”), if set to
true
enables SISC to scan data for
circularity and data sharing before writing values. If such
sharing is found, an alternate printer is invoked which will
emit a representation compatible with the circular structure
representation described in the previous section.
Alternately, SISC also supports SRFI-38, which describes
the functions write-showing-shared
and
read-with-shared-structure
.
In addition to the R5RS standard control features, two
additional forms,
when
and unless
, are
supported by SISC.
syntax:
(when condition expression [expressions] ...) => value
Evaluates
condition
, an expression. If true, the expressions that follow are evaluated, in order, the value of the last being returned. If not true, the result is unspecified.
syntax:
(unless condition expression [expressions] ...) => value
Evaluates
condition
, an expression. If false, the expressions that follow are evaluated, in order, the value of the last being returned. If true, the result is unspecified.
SISC provides a hygienic macro system that fully conforms to
the R5RS standard. The macro system is provided by the
portable syntax-case macro expander. In addition to R5RS
macros, the expander provides a more flexible
macro definition tool called
syntax-case
. A full description of the
capabilities of the expander is best found in the
Chez Scheme Users Guide
, specifically
Section 9.2, Syntax-Case
.
In addition, SISC supports non-hygienic, legacy macro support
in two forms; define-macro
and
defmacro
. These forms, found in older
Scheme code written for R4RS
compliant Scheme systems, should be used only
for executing legacy code which relies on it. New code should
use the safer and more flexible syntax-case
or the standard syntax-rules
macros.
syntax:
(define-macro (name . args) body ...)
syntax:
(define-macro name transformer)
In the first form,
define-macro
creates a macro transformer bound toname
, which when applied will have raw s-expressions bound to one or more parameters (args
). The(name . args)
name and formal parameter form is identical to the short form for procedure definition withdefine
.The transformer's body will then, using the s-expressions bound to its arguments, return a new s-expression that is the result of the macro transformation.
The second form binds an arbitrary procedure to the syntactic keyword
name
, using that procedure to transform occurences of that named syntax during future evaluations.
syntax:
(defmacro name args body ...)
defmacro
is another macro definition form supported by some Scheme systems. Its semantics are equivalent to:(define-macro (name . args) body ...)
Errors can be raised by primitives in libraries and Scheme-level code. SISC provides a sophisticated mechanism for handling these errors when they occur during program execution.
During the execution of any program, there is always a continuation that represents the rest of a computation. In addition, one can imagine all the activities that will occur as a result of an error. This sequence of actions is explicitly represented in SISC as a failure continuation.
Two values must be applied to a failure continuation. The first is an error record, a datastructure which describes the error (and may contain information about the name of the function that generated the error, a descriptive message about the error, etc.). The second is the continuation of the expression that raised the error. All errors raised in SISC automatically and implicitly obtain and apply these values to the active failure continuation. Applying the error record and error continuation to the failure continuation will not return to the continuation of the application, unless that continuation was captured and later invoked in a non-local entrance.
A programmer may wish to augment current failure
continuation, choosing a different set of actions to occur
for a body of code if it raises an error. To facilitate
this, SISC provides the
with-failure-continuation
procedure.
procedure:
(with-failure-continuation handler thunk) => value
procedure:
(with/fc handler thunk) => value
with-failure-continuation
takes as arguments a thunk (a zero-argument procedure) to be evaluated. The thunk will be evaluated in the continuation of thewith/fc
function, and with a failure continuation defined by the provided error handler. If during the evaluation of the thunk an error is raised, the first, two argument procedure is called with values describing the error and its context. If no error occurs, value of the thunk is applied to the continuation of thewith/fc
expression.
The error handler required as an argument to
with-failure-continuation
must accept
two values. The first is a value containing information
about the error that occurred. This is often an association
list containing a number of attributes of the error. The
second is a procedure encapsulating the continuation that
was in place at the site of the error. This continuation is
referred to as the error continuation
When an error occurs, the error handler may choose one of
three courses in dealing with the error. First, the handler
may choose to return an alternate value to be applied to the
continuation of the with/fc
expression.
Second, the handler may restart the computation from the
error site by invoking the error continuation with a value
that should be returned in place of the expression that
caused the error. Finally, the handler may choose to
propagate the error (or a new error) to the failure
continuation of the with/fc
expression.
This can be done with the throw
function described in the section called “Raising Errors”.
The currently active failure continuation may be obtained
explicitly using the
call-with-failure-continuation
procedure. This continuation may be applied to appropriate
values at any time in the future.
procedure:
(call-with-failure-continuation procedure) => value
procedure:
(call/fc procedure) => value
Calls the given one-argument procedure with the currently active failure continuation.
Failure continuations exist as an attribute of the ordinary
continuations of Scheme expressions. Because of this, the
invocation of a continuation may cause a different failure
continuation to become active in the region of the captured
continuation. Specifically, the failure continuation in place
at the call/cc
expression will be
reinstated when that continuation is later invoked.
Similarly, invoking a continuation that escapes a region of code will cause any created failure continuations to be abandoned, unless the region is itself captured in a continuation and later invoked.
See also the section called “dynamic-wind
”.
An error record is the value usually propagated with an error in SISC. It is a datastructure containing such information as the location of the error, a descriptive message about the error, and possibly other error metadata.
Error records can be created in advance of actually
raising an error with the make-error
function.
The function allows the programmer to create error records that
contain a location and a message or value. No field of an
error record is required.
procedure:
(make-error [location] [message] [arguments] ...) => error-record
Constructs an error record. If present, a symbol, and not
#f
, the first argument is the location of the error, which may be a symbol equivalent to a function identifier. If present, the message is a format-string processed with the optional arguments that follow as byformat
in SRFI-28. The remaining arguments must only be present if the format-string is present as the message.
procedure:
(make-error [location] error-value) => error-record
Constructs an error record. If present, a symbol, and not
#f
, the first argument is the location of the error. The second argument is an arbitrary Scheme value that will be the error value. This value will be accessible with theerror-message
function.
None of the fields of an error-record are required. One may
create an error record with no information, an error record
with only a location, or an error record with only a message
or value. Below are some examples (for an explanation of
the throw
procedure see the section called “Raising Errors”).
(throw (make-error)) ; => Error. (throw (make-error 'foo)) ; => Error in foo. (throw (make-error "something ~a happened" 'bad)) ; => Error: something bad happened (throw (make-error 3)) ; => Error: 3 (throw (make-error #f 'foo)) ; => Error: foo (throw (make-error 'foo "something ~a happened" 'bad)) ; => Error in foo: something bad happened
In addition, an error record may be created that adds additional information to an error record that was already created. This is useful when an error was caught in an error handler, and one wishes to raise an error from the handler that contains additional information about the local location or error message as well as the error that was caught.
procedure:
(make-nested-error local-error parent-error parent-error-continuation) => error-record
procedure:
(make-nested-error local-error exception) => error-record
The first version creates an error record which has
parent-error
(and its associatedparent-error-continuation
) as the root cause of an error-record passed aslocal-error
.The second version creates an error record which has
exception
(see the section called “Exceptions”) as the root cause of an error passed aslocal-error
.
An example of the creating, throwing, and display of a nested error follows.
(with-failure-continuation (lambda (m e) (throw (make-nested-error (make-error 'foo "could not call bar.") m e))) (lambda () (error 'bar "something went wrong."))) ;=> Error in foo: could not call bar. ; Caused by Error in bar: something went wrong.
An error record contains several useful pieces of information. The following functions allow the programmer to access that information.
procedure:
(error-location error-record) => symbol
Obtains the location of the error, a symbol which may be a function identifier. If there is no location specified,
#f
is returned.
procedure:
(error-message error-record) => value
Obtains the message of the error, which may be a string which is a descriptive message of the error, or an arbitrary value (as created by the second form of
make-error
). If there is no message specified,#f
is returned.
procedure:
(error-parent-error error-record) => error-record
Obtains the parent error of the error. This is the value of the second argument to the
make-nested-error
function. If there is no parent specified,#f
is returned.
procedure:
(error-parent-continuation error-record) => error-continuation
Obtains the parent error continuation of the error. This is the value of the third argument to the
make-nested-error
function. If there is no parent specified,#f
is returned.
The fundamental mechanism for raising an error in
application code is provided by the throw
procedure.
procedure:
(throw error-record [error-continuation]) => does not return
procedure:
(throw exception) => does not return
The first verison applies the given error record to the current failure continuation. If provided, the error continuation is designated by the optional parameter. If not, the continuation of the throw expression is used.
The second form applies the current failure continuation to the error record and error continuation extracted from the supplied
exception
(see the section called “Exceptions”).
If invoked from an error-handler with the values of the handler's formal parameters, throw has the effect of propagating the error in a manner that is equivalent to the absence of the modified failure-continuation.
throw
could be defined in terms of
call-with-failure-continuation
as:
(define (throw error . args) (call-with-failure-continuation (lambda (fk) (if (null? args) (call-with-current-continuation (lambda (k) (fk error k))) (fk error (car args))))))
For convenience and compatibility with SRFI-23, the function
error
is provided. Its syntax is
identical to make-error
, but it
immediately applies the resulting error record to the
current failure continuation with the current continuation
as the error continuation.
procedure:
(error [location] [message] [arguments] ...) => does not return
Raises an error record whose location, if provided, is
location
, a symbol; and whose error message, if present, ismessage
. If provided, the error message is a format-string that is processed, with the optionalargument
s, as with theformat
function in SRFI 28.
procedure:
(error [location] error-value) => does not return
Raises an error record whose location, if present, is the symbol
location
, and and whose error-value is any arbitrary Scheme value.
error
can be implemented in terms of
throw
and make-error
:
(define (error . args) (throw (apply make-error args)))
Exceptions in SISC are a simple wrapper around an error record and an associated error continuation.
Exceptions are created with
procedure:
(make-exception error-record error-continuation) => exception
Constructs an exception from an
error-record
and anerror-continuation
, e.g. as obtained from the arguments of a handler procedure passed towith-fc
.
Accessors and a type-test are provided by the following procedures:
procedure:
(exception-error exception) => error-record
Returns the
exception
's error record.
procedure:
(exception-continuation exception) => error-continuation
Returns the
exception
's error continuation.
procedure:
(exception? value) => #t/#f
Returns #t if
value
is an exception object, #f otherwise.
At this point, a few examples may be helpful:
(+ 1 (/ 1 0) 3)
; => A divide by zero error is raised
Example 3.3. Return a new value
(with-failure-continuation
(lambda (error-record error-k)
'error)
(lambda () (+ 1 (/ 1 0) 3)))
; => The symbol 'error
Example 3.4. Restart with a different value
(with-failure-continuation (lambda (error-record error-k) (error-k 2)) (lambda () (+ 1 (/ 1 0) 3))) ; => 6
Example 3.5. Propagate the error
(with-failure-continuation
(lambda (error-record error-k)
(throw error-record error-k))
(lambda () (+ 1 (/ 1 0) 3)))
; => A divide by zero error is raised
Example 3.6. Propagate a different error with the same error continuation
(with-failure-continuation
(lambda (error-record error-k)
(throw (make-error '/ "could not perform the division.") error-k))
(lambda () (+ 1 (/ 1 0) 3)))
; => An error is raised: Error in /: could not perform the division.
Example 3.7. Raise a new error
(with-failure-continuation
(lambda (error-record error-k)
(error 'example-function "could not evaluate the expression."))
(lambda () (+ 1 (/ 1 0) 3)))
; => An error is raised: Error in example-function: could not evaluate the expression.
Note that the difference between Example 3.6, “Propagate a different error with the same error continuation” and
Example 3.7, “Raise a new error” is that in the former,
the computation can still be restarted from the second
argument of the addition if an outside handler catches the
newly raised exception and applies the continuation. This is
not true in the last example, as its a new error whose
continuation is the same as the
with-failure-continuation
expression.
R5RS does not specify the behavior of
dynamic-wind
in the case
where an error is raised while evaluating the
during thunk. SISC chooses to view
an error raised in that section as an instance of the
dynamic extent being exited. In other words, if an error is
raised in the dynamic extent of a dynamic-wind expression,
SISC will ensure that the after thunk
is evaluated before the error is propagated to the
failure-continuation of the dynamic-wind expression.
Example 3.8. Errors and dynamic-wind
(define x 0) (dynamic-wind (lambda () (set! x (+ x 1))) (lambda () (/ 1 0)) (lambda () (set! x (+ x 1)))) ; => A divide by zero error is raised, and the value of x is 2
If an error is raised in either the before or after thunks, no additional measures are taken. The error is propagated to the failure-continuation of the dynamic-wind as if the dynamic-wind call was an ordinary function application. Explicitly, if an error is raised from before, neither during nor after will be executed. If an error is raised in after, the results of evaluating before and during remain valid.
Also noteworthy is what happens if a continuation is invoked that exits from either the before or after thunks. Such a case is treated just as if a continuation was invoked during the evaluation of an operand to an application. This is to say that no additional steps will be taken by SISC. If before is escaped by a continuation invocation, neither during nor after will be executed. If after is escaped, the results of before and during remain valid.
In summary, extraordinary evaluation is only possible during the evaluation of the during thunk. The before and after thunks are evaluated with the dynamic environment and dynamic-wind stack of the call to dynamic-wind itself.
Symbolic environments and property maps provide additional named global environments useful for storing program specific data without exposing it to the general purpose top-level environment.
A property map is dictionary structure tied
to the interaction environment which maps symbolic
names to Scheme values. First-class symbolic environments provide
a similar mapping, but can be used
as first class values (including as an argument to eval
).
Symbolic environments are used to implement SISC's global (top level)and
report environments.
Access to symbolic environments is performed through the
getprop
and putprop
functions. All symbolic environment operations are thread
safe.
procedure:
(getprop binding-name plist-name [default-value]) => value
procedure:
(getprop binding-name environment [default-value]) => value
Attempts a lookup of
binding-name
in an environment.In the first form, the the binding is resolved in the interaction-environment's property list named
plist-name
, a symbol. If the environment is not found or the binding doesn't exist,default-value
is returned if provided, otherwise#f
is returned.In the second form, the binding is resolved in a first-class symbolic environment.
procedure:
(putprop binding-name plist-name value) => undefined
procedure:
(putprop binding-name environment value) => undefined
Sets the value of a binding named with the symbol
binding-name
in a property list or first class symbolic environment.In the first form, the binding is resolved using a symbolic name (
plist-name
) in the interaction environment's property lists. If the map does not yet exist, it is created as an empty map.In the second form, the binding is resolved in the provided first class symbolic environment. If the binding does not yet exist in the given environment, it is created. If a binding previously existed, its previous value is discarded.
Symbolic environments are a first class datatype in SISC. The
top-level environment itself is merely a special cased symbolic
environment. To obtain the top-level environment as a first
class value, one can use the
interaction-environment
function that is an
optional procedure in R5RS. Another useful environment is the
R5RS report environment available by calling:
(scheme-report-environment 5)
Each call to scheme-report-environment
returns a new environment that contains only the bindings
available in the Scheme report. Finally, the initial
environment available to the programmer when SISC starts can
be retrieved using the
sisc-initial-environment
function:
procedure:
(sisc-initial-environment) => environment
Returns the initial SISC interaction environment.
Like scheme-report-environment
, each call
to sisc-initial-environment
returns a
distinct environment which contains only the bindings initially
available when SISC starts. An interesting use of this would
be to define one or more distinct initial-environments, bound to
toplevel variables. One could then define Scheme code and data
in each environment that can use the full SISC language but
cannot see any bindings in other environments.
Finally, R5RS states that it is an error to modify the
contents of a top-level variable that has not yet been created.
SISC adheres to the standard, and raises an error when any
unbound variable in a symbolic environment (including the top-level)
is modified using set!
. This
differs from some Scheme systems that will silently create the
binding and set it to the new value.
SISC contains a mechanism for creating a symbolic environment which is chained to another environment, such that new and modified bindings are created in the new, child environment, but bindings may also be resolved from the parent if not present in the child. SISC uses this functionality to protect the contents of the R5RS and SISC initial environments from modification. One can use it in a similar way, protecting the bindings in the parent for sandboxing or other purposes.
procedure:
(make-child-environment parent-environment) => environment
Creates a new environment, initially empty of its own bindings, but which chains to the provided
parent-environment
when resolving a binding.
procedure:
(parent-environment environment) => environment
Obtains the parent environment of a symbolic environment. If the given environment has no parent (e.g. is not chained),
#f
is returned.
The remaining functions in this chapter are not easily classified, but nevertheless are useful and worth describing.
procedure:
(circular? datum) => #t/#f
Returns
#t
if the given datum is circular. A datum is circular if it is a compound datum (lists or vectors for example), and one of its elements is a reference to itself, or a reference to a sub-element which creates a cycle.
procedure:
(compose [function] ...) => procedure
compose
takes zero or more functions of one argument and returns a new function of one argument that will apply to that argument to the selected functions in reverse order. If no functions are provided, the identity function is returned.For example, the function
caddr
could be simply defined as:(define caddr (compose car cdr cdr))
The
iota
function produces a list whose elements are the integers 0 ton
-1 inclusive.
syntax:
(time [iterations] expression) => list
Evaluates the given expression
iterations
times, or ifiterations
is not provided, only once. When complete, a list is returned of the following form:(result (n ms))where result is the Scheme value that resulted from the last evaluation of the expression, and n is the number of milliseconds taken to evaluate the expression. If more than one iteration occurred, then the average number of milliseconds elapsed during each iteration is returned.
[2] Essentially arbitrary, see the section called “Limits” for a discussion of the physical limits of number representation