Oh, I hadn't noticed that SBCL has null-ary MAKE-BROADCAST-STREAM return a singleton. That accounts for the CLOSE behavior.
However, while I see a benefit of having a distinguished "null output" sink, but does it need to be a singleton returned by the (apparent) constructor? A variable in SB-EXT would suffice, and would be portably emulatable on other implementations:
#-sbcl ; SBCL already has this
(defvar *null-output* (make-broadcast-stream))
--
Anyway, the reason I care is that among some interface invariants that are desirable to have and seem to be intuitive readings of the standard include:
a. when CLOSE runs to completion, the stream is closed afterward, and
b. when a stream is closed, OPEN-STREAM-P returns false.
c. CLOSE does nothing on a closed stream.
Why these invariants? Well, I like to make sure streams get closed eventually, and in programs that create a dynamic number of streams, that's easier to do when CLOSE and OPEN-STREAM-P have those guarantees.
That said, I'd agree that if a distinguished stream is to serve as a global "null output" sink, closing that stream would likely cause trouble, so I can understand why you've made CLOSE a no-op for the singleton broadcast stream.
OTOH, it's already the case that there are a number of distinguished global streams that it's bad to close (the values of *stdin*, *stdout*, *stderr*, *tty*, for example; and if synonym streams get their own distinct open-ness as discussed in 1904257, then any of the standard I/O customization variables). If /that/ problem is worth solving for, ISTM an orthogonal mechanism equivalent to the following could be nice to have.
--
(defvar *never-close-streams* ())
(define-condition never-close-error (error)
((stream :initarg :stream :reader never-close-error-stream))
(:report (lambda (error stream) (format stream "~S should not normally be closed." (never-close-error-stream error)))))
;; User interface.
(defun never-close (stream)
"Make it an error to try to close STREAM. Note that this does not persist across
SAVE-LISP-AND-DIE; use an *INIT-HOOK* function to reinstate."
(pushnew stream *never-close-streams*))
;; Then stream-init can put *stdin*, *stdout*, *stderr*, *tty*, and
;; so forth onto *never-close-streams*, and stream-deinit could
;; reset *no-close-streams* to nil.
--
Untested, but if you're interested I can elaborate on this orthogonal mechanism. Let me know?
Oh, I hadn't noticed that SBCL has null-ary MAKE-BROADCAST- STREAM return a singleton. That accounts for the CLOSE behavior.
However, while I see a benefit of having a distinguished "null output" sink, but does it need to be a singleton returned by the (apparent) constructor? A variable in SB-EXT would suffice, and would be portably emulatable on other implementations:
--
(defpackage "MY-PACKAGE"
#+sbcl
(import-from "SB-EXT" "*NULL-OUTPUT*") ;say
...)
(in-package "MY-PACKAGE")
#-sbcl ; SBCL already has this -stream) )
(defvar *null-output* (make-broadcast
--
Anyway, the reason I care is that among some interface invariants that are desirable to have and seem to be intuitive readings of the standard include:
a. when CLOSE runs to completion, the stream is closed afterward, and
b. when a stream is closed, OPEN-STREAM-P returns false.
c. CLOSE does nothing on a closed stream.
Why these invariants? Well, I like to make sure streams get closed eventually, and in programs that create a dynamic number of streams, that's easier to do when CLOSE and OPEN-STREAM-P have those guarantees.
That said, I'd agree that if a distinguished stream is to serve as a global "null output" sink, closing that stream would likely cause trouble, so I can understand why you've made CLOSE a no-op for the singleton broadcast stream.
OTOH, it's already the case that there are a number of distinguished global streams that it's bad to close (the values of *stdin*, *stdout*, *stderr*, *tty*, for example; and if synonym streams get their own distinct open-ness as discussed in 1904257, then any of the standard I/O customization variables). If /that/ problem is worth solving for, ISTM an orthogonal mechanism equivalent to the following could be nice to have.
-- close-streams* ())
(defvar *never-
(define-condition never-close-error (error) error-stream) )
( format stream "~S should not normally be closed."
(never- close-error- stream error)))))
((stream :initarg :stream :reader never-close-
(:report (lambda (error stream)
(defmethod close :before ((stream stream)) close-streams* )
(when (member stream *never-
(cerror "Continue" 'never-close-error :stream stream)))
;; User interface. close-streams* ))
(defun never-close (stream)
"Make it an error to try to close STREAM. Note that this does not persist across
SAVE-LISP-AND-DIE; use an *INIT-HOOK* function to reinstate."
(pushnew stream *never-
;; Then stream-init can put *stdin*, *stdout*, *stderr*, *tty*, and close-streams* , and stream-deinit could
;; so forth onto *never-
;; reset *no-close-streams* to nil.
--
Untested, but if you're interested I can elaborate on this orthogonal mechanism. Let me know?