I am amending my post as I understand that a more thorough answer is needed.
An understanding of packages
Hi @LittleCat1,
Your question is a difficult one and it has been the source of a fair amount of headache for me over the last couple months or so when I started working with packages. It is my understanding that packages are used by such a small minority that it is sparsely documented and difficult to find resources on "good practices" in terms of packages. This was the reason that I even created this list in the first place.
This is not to say that there are no resources at all, but simply you might be surprised that one of the best resources on packages was written a rather long time ago, and that is a book by Maeder called "Programming in Mathematica". Chapter 2 is pretty much all you need which is expanded further in his book.
To your question
Your question is difficult for the following purpose: It depends on how you plan to use the package in the broadest sense. Ideally, a package is self-contained and should not clash with anything that is already stored in your Kernel, however this is just the surface.
As it was beautifully explained by @Shiffrin in this and this thread, it all boils down to Context
management.
1a WorldPlot package
From my understanding, although your 1a case with WorldPlot package might make the most sense to you, to me -a naive and relatively new entry to the world of packages- this is a probably bad practise. This is because your sub-packages will be loaded directly to the Global'
context. This is not bad on its own, as long as you understand how variables can mix and be interpreted the same way within and outside your Context
. The afore-mentioned posts explain this in more detail.
The reason I say probably bad is because I do not know how your 1a package is written and how it depends on the larger notebook that runs it.
If I understand your structure correctly, then WorldData
and WorldNames
might as well be an Initialisation Cell
within your notebook rather than a package.
1b NumericalDifferentialEquationAnalysis package
This is perhaps the most standard way of writing packages and subpackages. Everything is self-contained and there is little risk of a variable clashing in two different Contexts.
As long as you understand that even Private'
functions within a package CAN be called using their "long names" (once again, check Leonid Shiffrin's link from earlier), then this is perhaps the safest way to write a package.
2. VectorFieldPlots package
This is a weird one. I am not certain I understand the structure you mentioned here. What I gather from this is that you have a parent package which calls certain sub-packages, and these sub-packages load the parent package?
I suspect that this was written this way for the sake of expediency rather than due to good practise. It is probably because the main package loads sub-packages that have universally used functions, and the author did not want to load a specific sub-package.
Generally speaking I would avoid self-referencing the main package as it increases the risk of something going awry on the output of your current Context'
.
Ideally you have all your universal tools in a "Tools.m`" package which you can simply load rather than rely on dependencies (or worse yet, sub-dependencies!) of other packages.
3. AuthorTools package
This is a more complex system and unless we see the entire structure I cannot comment on it. DeclarePackage
makes more sense (to me) within a notebook operating the Global'
context. This is a safeguarding function to protect variable clash. The fact that you have this in a package either points to a specialised case or to a limitation to my own knowledge.
Conclusion
I would suggest that you stick to the structure of your 1b case if you are new to this. Perhaps a more experienced user can comment on this.
Give Maeder's Chapter 2 a proper read. It is a very well written resource on packages and it even gives a good default "skeleton package" which you can find just below:
In case you haven't heard about this, a good convention is to have public functions always capitalised and private functions always lowercase. This makes your life significantly easier when troubleshooting. As always, you can call ANY function, private or not, by using its long name.
skeleton.m
(* :Title: Skeleton.m -- a package template *)
(* :Context: ProgrammingInMathematica`Skeleton` *)
(* :Author: Roman E. Maeder *)
(* :Summary:
The skeleton package is a syntactically correct framework for package
development.
*)
(* :Copyright: © <year> by <name or institution> *)
(* :Package Version: 2.0 *)
(* :Mathematica Version: 3.0 *)
(* :History:
2.0 for Programming in Mathematica, 3rd ed.
1.1 for Programming in Mathematica, 2nd ed.
1.0 for Programming in Mathematica, 1st ed.
*)
(* :Keywords: template, skeleton, package *)
(* :Sources:
Roman E. Maeder. Programming in Mathematica, 3rd ed. Addison-Wesley, 1996.
*)
(* :Warnings:
<description of global effects, incompatibilities>
*)
(* :Limitations:
<special cases not handled, known problems>
*)
(* :Discussion:
<description of algorithm, information for experts>
*)
(* :Requirements:
ProgrammingInMathematica/Package1.m
ProgrammingInMathematica/Package2.m
ProgrammingInMathematica/Package3.m
*)
(* :Examples:
<sample input that demonstrates the features of this package>
*)
(* set up the package context, including public imports *)
BeginPackage["ProgrammingInMathematica`Skeleton`", "ProgrammingInMathematica`Package1`", "ProgrammingInMathematica`Package2`"]
(* usage messages for the exported functions and the context itself *)
Skeleton::usage = "Skeleton.m is a package that does nothing."
Function1::usage = "Function1[n] does nothing."
Function2::usage = "Function2[n, (m : 17)] does even more nothing."
(* error messages for the exported objects *)
Skeleton::badarg = "You twit, you called `1` with argument `2`!"
Begin["`Private`"] (* begin the private context (implementation part) *)
Needs["ProgrammingInMathematica`Package3`"] (* read in any hidden imports *)
(* unprotect any system functions for which definitions will be made *)
protected = Unprotect[ Sin, Cos ]
(* definition of auxiliary functions and local (static) variables *)
Aux[f_] := Do[something]
staticvar = 0
(* definition of the exported functions *)
Function1[n_] := n
Function2[n_, m_ : 17] := n m /; n < 5 || Message[Skeleton::badarg, Function2, n]
(* definitions for system functions *)
Sin /: Sin[x_]^2 := 1 - Cos[x]^2
Protect[ Evaluate[protected] ] (* restore protection of system symbols *)
End[ ] (* end the private context *)
Protect[ Function1, Function2 ] (* protect exported symbols *)
EndPackage[ ] (* end the package context *)