0
$\begingroup$

I'm trying to be sneaky with a function and try to have an expression stored as a string (unevaluated) until a desired moment when it will be converted from a string to an expression and then have it parsed.

I am doing this because some computations are quite heavy and depending on what I want from the function I can either get the "a" or "b" outputs/paths. I suspect that SetDelayed or TagSetDelayed would probably be better suited here, but I'm not certain how to use them in this context.

This works beautifully as a normal function (non-packaged) in a notebook (v.13.2) however the moment I introduce this idea in a package environment, it fails to parse it.

The packaged version:

BeginPackage["SE`Question`"];
func1; (* declared in public part of package *)
Begin["`Private`"];
func1[a_,b_,output_:"a"]:=Block[{range,data1,data2,dict,dat1,dat2},
range=Range[10];
data1 = Table[i^a,{i,10}];
data2 = Table[i^b,{i,10}];
dict=<|
"a"->"{range,data1}\[Transpose]",
"b"->"{range,data2}\[Transpose]"
|>;
ToExpression[Evaluate[dict[output]]]
]
End[];
EndPackage[];

The non-packaged version

Clear[func2]
func2[a_,b_,output_:"a"]:=Block[{range,data1,data2,dict},
range=Range[10];
data1 = Table[i^a,{i,10}];
data2=Table[i^b,{i,10}];
dict=<|
"a"->"{range,data1}\[Transpose]",
"b"->"{range,data2}\[Transpose]"
|>;
ToExpression[Evaluate[dict[output]]]
]

Output

func1[2,4,"a"] (* the packaged function *)
func2[2,4,"a"]  (* the non-packaged function *)

enter image description here

If someone could please explain why this happens or better yet suggest an alternative I would be very grateful.

$\endgroup$
1
  • $\begingroup$ It seems that replacing the association part with dict=<| "a"->out1, "b"->out2 |>; Evaluate[dict[output]]/.{out1:>{range,data1}\[Transpose],out2:>{range,data2}\[Transpose]} using RuleDelayed does the trick. $\endgroup$
    – alex
    Mar 1 at 14:21

1 Answer 1

2
$\begingroup$

Explanation

When you evaluate

BeginPackage["SE`Question`"]

it resets the $ContextPath (EndPackage resets it back). A consequence of this is that when new symbols are encountered they will be in the

SE`Question

context. So, in your Block, the symbol data1 is (fully qualified)

SE`Question`data1

Now, when you're back in the Global context and you evaluate func1[2, 4, "a"], the data1 string becomes the data1 symbol, but that symbol fully qualified is

Global`data1

So, it doesn't have the value that you expect (and apparently is undefined, which is why it just displays as itself without resolving to some other value).

The simple fix

Just don't wrap your expressions in strings:

BeginPackage["AnotherSE`Question`"];
func4; (*declared in public part of package*)
Begin["`Private`"];
func4[a_, b_, output_ : "a"] := 
Block[
  {range, data1, data2, dict, dat1, dat2}, 
  range = Range[10];
  data1 = Table[i^a, {i, 10}];
  data2 = Table[i^b, {i, 10}];
  dict = <|"a" -> Transpose[{range, data1}], "b" -> Transpose[{range, data2}]|>;
  dict[output]]
End[];
EndPackage[];

The slightly better fix

In general, one should be careful to understand how variables are localized and to avoid letting local variables "leak". In your case, you don't need all of those extraneous variables at all.

BeginPackage["NewSE`Question`"];
func3; (*declared in public part of package*)
Begin["`Private`"];
func3[a_, b_] := func3[a, b, "a"];
func3[a_, _, "a"] := theRealStuff[a];
func3[_, b_, "b"] := theRealStuff[b];
func3[___] := "func3 is undefined for these arguments";
theRealStuff[x_] := Transpose[{Range[10], Range[10]^x}];
End[];
EndPackage[];

But even this is really awkward. A function that doesn't depend on some of its arguments should be a red flag. Maybe this is just a consequence of you trying to simplify your real problem, but if this is truly representative of what you're trying to do, then a better pattern would be to make the choice of which value to pass to the function outside of the function, as opposed to using a flag to choose which argument to process inside the function. But since you haven't shown us the larger context in which you'd choose the "a" or "b" flags, I don't have a demonstration for how to do that.

$\endgroup$
11
  • $\begingroup$ Hi @lericr! Thank you for your response. I suspected that I was messing with some Context, but could not troubleshoot it. I cannot understand why data1 would be in Global` as it is defined explicitely within Block. Why would that suddenly change? As for what i'm trying to do, I wanted to have a function that is my "go-to-do-all" Fourier function. Sometimes I want the frequency component, sometimes I want the phase component. My dirty workaround was the association trick which apparently does not like being in a package! $\endgroup$
    – alex
    Mar 1 at 16:47
  • $\begingroup$ The association is a red herring--that has nothing to do with the failure here. $\endgroup$
    – lericr
    Mar 1 at 17:02
  • $\begingroup$ Yes, data1 was inside a block. It was also inside a package definition, so it's fully qualified name had that package name as its context. But you executed the function in the Global context, and that is the moment when the string was turned into an expression. So, when the evaluator "saw" the symbol data1, it interpreted it as being in the Global context. $\endgroup$
    – lericr
    Mar 1 at 17:04
  • $\begingroup$ So, what you could do (I don't think it's a good idea, but it will work and will maybe help demonstrate what's going on), is inside your definition for func1, replace the string range with the string SE'Question'range and similarly for data1 and data2. (those single quotes should be backticks, but backticks make this comment formatted awkwardly.) $\endgroup$
    – lericr
    Mar 1 at 17:06
  • 1
    $\begingroup$ "I tend to use Block because I want to ensure that whatever variables I use will not overlap with anything I might have or will have". The better way is to make your functions depend only on their arguments (and other previously defined functions). $\endgroup$
    – lericr
    Mar 1 at 18:21

Your Answer

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

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