10
$\begingroup$

I'm trying to make a function f that takes a symbol, a value, and a context, and creates the symbol in the requested context and assigns it the passed value. The obvious doesn't work. I.e.:

Attributes[f]={HoldAllComplete};
f[symbol_,value_,context_]:=(Begin[context];Set[symbol,value];End[])

Creates the symbol in the current context, as opposed to the one that is passed as an argument. This seems to happen because the FrontEnd, as soon as it sees f getting evaluated, creates the symbol in the current context despite HoldAllComplete. I tried adding Remove[symbol] to the function but that doesn't work. Any thoughts?

$\endgroup$
1
  • $\begingroup$ I should clarify that I really need to pass the symbol, not the symbol's name. I realize it can be done when passing the symbol's name, but I can't use that approach because it would break a lot of existing code. I realize that I can overload the function and have a legacy version that takes the symbol and does not allow contexts, and one that takes in strings and does allow for contexts, but ideally I would like to avoid such a hack. $\endgroup$ Commented Apr 4, 2013 at 18:47

3 Answers 3

7
$\begingroup$

The problem is that if you pass a symbol, it will be created already during the parsing stage, when you pass it, in the current context. Therefore I suggest to pass its string name instead. This function will do the job:

ClearAll[f];
f[symbolName_String, value_, context_] :=
  Block[{$ContextPath},
    BeginPackage[context];
    ToExpression[
       symbolName, 
       StandardForm, 
       Function[name, Set[name, value], HoldAll]
    ];
    EndPackage[]
  ] 

for example

f["a", 10, "Test`"]
Test`a

(* 10 *)

If you still want to pass a symbol, you can use this:

ClearAll[f];
f[symbol_, value_, context_] :=
  With[{set = MakeBoxes[symbol = value]},
    Block[{$ContextPath},
      BeginPackage[context];
      ReleaseHold[MakeExpression@set];
      EndPackage[]]];

which is a version of the code I used here. But be aware that you will also create the symbol symbol in the current working context, so you may additionally use Remove to remove it.

So, for example:

f[a, 20, "Test`"]
Test`a

(* 20 *)
$\endgroup$
6
  • $\begingroup$ Thanks for the suggestion but this won't work for my needs. See the comment that I added to the question. $\endgroup$ Commented Apr 4, 2013 at 18:49
  • 2
    $\begingroup$ @OleksandrR. The symbol with the same name may exist in some other context currently on the $ContextPath, in which case that one will be used and assigned a value - which is obviously not what one would like here. The use of BeginPackage and EndPackage effectively allows to reset the $ContextPath to just {"MyContext`","System`"}, which solves this problem. $\endgroup$ Commented Apr 4, 2013 at 19:24
  • $\begingroup$ @MohammedAlQuraishi I added a symbol-based version, see my edit. $\endgroup$ Commented Apr 4, 2013 at 19:29
  • $\begingroup$ @LeonidShifrin: Great, thank you! $\endgroup$ Commented Apr 5, 2013 at 3:02
  • 1
    $\begingroup$ @Mr.Wizard I will use your usual reply: "great minds think alike" :) $\endgroup$ Commented Apr 5, 2013 at 15:41
4
$\begingroup$

I believe this is what you want:

f[symname_String, value_, context_] := 
 (Begin[context];
  With[{s = Symbol[symname]}, Set[s, value]]; End[])

Then use it like this:

 f["myvar", 4, "MyContext`"]

Verify:

? MyContext`myvar

Hope that helps

$\endgroup$
3
  • $\begingroup$ Really? Maybe if you do a ClearAll[f], or restart a new, fresh Mathematica session? or give the function another name? I am guessing your old definition for f is in conflict with the one I propose. Notice the idea is the same as the one by Leonid, give the name as a string "myvar" instead of myvar $\endgroup$ Commented Apr 4, 2013 at 18:46
  • 1
    $\begingroup$ It does work for me. But, invoking f twice for the same symbol will produce an error. You need to take Leonid's approach of using ToExpression rather than Symbol if you want to avoid that. $\endgroup$ Commented Apr 4, 2013 at 18:56
  • $\begingroup$ @QuantumMathematica Yes indeed, on a fresh kernel it does work. $\endgroup$ Commented Apr 4, 2013 at 19:33
2
$\begingroup$

I believe this meets your specification:

Attributes[f] = {HoldAllComplete};

f[symbol_, value_, context_] :=
 ToHeldExpression[context <> SymbolName@Unevaluated@symbol] /. _[x_] :> (x = value)
$\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.