Unintuitive and stupid (but documented and consistent) behavior of :REEXPORT in UIOP:DEFINE-PACKAGE
I had assumed that the :reexport
clause of uiop:define-package
would export only symbols already present in the newly-defined package. That is, given a package like:
(uiop:define-package :foo
(:import-from :alexandria #:array-length)
(:reexport :alexandria))
my expectation is that:
(find-symbol "ARRAY-LENGTH" :foo) ; => ALEXANDRIA:ARRAY-LENGTH, :EXTERNAL
(find-symbol "ARRAY-INDEX" :foo) ; => NIL, NIL
Instead, the second find-symbol
does:
(find-symbol "ARRAY-INDEX" :foo) ; => FOO:ARRAY-INDEX, :EXTERNAL
(eq 'foo:array-index 'alexandria:array-index) ; => NIL
This behavior is described in the documentation, as:
REEXPORT -- Takes a list of package designators. For each package, p, in the list, export symbols with the same name as those exported from p. Note that in the case of shadowing, etc. the symbols with the same name may not be the same symbols. That is,
:reexport PACKAGE
behaves like:
(iter (for SYM in-package PACKAGE external-only t)
(export (symbol-name SYM) NEW-PACKAGE))
I believe it should instead be:
(iter (for SYM in-package PACKAGE external-only t)
(when (find-symbol (symbol-name SYM) NEW-PACKAGE)
(export (symbol-name SYM) NEW-PACKAGE)))
That is, it should export any symbol that exists in NEW-PACKAGE that shares the name as one exported from PACKAGE (regardless of whether they're the same symbol), but it should not intern anything into NEW-PACKAGE. (Note that the :use-reexport
and :mix-reexport
clauses exist to both intern and reexport all the symbols from a package.)
Do y'all agree with my proposed behavior? Is this a breaking change, and would it be too breaking for a minor release like 3.4?
Somewhat relatedly, I'd like to define an (:import-from-reexport PACKAGE &rest SYMBOLS)
clause for uiop:define-package
, and would appreciate feedback on the name/keyword/head.