Table of Contents
The optional SISC libraries are modules whose definition is included in the full SISC distribution, but not the lite distribution.
Requires: (import misc)
In addition to the standard R5RS definition syntaxes, SISC provides an additional value definition and syntax definition form.
First, define-values
, which allows
more than one binding to be created at once, given the
multiple-value return of its body.
syntax:
(define-values (binding binding ... ) expression) => undefined
Evaluates the expression in the body, which must return the same number of values as there are binding names. Each value is then bound (in an undefined order) to each binding name.
define-values
behaves like
define
in terms of which environment the
bindings are created. If the define-values
statement is at the top-level then bindings are created in the
top-level environment. If the statement is in a lexical
environment, then it behaves just as an internal define.
Second, define-simple-syntax
provides a
shorthand for syntax definition when the syntactic form's
appearance is similar to a function.
syntax:
(define-simple-syntax (name vars ... ) body) => undefined
Creates a syntactic form with the given name, and any number of listed syntactic variables, which expands to the given body (with instances of the syntactic variables hygienically expanded).
Here is an example usage of
define-simple-syntax
to define the
when
macro:
(define-simple-syntax (when condition body ...) (if condition (begin body ...)))
Requires: (import logicops)
In addition to the R5RS set of procedures that deal with numbers, SISC provides operators for performing bitwise logic operations on exact integers.
procedure:
(logand integer [integer] ...) => integer
Performs the logical AND of all the provided arguments.
procedure:
(logor integer [integer] ...) => integer
Performs the logical OR of all the provided arguments.
procedure:
(logxor integer [integer] ...) => integer
Performs the logical exclusive-OR of all the provided arguments.
procedure:
(lognot integer) => integer
Performs the logical NOT of the provided integer.
procedure:
(logcount integer) => integer
Returns the count of the number of 1 bits in the representation of a given positive integer, or 0 bits in a negative integer.
In addition, two operators are provided to perform arithmetic shifts on any integer (these operators do not have the range limitation the previous logical functions do). The shift operators return a newly generated number formed by shifting the provided number left or right by the given number of bits.
procedure:
(ashl integer bits) => integer
Arithmetically shifts
integer
left bybits
bits.
procedure:
(ashr integer bits) => integer
Arithmetically shifts
integer
right bybits
bits.
Mathematically, if r is the number, and s is the number of bits, ashl calculates:
r x 2s
while ashr calculates
r / 2s
in the integer domain. Both ashl and ashr operate on exact integers and produce only exact integers.
Requires: (import record)
SISC provides a native implementation of record types as
defined in SRFI-9. See http://srfi.schemers.org/srfi-9/ for details. In
addition to the define-record-type
syntax
provided by SRFI-9, a more compact (but less flexible)
define-struct
syntax is offered.
syntax:
(define-struct name (field ...))
Defines a SRFI-9 record type as follows:
(define-record-type (make-name
field
...)name
? (field
name
-field
set-name
-field
!) ...)i.e. naming conventions are used to determine the names of the record type constructor, predicate, field access and field modifier procedures.
Records are eq?
and
eqv?
if and only if they are
identical. Records are equal?
if and only
if they are instances of the same record type and all their
fields are equal?
.
It is also possible to define
non-generative record types, using
define-nongenerative-record-type
and
define-nongenerative-struct
. Non-generative
record types are associated with a user-specified guid. If an
attempt is made to define a record type with a guid that is
already bound to an existing record type then the existing
record type is modified, instead of a new record type being
created. Non-generative record types are serialised specially
such that deserialising them also performs this check. By
contract, deserialisation of ordinary, generative record types
and their instances results in duplicate types being created,
which is usually not desirable.
syntax:
(define-nongenerative-record-type name guid (constructor-name field ...) (predicate ...) (field-spec ...))
This is the same as
define-record-type
, except that the resulting record type is non-generative withguid
, a symbol, as the unique identifier.
syntax:
(define-nongenerative-struct name guid (field ...))
This is the same as
define-struct
, except that the resulting struct is non-generative withguid
, a symbol, as the unique identifier.
Requires: (import hashtable)
Hash tables store mappings of keys to values. Hence they are similar to association lists, except that hash tables allow retrieval, addition and modification in constant time whereas association lists typically perform these operations in linear time based on the number of elements.
Hash tables are a distinct data type. They can be created empty or filled with the contents of an association list. The converse, creating an association list from a hash table, is also supported.
procedure:
(make-hashtable [equivalence-predicate] [hash-function] [thread-safe?] [weak?]) => hashtable
Creates a hash table. The first optional argument supplies the equivalence test procedure that the hashtable should use for comparison of keys. This must be a function accepting two arguments and returning a boolean. It defaults to
equal?
.The second optional argument supplies the hash function, which must accept one argument and return a numeric value. For the equivalence predicates
eq?
,eqv?
,equal?
,string=?
,string-ci=?
it defaults tohash-by-eq
,hash-by-eqv
,hash-by-equal
,hash-by-string=
,hash-by-string-ci=?
respectively, andhash-by-equal
otherwise.The third optional argument determines whether operations on the hash table should be made thread-safe. The default is #t. Thread synchronization (see Chapter 6, Threads and Concurrency ) is required if there are potentially several threads operating concurrently on the hash table and one of these threads performs a structural modification (i.e. adds or removes an entry; merely changing the value of an entry is not a structural modification). Failure to enforce proper thread synchronization has unpredicatable results.
The fourth optional argument determines whether the keys in the hash table are held with weak references, allowing them to be garbage collected, and automatically removed from the hashtable when they are not referenced from elsewhere. The default is #t.
For reasons of disambiguation, the hash function argument can only be supplied if the preceeding equivalence predicate was also supplied, and the weakness argument can only be supplied if the preceeding thread-safety argument was also supplied.
The equivalence and hash function must produce stable results for the keys in a hash table.
The effects of invoking an escaping continuation inside the equivalence predicate or hash function, or invoking a continuation captured inside the equivalence predicate or hash function after that function has returned, are unspecified.
procedure:
(alist->hashtable alist [equivalence-predicate] [hash-function] [thread-safe?] [weak?]) => hashtable
Creates a hashtable and initializes it with the keys and values found in
alist
.alist
must be a list of pairs, with thecar
of each pair representing a key and thecdr
representing its associated value. The optional arguments are the same as formake-hashtable
.If there are multiple pairs which contain the same key (with respect to chosen equivalence test) then the resulting hash table will associate the key with the value of the last such pair.
procedure:
(hashtable? value) => #t/#f
Returns #t if
value
is a hash table, #f otherwise.
procedure:
(hashtable/equivalence-function hashtable) => procedure
Returns the equivalence predicate of
hashtable
.
procedure:
(hashtable/hash-function hashtable) => procedure
Returns the hash function of
hashtable
.
procedure:
(hashtable/thread-safe? hashtable) => #t/#f
Returns #t if
hashtable
is thread safe, #f otherwise.
procedure:
(hashtable/weak? hashtable) => #t/#f
Returns #t if the keys in
hashtable
are held by weak references, #f otherwise.
procedure:
(hashtable/size hashtable) => number
Returns the number of key/value pairs stored in
hashtable
.
procedure:
(hashtable->alist hashtable) => alist
Returns an association list comprising the elements of
hashtable
. The list contains pairs whosecar
s are they keys found inhashtable
and whosecdr
s contain the associated values.
Several hash functions that return results consistent with common equivalence predicates are predefined.
procedure:
(hash-by-eq value) => number
procedure:
(hash-by-eqv value) => number
procedure:
(hash-by-equal value) => number
procedure:
(hash-by-string= string) => number
procedure:
(hash-by-string-ci= string) => number
These procedures return a hash code of their argument that is consistent with
eq?
,eqv?
,equal?
,string=?
,string-ci=?
, respectively.
All hash table access operations follow a similar pattern. They return the value that was associated with the the given key at the time the operation was invoked. If no binding for the key existed, an optionally supplied value is returned that defaults to #f. This allows the programmer to associate keys with #f values and distinguish this case from not having any association for a key.
procedure:
(hashtable/put! hashtable key val [nobinding]) => value
Associates
key
withval
inhashtable
. Returns the previous association ofkey
ornobinding
, which defaults to #f, ifkey
has no previous association.
procedure:
(hashtable/get hashtable key [nobinding]) => value
Returns the value associated with
key
inhashtable
, ornobinding
, which defaults to #f, ifkey
has no association.
procedure:
(hashtable/get! hashtable key thunk [unsafe?]) => value
Returns the value associated with
key
inhashtable
. Ifkey
has no association thenthunk
is called and the result is associated withkey
inhashtable
and also returned. Theunsafe?
, which defaults to #t, indicates whetherthunk
may invoke escaping continuations or raise errors. Settingunsafe?
to #f results in more efficient execution but may cause deadlocks ifthunk
is unsafe. See alsomutex/synchronize-unsafe
in the section called “ High-level Concurrency ”.When
hashtable
is thread-safe this operation is atomic.
procedure:
(hashtable/contains? hashtable key) => #t/#f
Returns the #t if
hashtable
contains an entry forkey
, #f otherwise.
procedure:
(hashtable/remove! hashtable key [nobinding]) => value
Removes the association of
key
inhashtable
. Returns the associated value ofkey
ornobinding
, which defaults to #f, ifkey
has no association.
Bulk operations are operations that apply to all elements of a hash table.
procedure:
(hashtable/clear! hashtable)
Removes all elements from
hashtable
.
procedure:
(hashtable/keys hashtable) => list
Returns the keys contained in
hashtable
.
procedure:
(hashtable/for-each proc hashtable)
Applies
proc
to each element ofhashtable
.proc
is called with two parameters - the key and the value of the element.
procedure:
(hashtable/map proc hashtable) => list
Applies
proc
to each element ofhashtable
.proc
is called with two parameters - the key and the value of the element. The results of callingproc
are returned as a list.
Requires: (import buffers)
Binary buffers provide an opaque container for a fixed amount of binary data. The binary buffer library provides a number of functions for creating and accessing those buffers. The buffer is very similar to a vector, in that it is a randomly accessable, zero-based structure. But as a tradeoff for space efficiency, binary buffers are only capable of storing bytes. The bytes are stored as 8-bit, unsigned fixed integers (of the range 0-255).
procedure:
(buffer? value) => #t/#f
Returns true if and only if the provided argument is a binary buffer.
procedure:
(make-buffer size [fill-value]) => buffer
Creates a new buffer capable of storing
size
bytes.size
must be a fixed non-negative integer. If provided, the value of all bytes in the buffer is initialized tofill-value
. If not provided, the contents of the buffer is unspecified.
procedure:
(buffer [value] ...) => buffer
Creates a new buffer whose size is equal to the number of arguments given and whose contents are the bytes given as arguments.
procedure:
(buffer-length buffer) => fixed integer
Returns the capacity of the given buffer.
procedure:
(buffer-ref buffer index) => fixed integer
Returns the byte at offset
index
in the specified buffer. It is an error ifindex
is out of range.
procedure:
(buffer-set! buffer index new-value) => undefined
Sets the byte at offset
index
of the specified buffer to the given fixed integernew-value
. It is an error ifindex
is out of range.
procedure:
(buffer-copy! source-buffer source-offset dest-buffer dest-offset [count]) => undefined
Copies
count
bytes starting from indexsource-offset
in the source buffer to successive bytes starting at indexdest-offset
in the destination buffer. Ifcount
is unspecified, it is assumed to be the length of the source buffer. It is an error to copy more bytes from the source buffer than are available, or to copy more bytes into the destination buffer than its capacity allows.
Buffers are serializable (can exist in loadable libraries or
a SISC heap), but are not representable in an s-expression.
For this reason, they bear the printed representation of
#<buffer>
.
Requires: (import procedure-properties)
SISC allows key/value bindings to be associated with procedures. This has a number of applications. For instance, generic procedures store their methods in a procedure property.
Keys must be symbols. Values are any valid Scheme value. All operations are thread-safe.
procedure:
(procedure-property proc symbol [nobinding]) => value
Returns the value associated with the property
symbol
of procedureproc
, ornobinding
, which defaults to #f, if the property is not set.
procedure:
(set-procedure-property! proc symbol val [nobinding]) => value
Sets the property
symbol
of procedureproc
to the valueval
. Returns the previous value of the property ornobinding
, which defaults to #f, if the property was unset.
procedure:
(procedure-property! proc symbol thunk [unsafe?]) => value
Returns the value associated with the property
symbol
of procedureproc
. If the property is unset thenthunk
is called and the property is set to the result, which is also returned. Theunsafe?
, which defaults to #t, indicates whetherthunk
may invoke escaping continuations or raise errors. Settingunsafe?
to #f results in more efficient execution but may cause deadlocks ifthunk
is unsafe. See alsomutex/synchronize-unsafe
in the section called “ High-level Concurrency ”.
Requires: (import libraries) [4]
Scheme code can be packaged into libraries that can have dependencies on other libraries and can be loaded as required. Libraries are identified by a name that follows Java package file naming conventions, i.e. using path-style names typically containing domain, organisation name, project name and library name. For instance, if company Foo produces a library Baz for project Bar and that library contains three files, the file structure might look as follows:
com/foo/bar/baz.scm com/foo/bar/baz/baz1.scm com/foo/bar/baz/baz2.scm com/foo/bar/baz/baz3.scm
This library can be made accessible from SISC by adding the base directory or a jar file containing these files to the Java class path. Libraries are loaded by the following procedure.
procedure:
(require-library name) => undefined
Checks whether the library identified by
name
(a string), has already been loaded and, if not, loads it. An error is raised if the library cannot be found.Libraries are loaded using the
load
procedure from a resource located by thefind-resource
procedure. The name of the resource is derived from the name of the library by appending ".scc", ".sce" and, if that does not succeed, ".scm".
Note that require-library
only loads a
single file. The definition of dependencies on other libraries
and the loading of other files therefore needs to happen
within that file. For instance, the file
com/foo/bar/baz.scm
from the above
example might contain the following:
(require-library 'com/foo/bar/boo) (load "baz/baz1.scm") (load "baz/baz2.scm") (load "baz/baz3.scm")
It is possible to programmatically check whether a particular library exists and whether it has been loaded:
procedure:
(library-exists? name) => #t/#f
Returns #t if the library identified by
name
(a string) exists, #f otherwise.
procedure:
(library-loaded? name) => #t/#f
Returns #t if the library identified by
name
(a string) has been loaded, #f otherwise.
Requires: (import os)
The operating system interface currently contains functions for spawning external processes on the host operating system, obtaining input/output ports to the resulting process, and monitoring their status.
Two procedures exist for spawning processes:
procedure:
(spawn-process program/commandline [arglist]) => process
Spawns a process, returning a process handle. If the optional argument list is provided, then the first argument is the binary to run with those arguments. If omitted, the first argument is tokenized as a commandline and used to spawn the process.
procedure:
(spawn-process-with-environment program arglist environment [working-directory]) => process
procedure:
(spawn-process/env program arglist environment [working-directory]) => process
Spawns a process named by
program
with the arguments given inarglist
, in the givenenvironment
. The environment is an association list of strings to strings. The key in the association list is an environment variable name, and the corresponding value is the value to assign to that environment variable. If the environment parameter is#f
, the environment variables of the current SISC instance are used.The optional parameter
working-directory
specifies the directory which will be set as the current directory when the process is spawned. If ommited, the value of thecurrent-directory
parameter (i.e. the current directory of the running Scheme program) is used instead.
procedure:
(process? value) => #t/#f
Returns
#t
if the given value is a process handle.
Once started, a process will run in parallel to the current Scheme program according to the usual scheduling of the host platform. The process handle obtained can be used to obtain the input, output, and error streams of the process using the following functions:
procedure:
(get-process-stdout process) => binary-input-port
Returns a binary input port which will read bytes which the given process has written to its standard output stream.
procedure:
(get-process-stderr process) => binary-input-port
Returns a binary input port which will read bytes which the given process has written to its standard error stream.
procedure:
(get-process-stdin process) => binary-output-port
Returns a binary output port which when written to will send bytes to the given process' standard input stream.
Finally, functions are provided to check the status of a spawned process, and to wait for a process to complete:
procedure:
(process-terminated? process) => integer or #f
Checks to see if the given process has terminated, and returns the process' return code if so. If the process is still running,
#f
is returned.
procedure:
(wait-for-process process) => integer or #f
Waits for the given process to terminate, and returns the process' return code if so. The wait operation may be interrupted by other code, in which
#f
is returned.
SISC provides hooks for accessing a number of third-party Scheme libraries.
This functionality has not undergone much testing.
The Scheme Requests For Implementation (SRFI) process aims to coordinate libraries and other additions to the Scheme language between different Scheme implementations. For details see http://srfi.schemers.org/ which describes the process and contains a list of all available SRFIs.
In SISC each SRFI is encapsulated in a module. See Chapter 10,
Modules and Libraries
for details of SISC's module system.
The definitions for SRFI modules are not included in the
standard SISC heap build and hence must be loaded
separately from various compiled library files in the
sisc-lib.jar
jar file in the root
directory of the SISC binary distribution. As long as this
jar file is on the classpath, which is the case by default,
any SRFI's module definition may be loaded with the expression
(require-library
'sisc/libs/srfi/srfi-
, where
n
)n
is the SRFI's number. For example:
(require-library 'sisc/libs/srfi/srfi-9)
All SRFI's may be loaded at once by requiring
sisc/libs/srfi
.
SISC currently supports SRFIs
0, 1, 2, 5, 6, 7, 8, 9, 11, 13, 14, 16, 18, 19, 22, 23,
25, 26, 27, 28, 29, 30, 31, 34, 35, 37, 38, 39, 40, 42, 43,
45, 48, 51, 54, 55, 59, 60, 61, 62, 66, 67, 69 and 78.
Once the SRFI module definitions have been loaded as
described above, a SRFI n
can be
imported using
(import srfi-n
)
e.g.
(import srfi-1) (xcons 1 2) ;=> (2 . 1)
SRFI modules, like all modules in SISC, can be imported/used by other modules. Doing so does not pollute the top-level environment with the definitions exported by the module, i.e. any code outside the importing module remains unaffected.
If, however, an SRFI is to be imported into the top-level,
one can use the require-extension
mechanism (see the section called “require-extension
”).
Some SRFIs have built-in extension points that Scheme implementations can use to augment a SRFI's functionality. It is also the case that some SRFIs would benefit from slightly extended APIs.
This section documents the SRFI extensions implemented by SISC.
The make-hash-table
function takes
two additional optional arguments:
thread-safe?
and
weak?
. See make-hashtable
in the section called “Creation and Introspection” for details.
The basic hash table API is extended in a separate module:
Requires: (import srfi-69-ext)
procedure:
(hash-table-thread-safe? hashtable) => #t/#f
Returns #t if
hashtable
is thread safe, #f otherwise.
procedure:
(hash-table-weak? hashtable) => #t/#f
Returns #t if the keys in
hashtable
are held by weak references, #f otherwise.
procedure:
(hash-table-ref! hashtable key thunk) => value
Returns the value associated with
key
inhashtable
. Ifkey
has no association thenthunk
is called and the result is associated withkey
inhashtable
and also returned.When
hashtable
is thread-safe this operation is atomic.
procedure:
(hash-table-ref! hashtable key default) => value
Returns the value associated with
key
inhashtable
. Ifkey
has no association thendefault
is associated withkey
inhashtable
and also returned.When
hashtable
is thread-safe this operation is atomic.
The SLIB portable scheme library provides compatibility and utility functions for standard Scheme implementations. It is supported by many Schemes, including SISC.
The latest version of SLIB is available from http://swissnet.ai.mit.edu/~jaffer/SLIB.html as both a zip file and RPM. The site also hosts an online version of the SLIB manual.
Download SLIB and install it in a
convenient location. The RPM will by default be installed
in /usr/share/slib/
. Do not worry when
you see some errors about missing programs such as
mzscheme and
scheme48 when installing the RPM
- these happen because SLIB tries
to auto-configure itself for various Schemes that you may
not have installed on your system.
Using SLIB in SISC requires two Java system properties to be set:
sisc.home
.
This should (but does not actually
have to) point to the location
where you have installed SISC. If you are using
one of the scripts from the binary SISC
distribution in order to run SISC then this
property will automatically be set to the value of
the SISC_HOME
environment variable.
sisc.slib
.
This must point to the location where you installed
SLIB. Other Schemes
supporting SLIB tend to
use an environment variable
SCHEME_LIBRARY_PATH
, so it is
advisable to define that (if it is not already
defined) and run Java with a
-Dsisc.slib=...
option based on the environment variable. If you are
using the scripts from the binary $SISC;
distribution in order to run SISC then you can set
the property by adding the
-Dsisc.slib=...
to
the JAVAOPT
environment variable.
Note that the value of this property should be a
fully qualified url,
e.g. file:///usr/share/slib
You need to ensure that all potential users of SLIB have read permissions to files in the directories referred to by the above system properties.
Make sure that the above system properties are set and
that you have write permissions to the
sisc.home
directory; often this means you
need to be logged in as a privileged user.
Start SISC as you normally would. At the prompt type
(require-library 'sisc/libs/slib) (require 'new-catalog) (exit)
The above should create a file slibcat
in the sisc.home
directory. It is a good idea
to check that this has indeed happened.
Make sure the above system properties are set. Start SISC as you normally would. At the prompt load the SISC SLIB as described above, i.e.
(require-library 'sisc/libs/slib)
You can now load SLIB modules
using require
, e.g.
(require 'tsort) (tsort '((shirt tie belt) (tie jacket) (belt jacket) (watch) (pants shoes belt) (undershorts pants shoes) (socks shoes)) eq?)
loads the topological sorting module and invokes one of the procedures defined by it.
Please refer to the SLIB manual for further details of what modules are available. Note however that, as with most other Schemes supported by SLIB, there will be some modules that are not available or do not work in SISC.
Requires: (import libraries)
SISC allows the creation of compiled libraries
which contain compiled scheme code. These
libraries can then be executed into a running SISC session in
order to extend the functionality without processing or
possessing the original source. Such libraries can be loaded
using load
as would any ordinary Scheme
source file.
Compiled code files (.scc
) are created using
the compile-file
function, which takes a
Scheme source file and a target output file, and processes the
Scheme source through the various expansion and compilation
phases, and then serializes the resulting SISC
microexpressions to the given target file. The resulting file
may then be loaded with load
as any
ordinary Scheme file would, or can be placed in the library path
and resolved using require-library
.
procedure:
(compile-file source-file target-file) => undefined
Compiles the Scheme source present in
source-file
, writing the resulting micro-expressions intotarget-file
, suitable for loading.As a side effect, the micro-expressions are also evaluated, i.e. in effect
compile-file
compiles and evaluates thesource-file
. The latter is necessary because the compilation of an expression may depend on the results of evaluating a previous expression, e.g. as is typically the case for libraries that depend on other libraries.
.sll
Deprecation
Scheme Loadable Libraries (.sll
files) were
deprecated in version 1.9.
This was due to unresolvable incompatibilities in the engine's
closure representation and the .sll
functionality.