Obviously, the externally visible API is published by exporting symbols. But... what if I have multiple packages (say A, B and C) and A's exported symbols are not all meant to be part of the external API - some of them are needed for B and C? (similarly, B exports some symbols for A and C and some for the external API; C is the 'toplevel' package and all its exported symbols are part of the public API; I want to keep things modular and allow A to hide its innards from B and C, so I avoid '::').
My solution right now is to re-export everything that is meant to be public from C and document that the public API consists only of C's exported symbols and people should stay away from public symbols of A and B under pain of bugs and code broken in the future when internal interfaces change.
Is there a better way?
UPDATE: This is my implementation of my understanding of Xach's answer:
First, let me complete my example. I want to export symbols symbol-a-1
and symbol-a-2
from package a
, symbols symbol-b-1
and symbol-b-2
from package b
and symbols api-symbol-1
and api-symbol-2
from package c
. Only the symbols exported from c
are part of the public API.
First, the definition for a
:
(defpackage #:a
(:use #:cl))
Note that there aren't any exported symbols :-)
A helper macro (uses Alexandria):
(defmacro privately-export (package-name &body symbols)
`(eval-when (:compile-toplevel :load-toplevel :execute)
(defun ,(alexandria:format-symbol *package*
"IMPORT-FROM-~a"
(symbol-name package-name)) ()
(list :import-from
,package-name
,@(mapcar (lambda (to-intern)
`',(intern (symbol-name to-intern) package-name))
symbols)))))
Use the macro to 'export privately' :-) :
(privately-export :a :symbol-a-1 :symbol-a-2)
Now the definition of b
:
(defpackage #:b
(:use #:cl)
#.(import-from-a))
... b
's 'exports':
(privately-export :b :symbol-b-1 :symbol-b-2)
... c
's definition:
(defpackage #:c
(:use #:cl)
#.(import-from-a)
#.(import-from-b)
(:export :api-symbol-1 :api-symbol-2)
Problems with this approach:
a
cannot use symbols fromb
(withoutimport
ing symbols fromb
froma
after both have been defined);- the syntax
package:symbol
is basically not usable for symbols exported 'privately' (it's either justsymbol
orpackage::symbol
).