by Wolfgang Corcoran-Mathe
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 srfi-271@nospamsrfi.schemers.org. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.
This SRFI proposes a pattern of libraries for binary input ports that produce random bytes. Libraries are divided into “randomized” and “determinized” categories to address different uses of random data. The design leaves the details of random number generation to the implementer and the transformation of bytes to other types (floats, etc.) to higher-level libraries. A mechanism for saving random-port states and propagating them to new ports is also provided.
None at present.
As a foundation for random-number generation, the well-known (and excellent) SRFI 27 includes both too little and too much. On the one hand, it makes no distinction between sources of cryptographic-quality pseudorandom data and sources of the deterministic pseudorandom data sometimes needed for testing and other purposes. On the other, it includes ways of parameterizing library procedures over user-defined random sources, thus adding avoidable complexity to the specification.
In this SRFI I take a different tack and try to describe a framework for random-number generation that adds as little as necessary to the language. This SRFI:
Uses binary input ports, rather than a novel type, for random sources.
Uses the Scheme library system — and only the Scheme library system — to distinguish between randomized* sources and determinized sources, as well as between different kinds of randomized and determinized sources.
Specifies no procedures for drawing random data. The
random-byte-seeking user is pointed in the direction of
read-u8 and read-bytevector. Those
in search of other kinds of random data will need an additional
library; see Usage and
compatibility for some ideas.
*: Throughout this SRFI, I use the term “randomized” to denote sources that should produce “cryptographic-quality” random data. See the Terminology section for a precise — or at least longer — definition.
Everyone agrees that it is sometimes useful to generate
the same random sequence several times, e.g. on each run of
a test suite. Whether to allow random number generators to
be “seeded”, and what forms those seeds should take, is
controversial. One
source may require more seed data than another,
so we can’t take the one-size-fits-all approach of C’s
srand (C23
section 7.24.3.2) and other obsolete systems.
This SRFI takes an approach similar to that of SRFI 27 in
allowing random-port states to be manipulated as
semi-opaque objects (i.e. objects of unspecified type with
external representations).
Whereas SRFI 27 allows the user to
get the state of any source, this SRFI only
exposes the states of determinized random ports.
The states of randomized ports cannot in general
be represented by Scheme values. (Consider a random port
backed by a hardware random-number generator, for example.)
Random-port states can be compared for equality with
random-port-state=?, a feature not explicitly
provided by SRFI 27.
Random ports are seeded only when they are created. The
make-random-port constructor accepts an optional state
argument which, if present, is used as the initial state for the new
port. SRFI 27’s “reseeding” interface
(random-source-state-set!, etc.) has been discarded as
unnecessary, since it is easy enough to create a new random port
with a desired state. Random ports can also be seeded with data
from other random ports. This eliminates the need for awkward
initialization procedures like SRFI 27’s
random-source-randomize!. Given a single good-quality
randomized port, it is easy to create determinized ports with
random initial states.
Input ports have been in the Scheme Reports since the early days
(the RRRS). Thus they fit easily into
existing programs and come with a wealth of standard
inspection procedures (u8-ready?, etc.). In
contrast, an opaque type like SRFI 27's random-source needs a
fair amount of wheel-reinvention — procedures like the utterly
elementary read-u8 have to be specified anew.
SRFI 194's generator-based
interface is another option.
Generators (see
SRFI 158),
however, are no more than nullary Scheme procedures, and are perhaps
too simple for the present purpose.
They mimic input ports in several ways, but there are important
port operations that they cannot support in current Scheme.
(For example, it isn’t possible to write peek or
ready? procedures for generators.) Some have
suggested adding general language features to address these
weaknesses. We can look forward to a future Scheme in which
ports and generators have all of the same features and compete,
pointlessly, for control of every I/O domain.
Back to the point. Some of the standard port operations that are
not available for SRFI 158 generators are useful for random ports.
For example, some random sources (traditionally, though
unfortunately) may block until sufficient entropy is available.
With random ports, applying u8-ready? should be enough
to alert you of this situation. Port positioning (described in
R6RS and SRFI 192)
may also be useful when working with random-number generators that
allow seeking through their output stream. Transcoders, another
R6RS port feature (also described in
SRFI 181), could
be used to make ports that produce random strings. Programmers
will no doubt discover other ways to use standard port features
with random ports.
The words “must”, “may”, etc., though not upper-cased in this SRFI, should be interpreted according to RFC 2119.
A random port is a binary input port which produces random data. Reading from a random port must not produce an end-of-file object.
A randomized random port should produce data from either a high-entropy external source (e.g. the operating system’s entropy pool or a hardware random-number generator) or from a deterministic generation algorithm which has been seeded from such a source. The sequence produced by a randomized port should be unpredictable, even by an adversary who can read past outputs and simulate generation of outputs on demand.
A determinized random port must produce data
generated by a fully
deterministic algorithm. Two determinized random
ports from the same library and with the same states (in the
sense of
random-port-state=?)
must produce the same sequence of pseudorandom numbers.
A random-port state is an object representing
the state of a random port (or, more precisely, of the
random-number generator embodied by a random port). A type
of random-port states must have an external representation and must
support write/read invariance: a random-port
state that has been returned as part of a literal expression,
or read using the read procedure, and subsequently
written out using the write procedure, will read
back in as the same state in the sense of the
random-port-state=?
procedure associated with that state type.
A Scheme implementation conforming to this specification provides at least the following R7RS libraries (R7RS section 5.6):
(srfi 271)(srfi 271 randomized)(srfi 271 determinized)The usual library-name-format variants for these libraries may also be provided:
(srfi :271 randomized), etc.(srfi srfi-271 randomized), etc.(srfi 271) is an alias for
(srfi 271 randomized). (Rationale:
Rushed or incautious programmers who just want random numbers
get those most likely to be of cryptographic quality. See
Taylor Campbell’s
comments on preferable defaults.)
The (srfi 271 randomized) library and any
libraries with the (srfi 271 randomized)
prefix provide randomized random ports. Similarly,
(srfi 271 determinized) and any libraries
with the (srfi 271 determinized) prefix provide
determinized random ports.
Both the randomized and determinized
libraries must export the make-random-port
constructor. determinized libraries must provide
an additional interface
for creating random ports with predetermined initial
states.
Implementers are encouraged to provide additional libraries
under
the (srfi 271) prefix. If they
do, they must ensure that each library follows the protocol
detailed above.
A list of example
library names is provided below.
This section is informational.
Each library is tied to a specific generation mechanism; import the library that provides the one you want. There is no other mechanism in this specification for choosing between random-number generators.
No means is provided here for getting floats, arbitrary integers, or other data out of random ports. This is left to a higher-level library, similar to SRFI 194, which can transform the binary output of a random port into values of other types.
Implementations might provide both randomized and determinized libraries for the same random-number generator. For security-relevant applications, it may be undesirable to allow ports to be initialized from known, possibly low-entropy, states. The randomized library interface, which does not allow this, could be seen as slightly safer.
If this proposal is incorporated into the R7RS, I suggest
that aliases under the library prefix (scheme random)
(e.g. (scheme random randomized),
(scheme random determinized), etc.) be provided.
Each library with the (srfi 271) prefix exports
a make-random-port procedure.
(make-random-port) → binary-input-port
Returns a new random port.
determinized librariesThe following procedures are exported by all libraries with
the (srfi 271 determinized) prefix.
(make-random-port [initializer]) →
binary-input-port
As above, but the optional initializer argument is used as follows:
If initializer is a random-port state in
the sense of random-port-state?, then
the initial state of the random port returned by
make-random-port is the same as that
represented by initializer.
If initializer is a binary input port, then
the returned port’s state is constructed from data read
from initializer. The exact process of
constructing a random-port state from arbitrary data is
unspecified, but if two initializer ports
produce the same sequence, then the random ports
initialized from them must have the same initial
states (in the sense of
random-port-state=?).
An error satisfying
random-port-initialization-error? is
signaled if make-random-port cannot obtain
enough data from initializer to initialize a
random-port state, or if the data obtained is unsuitable.
Implementations should allow initialization
from arbitrary infinite sequences. An exception is the
all-zeros sequence (0, 0, …), which is known to cause
problems for many algorithms. Implementations may allow
initialization by the all-zeros sequence.
If initializer is neither a random-port state nor a binary input port, then the behavior is undefined.
Calling make-random-port without an
initializer is equivalent to
(call-with-port (randomized:make-random-port)
make-random-port)
where randomized:make-random-port denotes the
make-random-port procedure exported by
(srfi 271 randomized).
The difference between initializing a random port with a random port and initializing it with a random-port state is a little subtle. The following examples are not equivalent:
(make-random-port rport)
(make-random-port
(random-port-state rport))
The first example returns a port whose initial state is constructed from (random) data read from the random port rport, while the second returns a port whose initial state is the same as rport’s state.
(random-port? obj) → boolean
Returns #t if obj is a random port
in the sense that it can be safely passed to procedures from
the same library which accept a
random-port argument.
Returns #f otherwise.
Rationale: random-port? may be used to “guard”
calls to procedures like random-port-state so as
to avoid the undefined behavior associated with ill-typed
procedure arguments.
If the random-port? procedure exported
by one random-port library is applied to a random port
from another library, the return value is unspecified but
will always be #t or #f.
Rationale: Some implementations may introduce a
type of random ports which encompasses all of the ports
created by (srfi 271) libraries. Others may
choose to make each library’s port type disjoint from all
other types. The specification of random-port?
is meant to allow both approaches.
(random-port-state random-port) →
state
Returns a random-port state representing the state of
random-port. If the value returned by
random-port-state is mutated, the behavior is
undefined.
If random-port does not satisfy
random-port?, then the behavior is
undefined.
(random-port-state? obj) →
boolean
Returns #t if obj is a valid
random-port state in the context of the exporting library.
Returns #f otherwise.
If the random-port-state? procedure exported
by one random-port library is applied to a random-port state
from another library, the return value is unspecified but
will always be #t or #f.
(random-port-state=?
state1 state2
state3 …) → boolean
Returns #t if all the states are
equal, and #f otherwise. If
random-port-state=? is true when applied to two
states s1 and s2,
then random ports returned by
(make-random-port s1) and
(make-random-port s2) must
produce the same byte sequence.
If any of the states does not satisfy
random-port-state?, then the behavior is
undefined.
(random-port-initialization-error?
obj) → boolean
Returns #t if obj is an object
signaled by make-random-port in the circumstances
described above, and #f otherwise.
This section is informational.
(import (scheme base)
(scheme read)
(scheme write)
(srfi 271 determinized)
(prefix (srfi 271 randomized) r:))
(define rport (make-random-port))
;; Explicit initialization using another port.
(let ((rport2 (make-random-port (r:make-random-port))))
(equal? (read-bytevector 100 rport)
(read-bytevector 100 rport2)))
;; ⇒ #f with a high probability
(define rport-state (random-port-state rport))
;; Initialization with a random-port state.
(define rport3 (make-random-port rport-state))
(equal? (read-bytevector 100 rport)
(read-bytevector 100 rport3))
;; ⇒ #t
(random-port-state=? (random-port-state rport)
(random-port-state rport3))
;; ⇒ #t
;; Serializing a random-port state.
(let ((output (open-output-string)))
(write rport-state output)
(let* ((str (get-output-string output))
(input (open-input-string str))
(serialized-state (read input)))
(random-port-state=? rport-state serialized-state)))
;; ⇒ #t
This is a revised version of an example provided by Peter McGoron.
This section is informational.
Names are based on Sebastiano Vigna’s PRNG page and Wikipedia.
randomized libraries(srfi 271 randomized urandom)
Library which draws random data from the
/dev/urandom file, which is available on most
UNIX-like systems.
(srfi 271 randomized hwrng)
Linux-only library which draws random data from a hardware
random-number generator via the /dev/hwrng
file.
determinized libraries(srfi 271 determinized chacha8)(srfi 271 determinized chacha12)(srfi 271 determinized chacha20)Libraries using ChaCha stream ciphers.
(srfi 271 determinized xorshift)
Library using some kind of xorshift generator. These are often very fast, but the simplest xorshift implementations may not be very reliable. It might be appropriate to use such a library for toy or educational programs.
(srfi 271 determinized xoshiro256++)
(srfi 271 determinized xoshiro256**)
xoshiro libraries. The authors of the xoshiro generators claim they are faster and more reliable than xorshift generators.
(srfi 271 determinized xoshiro-kawai)Reserved for use by the Gauche Scheme implementation.
(srfi 271 determinized mwc256)Library for a 256-bit multiply-with-carry (MWC) source. MWC generators tend to have extremely long periods, ranging from around 260 to 22,000,000.
This section is informational.
Since this SRFI does not provide procedures for getting generally useful data types (integers within arbitrary ranges, reals, etc.) out of random ports, a higher-level interface is needed to make random-port programming congenial. Such an interface should work with random ports from any SRFI 271 library, to avoid multiplying libraries unnecessarily.
For reasons detailed below, SRFI 27 would make a poor
interface to random ports. SRFI 194, while specified in
terms of SRFI 27, is a better fit, and it provides more
random-data generators than most programmers will ever need.
Wrapping random ports in SRFI 158 generators, however, would
hide several useful properties of input ports (see the
Rationale for details). My preference
(which may be reified in a future SRFI) is a library of bland
port-reading procedures, e.g. read-random-integer,
read-random-real, etc., which would do the (sometimes
tricky) job of extracting random values without
hiding the ports themselves.
That being said, SRFI 27 remains the best-known random-number interface in Scheme, and implementers may want to provide both SRFI 27 and 271 interfaces. A discussion of SRFI 27 / 271 compatibility issues follows.
SRFI 27’s random sources are similar to random ports, but they support a few operations that cannot be directly implemented in terms of this SRFI’s forms:
random-source?Why not: Random ports are input ports, but SRFI 27 requires that random sources be of a disjoint type.
random-source-state-set!random-source-randomize!random-source-pseudo-randomize!Why not: This SRFI does not provide operations for directly changing the state of an existing random port.
SRFI 27’s type-disjointness requirement could be satisfied by wrapping a random port in a record type. State mutation could then be implemented by replacing the port within the random-source wrapper with a newly-created port (since this SRFI only allows states to be set during port creation).
A portable implementation of random ports is currently impossible. A sample implementation of this SRFI developed for Gauche (running on POSIX systems) is available in the SRFI repository.
Thanks to John W. Cowan and Peter McGoron for helping to chip away everything in this SRFI that did not look like an elephant.
Thanks to Taylor Campbell and Alaric Snell-Pym for suggestions and elucidations. In particular, the definition of a randomized port was adapted from Alaric’s description of the desirable properties of such a source; any inaccuracies in the SRFI’s version are my own doing.
Thanks to Zhu Zihao for pointing out the connection between port positions and seekable random-number generators.
Thanks to the members of the R7RS Second Working Group for advice and discussion.
Thanks to those who provided feedback via the SRFI mailing list or on the #scheme IRC channel.
Of course, none of this should be taken to suggest that any of the people mentioned above endorse this SRFI.
S. Bradner, Key words for use in RFCs to Indicate Requirement Levels (RFC 2119), 1997. https://datatracker.ietf.org/doc/html/rfc2119
William Clinger, ed., Revised Revised Report on Scheme (RRRS), MIT Artificial Intelligence Memo No. 848, August 1985.
Michael Sperber, R. Kent Dybvig, Matthew Flatt, & Anton van Straaten, eds., Revised6 Report on the Algorithmic Language Scheme (R6RS), 2007. https://r6rs.org.
Alex Shinn, John Cowan, & Arthur A. Gleckler, eds., Revised7 Report on the Algorithmic Language Scheme (R7RS Small), 2013. https://small.r7rs.org
Sebastiano Vigna, A PRNG shootout, 2026. https://prng.di.unimi.it
ISO/IEC 9899:2024. Programming languages — C (C23). ISO/IEC, 2024.
Alaric Snell-Pym. Some thoughts on random number generation. Communication to the R7RS WG2 mailing list, March 2026.
Sebastian Egner. SRFI 27: Sources of random bits. https://srfi.schemers.org/srfi-27/, 2002.
Shiro Kawai, John Cowan, and Thomas Gilray. SRFI 158: Generators and Accumulators. https://srfi.schemers.org/srfi-158/, 2017.
John Cowan, SRFI 181: Custom ports (including transcoded ports). https://srfi.schemers.org/srfi-181/, 2020.
John Cowan and Shiro Kawai, SRFI 192: Port Positioning. https://srfi.schemers.org/srfi-192/, 2020.
Shiro Kawai, Arvydas Silanskas, Linas Vepštas, and John Cowan. SRFI 194: Random data generators. https://srfi.schemers.org/srfi-194/, 2020.
Taylor Campbell. Personal communication, 2025. Available on the R7RS issue tracker.
© 2026 Wolfgang Corcoran-Mathe.
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.