[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/stream f48552c066 1/7: Change 'stream.el' to use struct
From: |
Stefan Monnier |
Subject: |
[elpa] externals/stream f48552c066 1/7: Change 'stream.el' to use structures instead of cons cells. |
Date: |
Sun, 17 Nov 2024 21:15:42 -0500 (EST) |
branch: externals/stream
commit f48552c0668c36036ebeb6a3c87269049a2e61d9
Author: Earl Hyatt <okamsn@protonmail.com>
Commit: Stefan Monnier <monnier@iro.umontreal.ca>
Change 'stream.el' to use structures instead of cons cells.
* stream.el (stream): Define the structure using 'cl-defstruct'. Set
safety to 0 using 'cl-declaim' to avoid checking the type of the argument
to 'stream--force' multiple times. Instead, explicitly check a single time
in 'stream--force', which must be used inside the public functions anyway.
* stream.el (stream-make): Change to use new structure constructor
'stream--make-stream'.
* stream.el (streamp): Delete existing definition to avoid conflicts
with definition generated for structures.
* stream.el (stream--force, stream-first, stream-rest)
(stream-empty, stream-empty-p): Redefine to use structure slots.
* stream.el (stream--fresh-identifier, stream--evald-identifier):
Remove now unused definitions.
* stream.el (stream--generalizer, cl-generic-generalizers): Remove
these specializers from the old, cons-based implementation.
---
stream.el | 138 +++++++++++++++++++++++++++++++++++++++++---------------------
1 file changed, 92 insertions(+), 46 deletions(-)
diff --git a/stream.el b/stream.el
index 7135ee0b40..2439f958cb 100644
--- a/stream.el
+++ b/stream.el
@@ -63,33 +63,94 @@
;;; Code:
-(eval-when-compile (require 'cl-lib))
+(eval-when-compile
+ (require 'cl-lib)
+ ;; Set safety to 0 to avoid checking the type of the argument multiple times
+ ;; within `stream--force', which is used frequently.
+ (cl-declaim (optimize (safety 0))))
+
(require 'seq)
-(eval-and-compile
- (defconst stream--fresh-identifier '--stream-fresh--
- "Symbol internally used to streams whose head was not evaluated.")
- (defconst stream--evald-identifier '--stream-evald--
- "Symbol internally used to streams whose head was evaluated."))
+(cl-defstruct (stream (:constructor stream--make-stream)
+ (:conc-name stream--)
+ (:predicate streamp)
+ :named)
+
+ "A lazily evaluated sequence, compatible with the `seq' library's functions."
+
+ (evaluated
+ nil
+ :type boolean
+ :documentation "Whether the head and tail of the stream are accessible.
+
+This value is set to t via the function `stream--force' after it
+calls the updater function.")
+
+ (first
+ nil
+ :type t
+ :documentation "The first element of the stream.")
+
+ (rest
+ nil
+ :type (or stream null)
+ :documentation "The rest of the stream, which is itself a stream.")
+
+ (empty
+ nil
+ :type boolean
+ :documentation "Whether the evaluated stream is empty.
+
+A stream is empty if the updater function returns nil when
+`stream--force' evaluates the stream.")
+
+ (updater
+ nil
+ :type (or function null)
+ :documentation "Function that returns the head and tail of the stream when
called.
+
+The updater function returns the head and tail in a cons cell.
+If it returns nil, then the stream is empty and `empty' is
+set to t. After this function is called, assuming no errors were signaled,
+`evaluated' is set to t.
+
+In the case of the canonical empty stream (see the variable `stream-empty'),
+this slot is nil."))
(defmacro stream-make (&rest body)
"Return a stream built from BODY.
-BODY must return nil or a cons cell whose cdr is itself a
-stream."
+
+BODY must return a cons cell whose car would be the head of a
+stream and whose cdr would be the tail of a stream. The cdr must
+be a stream itself in order to be a valid tail. Alternatively,
+BODY may return nil, in which case the stream is marked empty
+when the stream is evaluated."
(declare (debug t))
- `(cons ',stream--fresh-identifier (lambda () ,@body)))
+ `(stream--make-stream :evaluated nil
+ :updater (lambda () ,@body)))
(defun stream--force (stream)
- "Evaluate and return the first cons cell of STREAM.
-That value is the one passed to `stream-make'."
- (cond
- ((eq (car-safe stream) stream--evald-identifier)
- (cdr stream))
- ((eq (car-safe stream) stream--fresh-identifier)
- (prog1 (setf (cdr stream) (funcall (cdr stream)))
- ;; identifier is only updated when forcing didn't exit nonlocally
- (setf (car stream) stream--evald-identifier)))
- (t (signal 'wrong-type-argument (list 'streamp stream)))))
+ "Evaluate and return the STREAM.
+
+If the output of the updater function is nil, then STREAM is
+marked as empty. Otherwise, the output of the updater function
+is used to set the head and the tail of the stream."
+ ;; Check explicitly so that we can avoid checking
+ ;; in accessors by setting safety to 0 via `cl-declaim'.
+ (cl-check-type stream stream)
+ (if (stream--evaluated stream)
+ stream
+ (pcase (funcall (stream--updater stream))
+ (`(,head . ,tail)
+ (setf (stream--first stream) head
+ (stream--rest stream) tail))
+ ((pred null)
+ (setf (stream--empty stream) t))
+ (bad-output
+ (error "Bad output from stream updater: %S"
+ bad-output)))
+ (setf (stream--evaluated stream) t)
+ stream))
(defmacro stream-cons (first rest)
"Return a stream built from the cons of FIRST and REST.
@@ -190,13 +251,12 @@ range is infinite."
(stream-range (+ start step) end step))))
-(defun streamp (stream)
- "Return non-nil if STREAM is a stream, nil otherwise."
- (let ((car (car-safe stream)))
- (or (eq car stream--fresh-identifier)
- (eq car stream--evald-identifier))))
-
-(defconst stream-empty (cons stream--evald-identifier nil)
+(defconst stream-empty
+ (stream--make-stream :evaluated t
+ :first nil
+ :rest nil
+ :empty t
+ :updater nil)
"The empty stream.")
(defun stream-empty ()
@@ -205,17 +265,19 @@ range is infinite."
(defun stream-empty-p (stream)
"Return non-nil if STREAM is empty, nil otherwise."
- (null (cdr (stream--force stream))))
+ (stream--empty (stream--force stream)))
(defun stream-first (stream)
"Return the first element of STREAM.
Return nil if STREAM is empty."
- (car (stream--force stream)))
+ (stream--first (stream--force stream)))
(defun stream-rest (stream)
"Return a stream of all but the first element of STREAM."
- (or (cdr (stream--force stream))
- (stream-empty)))
+ (setq stream (stream--force stream))
+ (if (stream--empty stream)
+ (stream-empty)
+ (stream--rest stream)))
(defun stream-append (&rest streams)
"Concatenate the STREAMS.
@@ -242,22 +304,6 @@ elements in the STREAMS in order."
(setq ,stream (stream-rest ,stream))))
-;;; cl-generic support for streams
-
-(cl-generic-define-generalizer stream--generalizer
- 11
- (lambda (name &rest _)
- `(when (streamp ,name)
- 'stream))
- (lambda (tag &rest _)
- (when (eq tag 'stream)
- '(stream))))
-
-(cl-defmethod cl-generic-generalizers ((_specializer (eql stream)))
- "Support for `stream' specializers."
- (list stream--generalizer))
-
-
;;; Implementation of seq.el generic functions
(cl-defmethod seqp ((_stream stream))
- [elpa] externals/stream updated (5c0ffd088f -> 1e3c1436cc), Stefan Monnier, 2024/11/17
- [elpa] externals/stream b2f650a8a6 4/7: Add an implementation of `seq-concatenate` for streams., Stefan Monnier, 2024/11/17
- [elpa] externals/stream 0ad03f585b 5/7: Add test for delayed evaluation for seq-drop-while for streams., Stefan Monnier, 2024/11/17
- [elpa] externals/stream daa19594e7 3/7: Add generalized variables for streams that error when used., Stefan Monnier, 2024/11/17
- [elpa] externals/stream 1e3c1436cc 7/7: Shorten the documentation strings of seq-take-while and seq-drop-while., Stefan Monnier, 2024/11/17
- [elpa] externals/stream bd614d022b 2/7: Add more efficient method for making streams from arrays., Stefan Monnier, 2024/11/17
- [elpa] externals/stream f48552c066 1/7: Change 'stream.el' to use structures instead of cons cells.,
Stefan Monnier <=
- [elpa] externals/stream 202db1e4b5 6/7: Fix the quoting in the documentation of `seq-mapn` for streams., Stefan Monnier, 2024/11/17