2
$\begingroup$

Minimal example:

BeginPackage["plots`"]
s::usage = "s[max] plots ..."
x
y
Begin["`Private`"]
s[max_] := 
 Plot[Sin[x], {x, 0, max}, AxesLabel -> {HoldForm@x, HoldForm@y}]
End[]
EndPackage[]

After executing s[...] labels are x and y. But if I have (before or after package loading) some definitions for x and y I will get warnings abot multiple contexts and shadowing.

I can remove x and y from exporting part of package, but then I get plots`Private`x and similar for axes labels.

I used to set labels with HoldForm@x to avoid possible definitions for x and to have usual TraditionalForm for label.

How to solve this? Thanks.

$\endgroup$
7
  • 1
    $\begingroup$ Why not using string? $\endgroup$
    – xzczd
    Commented Jul 4, 2023 at 2:53
  • $\begingroup$ @xzczd, please elaborate. How to combine string and TraditionalForm? $\endgroup$
    – LittleCat1
    Commented Jul 4, 2023 at 2:55
  • $\begingroup$ You mean you need it be italic? Then Style["x", Italic]. You can also select the symbol in string and press Ctrl+i. $\endgroup$
    – xzczd
    Commented Jul 4, 2023 at 2:59
  • $\begingroup$ @xzczd, plrobably this is a way to go. But I mean, in a notebook I use Axeslabel ->{HoldForm@x,HoldForm@y} and get nicely formatted labels. Can this be done with packages and all this "nightmare" of contexts? $\endgroup$
    – LittleCat1
    Commented Jul 4, 2023 at 3:02
  • $\begingroup$ @xzczd, I tried your suggestion with Style["\!\(\*SubscriptBox[\(\[Beta]\), \(y\)]\)", Italic] and HoldForm[Subscript[\[Beta], y]], they look different, \[Beta] e.g. $\endgroup$
    – LittleCat1
    Commented Jul 4, 2023 at 3:07

2 Answers 2

2
$\begingroup$

Expanding my comment to an answer. If you insist on the HoldForm approach, setting

Internal`$ContextMarks = False;

is a possible workaround. If you don't want to set Internal`$ContextMarks globally, modify s to the following:

…
s3[max_] := 
 Plot[Sin[x], {x, 0, max}, 
  AxesLabel -> 
   Block[{Internal`$ContextMarks = False}, 
    ToExpression@MakeBoxes@{HoldForm@x, HoldForm@y}]]
…

If you have many functions to be fixed in the package, define a helper function for the task:

BeginPackage["plots`"]
s::usage = "s[max] plots ..."
s2
Begin["`Private`"]

nomark = Function[expr, 
   Block[{Internal`$ContextMarks = False}, ToExpression@MakeBoxes@expr], HoldAll];

s[max_] := Plot[Sin[x], {x, 0, max}, AxesLabel -> nomark@{HoldForm@x, HoldForm@y}]
s2[max_] := Plot[Cos[x], {x, 0, max}, AxesLabel -> nomark@{HoldForm@x, HoldForm@y}]
End[]
EndPackage[]
$\endgroup$
3
  • $\begingroup$ Is it safe to put this Internal`$ContextMarks = False command in init.m? And why we need ToExpression / MakeBoxes staff? $\endgroup$
    – LittleCat1
    Commented Jul 4, 2023 at 3:55
  • $\begingroup$ @LittleCat1 Setting it globally to False can cause trouble in certain case, see the end of mathematica.stackexchange.com/a/162834/1871 . Internal`$ContextMarks actually effects in MakeBoxes stage, so Block is not enough. Try e.g. Block[{Internal`$ContextMarks = False}, a`b]. Also, observe the output of Global`b = 123; Block[{Internal`$ContextMarks = False}, a`b // MakeBoxes // ToExpression] to understand how it works. $\endgroup$
    – xzczd
    Commented Jul 4, 2023 at 4:01
  • $\begingroup$ OK, I see. So I will stick with a helper function to strip context marks. $\endgroup$
    – LittleCat1
    Commented Jul 4, 2023 at 4:09
3
$\begingroup$

The correct solution is to pass $x$ and $y$ symbols in the call itself. This is what Mathematica does. See module-and-local-variable

First option

So your package will becomes

Quit[]

BeginPackage["plots`"]
s::usage="s[max] plots ..."
Begin["`Private`"]
s[max_,x_,y_]:=Plot[Sin[x],{x,0,max},AxesLabel->{x,y}]
End[]
EndPackage[]

And now

s[10,x,y]

Mathematica graphics

Second option

Option 1 is what I would do.

But if you do not want to pass the symbols to use, another option is to have a private function in your package, called strip which you call it on any local module symbol that you want to return back to the caller. This function basically knowns you package name and will strip away the leading package indicator from any symbol. So your package becomes

Quit[]
x=10; (*does not affect anything now*)

BeginPackage["plots`"]
s::usage="s[max] plots ..."
Begin["`Private`"]

(*private stipping function*)
strip[expr_]:=Module[{},
   StringReplace[ToString[expr,FormatType->TraditionalForm],"plots`Private`"~~c:LetterCharacter~~"$"~~DigitCharacter..:>c]
]

(*even though x,y here are local to this module *)
(*when returning, they will NOT have any $ or package indicators*)

s[max_]:=Module[{x,y},
   Plot[Sin[x],{x,0,max},AxesLabel->{strip@x,strip@y}]]
End[]
EndPackage[]

and now

s[10]

Mathematica graphics

For completion, will add option suggested by xzczd in comment. But for this, the symbols $x,y$ have to be package wide visible. I prefer myself not to have this, as they become global to the whole package.

Third option

Quit[]
x=10;

BeginPackage["plots`"]
s::usage="s[max] plots ..."
Begin["`Private`"]
Internal`$ContextMarks=False; (*this strips package name*)
(*notice, now x,y are global to the package (but private to world) *)
x 
y
s[max_]:=Module[{},
  Plot[Sin[x],{x,0,max},AxesLabel->{x,y}]]

End[]
EndPackage[]

And now

s[10]

Mathematica graphics

$\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.