From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Philip Kaludercic Newsgroups: gmane.emacs.bugs Subject: bug#73431: Add `setf` support for `stream.el` in ELPA Date: Sat, 05 Oct 2024 09:14:01 +0000 Message-ID: <87a5fic3om.fsf@posteo.net> References: <827cc7fc-10be-4b93-bd67-f275193e5d84@protonmail.com> <068ecfc3-b452-49a8-89bb-f42012aea5d4@protonmail.com> <6caa1395-a3b2-4e70-b905-1cbfee0f92bd@protonmail.com> <87y138hjij.fsf@web.de> <875xqa8fby.fsf@posteo.net> <932d7782-5685-4a43-b34c-fc0bb3a958e4@protonmail.com> <87zfnk6ydj.fsf@posteo.net> <9db72ab8-604e-451d-b9a0-74fa5b3fa81f@protonmail.com> Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="9129"; mail-complaints-to="usenet@ciao.gmane.io" Cc: michael_heerdegen@web.de, 73431@debbugs.gnu.org, nicolas@petton.fr, monnier@iro.umontreal.ca To: Okamsn Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Sat Oct 05 11:15:29 2024 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1sx0sq-0002Dt-Ms for geb-bug-gnu-emacs@m.gmane-mx.org; Sat, 05 Oct 2024 11:15:29 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sx0sO-0007QC-H4; Sat, 05 Oct 2024 05:15:00 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sx0sM-0007Ph-Ip for bug-gnu-emacs@gnu.org; Sat, 05 Oct 2024 05:14:58 -0400 Original-Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1sx0sM-0002vI-Ak for bug-gnu-emacs@gnu.org; Sat, 05 Oct 2024 05:14:58 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debbugs.gnu.org; s=debbugs-gnu-org; h=MIME-Version:Date:References:In-Reply-To:From:To:Subject; bh=VGX/UoYJhIf3doThBdyGiTYD/Zg6QlkapIo7TQQ30yI=; b=nYyWRNynbxbMWENFF0mQ8xZbGSLULMh+zMB5LuvW4/moJTR6p5nlTcj4LhaI8+x35NsxB9BO/i1DzILRW3Mdv//BIoh9kVjdaJOlOyrYrKkSoVojjnm7H4Jq/RES0uqe0b9d92ul3OH3wit/0rAcJOeL1u4/4Dnxgb2Zgj7QpM9OEkpqx5LUlJU6aOcnR/AJgtchoyZ96pFex/VUJ0L9KvfMMC0XrLvm6mXN/NkG2lyp1Y67hyYdgKaU/M+hrNvSw72+wrfR4fuDer62Err/FbSDdcu7QPaRZB19CjdoueFKPVkGnmSvFc9ekgLMREWO9Sq0irYIe/1MF/RYoBuqCw==; Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1sx0sQ-0001A0-F4 for bug-gnu-emacs@gnu.org; Sat, 05 Oct 2024 05:15:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Philip Kaludercic Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sat, 05 Oct 2024 09:15:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 73431 X-GNU-PR-Package: emacs X-Debbugs-Original-Cc: Michael Heerdegen , "Okamsn via \"Bug reports for GNU Emacs, the Swiss army knife of text editors\"" , Nicolas Petton , Stefan Monnier , 73431@debbugs.gnu.org Original-Received: via spool by submit@debbugs.gnu.org id=B.17281196634397 (code B ref -1); Sat, 05 Oct 2024 09:15:02 +0000 Original-Received: (at submit) by debbugs.gnu.org; 5 Oct 2024 09:14:23 +0000 Original-Received: from localhost ([127.0.0.1]:37285 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1sx0rl-00018q-TS for submit@debbugs.gnu.org; Sat, 05 Oct 2024 05:14:23 -0400 Original-Received: from lists.gnu.org ([209.51.188.17]:37522) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1sx0ri-00018e-Vj for submit@debbugs.gnu.org; Sat, 05 Oct 2024 05:14:20 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sx0re-0007NZ-4d for bug-gnu-emacs@gnu.org; Sat, 05 Oct 2024 05:14:14 -0400 Original-Received: from mout02.posteo.de ([185.67.36.66]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sx0rb-0002uA-Gb for bug-gnu-emacs@gnu.org; Sat, 05 Oct 2024 05:14:13 -0400 Original-Received: from submission (posteo.de [185.67.36.169]) by mout02.posteo.de (Postfix) with ESMTPS id 4A0D9240101 for ; Sat, 5 Oct 2024 11:14:04 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.net; s=2017; t=1728119644; bh=rbo80bOU5PAYktwukqipyXNf3e0+HJ4pPVV6JOLASnI=; h=From:To:Cc:Subject:Autocrypt:OpenPGP:Date:Message-ID:MIME-Version: Content-Type:Content-Transfer-Encoding:From; b=rYlsZyTuki2I9mZ8nk7myncLqJoK5twW9UL5941iuICzOc7QWkm7APsQ9TZYYm6VM ExBiOliDbgDb0eFog+kV7Gt2yPLB2ws7JvDXZHsjWS3uwKH/Ws/sZ+JfWTlXneUUyH sdlnbng/+C4K0CvHhaet7oGYqcLQKRyCyX3ZWagLTZSuYTP2DEdspzZasPmEBWdPjW wtkDBd6WdtK7Vf1SLTdoD+bbjTxELTM8FQ1R1hY1TPiGkM4N84SY1AfgZ4sY/TVNWE gekSpt0wSlsewqZ20VhIVeX7b53PL4QWrOu5prRwuRcF4hRKDCHIM4ZPO3t3UTQ7Ad 2rxgGlDDx54mg== Original-Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4XLKV24dmDz6twM; Sat, 5 Oct 2024 11:14:02 +0200 (CEST) In-Reply-To: <9db72ab8-604e-451d-b9a0-74fa5b3fa81f@protonmail.com> (okamsn@protonmail.com's message of "Sat, 05 Oct 2024 02:44:18 +0000") Autocrypt: addr=philipk@posteo.net; keydata= mDMEZBBQQhYJKwYBBAHaRw8BAQdAHJuofBrfqFh12uQu0Yi7mrl525F28eTmwUDflFNmdui0QlBo aWxpcCBLYWx1ZGVyY2ljIChnZW5lcmF0ZWQgYnkgYXV0b2NyeXB0LmVsKSA8cGhpbGlwa0Bwb3N0 ZW8ubmV0PoiWBBMWCAA+FiEEDg7HY17ghYlni8XN8xYDWXahwukFAmQQUEICGwMFCQHhM4AFCwkI BwIGFQoJCAsCBBYCAwECHgECF4AACgkQ8xYDWXahwulikAEA77hloUiSrXgFkUVJhlKBpLCHUjA0 mWZ9j9w5d08+jVwBAK6c4iGP7j+/PhbkxaEKa4V3MzIl7zJkcNNjHCXmvFcEuDgEZBBQQhIKKwYB BAGXVQEFAQEHQI5NLiLRjZy3OfSt1dhCmFyn+fN/QKELUYQetiaoe+MMAwEIB4h+BBgWCAAmFiEE Dg7HY17ghYlni8XN8xYDWXahwukFAmQQUEICGwwFCQHhM4AACgkQ8xYDWXahwukm+wEA8cml4JpK NeAu65rg+auKrPOP6TP/4YWRCTIvuYDm0joBALw98AMz7/qMHvSCeU/hw9PL6u6R2EScxtpKnWof z4oM OpenPGP: id=philipk@posteo.net; url="https://keys.openpgp.org/vks/v1/by-email/philipk@posteo.net"; preference=signencrypt Received-SPF: pass client-ip=185.67.36.66; envelope-from=philipk@posteo.net; helo=mout02.posteo.de X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:293007 Archived-At: Okamsn writes: > Philip Kaludercic wrote: >> Okamsn writes: >>=20 >>> Philip Kaludercic wrote: >>>> Okamsn writes: >>>>> Michael Heerdegen wrote: >>>>>> Does changing the internal representation of streams have an effect >>>>>> on the speed of the run code? >>>>> >>>>> I think that it does make it slower. I am trying to test it, and I th= ink >>>>> that making records is slower than making cons cells. I think that >>>>> accessing the rest of the stream takes longer because the accessors >>>>> created by `cl-defstruct` always perform type checking. It seems to t= ake >>>>> about twice as long when compared to naively using `car` and `cdr`. >>>>> >>>>> Do you think that it would be better to disable the type checking in = the >>>>> accessors? If so, would you please share how to do that? The manual >>>>> talks about using `(optimize (safety 0))` in a declare form, but it a= lso >>>>> seems to say that it cannot be done locally for just the `cl-defstruc= t` >>>>> usage. If it cannot be done, do think it makes sense to use >>>>> `make-record` directly, along with custom function to replace the >>>>> generated accessors? >>>> >>>> It the overhead noticeable, or just measurable? >>> >>> I=E2=80=99m not sure what counts as =E2=80=9Cnoticeable=E2=80=9D. >>=20 >> I'd say that real-world code that uses stream.el gets slower. For me >> the main value of synthetic benchmarks is only the speed difference in >> orders of magnitude and in the number of GC interrupts. >>=20 >>> ... > >> >>> You can see that the struct-based run times are about twice as long. I >>> think this is from the extra work done by the accessors. For example, >>> the type-checking is run multiple times when accessing the =E2=80=9Cfir= st=E2=80=9D and >>> =E2=80=9Crest=E2=80=9D slots, because the accessors are also used in `s= tream--force`. >>=20 >> Type checking isn't always that bad; Do you see an (easy) way to avoid >> type checking from running multiple times? > > Please see the attached file. By setting `safety` to 0 and explicitly=20 > checking only once in `stream--force`, we can avoid the multiple checks=20 > when evaluating an unevaluated stream. That helps when going through the= =20 > stream the first time, but that still leaves multiple calls to=20 > `stream--force` when iterating. I don't have any ideas for the latter. > > Setting safety to 0 is about 15% faster than having the accessors do=20 > type checking, but it still isn't as fast as the current cons-based=20 > implementation. From what I have tried, iterating through nested arrays= =20 > seems slower than iterating through a normal list. > > From 98ffcbe184fdbc403afdf5b1c48e77525dc1d476 Mon Sep 17 00:00:00 2001 > From: Earl Hyatt > Date: Sat, 28 Sep 2024 15:09:10 -0400 > Subject: [PATCH v2] Change 'stream.el' to use structs instead of cons cel= ls.=20 > Update features. > > * 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 ti= me > 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 (stream--force, stream-first, stream-rest) > (stream-empty, stream-empty-p): Redefine to use structure slots. Move > to be closer to the structure definition. > > * stream.el (stream-first, stream-rest): Signal an error when trying to u= se > these functions as places for 'setf'. > > * stream.el (stream--fresh-identifier, stream--evald-identifier): > Remove now unused definitions. > > * stream.el (stream): Add a method that accepts a stream, returning it > unmodified. This makes mapping across multiple sequences easier. > > * stream.el (stream): Add a method that accepts an array and which does n= ot > create sub-sequences of the array, unlike the implementation for generic > sequences. This is a bit faster and is a good example of a custom updater > function. > > * stream.el (stream--generalizer, cl-generic-generalizers): Remove > these specializers from the old, cons-based implementation. > > * stream.el (seq-elt): Signal an error when trying to use this function a= s a > place for 'setf'. > > * stream.el (seq-concatenate): Add methods that did not work as expected = with > the generic implementation. > > * tests/stream-tests.el (stream-seq-concatenate-test, stream-seq-mapcat-t= est) > (stream-array-test): Add tests for these features. > > * tests/stream-tests.el: Test that evaluation is delayed for seq-drop-whi= le > using deftest-for-delayed-evaluation. > --- > stream.el | 213 +++++++++++++++++++++++++++++------------- > tests/stream-tests.el | 26 ++++++ > 2 files changed, 176 insertions(+), 63 deletions(-) > > diff --git a/stream.el b/stream.el > index 7135ee0..23b8700 100644 > --- a/stream.el > +++ b/stream.el > @@ -66,36 +66,135 @@ > (eval-when-compile (require 'cl-lib)) > (require 'seq) >=20=20 > -(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.")) > +;; Set safety to 0 to avoid checking the type of the argument multiple t= imes > +;; within `stream--force', which is used frequently. > +(cl-declaim (optimize (safety 0))) > +(cl-defstruct (stream (:constructor stream--make-stream) > + (:predicate streamp) > + :named) > + > + "A lazily evaluated sequence, compatible with the `seq' library's func= tions." > + > + (evaluated--internal > + nil > + :type boolean > + :documentation "Whether the head and tail of the stream are accessibl= e. > + > +This value is set to t via the function `stream--force' after it > +calls the updater function.") > + > + (first--internal > + nil > + :type (or t null) Isn't this type just t? Not a proof, but this doesn't signal: (mapatoms (lambda (sym) (when (and (boundp sym) (not (cl-typep (symbol-value sym) '(or t null)))) (throw 'fail sym)))) > + :documentation "The first element of the stream.") > + > + (rest--internal > + nil > + :type (or stream null) > + :documentation "The rest of the stream, which is itself a stream.") > + > + (empty--internal > + 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--internal > + 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--internal' is > +set to t. After this function is called, assuming no errors were signal= ed, > +`evaluated--internal' is set to t. > + > +In the case of the canonical empty stream (see the variable `stream-empt= y'), > +this slot is nil.")) > + > +(defun stream--force (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'. > + (unless (streamp stream) > + (signal 'wrong-type-argument (list 'stream stream))) If you are already using cl-lib, you could also make use of `cl-check-type'. > + (if (stream-evaluated--internal stream) > + stream > + (pcase (funcall (stream-updater--internal stream)) > + (`(,head . ,tail) > + (setf (stream-first--internal stream) head > + (stream-rest--internal stream) tail)) > + ((pred null) > + (setf (stream-empty--internal stream) t)) > + (bad-output > + (error "Bad output from stream updater: %s" > + bad-output))) > + (setf (stream-evaluated--internal stream) t) > + stream)) >=20=20 > (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." > - (declare (debug t)) > - `(cons ',stream--fresh-identifier (lambda () ,@body))) >=20=20 > -(defun stream--force (stream) Did you change the order of the definitions? > - "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))))) > +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)) > + `(stream--make-stream :evaluated--internal nil > + :updater--internal (lambda () ,@body))) >=20=20 > (defmacro stream-cons (first rest) > "Return a stream built from the cons of FIRST and REST. > -FIRST and REST are forms and REST must return a stream." > + > +FIRST and REST are forms. REST must return a stream." > (declare (debug t)) > `(stream-make (cons ,first ,rest))) > + > +(defconst stream-empty > + (stream--make-stream :evaluated--internal t > + :first--internal nil > + :rest--internal nil > + :empty--internal t > + :updater--internal nil) > + "The empty stream.") > + > +(defun stream-empty () > + "Return the empty stream." > + stream-empty) This definition also appears unchanged? Please try to keep the diff as small as possible. > + > +(defun stream-empty-p (stream) > + "Return non-nil if STREAM is empty, nil otherwise." > + (stream-empty--internal (stream--force stream))) > + > +(defun stream-first (stream) > + "Return the first element of STREAM, evaluating if necessary. > + > +If STREAM is empty, return nil." > + (stream-first--internal (stream--force stream))) > + > +(defun \(setf\ stream-first\) (_store _stream) > + "Signal an error when trying to use `setf' on the head of a stream." > + (error "Streams are not mutable")) This should also be part of a second patch. > +(defun stream-rest (stream) > + "Return the tail of STREAM, evaluating if necessary. > + > +If STREAM is empty, return the canonical empty stream." > + (if (stream-empty-p stream) > + stream-empty > + (stream-rest--internal (stream--force stream)))) > + > +(defun \(setf\ stream-rest\) (_store _stream) > + "Signal an error when trying to use `setf' on the tail of a stream." > + (error "Streams are not mutable")) > + > >=20=20 > ;;; Convenient functions for creating streams > @@ -103,6 +202,10 @@ (defmacro stream-cons (first rest) > (cl-defgeneric stream (src) > "Return a new stream from SRC.") >=20=20 > +(cl-defmethod stream ((stream stream)) > + "Return STREAM unmodified." > + stream) > + > (cl-defmethod stream ((seq sequence)) > "Return a stream built from the sequence SEQ. > SEQ can be a list, vector or string." > @@ -112,6 +215,24 @@ (cl-defmethod stream ((seq sequence)) > (seq-elt seq 0) > (stream (seq-subseq seq 1))))) >=20=20 > +(cl-defmethod stream ((array array)) > + "Return a stream built from the array ARRAY." > + (let ((len (length array))) > + (if (=3D len 0) > + (stream-empty) > + ;; This approach could avoid one level of indirection by setting > + ;; `stream-updater--internal' directly, but using `funcall' makes = for a > + ;; good example of how to use a custom updater function using the = public > + ;; interface. > + (let ((idx 0)) > + (cl-labels ((updater () > + (if (< idx len) > + (prog1 (cons (aref array idx) > + (stream-make (funcall #'updater))) > + (setq idx (1+ idx))) > + nil))) > + (stream-make (funcall #'updater))))))) > + > (cl-defmethod stream ((list list)) > "Return a stream built from the list LIST." > (if (null list) > @@ -190,33 +311,6 @@ (defun stream-range (&optional start end step) > (stream-range (+ start step) end step)))) > >=20=20 > -(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) > - "The empty stream.") > - > -(defun stream-empty () > - "Return the empty stream." > - stream-empty) > - > -(defun stream-empty-p (stream) > - "Return non-nil if STREAM is empty, nil otherwise." > - (null (cdr (stream--force stream)))) > - > -(defun stream-first (stream) > - "Return the first element of STREAM. > -Return nil if STREAM is empty." > - (car (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))) > - > (defun stream-append (&rest streams) > "Concatenate the STREAMS. > Requesting elements from the resulting stream will request the > @@ -240,22 +334,7 @@ (defmacro stream-pop (stream) > `(prog1 > (stream-first ,stream) > (setq ,stream (stream-rest ,stream)))) > - >=20=20 > -;;; 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)) > >=20=20 > ;;; Implementation of seq.el generic functions > @@ -273,6 +352,9 @@ (cl-defmethod seq-elt ((stream stream) n) > (setq n (1- n))) > (stream-first stream)) >=20=20 > +(cl-defmethod (setf seq-elt) (_store (_sequence stream) _n) > + (error "Streams are not mutable")) > + > (cl-defmethod seq-length ((stream stream)) > "Return the length of STREAM. > This function will eagerly consume the entire stream." > @@ -417,6 +499,11 @@ (defmacro stream-delay (expr) > (cl-defmethod seq-copy ((stream stream)) > "Return a shallow copy of STREAM." > (stream-delay stream)) > + > +(cl-defmethod seq-concatenate ((_type (eql stream)) &rest sequences) > + "Make a stream which concatenates each sequence in SEQUENCES." > + (apply #'stream-append (mapcar #'stream sequences))) > + > >=20=20 > ;;; More stream operations > diff --git a/tests/stream-tests.el b/tests/stream-tests.el > index ba304f1..defc544 100644 > --- a/tests/stream-tests.el > +++ b/tests/stream-tests.el > @@ -212,6 +212,27 @@ (ert-deftest stream-delay-test () > (and (equal res1 5) > (equal res2 5))))) >=20=20 > +(ert-deftest stream-seq-concatenate-test () > + (should (streamp (seq-concatenate 'stream (list 1 2) (vector 3 4) (str= eam (list 5 6))))) > + (should (equal '(1 2 3 4 5 6) > + (seq-into (seq-concatenate 'stream > + (list 1 2) > + (vector 3 4) > + (stream (list 5 6))) > + 'list)))) > + > +(ert-deftest stream-seq-mapcat-test () > + (should (streamp (seq-mapcat #'stream (list (list 1 2) > + (vector 3 4) > + (stream (list 5 6))) > + 'stream))) > + (should (equal '(1 2 3 4 5 6) > + (seq-into (seq-mapcat #'stream (list (list 1 2) > + (vector 3 4) > + (stream (list 5 6)= )) > + 'stream) > + 'list)))) > + > (ert-deftest stream-seq-copy-test () > (should (streamp (seq-copy (stream-range)))) > (should (=3D 0 (stream-first (seq-copy (stream-range))))) > @@ -234,6 +255,10 @@ (ert-deftest stream-list-test () > (dolist (list '(nil '(1 2 3) '(a . b))) > (should (equal list (seq-into (stream list) 'list))))) >=20=20 > +(ert-deftest stream-array-test () > + (dolist (arr (list "cat" [0 1 2])) > + (should (equal arr (seq-into (stream arr) (type-of arr)))))) > + > (ert-deftest stream-seq-subseq-test () > (should (stream-empty-p (seq-subseq (stream-range 2 10) 0 0))) > (should (=3D (stream-first (seq-subseq (stream-range 2 10) 0 3)) 2)) > @@ -296,6 +321,7 @@ (deftest-for-delayed-evaluation (stream-append (make= -delayed-test-stream) (make > (deftest-for-delayed-evaluation (seq-take (make-delayed-test-stream) 2)) > (deftest-for-delayed-evaluation (seq-drop (make-delayed-test-stream) 2)) > (deftest-for-delayed-evaluation (seq-take-while #'numberp (make-delayed-= test-stream))) > +(deftest-for-delayed-evaluation (seq-drop-while #'numberp (make-delayed-= test-stream))) > (deftest-for-delayed-evaluation (seq-map #'identity (make-delayed-test-s= tream))) > (deftest-for-delayed-evaluation (seq-mapn #'cons > (make-delayed-test-stream) --=20 Philip Kaludercic on siskin