13. SRFIs

Scheme Request For Implementation (SRFI) process grew out of the Scheme Workshop held in Baltimore, MD, on September 26, 1998, where the attendees considered a number of proposals for standardized feature sets for inclusion in Scheme implementations. Many of the proposals received overwhelming support in a series of straw votes. Along with this there was concern that the next Revised Report would not be produced for several years and this would prevent the timely implementation of standardized approaches to several important problems and needs in the Scheme community.

Only the implemented SRFIs are (briefly) presented here. For further information on each SRFI, please look at the official SRFI site.

13.1. Supported SRFIs

STklos supports 126 finalized SRFIS. Some of these SRFIS are embedded and some are external.

An embedded SRFI can be directly used without any particular action, whereas an external needs to be loaded before use.

The following SRFIS are implemented:

- SRFI-0 — Feature-based conditional expansion construct
- SRFI-1 — List Library
- SRFI-2 — AND-LET*: an AND with local bindings, a guarded LET* special form
- SRFI-4 — Homogeneous numeric vector datatypes
- SRFI-5 — A compatible let form with signatures and rest arguments
- SRFI-6 — Basic String Ports
- SRFI-7 — Feature-based program configuration language
- SRFI-8 — Receive: Binding to multiple values
- SRFI-9 — Defining Record Types
- SRFI-10 — Sharp Comma External Form
- SRFI-11 — Syntax for receiving multiple values
- SRFI-13 — String Library
- SRFI-14 — Character-Set Library
- SRFI-15 — Syntax for dynamic scoping (withdrawn)
- SRFI-16 — Syntax for procedures of variable arity
- SRFI-17 — Generalized set!
- SRFI-18 — Multithreading support
- SRFI-19 — Time Data Types and Procedures
- SRFI-22 — Running Scheme Scripts on Unix
- SRFI-23 — Error reporting mechanism
- SRFI-25 — Multi-dimensional Arrays
- SRFI-26 — Notation for Specializing Parameters without Currying
- SRFI-27 — Source of random bits
- SRFI-28 — Basic Format Strings
- SRFI-29 — Localization
- SRFI-30 — Nested Multi-line Comments
- SRFI-31 — A special form for recursive evaluation
- SRFI-34 — Exception Handling for Programs
- SRFI-35 — Conditions
- SRFI-36 — I/O Conditions
- SRFI-37 — args-fold: a program argument processor
- SRFI-38 — External representation of shared structures
- SRFI-39 — Parameters objects
- SRFI-41 — Streams
- SRFI-43 — Vector library
- SRFI-45 — Primitives for Expressing Iterative Lazy Algorithms
- SRFI-46 — Basic Syntax-rules Extensions
- SRFI-48 — Intermediate Format Strings
- SRFI-51 — Handling rest list
- SRFI-54 — Formatting
- SRFI-55 — Require-extension
- SRFI-59 — Vicinity
- SRFI-60 — Integers as bits
- SRFI-61 — A more general COND clause
- SRFI-62 — S-expression comments
- SRFI-64 — A Scheme API for test suites
- SRFI-66 — Octet Vectors
- SRFI-69 — Basic Hash Tables
- SRFI-70 — Numbers
- SRFI-74 — Octet-Addressed Binary Blocks
- SRFI-87 — ⇒ in case clauses
- SRFI-88 — Keyword Objects
- SRFI-89 — Optional Positional and Named Parameters
- SRFI-94 — Type-Restricted Numerical Functions
- SRFI-95 — Sorting and Merging
- SRFI-96 — SLIB Prerequisites
- SRFI-98 — Interface to access environment variables
- SRFI-100 — define-lambda-object
- SRFI-111 — Boxes
- SRFI-112 — Environment Inquiry
- SRFI-113 — Sets and Bags
- SRFI-115 — Scheme Regular Expressions
- SRFI-116 — Immutable List Library
- SRFI-117 — Queues based on lists
- SRFI-118 — Simple adjustable-size strings
- SRFI-125 — Intermediate hash tables
- SRFI-127 — Lazy Sequences
- SRFI-128 — Comparators (reduced)
- SRFI-129 — Titlecase procedures
- SRFI-130 — Cursor-based string library
- SRFI-132 — Sort Libraries
- SRFI-133 — Vector Library (R7RS-compatible)
- SRFI-134 — Immutable Deques
- SRFI-135 — Immutable Texts
- SRFI-137 — Minimal Unique Types
- SRFI-138 — Compiling Scheme programs to executables
- SRFI-141 — Integer Division
- SRFI-143 — Fixnums
- SRFI-144 — Flonums
- SRFI-145 — Assumptions
- SRFI-151 — Bitwise Operations
- SRFI-152 — String Library (reduced)
- SRFI-154 — First-class dynamic extents
- SRFI-156 — Syntactic combiners for binary predicates
- SRFI-158 — Generators and Accumulators
- SRFI-160 — Homogeneous numeric vector libraries
- SRFI-161 — Unifiable Boxes
- SRFI-162 — Comparators sublibrary
- SRFI-169 — Underscores in numbers
- SRFI-170 — POSIX API
- SRFI-171 — Transducers
- SRFI-173 — Hooks
- SRFI-174 — POSIX Timespecs
- SRFI-175 — ASCII character library
- SRFI-176 — Version flag
- SRFI-178 — Bitvector library
- SRFI-180 — JSON
- SRFI-185 — Linear adjustable-length strings
- SRFI-189 — Maybe and Either: optional container types
- SRFI-190 — Coroutines Generators
- SRFI-192 — Port Positioning
- SRFI-193 — Command line
- SRFI-195 — Multiple-value boxes
- SRFI-196 — Range Objects
- SRFI-207 — String-notated bytevectors
- SRFI-208 — NaN procedures
- SRFI-214 — Flexvectors
- SRFI-215 — Central Log Exchange
- SRFI-216 — SICP Prerequisites (Portable)
- SRFI-217 — Integer Sets
- SRFI-219 — Define higher-order lambda
- SRFI-221 — Generator/accumulator sub-library
- SRFI-222 — Compound objects
- SRFI-223 — Generalized binary search procedures
- SRFI-224 — Integer Mappings
- SRFI-227 — Optional Arguments
- SRFI-228 — A further comparator library
- SRFI-229 — Tagged Procedures
- SRFI-230 — Atomic Operations
- SRFI-232 — Flexible Curried Procedures
- SRFI-233 — INI files
- SRFI-234 — Topological sorting
- SRFI-235 — Combinators
- SRFI-236 — Evaluating expressions in an unspecified order
- SRFI-238 — Codesets
- SRFI-244 — Multiple-value definitions

13.2. Using a SRFI

Using a particular SRFI can be done with the special form cond-expand defined in SRFI-0 which is fully supported by STklos. This form accepts features identifiers which are of the form srfi-n where n represents the number of the SRFI supported by the implementation (for instance srfi-1 or srfi-30).

For instance, to use srfi-n, you can use

(cond-expand
 (srfi-n))

This forms does nothing if srfi-n is an embedded SRFI and ensures that all the files needed by this SRFI will be properly loaded if it is an external SRFI.

STklos also offers the primitive require-feature which ensures (eventually) the loading of files needed to use a given SRFI. This primitive accepts several forms to ensure that the SRFI can be used. For instance, to use SRFI-1 (List Library), the following forms are possible:

