8
$\begingroup$

When making a package, I find it a bit difficult to remember to add functions as public, and update the ::usage tag.

Is there some neat trick to automatically make every function in a package public, if it has a usage tag defined?

That is, one defines all functions in the private scope, but as the package loads, it runs some command that makes every function with the usage tag available to the public scope. Is this possible? Is there a better solution?

$\endgroup$
7
$\begingroup$

"Making a symbol public" in practice means creating the symbol as

MyPackage`MySymbol

and not as

MyPackage`Private`MySymbol

The typical way to do this is to "mention" the symbol before Begin["`Private`"]. This mention does not have to be in a usage message. You can just write the symbol name in that section, like this:

BeginPackage["MyPackage`"]

MySymbol

Begin["`Private`"]

MySymbol::usage = "MySymbol does this and that.";
MySymbol[x_] := x^2

End[]
EndPackage[]

This is the solution I recommend for you.


But as I understand your question, you are asking to avoid even doing this, and have everything about MySymbol together on adjacent lines of the source file.

For a trivial package like the above this is achievable like this:

BeginPackage["MyPackage`"]

Begin["`Private`"]

(* first mention of MySymbol is here: *)
MyPackage`MySymbol::usage = "MySymbol does this and that.";
MySymbol[x_] := x^2

End[]
EndPackage[]

The symbol is created at its very first mention. Make sure that you explicitly create it in the correct context (MyPackage`). Subsequent occurrences of MySymbol will be found by the system in the in the MyPackage` context.

I do not suggest you do this though. Imagine that at some point you add a second symbol that depends on the first one:

BeginPackage["MyPackage`"]

Begin["`Private`"]

MyPackage`SecondSymbol::usage = "SecondSymbol[]";
SecondSymbol[] := MySymbol[123] (* now first mention is here!!! *)

MyPackage`MySymbol::usage = "MySymbol does[x] this and that.";
MySymbol[x_] := x^2

End[]
EndPackage[]

Now the first mention of MySymbol may occur earlier, and it is very easy to forget to add the full context to it. The risk of introducing bugs is too high, so I do not think it's a good idea to try to keep all lines together.

It's better to "declare" all public symbol at the beginning (without a usage if you prefer), similarly to how one must do the same in C or C++. In C, a function must be at least declared (if not defined) before its first use. If you don't, the compiler will give you an error. Mathematica is more dangerous because it won't give you an error, things will just not work as you expect.


It's easy to see that within the documented functionality of Mathematica there is no safe way around mentioning the public symbols at the beginning. This is because Mathematica does not read the package source at once. It reads and evaluates it line by line. You are asking to make all symbols with a usage automatically public. But when Mathematica first encounters such a symbol, it does not yet know if it will get a usage later in the file. Thus what you ask is not possible within documented means.

But there is an undocumented package format which allows it. It is described here:

With this package format, Mathematica first parses the entire source file and decides on the context of each symbol in advance, based on the PackageExport declarations. It will evaluate the lines only afterwards.

The package would look like this:

Package["MyPackage"]

PackageExport["MySymbol"]
MySymbol::usage = "MySymbol[x] does this and that";
MySymbol[x_] := x^2

What's public is decided not based on the usage but based on PackageExport. The PackageExport line may be located anywhere within the file.

Thus this method allows doing almost exactly what you were asking for. I am hesitant to recommend it though because it is not documented, and it has undergone some changes since version 10.0. Specifically, how $Context and $ContextPath are set while reading the package has changed in v11.0. I use this package format myself (for reasons different from yours), and originally this caused some problems which were not easy to work around. If you run into trouble like this, you will be on your own solving it. You won't find any documentation and Wolfram Support will likely refuse to assist.

I suggest to stick to the classic package format, mention the public symbols at the beginning, and only move the usage message to be together with the rest of the definition.

$\endgroup$
  • $\begingroup$ I think this answers my question - thanks! $\endgroup$ – Per Alexandersson Feb 23 '19 at 15:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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