Define private function in a mathematica package

2.2k views Asked by At

I'm not sure I got how to define private functions right. When I'm writing a package mathematica, I just do this:

BeginPackage["myPackage`"]
myPublicFunction::usage="myPublicFunction blahblahblah";
Begin["Private"]
myPrivateFunction[input_]:= ... ;
myPublicFunction[input_]:= ... ;
End[]
EndPackage[]

Is this the correct way or am I missing something?

1

There are 1 answers

5
Leonid Shifrin On BEST ANSWER

Yep, that's a correct way. It may pay off to understand some of the internal package mechanics. Mathematica contexts are similar to namespaces in other languages. They can be nested. Every symbol belongs to some context. At any given moment, some context is "current". Whenever a new symbol is created, the system must decide to which context the symbol will belong. This happens at parse-time. The fundamental quantity (variable) here is $ContextPath. It is basically the search path for symbols. It is a list of contexts, and whenever the system sees a new symbol, it tests if the symbol with the same short name (that is, the name of the symbol proper, without the context) exists in some context on the $ContextPath. If it does exist, then the given occurrence of the symbol will be associated with that existing one. If it does not, then the symbol is created in a current context. Note that this is a dynamic thing - if you change the $ContextPath at any moment, the next symbol occurrence can be associated with a different symbol.

Anyways, what BeginPackage does is that it simply replaces the current value of $ContextPath with just {youPublicPackageContext, "System'"}, plus possibly additional contexts that you publicly import through the second optional argument of BeginPackage. Therefore, all symbols that are in the "public" section, are parsed into the public context, if they are not in "System'" or other contexts that you import. And what EndPackage does is to restore the value of the $ContextPath to what it was before you started loading the package. So, technically the usage message is not the only way to make a symbol public in your main context - you could just as well simply type a symbol with a semicolon, like myFunction; (this practice is discouraged, I just mentioned it to clarify the mechanism). Now, what happens when you enter Begin["'Private'"] is that the current context becomes YourContext'Private' (a sub-context). The $ContextPath is not changed. Therefore, any symbol entered there, which does not exist in your public package or other imported packages (that is, contexts currently on the $ContextPath), automatically is parsed into the 'Private' subcontext.

What really makes these symbols private is that whenever you import your package into some other context (package), only the main package is added to the $ContextPath, but not its sub-packages. Technically, you can break encapsulation by manually adding YourPackage'Private' to the $ContextPath (say, PrependTo[$ContextPath, YourPackage'Private']), and then all your private functions and other symbols will become public in that particular context where you do the import. Again, this practice is discouraged, but it explains the mechanics. The bottom line is that the notion of private or public can be entirely understood when we know how symbols are parsed, and what are the manipulations with $ContextPath and $Context (another system variable giving the value of the current context), that are performed by commands such as Begin and BeginPackage. To put it another way, one could, in principle, emulate the actions of BeginPackage,Begin, End and EndPackage with a user-defined code. There are just a few principles operating here (which I tried to outline above), and the mechanism itself is in fact very much exposed to the user, so that if, in some rare cases, one may want some other behavior, one can make some "custom" manipulations with $ContextPath and Context, to ensure some non-standard way of symbol parsing and therefore, control package-scale encapsulation in some "non-standard" way. I am not encouraging this, just mentioning to emphasize that the mechanism is in fact much simpler and much more controllable than it may seem on the surface.