Would a user of this package "Main" normally only have ff as an available routine? Users would not normally dig into the tree structure using contexts or even know what the sublevel contexts are.
So the development of the application is like this: There is a master programmer. The master programmer has A level programmers who work for him and speak only to him. Each A level programmer may have B level programmers who work for them and speak only to them. In turn the B level programmers may have C level programmers, etc. The master programmer is the only one who speaks to the outside world.
This is an incredibly structured and organized system! Very authoritarian. Agreed that some projects may require it.
I would like to suggest that this is not the way that scientists or mathematicians work or the way research or study projects are organized. To begin with they don't know what the tree structure is in advance. If they knew they wouldn't be doing research or carrying out a study program. If they commit to a tree structure they will be limiting their flexibility and maybe biasing their thinking. Otherwise they must be continually revising the tree structure, which can get to be a messy chore.
These might be projects that involve just one or a small number of collaborators. Still, over time, they may assemble a very worthwhile application with multiple packages, documentation and accompanying notebooks. I believe these kind of users can be important to WRI, more so than very large scale industrial projects.
For these types of projects a looser parallel structure is needed. The folder structure for such an application might look like the following:
`$UserBaseDirectory`/Applications/
ApplicationName/
Documentation Folder,
FrontEnd Folder
Kernel/init.m
PackageA.m
PackageB.m
PackageC.m
The three packages will all export routines to final users. In addition they may use routines exported by each other. Why might that happen? Because an organization by topic, or by individuals, may not correspond to a pure tree structure.
Here, I believe, is a way to fix it. Use a special form of init.m file for the application.
Declare the packages in the application.
ApplicationNamePackages = {"PackageA`", "PackageB`", "PackageC`"};
Context names to be added to $ContextPath
in each package. Used in each package.
System`$ApplicationNameContextPaths =
"ApplicationName`" <> # & /@ ApplicationNamePackages;
Each package will then use the following statement after the BeginPackage statement. (As with Pieter only the first argument is used in the BeginPackage statement.)
$ContextPath=Union[$ContextPath,System`$ApplicationNameContextPaths];
Going back to the init.m file, the following routine will read the package Public symbols for each package, to establish contexts, but clear all the definitions including all definitions in the Private context. (It would be sort of nice if we had a command that would read only the Public part of a package and never touch the definitions in the Private section.)
ReadApplicationNamePackageNames[packageList_] :=
Module[{fullPackageName, packageSymbolNames},
Do[
fullPackageName = "ApplicationName`" <> pname;
Get[fullPackageName];
packageSymbolNames = Names[fullPackageName <> "*"];
Unprotect /@ packageSymbolNames;
Clear /@ packageSymbolNames;
Clear @@ {fullPackageName <> "Private`*"},
{pname, packageList}];
]
The following does this for the packages.
ReadApplicationNamePackageNames[ApplicationNamePackages];
Finally, we reload all of the packages to obtain the definitions, now all placed in the correct context.
Get["ApplicationName`PackageA`"]
Get["ApplicationName`PackageB`"]
Get["ApplicationName`PackageC`"]
That is the end of the init.m file. The key requirement here is that all contexts for the application are known before any Private section code is established. It's possible that duplicate names might be used from different packages, which will produce shadowed warnings. But this can also occur in using routines from different applications or conflicting with WRI symbols. This is easily fixed and only a minor problem.
Perhaps this approach can be combined with the method described by Pieter. We would have more than one "Main" package, and these might or might not contain sub-trees. So in the above init.m code PackageA, PackageB and PackageC, would be three "Main" packages.