by Daphne Preston-Kendal, Peter McGoron
For editor's use only. Please do not edit this section.
??? the draft/final/withdrawn status of the SRFI, information on how to subscribe to its mailing list, and important dates in its history. The editor will add this section.
An alternative interface for creating custom ports and for accessing information from ports is defined. The interface is more flexible and extensible than the rigid set of port operations defined for custom ports in the R6RS and in its close relative SRFI 181. Some other minor issues with these previous specifications are also corrected. The custom ports defined here are sufficient to implement SRFI 6 / R7RS-Small string ports and SRFI 271 random binary ports.
make-overlaid-port)
An alternative would be to install an exception handler around the
call to proc, raising an exception of its own with a new
&port-creation-violation condition type with a field to
access the unlocked copy. This might be cleaner even if less flexible.
port-lock!)
[phm]: Should this end with a !?
facet? be a procedure?write-bytevector that in turn
depends on write-u8, but can be overridden to be faster.
make-transcoded-facet)
[phm]: Should this be exposed? It would be possible to add this facet
to a port without actually transcoding it. Including these procedures
would also require
supporting the R6RS
transcoders, so maybe these be optional for
R7RS.
make-port-facet-type):
[phm]: This is somewhat awkward to use. Is there a more succinct way
to capture the usual case? One example that comes to mind is to export
accessors instead of using an accessor procedure. A version like
R7RS's
define-record-type could be here, while
R6RS systems
could subclass a facet object.
read-bytes-facet, etc.)
[phm]: I am not convinced of the argument generators/accumulators.
Most programs trust code running in the process, and should not send
objects with secrets to untrusted processes anyways. (They can just
allocate a bytevector/string and splice-in the read data if they
are paranoid.) With regards to string mutation, this could be solved
by having the procedures always return either a char or a string.
The only unavoidable performance penalty is the rare occurence of
get-string-n! (in
R6RS). Otherwise the
procedures will allocate strings anyways. Performance penalties
can also be obviated by allowing for facets that implement certain
procedures like get-line themselves.
make-close-input-facet)
Maybe disallow bidirectional ports.
make-line-buffered-input-port)
[phm]: Should this procedure have a configurable EOL?
make-line-buffered-output-port,
make-block-buffered-output-port)
Is automatically flushing the Right Thing?
What should happen if the port position is set and the buffer is
not empty? Automatically flush, or signal an error?
make-size-limited-input-port,
make-size-limited-output-port):
Should this signal an error of some more specific type?
The custom ports of the R6RS and SRFI 181 provide useful functionality, but in a fixed and inflexible way. They support only a fixed set of port operations. The faceted ports proposal provides a flexible and extensible algebra for creating efficient I/O interfaces.
The relation between ports and facets in this SRFI can be thought of as similar to the relation between compound and simple conditions in R6RS, or compounds and components in SRFI 222. However, unlike in condition objects, a port is not a facet, and a facet is not a port: the two form distinct layers of the interface. It is not possible to compound two ports together into a single port – you cannot compound an input port and an output port into a single bidirectional port – for reasons including that the port position state of the two underlying ports would be out of synch with one another once any reading or writing had taken place. Therefore it is also not possible to extract a list of the facets from a port after creating it: you must only interact with the port using the public API. To ensure this, it is also not possible to extract the field values of a facet from the facet itself: it must be compounded into a port first to provide a public API.
In a sense, one can think of facets as an arbitrarily-extensible
set of keyword arguments to the make-port operation:
the behaviour of them and restrictions on them are compatible with
this interpretation.
Compared to R6RS and SRFI 181, this interface allows ports to be extended with arbitrary properties and operations.
Note: As well as providing custom ports, the R6RS also completely redesigned the other I/O procedures of Scheme. Notable about the R6RS system is:
get and put in the names of procedures
instead of R5RS’s and
R7RS-Small’s use of
read and write;
current-input-port
and current-output-port arguments when no port is
explicitly specified.
Any procedure from R6RS, R7RS-Small, or any SRFI which takes a port as an argument can be used with the ports created by the procedures of this SRFI. The specification here uses the R7RS-Small names of procedures as examples but otherwise takes no position on which system is preferable.
This proposal does not directly deal with the creation of ports from operating system resources. Preliminarily, it is suggested that the following standard procedures have the properties in this table. More efficient userspace-level buffering should then be provided by using the buffering-related port creation helper procedures. All of these procedures should also make appropriate informative facets available, if possible.
| Procedure | Description |
|---|---|
open-binary-input-file,
open-binary-output-file
(R7RS-Small)
|
Open a raw binary port without buffering beyond what the operating
system provides. In POSIX terms, where
read-bytes-facet/write-bytes-facet directly
calls read/write, the close
system call directly in their close-input-facet
or close-output-facet, and the fsync
system call directly in their flush-output-facet.
|
open-input-file |
equivalent to
(make-transcoded-port (open-input-file STRING) (native-transcoder))
|
open-output-file |
equivalent to
(make-transcoded-port (open-input-file STRING) (native-transcoder))
|
open-file-input-port, open-file-output-port,
open-file-input/output-port
(R6RS)
|
equivalent to setting up such a raw port and wrapping it in corresponding buffering and transcoded ports, although their full functionality cannot be provided in terms of the R7RS-Small procedures with only the wrapping functionality of this SRFI (in particular, input/output ports and the file-options arguments cannot be implemented with R7RS-Small) |
standard-input-port, standard-output-port,
standard-error-port
(R6RS)
|
should return raw binary ports with only operating system buffering,
like the suggestion for the open-binary-output-file
procedures.
|
In procedure signature, square brackets [] denote a group
of optional arguments that must either be all present or all absent.
Nested square brackets denote parts of that group that are optional
even when other arguments in the group are present. For example,
(make-port-position-facet [getter [setter]])
is a procedure signature for make-port-position-facet
that can be called with zero arguments, one argument getter,
or two arguments getter and setter.
This SRFI uses the term ‘assertion violation’ for certain erroneous uses. An R7RS implementation should raise an error in those cases.
The procedures from this SRFI are exported from (SRFI
:### faceted-ports ⟨sublibrary⟩) (SRFI 97 convention)
or (SRFI ### ⟨sublibrary⟩) (R7RS convention). By default,
the procedures are exported from the main library.
(srfi ###)
make-port,
make-overlaid-port,
port-lock!
make-read-bytes-facet,
read-bytes-facet?,
make-write-bytes-facet,
write-bytes-facet?,
make-read-bytes-facet,
read-bytes-facet?,
make-write-bytes-facet,
write-bytes-facet?,
make-close-input-facet,
close-input-facet?,
make-close-output-facet,
close-output-facet?,
port-position-facet,
port-position-facet?,
make-u8-ready?-facet,
u8-ready?-facet?,
make-char-ready?-facet,
char-ready?-facet?,
make-flush-output-facet,
flush-output-facet?,
file-name-facet,
file-name-facet?,
port-file-name,
make-file-descriptor-facet,
file-descriptor-facet?,
port-file-descriptor,
make-port-facet-type
(srfi ### buffered)
make-line-buffered-input-port,
make-line-buffered-output-port,
make-block-buffered-input-port,
make-block-buffered-output-port
(srfi ### size-limited)
make-size-limited-input-port,
make-size-limited-output-port
(srfi ### transcoding)
make-transcoded-port,
make-transcoder-facet,
make-transcoder-facet?,
port-transcoder
(srfi ### compound)
make-input-output-port,
make-broadcast-port,
make-concatenated-port
(srfi ### r7rs)TODO
(srfi :### r6rs io)TODO
(srfi :### r6rs simple)TODO
Ports can be input ports (supporting procedures such as
write-bytevector or write-string), output
ports (supporting procedures such as read-bytevector
or read-string), or bidirectional ports (supporting both
kinds of procedures).
Ports can have either of two figure types: binary or textual.
The word ‘figure’ is used here in an application of its sense as a synonym for terms such as ‘character’, (alphabet) ‘symbol’, ‘sign’, ‘glyph’, etc. That is, ‘figure’ is to be understood as referring either an exact integer object in the range of 8-bit unsigned bytes, or for a character object, depending on which is appropriate for a particular port. This term was chosen because, as of the writing of this specification, it is not used in any other context in computing in a sense which would be likely to cause confusion with some other concept related to processing textual or binary data, nor is it used within Scheme to refer to any other existing type.
Each port has only one figure type. A bidirectional port must either be binary in both directions or textual in both directions.
Port locking is a concept introduced by the R6RS, which this SRFI generalizes and given a useful name. Port locking is used to control access to a port which is now ‘wrapped’ by another port: anything which holds a copy of the original port can no longer use it for further reading and writing; the process which wraps the new port receives a new, unlocked copy which it exposes access to through some new and presumably more featureful port interface. There is no general way to convert a locked port back into an unlocked port, although specific facet types could hypothetically offer this.
In R6RS, port locking is used in one way only, by the
transcoded-port procedure which wraps a binary port and
turns it into a textual port:
As a side effect, however, transcoded-port closes
binary-port in a special way that allows the new textual
port to continue to use the byte source or sink represented by
binary-port, even though binary-port itself is
closed and cannot be used by the input and output operations described
in this chapter.
— R6RS Standard Libraries,
pg. 34
This ‘special way’ of closing a port is what is referred to here as port locking.
Facets add functionality to ports. A port is simply a collection of facets. Each facet belongs to a specific facet type: new facet types can be defined, and each port can only have one facet of each type. Facet types are divided into several kinds: there are a small number of ‘essential facets’, of which every port must have exactly one or two, which provide basic reading and/or writing. ‘Utility facets’ provide additional functionality, such as closing ports, getting and setting port position, and flushing output. ‘Informational facets’ return the value of properties associated with each port. There are some restrictions on how essential facets can be combined. It is not possible to define new types of essential facets.
Underlying and overlaying of ports is a convenience concept built on the concept of port locking, and generalize the notion of transcoded ports in the R6RS. A new port can be overlaid on an existing port, which is locked. The existing port is called the underlying port. An overlaid port provides a higher-level view on the underlying port, or additional functionality such as buffering. Unlike in the R6RS, a binary port can be overlaid on a binary port, a textual port can be overlaid on a textual port, and a binary port can even be overlaid on a textual port.
When one port is overlaid on another, the informative and utility facets of the underlying port continue to be available on the overlaying port.
The generators and accumulators of SRFI 158 are used to access the buffers for reading and writing data instead of bytevectors and strings (and/or vectors of characters) as in the R6RS and SRFI 181.
For ports of either figure type, this brings a security advantage: a port’s internal reader or writer operation can cheaply be restricted from looking outside of its designated area in a (re-used) buffer and possibly seeing secret strings, leftover from some previous operation in the buffer, which it should not have access to.
For textual ports, this avoids using deprecated string mutation
operations for input ports, and allows implementations to use a flexible
and efficient representation of buffered characters, opaque to the
port implementation. In particular, the vector of characters approach
suggested by SRFI 181 wastes at least 4 bytes of storage per character
on 64-bit machines (increasing to 7 bytes per character for ASCII text),
and unnecessarily taxes the garbage collector with scanning a vector which
never contains collectable objects. A u32vector would alleviate these
problems, but generators and accumulators allow implementation to use
UTF-8 internally and don’t require constant use of the
char->integer and integer->char procedures.
However, the use of generators and accumulators means that ports must read and write to and from ports in order. Unlike in the R6RS and SRFI 181, random or reversed reading and writing is not possible. Given that the R6RS does not make any guarantees that would make out-of-order reading and writing from buffers useful anyway, this is not felt to be a great loss.
(make-port facet …)
procedure
Creates a new port which has the given facets. The new port does not overlay any port. The facets must all have distinct facet types. If any of the facet objects has previously been used to create another port, it is an assertion violation.
(make-overlaid-port
underlying-port
proc)
procedure
Create a new port overlaying the given underlying-port,
which must be an unlocked port. proc is called with an
unlocked copy of underlying-port as its argument, and
should return a list of facets which the newly-created port will
have. The list must not contain multiple facets of the same facet
type. Before make-overlaid-port returns but after it has
called proc, the underlying-port is locked.
Rationale: If constructing any of the facets raises an exception, underlying-port should not be left in a locked state where there is no corresponding unlocked port on which I/O operations can be resumed.
(port-lock!
port)
procedure
Locks the given port and returns an unlocked copy of the port. The port must not be locked already.
Locking a port prevents any operation which uses an essential or utility facet from being performed on that port. The unlocked copy must be used for all such future operations.
This is a low-level operation: the automatic locking of underlying
ports provided by make-overlaid-port should be used in the majority
of situations. However, there are circumstances where a port must
be created which has one or more ports conceptually underlying it,
in a way which the formal mechanism of underlying ports provided by
make-overlaid-port cannot support. (See the section
‘Port creation helper procedures’ for numerous examples of such ports.)
Every port must contain at least one of these facets. Furthermore,
a read-bytes-facet can be compounded together
with a write-bytes-facet or vice versa; and a
read-chars-facet can be compounded together with a
write-chars-facet or vice versa; but no other combination
of these essential facets is allowable. An assertion violation should be
signalled by make-port if none of these facets are present,
or if a combination of facets is used which violates this restriction.
The argument read-proc is a procedure of two arguments, count and accumulator.
When procedures such as read-bytevector are
called on a port which has a read-bytes-facet, the
read-proc of the facet will be called with a value of
count representing the number of bytes the implementation
wants to read from the port. The read-proc should call
the accumulator a maximum of count times,
each time with a single byte which is read from the port. If the port
reaches the EOF state before count bytes have been fed into
the accumulator, the accumulator should be
called once with the EOF object and then never again subsequently.
It is an assertion violation if the read-proc calls the accumulator fewer than count times when the final call was not with the EOF object as its argument. It is also an assertion violation if the read-proc calls the accumulator more than count times.
This facet is used by the
R7RS procedures
read-u8, read-bytevector,
read-bytevector!, and the
R6RS procedures
get-u8, get-bytevector-n,
get-bytevector-n!, get-bytevector-some,
and get-bytevector-all.
write-proc is a procedure of two arguments, count and generator:
When procedures such as write-bytevector are called on a
port which has a write-bytes-facet, the
write-proc is called with a value of count
representing the number of bytes the implementation wants to spit into
the port. The write-proc should call the generator
a maximum of count times; each call will return a new byte to
be written to the port. If an EOF state is to be sent to the port, the
generator will return the EOF object; all subsequent calls to
the generator will then also return the EOF object.
It is an assertion violation if the write-proc calls the generator fewer than count times when the final call did not return the EOF object. It is also an assertion violation if the write-proc calls the generator more than count times.
write-proc is a procedure of two arguments, count and generator:
When procedures such as write-bytevector are called on a port which
has a write-bytes-facet, the write-proc is called with a value of
count representing the number of characters the implementation wants
to spit into the port. The write-proc should call the generator
a maximum of count times; each call will return a new character to
be written to the port. If an EOF state is to be sent to the port, the
generator will return the EOF object; all subsequent calls to the
generator will then also return the EOF object.
It is an assertion violation if the write-proc calls the generator fewer than count times when the final call did not return the EOF object. It is also an assertion violation if the write-proc calls the generator more than count times.
Utility facets add additional I/O-related functionality to a port beyond basic reading and writing.
The close-proc argument is a procedure of no arguments. When
close-port or close-input-port is called
on a port with a close-input-facet, the
close-proc is called to close the port.
Note:
This is incompatible with R6RS and SRFI 181, where
bidirectional ports are closed in both directions at once. However,
it may be advantageous in some cases to close them at different times,
in particular with sockets on POSIX, which are bidirectional file
descriptors where
(close-input-port sock) should call shutdown(sock_fd, SHUT_RD),
(close-output-port sock) should call shutdown(sock_fd, SHUT_WR),
and (close-port sock) should call shutdown(sock_fd, SHUT_RDWR).
The close-proc argument is a procedure of no arguments. When
close-port or close-output-port is called on a
port with a close-output-facet, the close-proc
is called to close the port.
Note:
See close-input-facet.
The getter argument is a procedure of no arguments;
setter is a procedure of one argument, an exact nonnegative
integer. When port-position is called on a port that has a
port-position-facet, it returns the value of calling the
getter procedure; when set-port-position!
is called on a port that has a port-position-facet,
it calls setter with the new position that should be set.
If no setter is provided, the port will
support getting the port position but not setting it. The
port-has-set-port-position!? procedure from
R6RS/SRFI
192 will return #f on the port. If neither
getter nor setter is provided,
the port will not support getting nor setting the port
position, and the port-has-port-position? and
port-has-set-port-position!? procedures will both return
#f as if no port-position-facet were present:
this is useful when creating a port which overlays another port when
the additional abstraction added by the overlaying port causes port
positioning to no longer be meaningful.
The ready?-proc argument is a procedure of no arguments.
When u8-ready? is called on a port that has a
u8-ready?-facet, it returns the value of calling the
ready-proc.
Note: This is not compatible
with R6RS and SRFI 181, which simply
do not provide a way to check u8-ready? on a custom
port. (Indeed, R6RS does not provide u8-ready? nor
char-ready? at all.) A portable implementation of this
SRFI will have to re-export its own version of u8-ready?.
The ready?-proc argument is a procedure of no
arguments. When char-ready? is called on a port that has
a char-ready?-facet, it returns the value of calling the
ready-proc.
Note: See
u8-ready-facet.
The flush-proc argument is a procedure of no
arguments. When flush-output-port is called on a
port that has a flush-output-facet, it calls the
flush-proc. If flush-output-port is called
on a port which does not have a flush-output-facet,
it is a no-op.
Note:
R6RS does not provide
a way for custom ports to provide a flush-output-port
operation, but SRFI 181 does.
(make-file-name-facet
file-name)
procedure
(file-name-facet? obj)
procedure
(port-file-name port)
procedure
This facet allows the name of the file to be retrieved
from a port object which is reading from and/or writing
to that file. file-name must be a string. The
port-file-name procedure, called on a port which has a
file-name-facet facet, returns this string.
Note: This corresponds to the
id argument to the procedures to make custom ports
in R6RS and
SRFI 181.
In implementations built on top of R6RS,
the id will be set to the empty string if there is no
file-name-facet. In implementations built on top of
SRFI 181, the
id will be set to #f in this
case. (R6RS
requires that the id be a string;
SRFI 181
allows it to be any arbitrary object. Neither provides a means of
getting the file name back out of a port in any case, so the trick
suggested in the Implementation section will be needed to implement
the port-file-name procedure.)
(make-file-descriptor-facet
fd)
procedure
(file-descriptor-facet? obj)
procedure
(port-file-descriptor port)
procedure
On operating systems where open files, pipes, sockets, etc. are
represented by integer file descriptors, this facet allows the
number of the file descriptor to be retrieved from the port. The
fd argument must be an exact nonnegative integer. The
port-file-descriptor procedure, called on a port which
has a file-descriptor-facet, returns this integer.
Note: An important rule for
programs in high-level languages such as Scheme is ‘don’t
mess with file descriptors you aren’t given’. If
you are writing a program according to a certain protocol
(e.g. UCSPI)
which you know gives you certain ports at startup, you can
use those; you have been given them, and you can convert
them to ports with e.g. fd->port from SRFI
170. But once a port has been opened, a Scheme implementation will
feel that it has full control over buffering output to it, getting and
setting the port position, etc., and will not want or expect anything
else to go messing with the file descriptor underlying it. There
may be a limited set of operations which it would make sense to do
through an FFI on such a file descriptor (e.g.
fstat(2),
isatty(3)), but in general a file descriptor, once wrapped
in a Scheme port object, should only be accessed through that port,
to prevent undefined behaviour.
(make-transcoder-facet
transcoder)
(SRFI ### transcoding) procedure
(transcoder-facet? obj)
(SRFI ### transcoding) procedure
(port-transcoder port)
(SRFI ### transcoding) procedure
On transcoded ports, this facet allows the transcoder to be retrieved
from the port. The transcoder argument must be a transcoder. The
port-transcoder procedure, called on a port which has a
transcoder-facet, returns this transcoder.
(define-port-facet-type ⟨facet name⟩
(⟨constructor name⟩ ⟨field name1⟩ …)
⟨predicate name⟩
⟨accessor name⟩)syntax
Syntax: Each of ⟨facet name⟩, ⟨constructor name⟩, ⟨predicate name⟩, ⟨accessor name⟩ and all the ⟨field name⟩s are identifiers.
Semantics: Defines ⟨facet
name⟩ as a new type of port facet, distinct upon each
evaluation of a define-port-facet-type form from all
existing port facet types. The facet has as many fields as there
are ⟨field name⟩s. The ⟨facet name⟩
may be bound as a syntax keyword or to some other unspecified type of
runtime value.
The ⟨constructor name⟩ is bound to a constructor procedure of as many arguments as there are ⟨field name⟩s. When called, this procedure returns a newly-allocated port facet of the created type whose fields are set to the respective values of the arguments.
The ⟨predicate name⟩ is bound to a predicate procedure
of one argument. It returns #t if the argument is a port
facet of the newly-created type, or #f if the argument
is any other object.
The ⟨accessor name⟩ is bound to an accessor procedure of two mandatory arguments, port and facet-proc, and one optional argument, no-facet-proc. When called on a given port which has a facet of the newly-created type, the accessor procedure tail-calls the procedure facet-proc with the arguments that were given when the constructor procedure was called. When called on a port which does not have a facet of the created type, the accessor procedure tail-calls the procedure no-facet-proc with no arguments; if the no-facet-proc argument was not given, it signals an assertion violation.
These procedures provide R6RS-compatible means of controlling I/O buffering, equivalents of some of Common Lisp’s built-in stream types in Scheme, and some other useful utilities.
(make-line-buffered-input-port
underlying-port)
(SRFI ### buffered) procedure
Creates a new input port, overlaying the input port underlying-port, which reads at least a whole ‘line’ full of figures from the underlying port into an internal buffer in the store in one go. The returned input port will feed its consumer data from this buffer before going to the underlying-port to obtain more data.
A ‘line’ is a sequence of figures of a size determined by the figures themselves. A line ends when a particular figure is seen. The internal buffer may extend beyond the current line.
If the underyling-port is a binary port, the bytes
#x0, #xA, and #xD, and the
sequence #xD #xA, are considered line terminators. If
it is a textual port, the characters #\newline,
#\return, and the sequence "\r\n" are
considered line terminators.
Note to implementers: The returned port should wrap port position setting on the underlying-port so that the input buffer is cleared before the position is really set.
If underlying-port is bidirectional, the returned port will be bidirectional and will simply pass through information from the output side without buffering.
(make-line-buffered-output-port
underlying-port)
(SRFI ### buffered) procedure
Creates a new output port, overlaying the output port
underlying-port, which stores a whole ‘line’ full
of the figures which are written to it to a buffer in the store,
before sending the whole line to the underlying port in one go. The
flush-output-port procedure is automatically called on
the underlying port after each line is written to it. Additionally,
the flush-output-port procedure called on such a port can
be used to send written figures to the underlying port and empty the
buffer before a whole line has been written to the returned port.
Lines are defined as for make-line-buffered-input-port.
If underlying-port is bidirectional, the returned port will be bidirectional and will simply pass through information to the input side without buffering.
(make-block-buffered-input-port
underlying-port
[buffer-size])
(SRFI ### buffered) procedure
Creates a new input port, overlaying the input port underlying-port, which reads up to buffer-size figures from the underlying port into an internal buffer in the store in one go. The returned input port will feed its consumer data from this buffer before going to the underlying-port to obtain more data.
Note to implementers: The returned port should wrap port position setting on the underlying-port so that the input buffer is cleared before the position is really set.
If buffer-size is not given, an unspecified default size is used. For textual ports, the number of characters stored in the buffer may be smaller than the buffer-size.
If underlying-port is bidirectional, the returned port will be bidirectional and will simply pass through information from the output side without buffering.
(make-block-buffered-output-port
underlying-port
[buffer-size])
(SRFI ### buffered) procedure
Creates a new output port, overlaying the output
port underlying-port, which stores up to
buffer-size figures (a block of figures) written to it
to a buffer in the store, before sending all of those figures to the
underlying port in one go when that many figures have been written. The
flush-output-port procedure is automatically called on
the underlying port after each block is written to it. Additionally,
the flush-output-port procedure called on such a port can
be used to send written figures to the underlying port and empty the
buffer before a whole block has been written to the returned port.
If buffer-size is not given, an unspecified default size is used. For textual ports, the number of characters stored in the buffer may be smaller than the buffer-size.
If underlying-port is bidirectional, the returned port will be bidirectional and will simply pass through information to the input side without buffering.
(make-size-limited-input-port
underlying-port
limit)
(SRFI ### size-limited) procedure
The limit argument must be an exact nonnegative integer.
Creates a new input port overlaying the underlying-port. After limit figures have been read from the port, further attempts to read from the port will signal an assertion violation.
This procedure provides a generalized solution to the security
problems of reading limitless amounts of data from untrusted
sources, and is intended to succeed ad hoc solutions such as
the json-number-of-character-limit parameter from
SRFI 180.
(make-size-limited-output-port
underlying-port
limit)
(SRFI ### size-limited) procedure
The limit argument must be an exact nonnegative integer.
Creates a new output port overlaying the underlying-port. After limit figures have been written to the port, further attempts to write to the port will signal an assertion violation.
(make-transcoded-port
underlying-port
transcoder)
(SRFI ### transcoding) procedure
The underlying-port procedure must be a binary port.
Returns a new textual port which overlays the binary port underlying-port. Input to and/or output from the underlying-port will be transcoded using the given transcoder.
(make-input/output-port
input-port
output-port)
(SRFI ### compound) procedure
The input-port and output-port must have the same figure type.
The input-port and output-port will be locked by this operation, but the returned port does not formally overlay either the input-port nor the output-port, so any other facets of those ports will not be visible on the returned port.
(make-broadcast-port
output-port
…)
(SRFI ### compound) procedure
All of the output-ports must have the same figure type.
Returns a new output port which writes anything written to it to all of the given output-ports in sequence.
All of the output-ports will be locked by this operation, but the returned port does not formally overlay any of the output-ports, so any other facets of those ports will not be visible on the returned port.
(make-concatenated-port
input-port
…)
(SRFI ### compound) procedure
All of the input-ports must have the same figure type.
Returns a new input port which reads from each of the given input-ports in sequence.
All of the input-ports will be locked by this operation, but the returned port does not formally overlay any of the input-ports, so any other facets of those ports will not be visible on the returned port.
For implementations that support the
R7RS,
the library (SRFI ### r7rs) exports all procedures defined
in the R7RS § 6.13, except that they
work with faceted ports. Implementations are encouraged to make their default
port objects faceted ports, where the identifiers exported from this library
are the same as the ones exported from the standard libraries.
For implementations that support the
R6RS,
the libraries (SRFI :### faceted-ports r6rs port) and
(SRFI :### faceted-ports r6rs simple)
export all identifiers defined in the
R6RS Standard Libraries,
Chapter 8, except that they work with faceted ports.
Implementations are encouraged to make their default
port objects faceted ports, where the identifiers exported from this library
are the same as the ones exported from the standard libraries.
Except for the minor problems noted in some entries, it is possible to implement this proposal on top of R6RS or SRFI 181. Informative facets which are not part of the specification of custom ports in R6RS/SRFI 181 can be implemented by storing them in a hash table (weak-keyed, ideally, so that ports are still collectable).
An implementation for CHICKEN 6 is available in the SRFI repository. Note that this is a re-implementation of Scheme ports and hence does not interoperate with the standard Scheme port procedures.
Dimitris Vyzovitis’s design for sources and sinks in Gerbil was an inspiration for this proposal. The design of I/O in Python was another source of inspiration. Jani Juhani Sinervo’s port properties proposal suggested the inclusion of the informative facets.
Daphne Preston-Kendal wrote the whole SRFI. Peter McGoron wrote a CHICKEN implementation and edited the text of the SRFI into HTML.
© 2026 Daphne Preston-Kendal, Peter McGoron.
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.