9
$\begingroup$

I'm trying to create a function (in a package) to import a lot of data and create a symbol with UpValues to return specific results about the data. I've got it working fine in a regular notebook, but when I define the function within a package I can't get it to work properly.

A simple example of my problem:

TestPackage.m:

BeginPackage["TestPackage`"];

testFunction::usage= "testFunction[] does all kind of interesting things.";

Begin["`Private`"]

testFunction[name_Symbol]:=
 Module[{},(
  ClearAll[name];
  UpValues[name]=
   {
    test[name]->"hello!"
   };
  Protect[name];
 )]

End[]

EndPackage[]

I then call the function in a regular notebook:

Needs["TestPackage`", "/path/to/TestPackage.m"]

testFunction[a]
  • test[a] returns test[a]
  • and TestPackage`Private`test[a] returns the expected "hello!"

How do I change testFunction so that test[a] also returns "hello!"?

$\endgroup$
6
  • 1
    $\begingroup$ Welcome to Mathematica! I cannot tell you how much I appreciate your efforts in correctly marking up your question. Although, I did fix the issue with context names, which involved wrapping the whole thing in double grave marks, not single ones, but most people are not aware of that. I would encourage you to register your account so you can more fully participate here. Again, welcome, and I hope to see you here again. $\endgroup$
    – rcollyer
    Commented Dec 6, 2012 at 20:43
  • $\begingroup$ Thanks for the welcome @rcollyer! And for fixing my source, I couldn't figure out how to do that. $\endgroup$
    – jlmr
    Commented Dec 6, 2012 at 21:01
  • $\begingroup$ You're welcome. Three questions. 1. Why are you setting UpValues[...] directly, instead of using either UpSet (^=) or TagSet (name /: test[name] = ...) directly? 2. Have you tried setting a usage string on test, or if you have considered that why doesn't it do what you need? 3. Do you have any purpose for Module other than to provide convenient structure, as it doesn't do anything in your code? $\endgroup$
    – rcollyer
    Commented Dec 6, 2012 at 21:12
  • $\begingroup$ 2 answers and 1 question in return: 1. I didn't know about UpSet. It seems to produce the same results though. What's the difference between setting the values directly and using UpSet? 2. Where would I set a usage string on test? In the function definition or somewhere else? 3. It's true that Module doesn't serve any purpose here. It does however in the actual function I'm trying to write, which also needs a couple of temporary variables to store results. I thought it wise to include Module, since it also deals with scopes. $\endgroup$
    – jlmr
    Commented Dec 6, 2012 at 21:16
  • $\begingroup$ 1. There's no difference between what you're doing and UpSet, just that UpSet is the common way of accomplishing what you're doing, so more readily recognizable. 2. Set the usage string in the preamble of the package like you do for testFunction. 3. I figured that was the reason, but I encountered at least one person who believed it was performing localization while using it like in your code. $\endgroup$
    – rcollyer
    Commented Dec 6, 2012 at 21:45

2 Answers 2

9
$\begingroup$

The explanation can be found in the doc to BeginPackage and Begin

  • BeginPackage["context`"] makes context` and System`the only active contexts.
  • Begin["context`"] resets the current context.

Therefore, at the moment you first mention the symbol test it is not created it in the Global context. This can be seen from you (simplified) example

BeginPackage["TestPackage`"];
testFunction::usage = 
  "testFunction[] does all kind of interesting things.";
Begin["`Private`"];
testFunction[name_Symbol] := name /: test[name] := "hello!";
End[];
EndPackage[]

testFunction[a]
UpValues[a]

(* {HoldPattern[TestPackage`Private`test[a]] :> "hello!"} *)

beside giving test a ::usage message, you can give the context explicitly

testFunction[name_Symbol] := name /: Global`test[name] := "hello!";

Further note

  • I don't see why you use Module because you don't localize any variable. And if you want to input a sequence of commands, divided by ; (called a CompoundExpression) you can simply wrap it in parenthesis, as you did.
  • Your combination of ClearAll and Protect is such that you can call testFunction on a given Symbol only one time. On the second call you get messages since ClearAll cannot clear a protected symbol. What is the intention behind this?
$\endgroup$
3
  • $\begingroup$ Thanks for your answer. 1. Adding a ::usage message fixes the problem. I won't set the context explicitly, because I also want to call the function from other contexts than Global` . 2. Module is not used here, but will be used in the full package. 3. I used Protect because it prevents from accidentally setting a value to name which prevents the UpValues form working correctly. If there is a better way, I'd like to hear it. Thanks again! $\endgroup$
    – jlmr
    Commented Dec 7, 2012 at 9:03
  • $\begingroup$ @jlmr you may wish to Unprotect the Symbol at the beginning of your module. $\endgroup$
    – Mr.Wizard
    Commented Dec 7, 2012 at 12:20
  • $\begingroup$ @Mr.Wizard thanks for the tip! $\endgroup$
    – jlmr
    Commented Dec 7, 2012 at 12:40
7
$\begingroup$

You should export the test symbol, ie define it between BeginPackage["TestPackage`"] and Begin["`Private`"], for example by giving a usage to test.

test::usage = "test[name] returns ..."
(*or just*) test;

for your definition, as said already in comments, I would use

test[name] ^= "hello";

My answer to this post contains many application of UpValues, it could interest you: Struct equivalent in Mathematica?

You could also not export the symbol, that will be thus in the Private context of the package, and use the full name of test

TestPackage`Private`test[name] ^= "hello";
$\endgroup$
0

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.