(require-feature 'srfi-1)
(require-feature "srfi-1")
(require-feature 1)
(require-feature 'lists)   ;; Since this feature name is an alias for SRFI-1

The list of the aliases defined for the supported SRFIs is given in Table 2.

13.2.1. Embedded SRFIs

As said before, an embedded SRFI can be used directly without loading a support file. (Note that using require-feature works too and permits to ignore if the SRFI is embedded).

List of embedded SRFIs: srfi-0 srfi-6 srfi-8 srfi-10 srfi-11 srfi-15 srfi-16 srfi-18 srfi-22 srfi-23 srfi-28 srfi-30 srfi-31 srfi-34 srfi-38 srfi-39 srfi-45 srfi-46 srfi-55 srfi-62 srfi-70 srfi-87 srfi-88 srfi-98 srfi-111 srfi-112 srfi-118 srfi-138 srfi-143 srfi-145 srfi-169 srfi-176 srfi-192 srfi-193 srfi-195 srfi-208 srfi-219 srfi-244

13.2.2. External SRFIs

An external SRFI needs to load at least one external file. This can be done with require or require-feature. As with embedded SRFIS, using require-feature permits to ignore if the SRFI is external.

List of external SRFIs: srfi-1 srfi-2 srfi-4 srfi-5 srfi-7 srfi-9 srfi-13 srfi-14 srfi-17 srfi-19 srfi-25 srfi-26 srfi-27 srfi-29 srfi-35 srfi-36 srfi-37 srfi-41 srfi-43 srfi-48 srfi-51 srfi-54 srfi-59 srfi-60 srfi-61 srfi-64 srfi-66 srfi-69 srfi-74 srfi-89 srfi-94 srfi-95 srfi-96 srfi-100 srfi-113 srfi-115 srfi-116 srfi-117 srfi-125 srfi-127 srfi-128 srfi-129 srfi-130 srfi-132 srfi-133 srfi-134 srfi-135 srfi-137 srfi-141 srfi-144 srfi-151 srfi-152 srfi-154 srfi-156 srfi-158 srfi-160 srfi-161 srfi-162 srfi-170 srfi-171 srfi-173 srfi-174 srfi-175 srfi-178 srfi-180 srfi-185 srfi-189 srfi-190 srfi-196 srfi-207 srfi-214 srfi-215 srfi-216 srfi-217 srfi-221 srfi-222 srfi-223 srfi-224 srfi-227 srfi-228 srfi-229 srfi-230 srfi-232 srfi-233 srfi-234 srfi-235 srfi-236 srfi-238

13.2.3. SRFI features

For some SRFIs, STklos accepts that uses them with a name. This names are given Table 2.

Table 2. Feature identifiers
symbol require SRFI(s)

lists

srfi-1

and-let*

srfi-2

hvectors

srfi-4

program

srfi-7

records

srfi-9

case-lambda

srfi-16

error

srfi-23

random

srfi-27

args-fold

srfi-37

parameters

srfi-39

streams

srfi-41

rest-list

srfi-51

formatting

srfi-54

testing

srfi-64

hash-tables

srfi-69

boxes

srfi-111

sets-bags

srfi-113

immutable-lists

srfi-116

queues-as-lists

srfi-117

adjustable-strings

srfi-118

hash-table

srfi-125

lazy-sequences

srfi-127

comparators-reduced

srfi-128

titlecase

srfi-129

sort

srfi-132

vector

srfi-133

immutable-deques

srfi-134

immutable-texts

srfi-135

integer-division

srfi-141

bitwise-ops

srfi-151

posix

srfi-170

transducers

srfi-171

hooks

srfi-173

posix-timespecs

srfi-174

ascii

srfi-175

JSON

srfi-180

maybe-either

srfi-189

ini-files

srfi-233

topological-sort

srfi-234

combinators

srfi-235

conditions

srfi-35 srfi-36

generators

srfi-158 srfi-190

13.3. Misc. Information

Previous section described the general way to use the SRFIS implemented in STklos. This section concentrates on information not given above.

srfi-0 — Feature-based conditional expansion construct

SRFI-0 defines the cond-expand special form. It is fully supported by STklos. STklos defines several features identifiers which are of the form srfi-n where n represents the number of the SRFI supported by the implementation (for instance srfi-1 or srfi-30).

STklos cond-expand accepts also some feature identifiers which are the same that the ones defined in Table 2, such as case_lambda or generators.

Furthermore, the feature identifier stklos and STklos are defined for applications which need to know on which Scheme implementation they are running on.

srfi-4 — Homogeneous numeric vector datatypes

SRFI-4 is fully supported and is extended to provide the additional c64vector and c128vector types of SRFI-160 (Homogeneous numeric vector libraries).

STklos procedure

(uvector? uv)
(uvector-length uv)
(uvector-ref uv k)
(uvector-set! uv k val)
(uvector→list uv)
(uvector-tag uv)

These primitives extend SRFI-4. They can be called with any type of uniform (homogeneous) vector. The primitive uvector-tag returns the tag of the uniform vector, as a symbol

(uvector? #s16(-1 2 3))    => #t
(uvector-length #u32(1 2)) => 2
(uvector-length #s64(1 2)) => 2
(uvector-tag #u8(1 2 3))   => u8
(uvector-tag #c32())       => c32

srfi-10 — Sharp Comma External Form

SRFI-10 is fully supported. This SRFI extends the STklos reader with the #, notation which is fully described in this document (see primitive define-reader-ctor).

srfi-16 — Syntax for procedures of variable arity

SRFI-16 is fully supported. Note that case-lambda is now defined in R7RS.

srfi-17 — Generalized set!

SRFI-17 is fully supported. See the documentation of procedures set! and setter. However, requiring explicitly srfi-17 permits to define the setters for the (numerous) cXXXXr list procedures.

srfi-19 — Time Data Types and Procedures

SRFI-19 is fully supported. STklos offers, as an extension, the procedures date=?, date<?, date>?, date⇐? and date>=?. These will compare dates by first normalizing them to make the time zone offset irrelevant, so "2000 Nov 12 03:30:10 GMT-2" will be taken as equal to "2000 Nov 12 02:30:10 GMT-1".

STklos procedure

(time-difference time1 time2)
(time-difference! time1 time2)

These SRFI-19 procedures return the time duration (as an object of type time-duration) between time1 and time2. It is an error if time1 and time2 are of different time types.

time-difference creates a new time object, while time-difference! may use time1 to create the resulting object.

R5RS procedure

(time⇐? time1 time2)
(time<? time1 time2)
(time=? time1 time2)
(time>=? time1 time2)
(time>? time1 time2)

These are SRFI-19 predicates used to compare times. They return:

  • time⇐? : #t if time1 is before or at (less than or equal to) time2, #f otherwise.

  • time<? : #t if time1 is before (less than) time2, #f otherwise.

  • time=? : #t if time1 at (equal) time2, #f otherwise.

  • time>=? : #t if time1 is at or after (greater than or equal to) time2, #f otherwise.

  • time>? : #t if time1 is after (greater than) time2, #f otherwise.

An attempt to compare times of different type will raise an error.

STklos procedure

(time-resolution [time-type])

Clock resolution, in nanoseconds, of the system clock of type type time-type, which defaults to TIME-UTC.

(time-resolution)              => 1000000
(time-resolution time-process) => 1000000
(time-resolution time-tai)     => 1000000

srfi-22 — Running Scheme Scripts on Unix

SRFI-22 describes basic prerequisites for running Scheme programs as Unix scripts in a uniform way. Specifically, it describes:

  • the syntax of Unix scripts written in Scheme,

  • a uniform convention for calling the Scheme script interpreter, and

  • a method for accessing the Unix command line arguments from within the Scheme script.

SRFI-22 (Running Scheme Scripts on Unix) recommends to invoke the Scheme script interpreter from the script via a /usr/bin/env trampoline, like this:

#!/usr/bin/env stklos

Here is an example of the classical echo command (without option) in Scheme:

#!/usr/bin/env stklos

(define (main arguments)
  (for-each (lambda (x) (display x) (display #space))
            (cdr arguments))
  (newline)
  0)

srfi-23 — Error reporting mechanism

SRFI-23 is fully supported. Note that the STklos error is more general than the one defined in SRFI-23.

srfi-25 — Multi-dimensional Arrays

STklos implements the arrays of SRFI-25. All the forms defined in the SRFI are implemented in STklos, but some other functions, not present in the SRFI, are documented here.

STklos procedure

(shape? obj)

Checks if obj is an array shape. SRFI-25 dictates that a shape is an ordinary array, with rank two and shape (0 r 0 2), where r is the rank of the array that the shape describes. So, any array of shape (0 r 0 2 is a shape, for any non-negative integer r.

STklos procedure

(shared-array? array)

Will return #t when the array has its data shared with other arrays, and #f otherwise.

STklos procedure

(shape-for-each shape proc [index-object])

This procedure will apply proc to all valid sequences of indices in shape, in row-major order.

If index-object is not provided, then proc must accept as many arguments as the number of dimensions that the shape describes.

(shape-for-each (shape 1 3 10 12)
                (lambda (x y)
                  (format #t "[~a ~a]~%" x y)))
        |- [1 10]
           [1 11]
           [2 10]
           [2 11]

If index-object is provided, it is used as a place to store the indices, so proc must accept either a vector or an array (this is to avoid pushing and popping too many values when calling proc). index-object, when present, must be aither a vector or array.

(let ((vec (make-vector 2 #f)))
  (shape-for-each (shape 1 3 10 12)
                  (lambda (o)
                    (format #t "[~a ~a]~%"
                    (vector-ref o 0)
                    (vector-ref o 1)))
                  vec))
        |- [1 10]
           [1 11]
           [2 10]
           [2 11]

(let ((arr (make-array (shape 0 2))))
  (shape-for-each (shape 1 3 10 12)
                  (lambda (o)
                    (format #t "[~a ~a]~%"
                    (array-ref o 0)
                    (array-ref o 1)))
                  arr))
         |- [1 10]
            [1 11]
            [2 10]
            [2 11]

STklos procedure

(share-nths a d n)

Share-nths takes every n`th slice along dimension `d into a shared array. This preserves the origin.

(define a (array (shape 0 4 0 4)
                 -1 -2 -3 -4
                 -5 -6 -7 -8
                 -9 -10 -11 -12
                 -13 -14 -15 -16))

(share-nths a 0 2)
 => #,(<array> (0 2 0 4) -1  -2  -3  -4
                         -9 -10 -11 -12)

(share-nths a 1 2)
 => #,(<array> (0 4 0 2) -1  -3  -5  -7
                         -9 -11 -13 -15)

STklos procedure

(share-column arr k)

Shares whatever the second index is about. The result has one dimension less.

(define a (array (shape 0 2 0 2 0 2) -1 -2 -3 -4 -5 -6 -7 -8))

(share-column a 1) => #,(<array> (0 2 0 2) -3 -4 -7 -8)
(share-column a 0) => #,(<array> (0 2 0 2) -1 -2 -5 -6)

STklos procedure

(share-row arr k)

Shares whatever the first index is about. The result has one dimension less.

(define a (array (shape 0 2 0 2 0 2) -1 -2 -3 -4 -5 -6 -7 -8))

(share-row a 0) => #,(<array> (0 2 0 2) -1 -2 -3 -4)
(share-row a 1) => #,(<array> (0 2 0 2) -5 -6 -7 -8)

STklos procedure

(share-array/origin arr k …​)
(share-array/origin arr index)

change the origin of arr to k …​, with index a vector or zero-based one-dimensional array that contains k …​

(define a (array (shape 0 2 0 2 ) -1 -2 -3 -4))

(share-array/origin  a 1 1) => #,(<array> (1 3 1 3) -1 -2 -3 -4)

STklos procedure

(array-copy+share array)

Returns a copy of array. If array does not have its own internal data, but was built using share-array, then the new array will be similar — it will be a copy of array, sharing the elements in the same way.

STklos procedure

(array-size array)

Returns the number of elements in array.

STklos procedure

(array-shape array)

Returns the shape of array.

STklos procedure

(array→list array)

Returns a list that contains a copy of the elements of array, in row-major order. This is not recursive, and will not flatten the array.

STklos procedure

(array→vector array)

Returns a vector that contains a copy of the elements of array, in row-major order. The new vector does not share elements with the original array (it is a fresh copy). This is not recursive, and will not flatten the array.

STklos procedure

(array-length array dim)

Returns the length of dimension dim in array array.

STklos procedure

(array-map [shape] proc arr0 arr1 …​)

This procedure is similar to map for lists: it will run proc on an element of each of the arr0, arr1, …​ arguments, storing the result in the equivalent position of a newly created array.

The shapes of the arrays must be the same.

The procedure will create a new array with shape shape (or arr0's shape, if shape was not specified).

STklos procedure

(array-map! array [shape] proc arr0 arr1 …​)

For each valid index idx, applies proc to the corresponding position in arr0, arr1, …​ and then sets the same place in array to the result.

If shape is specified, it should specify a subarray of array, and only that section will be mapped.

STklos procedure

(array-append dim arr1 arr2 …​)

Appends arrays arr1, arr2, …​ along the specified dimension dim. The arrays must have equally many dimensions and all other dimensions equally long.

(define a (array (shape 0 2 0 3) 11 22 33 44 55 66))
(define b (array (shape 0 3 0 3) -11 -22 -33 -44 -55 -66 -77 -88 -99))
(define c (array (shape 0 1 0 3) 'a 'b 'c))

(array-append 0 a b c) =>  #,(<array> (0 6 0 3)
                                      11  22  33
                                      44  55  66
                                     -11 -22 -33
                                     -44 -55 -66
                                     -77 -88 -99
                                       a   b   c)

STklos procedure

(array-share-count array)

Returns the number of arrays that were built sharing array's elements through (share-array array shape proc), and that were not yet garbage collected. Note that it may take a long time for an object to be garbage collected automatically. It is possible to force a garbage collection pass by calling (gc), but even that does not guarantee that a specific object will be collected.

STklos procedure

(array-copy array)

Returns a copy of array. The new copy will have no data shared with any other array, even if the argument array did.

STklos procedure

(array-for-each-index arr proc [index-object])

Will loop through all valid indices of array, applying proc to those indices.

If index-object is not provided, then proc must accept as many arguments as the number of dimensions that the shape describes.

If index-object is provided, it is used as a place to store the indices, so proc must accept a vector or an array (this is to avoid pushing and popping too many values when calling proc). index-object, when present, must be aither a vector or array.

See the documentation of shape-for-each for more information on index-object.

STklos procedure

(tabulate-array shape proc)
(tabulate-array shape proc idx)

Returns a new array of shape shape, populated according to proc. Each valid index in shape is passed to proc, and the result is place in the according array position.

idx is an object that may be used to store the indices, and it may be either a vector or an array. If it is not present, or if it is #f, then an index vector will be created internally.

STklos procedure

(array-retabulate! arr shp proc [index-object])

Sets the elements of arr in shape to the value of proc at that index, using index-object if provided. This is similar to tabulate-array!, except that the array is given by the user.

(define arr (array (shape 0 2 0 2) 'a 'b 'c 'd))
(array-retabulate! arr (shape 0 2 0 2) (lambda (x y) (+ 1 x y)))
arr => #,(<array> (0 2 0 2) 1 2 2 3)

STklos procedure

(transpose arr k …​)

Shares arr with permuted dimensions. Each dimension from 0 inclusive to rank exclusive must appear once in k …​

This is a generalized transpose. It can permute the dimensions any which way. The permutation is provided by a permutation matrix: a square matrix of zeros and ones, with exactly one one in each row and column, or a permutation of the rows of an identity matrix; the size of the matrix must match the number of dimensions of the array.

The default permutation is [ 0 1 , 1 0 ] of course, but any permutation array can be specified, and the shape array of the original array is then multiplied with it, and index column vectors of the new array with its inverse, from left, to permute the rows appropriately.

(transpose (array (shape 0 4 0 4)
                  -1  -2   -3  -4
                  -5  -6   -7  -8
                  -9  -10 -11 -12
                  -13 -14 -15 -16))
 => #,(<array> (0 4 0 4)
              -1 -5  -9 -13
              -2 -6 -10 -14
              -3 -7 -11 -15
              -4 -8 -12 -16)

(transpose (array (shape 0 3 0 3 0 2)
                  -1 -2
                  -3 -4
                  -5 -6

                  -7 -8
                  -9 -10
                  -11 -12

                  -13 -14
                  -15 -16
                  -17 -18))
 => #,(<array> (0 2 0 3 0 3)
               -1  -7 -13
               -3  -9 -15
               -5 -11 -17

               -2  -8 -14
               -4 -10 -16
               -6 -12 -18)

srfi-27 — Source of random bits

SRFI-27 is fully supported. Using primitives random-integer or random-real automatically load this SRFI.

srfi-28 — Basic Format Strings

SRFI-28 is fully supported. Note that STklos format is more general than the one defined this SRFI.

srfi-35 — Conditions

SRFI-35 is fully supported. See Section 7.3 for the predefined conditions and when it is required to load this file.

srfi-36 — I/O Conditions

SRFI-36 is fully supported. See Section 7.3 Conditions) for the predefined conditions and when it is required to load this file.

srfi-55 — Require-extension

SRFI-55 is fully supported. Furthermore, STklos also accepts the symbols defined in Table 2 in a require-extension clause.

srfi-69 — Basic Hash Tables

SRFI-69 is fully supported. Note that the default comparison function in STklos is eq? whereas it is equal? for the SRFI. Furthermore the hash functions defined in the SRFI are not defined by default in STklos. To have a fully compliant SRFI-69 behaviour, you need use a require-feature in your code.

srfi-88 — Keyword Objects

SRFI-88 is fully supported. The only difference between the keywords defined in the SRFI document and the STklos keywords is on the zero-length keyword: For STklos, : is equivalent to the keyword #:||, whereas the SRFI considers that : is not a keyword but a symbol.

To obtain the symbol : in STklos, you must use |:|.

srfi-116 — Immutable List Library

STklos implements the arrays of SRFI-116.

STklos procedure

(ipair a d)

Returns a newly allocated ipair whose icar is a and whose icdr is d. The ipair is guaranteed to be different (in the sense of eqv?) from every existing object.

STklos procedure

(ilist obj …​)

Returns a newly allocated ilist of its arguments.

   (ilist 'a (+ 3 4) 'c)            =>  (a 7 c)
   (ilist)                          =>  ()

Being an ilist, its CAR, CDR and all sublists are immutable.

STklos procedure

(xipair d a)

The same as (lambda (d a) (ipair a d))

Of utility only as a value to be conveniently passed to higher-order procedures.

(xipair (iq b c) 'a) => (a b c)

The name stands for "eXchanged Immutable PAIR."

STklos procedure

(ipair* obj …​)

ipair* is like ilist except that the last argument to ipair* is used as the ,(emph "cdr") of the last pair constructed.

   (ipair* 1 2 3)        => (1 2 . 3)
   (ipair* 1 2 3 '(4 5)) => (1 2 3 4 5)
   (ipair*)              => ()

STklos procedure

(make-ilist n [fill])

Returns an n-element ilist, whose elements are all the value fill. If the fill argument is not given, the elements of the ilist may be arbitrary values.

(make-ilist 4 'c) => (c c c c)

STklos procedure

(ilist-tabulate n init-proc)

Returns an n-element ilist. Element i of the ilist, where 0 ⇐ i < n, is produced by (init-proc i). No guarantee is made about the dynamic order in which init-proc is applied to these indices.

(ilist-tabulate 4 values) => (0 1 2 3)

STklos procedure

(ilist-copy lst)

Copies the spine of the argument, including the ilist tail.

STklos procedure

(iiota count [ start step ]

Returns an ilist containing the elements

(start start+step …​ start+(count-1)*step)

The start and step parameters default to 0 and 1, respectively. This procedure takes its name from the APL primitive.

(iiota 5) => (0 1 2 3 4)
(iiota 5 0 -0.1) => (0 -0.1 -0.2 -0.3 -0.4)

STklos procedure

(icar ipair)
(icdr ipair)

These procedures return the contents of the icar and icdr field of their argument, respectively. Note that it is an error to apply them to the empty ilist.

(icar (iq a b c))       =>  a          (icdr (iq a b c))     =>  (b c)
(icar (iq (a) b c d))   =>  (a)        (icdr (iq (a) b c d)) =>  (b c d)
(icar (ipair 1 2))      =>  1          (icdr (ipair 1 2))    =>  2
(icar '())              =>  *error*    (icdr '())            =>  *error*

STklos procedure

(ipair? obj)

Returns true and only if x is a proper ilist — that is, a ()-terminated ilist.

STklos procedure

(proper-ilist? x)
(ilist? x)

These identifiers are bound either to the same procedure. In either case, true is returned iff x is a proper ilist — a ()-terminated ilist.

More carefully: The empty list is a proper ilist. An ipair whose icdr is a proper ilist is also a proper ilist. Everything else is a dotted ilist. This includes non-ipair, non-() values (e.g. symbols, numbers, mutable pairs), which are considered to be dotted ilists of length 0.

STklos procedure

(dotted-ilist? x)

Returns true if x is a finite, non-nil-terminated ilist. That is, there exists an n >= 0 such that icdrn(x) is neither an ipair nor (). This includes non-ipair, non-() values (e.g. symbols, numbers), which are considered to be dotted ilists of length 0.

(dotted-ilist? x) = (not (proper-ilist? x))

STklos procedure

(not-ipair? x)

This is the same as (lambda (x) (not (ipair? x)))

Provided as a procedure as it can be useful as the termination condition for ilist-processing procedures that wish to handle all ilists, both proper and dotted.

STklos procedure

(null-ilist? lst)

Ilist is a proper ilist. This procedure returns true if the argument is the empty list (), and false otherwise. It is an error to pass this procedure a value which is not a proper ilist. This procedure is recommended as the termination condition for ilist-processing procedures that are not defined on dotted ilists.

STklos procedure

(ilist= elt= ilist1 …​)

Determines ilist equality, given an element-equality procedure. Proper ilist A equals proper ilist B if they are of the same length, and their corresponding elements are equal, as determined by elt=. If the element-comparison procedure’s first argument is from ilisti, then its second argument is from ilisti+1, i.e. it is always called as (elt= a b) for a an element of ilist A, and b an element of ilist B.

In the n-ary case, every ilisti is compared to ilisti+1 (as opposed, for example, to comparing ilist1 to ilisti, for i>1). If there are no ilist arguments at all, ilist= simply returns true.

It is an error to apply ilist= to anything except proper ilists. It cannot reasonably be extended to dotted ilists, as it provides no way to specify an equality procedure for comparing the ilist terminators.

Note that the dynamic order in which the elt= procedure is applied to pairs of elements is not specified. For example, if ilist= is applied to three ilists, A, B, and C, it may first completely compare A to B, then compare B to C, or it may compare the first elements of A and B, then the first elements of B and C, then the second elements of A and B, and so forth.

The equality procedure must be consistent with eq?. That is, it must be the case that

(eq? x y) => (elt= x y)

Note that this implies that two ilists which are eq? are always ilist=, as well; implementations may exploit this fact to "short-cut" the element-by-element comparisons.

(ilist= eq?) => #t       ; Trivial cases
(ilist= eq? (iq a)) => #t

STklos procedure

(list-immutable+! lst)
(list-immutable! lst)

Destructive versions of list→ilist: both procedures change their argument so it will become an immutable list. List-immutable+! returns the list, while list-immutable! returns #void.

STklos procedure

(ifirst ipair)
(isecond ipair)
(ithird ipair)
(ifourth ipair)
(ififth ipair)
(isixth ipair)
(iseventh ipair)
(ieighth ipair)
(ininth ipair)
(itenth ipair)

Synonyms for car, cadr, caddr, …​

(ithird '(a b c d e)) => c

STklos procedure

(icar+icdr ip)

The fundamental ipair deconstructor. Returns two values: the icar and the icdrif ip.

STklos procedure

(itake x i)
(idrop x i)
(ilist-tail x i)

itake returns the first i elements of ilist x. idrop returns all but the first i elements of ilist x. ilist-tail is either the same procedure as idrop or else a procedure with the same behavior.

 (itake (iq a b c d e)  2) => (a b)
 (idrop (iq a b c d e)  2) => (c d e)

x may be any value — a proper or dotted ilist:

(itake (ipair 1 (ipair 2 (ipair 3 'd)))    => (1 2)
(idrop (ipair 1 (ipair 2 (ipair 3 'd))) 2) => (3 . d)
(itake (ipair 1 (ipair 2 (ipair 3 'd))) 3) => (1 2 3)
(idrop (ipair 1 (ipair 2 (ipair 3 'd))) 3) => d

For a legal i, itake and idrop partition the ilist in a manner which can be inverted with iappend:

(iappend (itake x i) (idrop x i)) = x

idrop is exactly equivalent to performing i icdr operations on x; the returned value shares a common tail with x.

STklos procedure

(itake-right dilist i)
(idrop-right dilist i)

Itake-right returns the last i elements of dilist. Idrop-right returns all but the last i elements of dilist.

(itake-right (iq a b c d e) 2) => (d e)
(idrop-right (iq a b c d e) 2) => (a b c)

The returned ilist may share a common tail with the argument ilist.

dilist may be any ilist, either proper or dotted:

(itake-right (iq ipair 1 (ipair 2 (ipair 3 'd))) 2) => (2 3 . d)
(idrop-right (ipair 1 (ipair 2 (ipair 3 'd))) 2)    => (1)
(itake-right (ipair 1 (ipair 2 (ipair 3 'd))) 0)    => d
(idrop-right (ipair 1 (ipair 2 (ipair 3 'd))) 0)    => (1 2 3)

For a legal i, itake-right and idrop-right partition the ilist in a manner which can be inverted with iappend:

(iappend (itake dilist i) (idrop dilist i)) = dilist

Itake-right's return value is guaranteed to share a common tail with dilist.

STklos procedure

(isplit-at x i)

Isplit-at splits the ilist x at index i, returning an ilist of the first i elements, and the remaining tail. It is equivalent to

(values (itake x i) (idrop x i))

R5RS procedure

(ilast ipair)
(last-ipair ipair)

Ilast returns the last element of the non-empty, possibly dotted, ilist ipair. Last-ipair returns the last ipair in the non-empty ilist pair.

(ilast (iq a b c))      => c
(last-ipair (iq a b c)) => (c)

STklos procedure

(ilength ilist)

Returns the length of its argument. It is an error to pass a value to ilength which is not a proper ilist (()-terminated).

The length of a proper ilist is a non-negative integer n such that icdr applied n times to the ilist produces the empty list.

STklos procedure

(iappend ilist1 …​)

Returns an ilist consisting of the elements of ilist1 followed by the elements of the other ilist parameters.

(iappend (iq x) (iq y))        =>  (x y)
(iappend (iq a) (iq b c d))    =>  (a b c d)
(iappend (iq a (b)) (iq (c)))  =>  (a (b) (c))

The resulting ilist is always newly allocated, except that it shares structure with the final ilisti argument. This last argument may be any value at all; an improper ilist results if it is not a proper ilist. All other arguments must be proper ilists.

(iappend (iq a b) (ipair 'c 'd))  =>  (a b c . d)
(iappend '() 'a)           =>  a
(iappend (iq x y))         =>  (x y)
(iappend)                  =>  ()

STklos procedure

(iconcatenate ilist-of-ilists)

Appends the elements of its argument together. That is, iconcatenate returns the same as

(iapply iappend ilist-of-ilists)

or, equivalently,

(ireduce-right iappend '() ilist-of-ilists)

As with iappend, the last element of the input list may be any value at all.

STklos procedure

(ireverse ilist)

Returns a newly allocated ilist consisting of the elements of ilist in reverse order.

(ireverse (iq a b c))              =>  (c b a)
(ireverse (iq a (b c) d (e (f))))  =>  ((e (f)) d (b c) a)

STklos procedure

(iappend-reverse rev-head tail)

Iappend-reverse returns (iappend (ireverse rev-head) tail). It is provided because it is a common operation — a common list-processing style calls for this exact operation to transfer values accumulated in reverse order onto the front of another ilist, and because the implementation is significantly more efficient than the simple composition it replaces. (But note that this pattern of iterative computation followed by a reverse can frequently be rewritten as a recursion, dispensing with the reverse and iappend-reverse steps, and shifting temporary, intermediate storage from the heap to the stack, which is typically a win for reasons of cache locality and eager storage reclamation.)

STklos procedure

(izip ilist1 ilist2 …​)

Returns the same as (lambda ilists (iapply imap ilist ilists))

If izip is passed n ilists, it returns an ilist as long as the shortest of these ilists, each element of which is an n-element ilist comprised of the corresponding elements from the parameter ilists.

(izip (iq one two three)
     (iq 1 2 3)
     (iq odd even odd even odd even odd even))
    => ((one 1 odd) (two 2 even) (three 3 odd))

(izip (iq 1 2 3)) => ((1) (2) (3))

STklos procedure

(iunzip1 ilist)
(iunzip2 ilist)
(iunzip3 ilist)
(iunzip4 ilist)
(iunzip5 ilist)

Iunzip1 takes an ilist of ilists, where every ilist must contain at least one element, and returns an ilist containing the initial element of each such ilist. That is, it returns (imap icar ilists). Iunzip2 takes an ilist of ilists, where every ilist must contain at least two elements, and returns two values: an ilist of the first elements, and an ilist of the second elements. Iunzip3 does the same for the first three elements of the ilists, and so forth.

(iunzip2 (iq (1 one) (2 two) (3 three))) =>
    (1 2 3)
    (one two three)

STklos procedure

(icount pred ilist1 ilist2 …​)

Pred is a procedure taking as many arguments as there are ilists and returning a single value. It is applied element-wise to the elements of the ilists, and a count is tallied of the number of elements that produce a true value. This count is returned. count is "iterative" in that it is guaranteed to apply pred to the ilist elements in a left-to-right order. The counting stops when the shortest ilist expires.

(icount even? (iq 3 1 4 1 5 9 2 5 6))            => 3
(icount < (iq 1 2 4 8) (iq 2 4 6 8 10 12 14 16)) => 3

STklos procedure

(imap proc ilist1 ilist2 …​)

proc is a procedure taking as many arguments as there are ilist arguments and returning a single value. imap applies proc element-wise to the elements of the ilists and returns an ilist of the results, in order. The dynamic order in which proc is applied to the elements of the ilists is unspecified.

(imap icadr (iq (a b) (d e) (g h))) =>  (b e h)

(imap (lambda (n) (expt n n))
     (iq 1 2 3 4 5))
    =>  (1 4 27 256 3125)

(imap + (iq 1 2 3) (iq 4 5 6)) =>  (5 7 9)

(let ((count 0))
  (imap (lambda (ignored)
         (set! count (+ count 1))
         count)
       (iq a b))) =>  (1 2) or (2 1)

STklos procedure

(ifor-each proc ilist1 ilist2 …​)

The arguments to ifor-each are like the arguments to imap, but ifor-each calls proc for its side effects rather than for its values. Unlike imap, ifor-each is guaranteed to call proc on the elements of the ilists in order from the first element(s) to the last, and the value returned by ifor-each is unspecified.

(let ((v (make-vector 5)))
  (ifor-each (lambda (i)
              (vector-set! v i (* i i)))
            (iq 0 1 2 3 4))
  v)  =>  #(0 1 4 9 16)

STklos procedure

(ifold kons knil ilist1 ilist2 …​)

The fundamental ilist iterator.

First, consider the single ilist-parameter case. If ilist1 = (e1 e2 …​ en), then this procedure returns

  (kons en ... (kons e2 (kons e1 knil)) ... )

That is, it obeys the (tail) recursion

(ifold kons knil lis) = (ifold kons (kons (icar lis) knil) (icdr lis))
(ifold kons knil '()) = knil

Examples:

  (ifold + 0 lis)                    ; Add up the elements of LIS.
  (ifold ipair '() lis)              ; Reverse LIS.
  (ifold ipair tail rev-head)        ; See APPEND-REVERSE.
;; How many symbols in LIS?
(ifold (lambda (x count) (if (symbol? x) (+ count 1) count))
      0
      lis)

;; Length of the longest string in LIS:
(ifold (lambda (s max-len) (max max-len (string-length s)))
      0
      lis)

If n ilist arguments are provided, then the kons function must take n+1 parameters: one element from each ilist, and the "seed" or fold state, which is initially knil. The fold operation terminates when the shortest ilist runs out of values:

(ifold ipair* '() (iq a b c) (iq 1 2 3 4 5)) => (c 3 b 2 a 1)

STklos procedure

(iunfold p f g seed [tail-gen])

Iunfold is best described by its basic recursion:

(iunfold p f g seed) =
    (if (p seed) (tail-gen seed)
        (ipair (f seed)
              (iunfold p f g (g seed))))
p        Determines when to stop unfolding.
f        Maps each seed value to the corresponding ilist element.
g        Maps each seed value to next seed value.
seed     The "state" value for the unfold.
tail-gen Creates the tail of the ilist; defaults to (lambda (x) '())

In other words, we use g to generate a sequence of seed values seed, g(seed), g2(seed), g3(seed), …​ These seed values are mapped to ilist elements by f, producing the elements of the result ilist in a left-to-right order. P says when to stop.

Iunfold is the fundamental recursive ilist constructor, just as ifold-right is the fundamental recursive ilist consumer. While iunfold may seem a bit abstract to novice functional programmers, it can be used in a number of ways:

;; Ilist of squares: 1^2 ... 10^2
(iunfold (lambda (x) (> x 10))
         (lambda (x) (* x x))
         (lambda (x) (+ x 1))
         1)

(iunfold null-ilist? icar icdr lis) ; Copy a proper ilist.

;; Read current input port into an ilist of values.
(iunfold eof-object? values (lambda (x) (read)) (read))

;; Copy a possibly non-proper ilist:
(iunfold not-ipair? icar icdr lis
         values)

;; Append HEAD onto TAIL:
(iunfold null-ilist? icar icdr head
        (lambda (x) tail))

Interested functional programmers may enjoy noting that ifold-right and iunfold are in some sense inverses. That is, given operations knull?, kar, kdr, kons, and knil satisfying

(kons (kar x) (kdr x)) = x and (knull? knil) = #t

then

(ifold-right kons knil (iunfold knull? kar kdr x)) = x

and

(iunfold knull? kar kdr (ifold-right kons knil x)) = x.

This combinator sometimes is called an "anamorphism;" when an explicit tail-gen procedure is supplied, it is called an "apomorphism."

STklos procedure

(ipair-fold kons knil ilist1 ilist2 …​)

Analogous to fold, but kons is applied to successive sub-ilists of the ilists, rather than successive elements — that is, kons is applied to the ipairs making up the lists, giving this (tail) recursion:

(ipair-fold kons knil lis) = (let ((tail (icdr lis)))
                              (ipair-fold kons (kons lis knil) tail))
(ipair-fold kons knil '()) = knil

Example:

(ipair-fold ipair '() (iq a b c)) => ((c) (b c) (a b c))

STklos procedure

(ireduce f ridentity ilist)

Ireduce is a variant of ifold.

Ridentity should be a "right identity" of the procedure f — that is, for any value x acceptable to f,

 (f x ridentity) = x

Ireduce has the following definition:

If ilist = (), return ridentity;
Otherwise, return (ifold f (icar ilist) (icdr ilist)).

…​in other words, we compute (ifold f ridentity ilist).

Note that ridentity is used only in the empty-list case. You typically use ireduce when applying f is expensive and you’d like to avoid the extra application incurred when ifold applies f to the head of ilist and the identity value, redundantly producing the same value passed in to f. For example, if f involves searching a file directory or performing a database query, this can be significant. In general, however, ifold is useful in many contexts where ireduce is not (consider the examples given in the ifold definition — only one of the five folds uses a function with a right identity. The other four may not be performed with ireduce).

;; take the max of an ilist of non-negative integers.
(ireduce max 0 nums) ; i.e., (iapply max 0 nums)

STklos procedure

(ifold-right kons knil ilist1 ilist2 …​)

The fundamental ilist recursion operator.

First, consider the single ilist-parameter case. If ilist1 = (e1 e2 …​ en), then this procedure returns (kons e1 (kons e2 …​ (kons en knil)))

That is, it obeys the recursion

(ifold-right kons knil lis) = (kons (icar lis) (ifold-right kons knil (icdr lis)))
(ifold-right kons knil '()) = knil

Examples:

(ifold-right ipair '() lis)          ; Copy LIS.

;; Filter the even numbers out of LIS.
(ifold-right (lambda (x l) (if (even? x) (ipair x l) l)) '() lis))

If n ilist arguments are provided, then the kons procedure must take n+1 parameters: one element from each ilist, and the "seed" or fold state, which is initially knil. The fold operation terminates when the shortest ilist runs out of values:

(ifold-right ipair* '() (iq a b c) (iq 1 2 3 4 5)) => (a 1 b 2 c 3)

STklos procedure

(iunfold-right p f g seed [tail])

Iunfold-right constructs an ilist with the following loop:

(let lp ((seed seed) (lis tail))
  (if (p seed) lis
      (lp (g seed)
          (ipair (f seed) lis))))
p         Determines when to stop unfolding.
f         Maps each seed value to the corresponding ilist element.
g         Maps each seed value to next seed value.
seed      The "state" value for the unfold.
tail      ilist terminator; defaults to '().

In other words, we use g to generate a sequence of seed values

seed, g(seed), g2(seed), g3(seed), ...

These seed values are mapped to ilist elements by f, producing the elements of the result ilist in a right-to-left order. P says when to stop.

Iunfold-right is the fundamental iterative ilist constructor, just as ifold is the fundamental iterative ilist consumer. While iunfold-right may seem a bit abstract to novice functional programmers, it can be used in a number of ways:

;; Ilist of squares: 1^2 ... 10^2
(iunfold-right zero?
              (lambda (x) (* x x))
              (lambda (x) (- x 1))
              10)

;; Reverse a proper ilist.
(iunfold-right null-ilist? icar icdr lis)

;; Read current input port into an ilist of values.
(iunfold-right eof-object? values (lambda (x) (read)) (read))

;; (iappend-reverse rev-head tail)
(iunfold-right null-ilist? icar icdr rev-head tail)

Interested functional programmers may enjoy noting that ifold and iunfold-right are in some sense inverses. That is, given operations knull?, kar, kdr, kons, and knil satisfying

(kons (kar x) (kdr x)) = x and (knull? knil) = #t

then

(ifold kons knil (iunfold-right knull? kar kdr x)) = x

and

(iunfold-right knull? kar kdr (ifold kons knil x)) = x.

STklos procedure

(ipair-fold-right kons knil ilist1 ilist2 …​)

Holds the same relationship with ifold-right that ipair-fold holds with ifold. Obeys the recursion

 (ipair-fold-right kons knil lis) =
     (kons lis (ipair-fold-right kons knil (icdr lis)))
 (ipair-fold-right kons knil '()) = knil

Example:

(ipair-fold-right ipair '() (iq a b c)) => ((a b c) (b c) (c))

STklos procedure

(ireduce-right f ridentity ilist)

Ireduce-right is the fold-right variant of ireduce. It obeys the following definition:

(ireduce-right f ridentity '()) = ridentity
(ireduce-right f ridentity (iq e1)) = (f e1 ridentity) = e1
(ireduce-right f ridentity (iq e1 e2 ...)) =
    (f e1 (ireduce f ridentity (e2 ...)))

…​in other words, we compute (ifold-right f ridentity ilist).

;; Append a bunch of ilists together.
;; I.e., (iapply iappend ilist-of-ilists)
(ireduce-right iappend '() ilist-of-ilists)

STklos procedure

(iappend-map f ilist1 ilist2 …​)

Equivalent to

(iapply iappend (imap f ilist1 ilist2 ...))

and

(iapply iappend (imap f ilist1 ilist2 ...))

Map f over the elements of the ilists, just as in the imap function. However, the results of the applications are appended together (using iappend) to make the final result.

The dynamic order in which the various applications of f are made is not specified.

Example:

(iappend-map (lambda (x) (ilist x (- x))) (iq 1 3 8))
    => (1 -1 3 -3 8 -8)

STklos procedure

(ipair-for-each f ilist1 ilist2 …​)

Like ifor-each, but f is applied to successive sub-ilists of the argument ilists. That is, f is applied to the cells of the ilists, rather than the ilists' elements. These applications occur in left-to-right order.

(ipair-for-each (lambda (ipair) (display ipair) (newline)) (iq a b c)) ==>
    (a b c)
    (b c)
    (c)

STklos procedure

(ifilter-map f ilist1 ilist2 …​)

Like imap, but only true values are saved.

(ifilter-map (lambda (x) (and (number? x) (* x x))) (iq a 1 b 3 c 7))
    => (1 9 49)

The dynamic order in which the various applications of f are made is not specified.

R5RS procedure

(imap-in-order f ilist1 ilist2 …​)

A variant of the imap procedure that guarantees to apply f across the elements of the ilisti arguments in a left-to-right order. This is useful for mapping procedures that both have side effects and return useful values.

STklos procedure

(ifilter pred ilist )

Return all the elements of ilist that satisfy predicate pred. The ilist is not disordered — elements that appear in the result ilist occur in the same order as they occur in the argument ilist. The returned ilist may share a common tail with the argument ilist. The dynamic order in which the various applications of pred are made is not specified.

(ifilter even? (iq 0 7 8 8 43 -4)) => (0 8 8 -4)

STklos procedure

(ipartition pred ilist)

Partitions the elements of ilist with predicate pred, and returns two values: the ilist of in-elements and the ilist of out-elements. The ilist is not disordered — elements occur in the result ilists in the same order as they occur in the argument ilist. The dynamic order in which the various applications of pred are made is not specified. One of the returned ilists may share a common tail with the argument ilist.

(ipartition symbol? (iq one 2 3 four five 6)) =>
    (one four five)
    (2 3 6)

STklos procedure

(iremove pred ilist)

Returns ilist without the elements that satisfy predicate pred, similar to

(lambda (pred ilist) (ifilter (lambda (x) (not (pred x))) ilist))

The ilist is not disordered — elements that appear in the result ilist occur in the same order as they occur in the argument ilist. The returned ilist may share a common tail with the argument ilist. The dynamic order in which the various applications of pred are made is not specified.

(iremove even? (iq 0 7 8 8 43 -4)) => (7 43)

STklos procedure

(imember x ilist [=])
(imemq x ilist)
(imemv x ilist)

These procedures return the first sub-ilist of ilis`t whose icar is `x, where the sub-ilists of ilist are the non-empty ilists returned by (idrop ilist i) for i less than the length of ilist. If x does not occur in ilist, then false is returned. Imemq uses eq? to compare x with the elements of ilist, while imemv uses eqv?, and imember uses equal?.

(imemq 'a (iq a b c))           =>  (a b c)
(imemq 'b (iq a b c))           =>  (b c)
(imemq 'a (iq b c d))           =>  #f
(imemq (list 'a)
        (ilist 'b '(a) 'c))     =>  #f
(imember (list 'a)
        (ilist 'b '(a) 'c)))    =>  ((a) c)
(imemq 101 (iq 100 101 102))    =>  *unspecified*
(imemv 101 (iq 100 101 102))    =>  (101 102)

The comparison procedure is used to compare the elements ei of ilist to the key x in this way:

(= x ei) ; ilist is (E1 ... En)

That is, the first argument is always x, and the second argument is one of the ilist elements. Thus one can reliably find the first element of ilist that is greater than five with (imember 5 ilist <)

Note that fully general ilist searching may be performed with the ifind-tail and ifind procedures, e.g.

(ifind-tail even? ilist) ; Find the first elt with an even key.

R5RS procedure

(ifind pred ilist)

Return the first element of ilist that satisfies predicate pred; false if no element does.

(ifind even? (iq 3 1 4 1 5 9)) => 4

Note that ifind has an ambiguity in its lookup semantics — if ifind returns false, you cannot tell (in general) if it found a false element that satisfied pred, or if it did not find any element at all. In many situations, this ambiguity cannot arise — either the ilist being searched is known not to contain any false elements, or the ilist is guaranteed to have an element satisfying pred. However, in cases where this ambiguity can arise, you should use ifind-tail instead of ifind, since ifind-tail has no such ambiguity:

(cond ((ifind-tail pred lis) => (lambda (ipair) ...)) ; Handle (icar ipair)
      (else ...)) ; Search failed.

STklos procedure

(ifind-tail pred ilist)

Return the first ipair of ilist whose icar satisfies pred. If no ipair does, return false.

Ifind-tail can be viewed as a general-predicate variant of the imember function.

Examples:

(ifind-tail even? (iq 3 1 37 -8 -5 0 0)) => (-8 -5 0 0)
(ifind-tail even? (iq 3 1 37 -5)) => #f

;; IMEMBER X LIS:
(ifind-tail (lambda (elt) (equal? x elt)) lis)

Ifind-tail is essentially idrop-while, where the sense of the predicate is inverted: Ifind-tail searches until it finds an element satisfying the predicate; idrop-while searches until it finds an element that doesn’t satisfy the predicate.

STklos procedure

(iany pred ilist1 ilist2 …​)

Applies the predicate across the ilists, returning true if the predicate returns true on any application.

If there are n ilist arguments ilist1 …​ ilistn, then pred must be a procedure taking n arguments and returning a boolean result.

Iany applies pred to the first elements of the ilisti parameters. If this application returns a true value, iany immediately returns that value. Otherwise, it iterates, applying pred to the second elements of the ilisti parameters, then the third, and so forth. The iteration stops when a true value is produced or one of the ilists runs out of values; in the latter case, iany returns false. The application of pred to the last element of the ilists is a tail call.

Note the difference between ifind and ianyifind returns the element that satisfied the predicate; iany returns the true value that the predicate produced.

Like ievery, `iany’s name does not end with a question mark — this is to indicate that it does not return a simple boolean (true or false), but a general value.

(iany integer? (iq a 3 b 2.7))   => #t
(iany integer? (iq a 3.1 b 2.7)) => #f
(iany < (iq 3 1 4 1 5)
       (iq 2 7 1 8 2)) => #t

STklos procedure

(ievery pred ilist1 ilist2 …​)

Applies the predicate across the ilists, returning true if the predicate returns true on every application.

If there are n ilist arguments ilist1 …​ ilistn, then pred must be a procedure taking n arguments and returning a boolean result.

Ievery applies pred to the first elements of the ilisti parameters. If this application returns false, ievery immediately returns false. Otherwise, it iterates, applying pred to the second elements of the ilisti parameters, then the third, and so forth. The iteration stops when a false value is produced or one of the ilists runs out of values. In the latter case, ievery returns the true value produced by its final application of pred. The application of pred to the last element of the ilists is a tail call.

If one of the ilisti has no elements, ievery simply returns true.

Like iany, `ievery’s name does not end with a question mark — this is to indicate that it does not return a simple boolean (true or false), but a general value.

R5RS procedure

(ilist-index pred ilist1 ilist2 …​)

Returns the index of the leftmost element that satisfies pred.

If there are n ilist arguments ilist1 …​ ilistn, then pred must be a function taking n arguments and returning a boolean result.

Ilist-index applies pred to the first elements of the ilisti parameters. If this application returns true, ilist-index immediately returns zero. Otherwise, it iterates, applying pred to the second elements of the ilisti parameters, then the third, and so forth. When it finds a tuple of ilist elements that cause pred to return true, it stops and returns the zero-based index of that position in the ilists.

The iteration stops when one of the ilists runs out of values; in this case, ilist-index returns false.

ilist-index even? (iq 3 1 4 1 5 9)) => 2
ilist-index < (iq 3 1 4 1 5 9 2 5 6) (iq 2 7 1 8 2)) => 1
ilist-index = (iq 3 1 4 1 5 9 2 5 6) (iq 2 7 1 8 2)) => #f

STklos procedure

(itake-while pred ilist)

Returns the longest initial prefix of ilist whose elements all satisfy the predicate pred.

(itake-while even? (iq 2 18 3 10 22 9)) => (2 18)

STklos procedure

(idrop-while pred ilist)

Drops the longest initial prefix of ilist whose elements all satisfy the predicate pred, and returns the rest of the ilist.

(idrop-while even? (iq 2 18 3 10 22 9)) => (3 10 22 9)

STklos procedure

(ispan pred ilist)
(ibreak pred ilist)

Ispan splits the ilist into the longest initial prefix whose elements all satisfy pred, and the remaining tail. Ibreak inverts the sense of the predicate: the tail commences with the first element of the input ilist that satisfies the predicate.

In other words: ispan finds the initial span of elements satisfying pred, and ibreak breaks the ilist at the first element satisfying pred.

Ispan is equivalent to

(values (itake-while pred ilist)
        (idrop-while pred ilist))

(ispan even? (iq 2 18 3 10 22 9)) =>
  (2 18)
  (3 10 22 9)

(ibreak even? (iq 3 1 4 1 5 9)) =>
  (3 1)
  (4 1 5 9)

STklos procedure

(idelete x ilist [=])

Idelete uses the comparison procedure =, which defaults to equal?, to find all elements of ilist that are equal to x, and deletes them from ilist. The dynamic order in which the various applications of = are made is not specified.

The ilist is not disordered — elements that appear in the result ilist occur in the same order as they occur in the argument ilist. The result may share a common tail with the argument ilist.

Note that fully general element deletion can be performed with the iremove procedures, e.g.:

;; idelete all the even elements from LIS:
(iremove even? lis)

The comparison procedure is used in this way: (= x ei). That is, x is always the first argument, and an ilist element is always the second argument. The comparison procedure will be used to compare each element of ilist exactly once; the order in which it is applied to the various ei is not specified. Thus, one can reliably remove all the numbers greater than five from an ilist with (idelete 5 ilist <).

STklos procedure

(ialist-cons key datum ialist)

Constructs a new ialist entry mapping key → datum onto ialist. This is the same as

(lambda (key datum ialist) (ipair (ipair key datum) ialist))

STklos procedure

(idelete-duplicates ilist [=])

Idelete-duplicates removes duplicate elements from the ilist argument. If there are multiple equal elements in the argument ilist, the result ilist only contains the first or leftmost of these elements in the result. The order of these surviving elements is the same as in the original ilist — idelete-duplicates does not disorder the ilist (hence it is useful for "cleaning up" immutable association lists).

The = parameter is used to compare the elements of the ilist; it defaults to equal?. If x comes before y in ilist, then the comparison is performed (= x y). The comparison procedure will be used to compare each pair of elements in ilist no more than once; the order in which it is applied to the various pairs is not specified.

Although idelete-duplicates can be implemented so it runs in time O(n2) for n-element ilists, the STklos implementation runs in linear expected time.

(idelete-duplicates (iq a b a c a b c z)) => (a b c z)

;; Clean up an ialist:
(idelete-duplicates (iq (a . 3) (b . 7) (a . 9) (c . 1))
                   (lambda (x y) (eq? (icar x) (icar y))))
    => ((a . 3) (b . 7) (c . 1))

STklos procedure

(iassoc key ialist [=] → ipair or #f
(iassq key ialist → ipair or #f
(iassv key ialist → ipair or #f

Ialist must be an immutable association list — an ilist of ipairs. These procedures find the first ipair in ialist whose icar field is key, and returns that ipair. If no ipair in ialist has key as its icar, then false is returned. Iassq uses eq? to compare key with the icar fields of the ipairs in ialist, while iassv uses eqv? and iassoc uses equal?.

(define e (iq (a 1) (b 2) (c 3)))
(iassq 'a e)                               =>  (a 1)
(iassq 'b e)                               =>  (b 2)
(iassq 'd e)                               =>  #f
(iassq (ilist 'a) (iq ((a)) ((b)) ((c))))  =>  #f
(iassoc '(a) (ilist '((a)) '((b)) '((c)))) =>  ((a))
(iassq 5 (iq (2 3) (5 7) (11 13)))         =>  *unspecified*
(iassv 5 (iq (2 3) (5 7) (11 13)))         =>  (5 7)

The comparison procedure is used to compare the elements ei of ilist to the key parameter in this way:

(= key (icar ei)) ; ilist is (E1 ... En)

That is, the first argument is always key, and the second argument is one of the ilist elements. Thus one can reliably find the first entry of ialist whose key is greater than five with (iassoc 5 ialist <).

Note that fully general ialist searching may be performed with the ifind-tail and ifind procedures, e.g.

;; Look up the first association in ialist with an even key:
(ifind (lambda (a) (even? (icar a))) ialist)

STklos procedure

(ialist-delete key ialist [=])

Ialist-delete deletes all associations from ialist with the given key, using key-comparison procedure =, which defaults to equal?. The dynamic order in which the various applications of = are made is not specified.

Return values may share common tails with the ialist argument. The ialist is not disordered — elements that appear in the result ialist occur in the same order as they occur in the argument ialist.

The comparison procedure is used to compare the element keys ki of ialist’s entries to the key parameter in this way: (= key ki). Thus, one can reliably remove all entries of ialist whose key is greater than five with (ialist-delete 5 ialist <)

STklos procedure

(replace-icar ipair object)
(replace-icdr ipair object)

Replace-icar returns an ipair with object in the icar field and the icdr of ipair in the icdr field.

Replace-icdr returns an ipair with object in the icdr field and the icar of ipair in the icar field.

STklos procedure

(list→ilist lst)
(ilist→list lst)

These procedures return an ilist and a list respectively that have the same elements as the argument. The tails of dotted (i)lists are preserved in the result, which makes the procedures not inverses when the tail of a dotted ilist is a list or vice versa. The empty list is converted to itself.

It is an error to apply list→ilist to a circular list.

STklos procedure

(pair→ipair pair)
(ipair→pair ipair)

These procedures, which are inverses, return an ipair and a pair respectively that have the same (i)car and (i)cdr fields as the argument.

STklos procedure

(tree→itree object)
(itree→tree object)

These procedures walk a tree of pairs or ipairs respectively and make a deep copy of it, returning an isomorphic tree containing ipairs or pairs respectively. The result may share structure with the argument. If the argument is not of the expected type, it is returned.

These procedures are not inverses in the general case. For example, a pair of ipairs would be converted by tree→itree to an ipair of ipairs, which if converted by itree→tree would produce a pair of pairs.

STklos procedure

(gtree→itree object)
(gtree→tree object)

These procedures walk a generalized tree consisting of pairs, ipairs, or a combination of both, and make a deep copy of it, returning an isomorphic tree containing only ipairs or pairs respectively. The result may share structure with the argument. If the argument is neither a pair nor an ipair, it is returned.

STklos procedure

(iapply procedure object …​ ilist)

The iapply procedure is an analogue of apply whose last argument is an ilist rather than a list. It is equivalent to

(apply procedure object ... (ilist->list ilist))

srfi-138 — Compiling Scheme programs to executables

SRFI-138 is fully supported. The stklos-compile program conforms to SRFI 138, accepting all the required command line options.

The -D x flag of stklos-compile will define a feature named x for use with cond-expand in the compiled code only. It will not include x in the features list of the runtime.

srfi-145 — Assumptions

SRFI-145 is fully supported. See the assume special form.

srfi-169 — Underscores in numbers

SRFI-169 is fully supported. See parameter accept-srfi-169-numbers to eventually forbid the usage of underscores in numbers.

srfi-216 — SICP Prerequisites (Portable)

SRFI-216 is fully supported. However, it defines the constant stream-null and the predicate stream-null? which are incompatible with the ones defined in the (stream primitive) library used by SRFI-41 or SRFI-221. Prefix the imported symbols of this SRFI, if you plan to use it with one of the previous libraries.

srfi-230 — Atomic Operations

SRFI-238 is fully supported if STklos was compiled with Posix threads. If STklos was compiled without thread support, the module (srfi 230) is defined, but it exports nothing.

srfi-238 — Codesets

SRFI-238 is fully supported. Furthermore, STklos adds the functions codeset-list and make-codeset.

STklos procedure

(codeset-list)

Retuns a list of known codeset names.

(codeset-list) => (errno signal)

STklos procedure

(make-codeset name lst)

returns a new codeset object of the given name (a symbol). The list lst is a list of triplets (code-number symbol message) where symbol and message can be #f if code-number has no associated name or message.

 (define cs
     (make-codeset 'foo
                   '((1   OK    "Everything is OK")
                     (1   YES   #f)                  ; Other name for OK
                     (2   KO    "We have a problem")
                     (2   NO    #f)                  ; Other name for KO
                     (3   MAYBE "To be determined")
                     (404 #f    "Not found"))))      ; No symbolic name

1. Documentation about hygienic macros has been stolen in the SLIB manual
1. In fact define-module on a given name defines a new module only the first time it is invoked on this name. By this way, interactively reloading a module does not define a new entity, and the other modules which use it are not altered.
2. This transcript uses the default toplevel loop which displays the name of the current module in the evaluator prompt.
1. Under Unix, you can simply connect to a listening socket with the telnet of netcat command. For the given example, this can be achieved with netcat localhost 12345
2. Port 13, if open, can be used for testing: making a connection to it permits to know the distant system’s idea of the time of day.
1. The "pattern matching compiler" has been written by Jean-Marie Geffroy and is part of the Manuel Serrano’s Bigloo compiler since several years [Bigloo]
1. This section is an adaptation of Jeff Dalton’s (J.Dalton@ed.ac.uk) "Brief introduction to CLOS" which can be found at http://www.aiai.ed.ac.uk/~jeff/clos-guide.html