When an application written in C++ depends on some libraries, the libraries are usually shipped together with the application. Two applications that depend on the same library will not share the same instance of that library. Compatibility problems are thus averted. Each application ships its own private instance of a known compatible (sometimes even patched) version of the library.
What is the best way to handle the same thing in Mathematica?
How should I design a package that is anticipated to be a dependency for multiple other packages?
Do we really need this? I think yes. LTemplate definitely needs this. What if I write a package whose only purpose is to handle persistent settings? It could (it is meant to) become a dependency for many others.
I do have a solution for this as I had to use it in LTemplate. I am asking this question because I think it will be useful to develop community standards. I would also like to see how others handle this problem and improve or simplify my own solution.
"What have you tried so far?"
Let's call the package SomePack. My requirements were the following:
- Be able to load and use SomePack interactively with the usual
- Be able to embed SomePack into another package, and ship them together.
- What if I want to load two packages each of which embed two incompatible versions of SomePack? When SomePack is embedded, it should have a unique context to avoid conflict with another instance embedded into another package.
Let's have the following directory structure:
SomePack | |- Kernel | | | \- init.m | |- SomePack.m | |- SomePackPrivate.m | \- SomePackInner.m
The files contain the following:
(* init.m *) Get["SomePack`SomePack`"]
(* SomePack.m *) BeginPackage["SomePack`"] Get["SomePack`SomePackInner`"] EndPackage
(* SomePackPrivate.m *) (* Note the leading ` characters below!! *) BeginPackage["`SomePack`"] Quiet[ (* prevent false shadow warnings when loading SomePack first and OtherPack later *) Get["`SomePackInner`"], General::shdw ] EndPackage
(* SomePackInner.m *) SomeSymbol::usage = "SomeSymbol"; Begin["`Private`"] (* code goes here *) End
If the package is not embedded, load it with
Its context will be
If it is embedded into OtherPack, then the
SomePack directory should go within the
OtherPack directory. Then
OtherPack.m should follow this structure:
BeginPackage["OtherPack`"] Get["`SomePack`SomePackPrivate`"] Begin["`Private`"] (* package code goes here *) End EndPackage
When loading OtherPack using
Needs["OtherPack`"], SomePack will also get loaded and it will reside in
OtherPack`SomePack`. Symbols from this context will be usable in the implementation of OtherPack, but they will not be public by default when loading OtherPack using