Package Usage and Development » Higher-Level Features
Paclets in the simplest case provide a single main package with the all of the functions implemented by the paclet. On the other hand, for any sufficiently complex package multiple packages will be needed. However, with the context mechanism Mathematica supplies it is not always clear what the best way to divide code over multiple packages is, so we'll provide a few different approaches
Predeclared Symbols / Shared Context
In the simplest case, every piece of the package will have the same context and simply directly share symbols. To make this work right, all of the package-level symbols need to be declared before any of the implementation files are loaded. One way this could work is having a directory structure like
MyPaclet
+ PacletInfo.m
Kernel
+ init.m
+ Component1.m
+ Component2.m
...
+ MyPaclet.m
And then have MyPaclet.m
look like:
BeginPackage["MyPaclet`"]
MyPacletSym1::usage="...";
MyPacletSym2::usage="...";
...
Begin["`Private`"];
Get/@
Select[
FileNames["*.m", FileNameJoin@{DirectoryName[$InputFileName], "Kernel"}],
FileNameTake[#]!="init.m"&
];
End[];
EndPackage[];
And then each Component*.m
file will simply provide definitions for the functions provided.
Folder-Based Contexts
A step up from this allows for different contexts to be supplied, taken from the folder the file is found in. This adds a layer of complexity, as we'll need a more complicated structure when declaring our symbols, but given the following directory layout:
MyPaclet
+ PacletInfo.m
Kernel
+ init.m
+ Component1.m
+ Component2.m
Subcontext
+ Component1.m
+ Component2.m
...
+ MyPaclet.m
We could use the following loader:
BeginPackage["MyPaclet`"]
MyPacletSym1::usage="...";
MyPacletSym2::usage="...";
...
BeginPackage["`Subcontext`"]
MySubcontextSym1::usage="...";
MySubcontextSym2::usage="...";
EndPackage[];
With[{`Private`dirPath=FileNameJoin@{DirectoryName[$InputFileName], "Kernel"}},
Map[
Function[
Begin[
StringRiffle[
Join[
Most@
FileNameSplit[
FileNameDrop[
DirectoryName[#],
FileNameDepth[`Private`dirPath]]
],
{
"Private",
""
}
],
"`"
]
];
Get@#;
End[]
],
Select[
FileNames["*.m", `Private`dirPath, Infinity],
FileNameTake[#]!="init.m"&
]
]
];
EndPackage[];
Packages with Autoloading
Depending on the size of the code, rather than simply load the entire package in when Get
is called it can be efficient to introduce autoloading.
What this mean is that we determine which symbols are declared in which packages by inspecting the package header, but instead of loading the package we set the OwnValues
to be a function that loads the package and then returns the symbol. By doing things this way we drastically cut down the load time for each function and keep parts of our package more appropriately contained.
The code for this can be a little bit long, so we won't get into it here, but more info on this can be found here .
Other Approaches
A list of other approaches with detailed pros-and-cons may be found here .