6
$\begingroup$

I have a package file myPackage.m with the following content:

BeginPackage["myPackage`"];
load[] := {a, b, c};
EndPackage[];

When Needs is called, and the file is parsed, all the symbols in the package immediately appear in the myPackage` namespace, regardless of whether load was called or not:

Needs["myPackage`"];
Names["myPackage`*"]
{"a", "b", "c", "load"}

Is there an elegant, domestic way to only parse symbols into the given context (myPackage` in this case) when the function that includes them is called and not at parse-time? No amount of HoldComplete, Unevaluated, etc. works. I rather avoid storing the rhs (or the symbols directly) of load as a string, ToExpression-ing it only at runtime.

The only option I can think of is to have load[]:=Get["load.m"] and then store the actual definition in load.m, but this is cumbersome if one has many different functions, like load1, load2, etc., because then each definition has to be stored in a different file. Is there a clever way of playing with contexts to overcome this challange?

The reason behind is that I have to store a large amount of equation-systems (say hundreds), each with a specific name and variables, and I want them (and their variables) to be available only if they are explicitly called for by name. Thus if I call load["eq12"], I expect that the symbols of "eq12" (but only its symbols) appear in the namespace. If then I call load["eq35"], its symbols are added to the namespace too. Also, I don't want the equation variables to appear as myPackage`private`x, so I try not to define the equations in a private` subcontext.

One can use the formal symbols of the System` context (like \[FormalX], which are already loaded to the context), but then the set of variables is limited to be single character-variables.

$\endgroup$
  • 1
    $\begingroup$ No worries, that one word just popped out when I was parsing your question, it's kind of famous as far as grammar goes :-) $\endgroup$ – Jason B. Mar 8 '16 at 14:03
  • 1
    $\begingroup$ Is using separate contexts for separate equation systems an option, e.g. myPackage`eq12`a? This way you could add/remove those additional contexts, to/from $ContextPath, at will. $\endgroup$ – jkuczm Mar 8 '16 at 14:43
  • $\begingroup$ An unparsed expression is a string. You can quote it to keep it unparsed, or you can parse it. The in-between state you ask for does not exist. $\endgroup$ – John Doty Mar 8 '16 at 15:00
  • 3
    $\begingroup$ @JohnDoty Well, it sort of exists - the boxed form of expressions. It does have the expression structure which a string lacks, yet symbols are still represented as strings. $\endgroup$ – Leonid Shifrin Mar 8 '16 at 15:15
  • $\begingroup$ @jkuczm This sounds interesting. My only concern is that then each equation definition should either contain a BeginPackage[]-EndPackage[] or each symbol should be written with its full name. Both are pain in the ass if not done programmatically. Would you perhaps care to develop an answer based on the idea? $\endgroup$ – István Zachar Mar 8 '16 at 15:27
4
$\begingroup$

There seems to be no way to fool the parsing, so you will either have to store the symbols in a separate file, or use some form of ToExpression.

What I would suggest is to use a softer version of ToExpression, and represent the r.h.s. as boxes rather than as a string. Here is what I mean:

BeginPackage["myPackage`"];

load;
boxed;
$context = $Context;
$contextPath = $ContextPath;

Begin["`Private`"];

boxed /: SetDelayed[lhs_, boxed[rhs_]] :=
  With[{boxedrhs = MakeBoxes[rhs]},
    lhs := Block[{$Context = $context, $ContextPath = $contextPath},
       ToExpression[boxedrhs]
    ]
  ]; 

Begin["`Temp`"]

load[] := boxed[{a, b, c}];

End[]
End[]
EndPackage[]; 

The symbols will still be created during parsing, but they will go into the `Private`Temp` context. If you want, you can remove them, adding a line like Remove["`Private`Temp`*"] at the end of the package.

The rhs of load is then converted to boxes. When load is called, the symbols get parsed with the same environment ($Context, $ContextPath), as if they would've been parsed inside the package normally.

$\endgroup$
  • $\begingroup$ This might just be what I'm looking for. Interestingly, I couldn't find an example where Mathematica does something similar with e.g. ExampleData. They are very careful to explicitly protect package variables, or use either strings or formal symbols. $\endgroup$ – István Zachar Mar 8 '16 at 15:21
  • $\begingroup$ @IstvánZachar One other possibility that came to mind but that I didn't mention is that you could DumpSave symbols to a string (or several strings), and then use something like ImportString to load them. The advantage of this scheme is that symbols will come already in the right context(s), bypassing the parsing, The disadvantage is that it is rather opaque. $\endgroup$ – Leonid Shifrin Mar 8 '16 at 15:26
  • $\begingroup$ It works nicely and provides a minimal overhead (wrapping each equation in boxed I can do easily). Let me just test it a bit more. $\endgroup$ – István Zachar Mar 8 '16 at 15:37
1
$\begingroup$

You could store symbols related to separate equation systems in separate contexts. This way you could add/remove those additional contexts, to/from $ContextPath, at will.

How to automate assigning of appropriate symbols to appropriate contexts depends on how exactly you're defining your equation systems. You could for example post-process definition of your load function and change context of all symbols from myPackage`Private` context found in DownValues[load].

BeginPackage["myPackage`"];
Unprotect["`*"];
Remove@Evaluate[# <> "*"] & /@ $contexts;
ClearAll["`*"]

load;
unload;
$contexts;

Begin["`Private`"];
ClearAll["`*"]

load["eq12"] := {a, b, c};
load["eq35"] := {x, y, z};

$contexts = {};

Module[{tag},
    $contexts = Join @@ Last@Reap[
        DownValues[load] = DownValues[load] /. (
            lhs : Verbatim[HoldPattern]@HoldPattern@load[eqName_String] :>
                rhs_
        ) :>
            With[{context = "myPackage`" <> eqName <> "`"},
                Sow[context, tag];
                (lhs :> (
                    If[! MemberQ[$ContextPath, context], 
                        PrependTo[$ContextPath, context]
                    ];
                    rhs
                )) /.
                    sym_Symbol /; Context[sym] === "myPackage`Private`" :>
                        RuleCondition@Symbol[
                            context <> SymbolName@Unevaluated[sym]
                        ]
            ]
        ,
        tag
    ]
];

unload[eqName_String] :=
    With[{context = "myPackage`" <> eqName <> "`"},
        ($ContextPath = DeleteCases[$ContextPath, context]) /; 
            MemberQ[$contexts, context]
    ]

End[];
EndPackage[];
$ContextPath = DeleteCases[$ContextPath, Alternatives @@ $contexts];

myPackage` context contains only package public symbols:

Names["myPackage`*"]
(* {"load", "unload", "$contexts"} *)

symbols related to specific equation systems are in separate contexts:

Names[# <> "*"] & /@ $contexts
(* {
    {"myPackage`eq12`a", "myPackage`eq12`b", "myPackage`eq12`c"},
    {"myPackage`eq35`x", "myPackage`eq35`y", "myPackage`eq35`z"}
} *)

which are not in $ContextPath:

$ContextPath
(* {"myPackage`", "CloudObjectLoader`", "StreamingLoader`",
    "IconizeLoader`", "PacletManager`", "System`", "Global`"} *)

After loading equation system:

load["eq12"]
(* {a, b, c} *)

appropriate context is added to $ContextPath:

$ContextPath
(* {"myPackage`eq12`", "myPackage`", "CloudObjectLoader`", "StreamingLoader`",
    "IconizeLoader`", "PacletManager`", "System`", "Global`"} *)

and appropriate variables are identified with contexts of appropriate equation systems:

Context[a]
(* "myPackage`eq12`" *)

After unloading of equation system its context is removed from $ContextPath:

unload["eq12"]
(* {"myPackage`", "CloudObjectLoader`", "StreamingLoader`",
    "IconizeLoader`", "PacletManager`", "System`", "Global`"} *)
$\endgroup$
  • $\begingroup$ While this is a nice solution, there is one aesthetic problem (the exposure of subcontexts) and one technical: the bulletproof generation of unique context names for each system. Nevertheless, deserves the +1. $\endgroup$ – István Zachar Mar 9 '16 at 13:08

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.