by John Cowan, Lassi Kortela, and Harold Ancell
This SRFI is currently in draft status. Here is an explanation of each status that a SRFI can hold. To provide input on this SRFI, please send email to
firstname.lastname@example.org. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.
This SRFI provides means to construct, return or signal, and extract information from Scheme interfaces with "foreign" systems such as the POSIX API, databases, and libraries.
'conventionkey requires a better name, and it's not
'sub-conventionwill follow the new name.
'conventionkey should be required for all status and error objects.
'messagebe replaced by the shorter
'column-numberbe in the SRFI's list of standard keys, or in Schemeregistry? They increase its surface by one half.
R6RS and R7RS-large have ambitions to become comprehensive ecosystems useful for developing real-world applications. This necessitates the development of many interfaces to "foreign" systems, ranging from direct Foreign Function Interfaces (FFIs) to low level assembler and C libraries, including APIs at that level such as POSIX, to protocols such as TCP/IP, all the way to client-server systems such as databases running in their own separate processes.
This SRFI provides a comprehensive framework for placing data about
the status of an interaction with a foreign system into an object,
starting with conventions to partition collections of them,
'generic-c-lib, to localize them, and to report them.
Sometimes it will make sense to simply return a status object, but
an often preferred method for errors is to raise an exception, as in
170, in which procedures never return error codes nor use an
analogue of the POSIX
errno variable to
indicate system-call-level errors.
Thus procedures can return useful values, and the programmer can assume that if a foreign interface procedure returns, it succeeded. This greatly simplifies the flow of the code from the programmer's point of view.
The universe of foreign statuses is divided into distinct collections
named conventions based on their source.
reserved for generic situations, such as sanity error checking before
a foreign interface is invoked.
Example conventions and sub-conventions include:
|'sqlstate||'postgresql||The PostgreSQL database|
|'generic-c-lib||'libsodium||The libsodium cryptography library|
POSIX errnos are a familiar error system with a
moderate degree of structure, and were the initial inspiration for
PostgreSQL is a representative SQL RDBMS with a richer set of SQLSTATE
data that can be transmitted to the user, and its client/server
organization is a useful counterpoint to using an FFI.
libsodium has a typical simple UNIX™ style C API that returns 0
on success and -1 on error, with no inherent structure.
'generic-c-lib convention is suitable for many of these
This SRFI uses property lists (plists). They are simple lists of alternating keys and values, such as:
('key1 "value1" 'key2 "value2")
(foreign-status? object)→ boolean (procedure)
Returns #f unless object is a SRFI 198 status object.(foreign-status? "This is not an foreign status object") ⇒ #f (foreign-status? (make-foreign-status <argument>)) ⇒ #t
(foreign-status-ref foreign-status-object key)→ value or
Returns the value in a foreign status object associated with key , or
#fif key is not in the object.(foreign-status-ref <foreign-status-object> <key not in object>) ⇒ #f (foreign-status-ref <POSIX-foreign-error-object> 'convention) ⇒ errno
(foreign-status-keys foreign-status-object)→ list (procedure)
Returns all the keys in a foreign status object in a list.(foreign-status-keys <POSIX-foreign-error-object>)⇒ (convention errno number name scheme-procedure foreign-interface message args heritage)(foreign-status-keys <PostgreSQL-wire-foreign-status-object>)⇒ (convention sub-convention sqlstate category class-text condition-name scheme-procedure foreign-interface message args)(foreign-status-keys <libsodium-foreign-status-object>)⇒ (convention sub-convention scheme-procedure foreign-interface message)
(foreign-status->plist foreign-status-object)→ plist (procedure)
Returns all the key/value pairs in a foreign status object as a plist, the
'conventionkey is guaranteed to be the first one. It is an error to mutate the plist.(foreign-status->plist <POSIX-foreign-error-object>)⇒ (convention errno number 2 name ENOENT scheme-procedure open-file foreign-interface open message "open-file called open: errno/ENOENT: No such file or directory" args ("not-a-valid-filename" 0 428) heritage . "SRFI 170")(foreign-status->plist <PostgreSQL-wire-foreign-status-object>)⇒ (convention sqlstate sub-convention postgresql sqlstate "28P01" category exception class-text "Invalid Authorization Specification" condition-name invalid_password scheme-procedure open-database-connection foreign-interface query-message message "open-database-connection: 28P01: incorrect password" args ("localhost" 5432 "ecb" "ecb"))(foreign-status->plist <libsodium-foreign-status-object>)⇒ (convention generic-c-lib sub-convention libsodium scheme-procedure generate-key foreign-interface sodium-init message "generate-key: ecb-generate-key could not initialize sodium library\n")
(make-foreign-status convention-value plist) → foreign-status-object (procedure)
(raise-foreign-error convention-value plist) → undefined (procedure)
These procedures are handed a value for the convention key, and a plist, and return an abstract data type object. It is an error to pass non-symbols as keys in the plist, if the plist does not have an even number of elements, or if the
'conventionkey/value pair is not in the object. If the plist argument is malformed, an error is signaled. Except for small memory implementations, these procedures must preserve the malformed argument for end users as the value of the
A recommended approach to handle a malformed argument is to create a status object with either
'erroras the value of the
'conventionkey, make the argument the value of the
'argskey, and supply an informative
raise-foreign-errorconstructs a foreign-error-object like
make-foreign-status, and raises an exception in a manner suitable for the Scheme implementation it is running on. In a R6RS or R7RS system following SRFI 35, or in a system supporting SRFI 18, raising an exception means to call
raiseon the object. In a system only supporting SRFI 23, raising an exception means calling
irritantsfollowing it.(make-foreign-status 'status '()))⇒ foreign-status-object(raise-foreign-error errno '(number 2 name ENOENT scheme-procedure open-file foreign-interface open message "open-file called open: errno/ENOENT: No such file or directory" arguments ("not-a-valid-filename" 0 428) heritage "SRFI 170")⇒ undefined(make-foreign-status generic-c-lib '(scheme-procedure generate-key foreign-interface sodium-init message "generate-key: ecb-generate-key could not initialize sodium library\n")⇒ foreign-status-object(raise-foreign-error 'error '()))⇒ undefined
convention: The only mandatory key/value pair, it is used to divide the universe of foreign interfaces. For more information see the text after the Specification heading.
sub-convention: To further subdivide large conventions such as
scheme-procedure: The Scheme procedure in which the status object is created. If there is no Scheme code interposed between the the user and a FFI, it can be omitted or have a value of
#f. If the plist argument to create a status object is malformed, its value should be
foreign-interface: The specific part of the foreign interface that was called, and responded with a status. For a library, the name of the library function that was called is suggested.
message: A message suitable for human understanding of an interface status. In the construction of the error message, implementers are encouraged to include relevant information from the above fields and the args field below. Implementations on small-memory systems can reasonably omit message strings.
args: The arguments handed to the Scheme procedure in which the status object is created. For login functions, you want to omit reporting and logging plaintext passwords, and login names are also dangerous because users will sometimes accidentally enter their password first. For cryptographic libraries, the same for secret and private keys and plaintext data.
inner: For nesting of a lower level status object.
source-filename: A string containing a filename that contained the source code that generated the status.
line-number: The line number in the above
'source-filenameof the beginning of the call to
column-number: The column in the line-number above.
An old Chibi Scheme sample implementation of this SRFI can be found in the srfi directory of the SRFI's repository, it will be updated when the API settles down.
Thanks to Olin Shivers and all the Scheme implementers who have
followed his work; this SRFI was inspired by scsh's
errno-error facility. Thanks also to all the
participants in the SRFI 170 and SRFI 198 mailing lists.
Copyright © 2020 John Cowan, Lassi Kortela, and Harold Ancell
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.