Here's how I do this. I have a standard load script I build from template for all of my packages. You can see one for my main development package here.
What it does then is look for a directory called Packages
in the top-level package directory (although there are config options to change this, if I remember correctly).
Then if the directory structure looks like:
MyPackage
Packages
+ pkg1.m
+ pkg2.m
A
+ subpkg1.m
B
+ subpkg2.m
I'll get out three contexts, "MyPackage`"
, "MyPackage`A"
, "MyPackage`B`"
. The way I then make all the symbols in each see eachother when they load is that I use lazy-loading. So first I scan through all the found package files and register all the symbol names in the appropriate context and setup an OwnValues
pattern so that when they're called for the first time they will load the package they're declared in using a special PackageGet
function. This is done by the PackageDeclare
function in the load script.
That PackageGet
function then uses Internal`WithLocalSettings
and System`Private`NewContextPath
and System`Private`RestoreContextPath
to set up a $ContextPath
that looks like:
{
"MyPackage`",
"MyPackage`A`",
"MyPackage`B`",
"MyPackage`PackageScope`Private`" (* I put a bunch of utility functions here *)
}
This way each symbol sees each other when they load, but no private declarations or anything like that.
I haven't fully described what I do, as I also have ways to handle circular dependencies and things, but this is a basic overview that perhaps one might implement for themselves.
I have found it to be a useful and powerful idiom reminiscent of the way python handles package loading.
Common.m
package containing the private functions that will be shared among other packages that need those functions. $\endgroup$Common.m
solution is that JITL (Just-in-time-loading viaDeclarePackage
) doesn't support a "names file". The drawback with a new-style package is that it is not scalable in terms of supporting JITL. If these two aspects are important (and they aren't always) but I argue they increase in importance with the size of a system so they might be for a "fairly large project". I am putting out shortly an extension ofBeginPackage-EndPackage
(which I will link to) that addresses this very issue. $\endgroup$DeclarePackage
is an old style of doing this, which is no longer used, at least not internally in the company. The current mechanism is to use the paclet symbol autoloading and paclet project loading, which plays well with new-style packages. $\endgroup$Stub
attribute transfers as previously discussed ( I'm not just talking about public functions but private ones the OP mentions e.g. what if you want these privately shared functions to incorporate JITL? can the current mechanism handle this?). My sense is that this new mechanism is tilted towards public functions. Naturally, I don't fully understand paclet symbol autoloading so ... $\endgroup$