Define private function in a mathematica package
Asked Answered
C

1

6

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?

Cosmology answered 27/2, 2011 at 19:57 Comment(0)
H
13

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.

Hayrick answered 27/2, 2011 at 21:1 Comment(5)
Thank you for the exhaustive reply. That helped a lot. I asked that question because I tried putting some function declarations from the public to the private "section" of my code, and then reupload the package import statement in the notebook where I test the package. Well, the names corresponding to those functions became red, but I could still run those functions as if they hadn't become private.Cosmology
I also noticed that mathematica (version 7) sometimes messes up with the currently imported packages. FOr instance, I wanted to create the same exact package but with debug prints all over the code. So I took the original package, changed the name on the top, and saved it as a different package. Well, mathematica then started complaining that I had two definitions of the same functions. Why would it bother?Cosmology
It looks to me like you aren't clearing old definitions when modifying your code. For instance, if you move symbols from "foo" to "fooprivate" and reload the package, when Mathematica parses the definitions in fooprivate it detects the conflicts with already-defined symbols in foo, and alerts you. So, you probably want to do something like ClearAll["foo`*"]; Remove["foo`*"] when reloading a modified package foo.Prism
@Ricky: The "Shadow" message you are referring to doesn't mean that there are "conflicting definitions" in the normal sense. It just means that with the current setting of $ContextPath some short symbols do not resolve uniquely. See this answer: stackoverflow.com/questions/4988815#4988815Hylomorphism
@Ricky I wrote a package a while ago, called PackageManipulations, which has a function PackageReload and a few other functions allowing to reload a modified package in a clean way. There is also an option KillShadowing there, which, if set to True, will Remove the conflicting symbols from other packages on the $ContextPath. Generally this is a "hard" way to deal with shadowing. A softer way would be to temporarily remove the conflicting contexts from the $ContextPath. Anyway, the package lives at mathprogramming-intro.org/additional_resources.htmlHayrick

© 2022 - 2024 — McMap. All rights reserved.