This is possible in the interactive session with $PreRead
. I will adopt my solution to the same problem posted in this Mathgroup thread. To quote my explanation from there, the essence of the present solution is to delay the parsing of the code (body) that must be executed inside a given context until run-time, that is, replace code
BeginPackage[yourcontext]
body
EndPackage[]
with something like
BeginPackage[yourcontext]
Unevaluated[ToExpression[body-in-the-box-form]]
EndPackage[]
This leads to parsing being performed after BeginPackage
statement
has been executed, which solves the problem.
So, here is the code:
ClearAll[stringify];
stringify[code_RowBox] :=
ToString[code /. x_String :> ToString[x, InputForm]];
ClearAll[shieldBody];
shieldBody[body_String]:=
RowBox[{"Unevaluated","[",RowBox[{"ToExpression","[",body,"]"}],"]"}]
ClearAll[namespaceF];
namespaceF[code_]:=
code/.
RowBox[{"inContext","[",RowBox[{context_,",",body_RowBox}],"]"}]:>
RowBox[
{
RowBox[{"BeginPackage","[",RowBox[{context}],"]"}],
";",
shieldBody[stringify[body]],
";",
RowBox[{"EndPackage","[","]"}]
}
];
To make this work, you will need to assign this to $PreRead
:
$PreRead = namespaceF;
The reason you need $PreRead
is that it is the only hook which is called before the parsing takes place. Anything you do after the parsing is over, including using $Pre
, putting stuff in Hold
, etc., won't help, because the contexts assigned to symbols during the parse-time. This also explains why your code didn't work.
Here is an illustration then:
inContext["TestContext`", Print[$Context]; z = 7; Print[Context[z]]]
During evaluation of In[18]:= TestContext`
During evaluation of In[18]:= TestContext`
Context[z]
(* "TestContext`" *)
Of course, since BeginPackage
is used, the symbol gets imported (the context stays in the $ContextPath
before Global`
), so subsequent references to z
in Mathematica session will reference TestContext`z
. If this importing is not desirable, one can change the BeginPackage
to some different code.
Note that this construct does not support nested contexts created with Begin
and End
- this is doable but I did not implement this. For instance, in
the following you may expect the symbol to be created in the `Private`
context:
inContext["TestContext`", Begin["`Private`"];test = 10;End[]]
But this is not so - it was created in TestContext`
and subsequently
imported, since by the parse-time that was the current context:
Context[test]
(* "TestContext`" *)
Now, for the packages, $PreRead
won't work, but one can get similar functionality by overloading Get
/ Needs
. I won't elaborate on this more here at this time, though.