8
$\begingroup$

Suppose I have a package with many functions, and I want to divide this in several sub-packages, so that public functions can be used by user as is and also sub-package may depend on functions of another sub-package. Examples of such structure are among packages distributed with Mathematica. There are two kinds of structures.

1a. WorldPlot. Its init.m only loads the main file: Get["WorldPlot`WorldPlot`"]. And WorldPlot.m loads other two: Get[ToFileName["WorldPlot","WorldNames.m"]]; Get[ToFileName["WorldPlot","WorldData.m"]]; These WorldNames and WorldData do not have any context declarations (no BeginPackage[], nor Begin[] inside them).

1b. NumericalDifferentialEquationAnalysis. Structure is the same as in 1a example, but sub-packages have Begin["`Private`"] blocks.

2. VectorFieldPlots. Its init.m loads main package and all sub-packages. And what seems strange to me, all sub-packages have BeginPackage["VectorFieldPlots`"] string despite their actual names.

My question is: what structure should I use in my package?

EDIT

OK, I feel I shoud rephrase the question(s). My initial idea was, if those packages are distributed with Mathematica they should, in some sense, represent "the right way of doing things". In the case of several sub-packages, what is the difference between (1) and (2) in my OP? What is the canonical (or preferred) method to use?

And I do not understand variant (2): all sub-packages have BeginPackage["VectorFieldPlots`"] despite their names. How is it possible?

I also found one more different example.

  1. AuthorTools. Its init.m has BeginPackage["AuthorTools`"], EndPackage[] at the beginning, and then DeclarePackage["AuthorTools`MakeBilateralCells`", { "DivideBilateral", ...}] commands.

Then each sub-package has directives like: BeginPackage["AuthorTools`StyleTools`", "AuthorTools`Common`"], usage messages and actual code inside Begin["`Private`"] ... End[]. If I understand correctly, these DeclarePackage commands allow to call public functions (inside sub-packages also) just by short name, not including their context. This package organization seems more logical and convenient.

$\endgroup$

1 Answer 1

2
$\begingroup$

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

  1. 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.

  2. 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 *)
$\endgroup$
1
  • $\begingroup$ thank you for thorough answer, I can accept it. I also found a question about problems with DeclarePackage here with no answer. So I will stick to (1b) variant. $\endgroup$
    – LittleCat1
    Apr 5, 2023 at 12:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.