4
$\begingroup$

I like to call my package private functions using

    mypkg`functionName[...]
  

i.e. add an explicit mypkg to each call. This way when looking at code I know which package the function lives in.

But the problem is that, even though the function is private and has no usage:: statement, Mathematica still exports it and the function can now be seen using ?? on the package and can also be called.

Here is MWE. The function parseODE is private.

BeginPackage["mypkg`"]
dsolve::usage = "mypkg`dsolve ode";

Begin["`Private`"]

dsolve[ode_] := Module[{},
      Print["In mypkg`dsolve"];
      mypkg`parseODE[ode]
   ];

parseODE[ode_] := Print["In mypkg`parseODE"];

End[];

EndPackage[];

But now

?mypkg`*

enter image description here

And the private function can now be seen and called

mypkg`parseODE[ode]
(*In mypkg`parseODE *)

Changing the code of the package to be like this (have to restart kernel also)

BeginPackage["mypkg`"]
dsolve::usage="mypkg`dsolve ode";

Begin["`Private`"]

dsolve[ode_]:=Module[{},
   Print["In mypkg`dsolve"];
   parseODE[ode] (*removed package name from call*)
];

parseODE[ode_]:=Print["In mypkg`parseODE"];

End[];

EndPackage[];

Now the private function parseODE do not show and can't be called

enter image description here

How to add package name in the call, inside my package, without having each function being exported as a result? This makes it very hard to write large code for me, as I always like to add package name to each call of function. I do this in Maple all the time with no problem. I'd like to do the same in Mathematica.

V 14

Update and workaround

I found that by calling the private function from inside the package using

 mypkg`Private`parseODE[ode]

Instead of

 mypkg`parseODE[ode]

Then it does not export it.

So I will use this as workaround. This make it little longer to type, but that is OK. I am still not sure why using mypkg`parseODE[ode] caused the function to be exported. It looks like this is just how packages work in Mathematica? but this is not common in other languages.

So now the code looks like this

BeginPackage["mypkg`"]
dsolve::usage = "mypkg`dsolve ode";

Begin["`Private`"]

dsolve[ode_] := Module[{},
      Print["In mypkg`dsolve"];
      mypkg`Private`parseODE[ode]
   ];

parseODE[ode_] := Print["In mypkg`parseODE"];

End[];

EndPackage[];

And

enter image description here

$\endgroup$
10
  • $\begingroup$ parseODE? What are you up to? ;) $\endgroup$
    – Chris K
    Commented May 28 at 0:47
  • $\begingroup$ @ChrisK I am porting my ode solver from Maple to Mathematica. It is about 70,000 lines of code, and trying to keep same layout and call flow. In Maple one can do moduleName:-function(...) from inside the module where the function is private, and this keeps the function private. I'd like to do the same call formats in Mathematica. I find modules in Maple much easier to understand and use than packages in Mathematica. $\endgroup$
    – Nasser
    Commented May 28 at 1:11
  • 1
    $\begingroup$ "I am still not sure why using mypkg`parseODE[ode] caused the function to be exported. It looks like this is just how packages work in Mathematica?" Yeah it's the design. By writing mypkg`parseODE[ode] explicitly, you'll create the symbol parseODE under the mypkg` context, but the context mypkg` is not private because it's created by BeginPackage["mypkg`"] so it's added to $ContextPath. In this example, the private context (to be precise, the context that hasn't been added to $ContextPath) is mypkg`Private` context created by the line Begin["`Private`"]. $\endgroup$
    – xzczd
    Commented May 28 at 2:02
  • $\begingroup$ @xzczd Yes, I figured it was something like you said. I'll keep the question open in case there is a workaround. Having to add Private to each call now is not fun, but will have to do it if there is no alternative. I know I can just call the function without prefixing its name with anything, but I do not like to do this. In Maple I always prefix the function name with the name of the module, even for private function calls. Thanks, $\endgroup$
    – Nasser
    Commented May 28 at 2:08
  • $\begingroup$ @Nasser Nice, looking forward to seeing it $\endgroup$
    – Chris K
    Commented May 28 at 3:00

2 Answers 2

3
$\begingroup$

Extend my comment to an answer.

I am still not sure why using mypkg`parseODE[ode] caused the function to be exported. It looks like this is just how packages work in Mathematica?

Yeah it's the design. By writing mypkg`parseODE[ode] explicitly, you'll create the symbol parseODE under the mypkg` context, but the context mypkg` is not private because it's created by BeginPackage["mypkg`"] so it's added to $ContextPath. In this example, the private context (to be precise, the context that hasn't been added to $ContextPath) is mypkg`Private` context created by the line Begin["`Private`"].

This make it little longer to type…

The new-in-13.0 $ContextAliases may help you. Possible usage:

BeginPackage["mypkg`"];
dsolve::usage = "mypkg`dsolve ode";

Begin["`Private`"];

$ContextAliases["mypkgP`"] = "mypkg`Private`";

dsolve[ode_] := Module[{}, Print["In mypkg`dsolve"];
   mypkgP`parseODE[ode]];

parseODE[ode_] := Print["In mypkg`parseODE"];

End[];

EndPackage[];

We may even define

$ContextAliases["mypkg`"] = "mypkg`Private`";

in the package. This will hijack the mypkg` in the package temporally and cause some warning, but should not be a problem as long as we know what we're doing.

$\endgroup$
2
  • $\begingroup$ Thanks. Typing one extra P is a lot better than typing the whole word Private. ContextAliases looks like useful function to learn about. $\endgroup$
    – Nasser
    Commented May 28 at 2:34
  • 1
    $\begingroup$ @Nasser We may even hijack the mypkg` context, see the update. $\endgroup$
    – xzczd
    Commented May 28 at 2:39
2
$\begingroup$

This is how I do it. There's no need to use full context names:

BeginPackage["mypkg`"];
dsolve::usage = "mypkg`dsolve ode";

Begin["`Private`"]; (* Sets the context. All new symbols will
                       be created in the mypkg`Private` context.
                       All such symbols, such as  parseODE, will
                       automatically refer to mypkg`Private`parseODE.
                    *)

dsolve[ode_] := Module[{},
   Print["In ", Context[dsolve], SymbolName[dsolve]];
   parseODE[ode]];

parseODE[ode_] := 
  Print["In ", Context[parseODE], SymbolName[parseODE]];

End[];

EndPackage[];

Example:

dsolve["ode"]
In mypkg`dsolve

In mypkg`Private`parseODE
Names["mypkg`*"]

(*  {"dsolve"}  *)

As long as all calls to parseODE occur before End[], they will refer to mypkg`Private`parseODE.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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