4
$\begingroup$

In this question, there is a beautiful method to copy a symbol. However, i'm not able to use it as an addon to my package. So the main question is

How to rename/copy a function defined in a package by loading one of its sub packages ?

I'll try to explain that in an minimal example

Let's say I have a package A`

BeginPackage["A`"]
f::usage = "My function";
Begin["Private`"];
f[x_] := x^2;
End[]
EndPackage[];

Due to the fact, that f was earlier named g and I would like my old notebooks still to work (and though I can rewrite my package functions, I would like to have a kind of legacy package (okay I could also just copy my Package and load the old or the new one - despite debugging twice that works)), so I define something like

BeginPackage["A`Sub`"]
SetAttributes[copy,HoldFirst];
new_~copy~org_:=With[{prop={Attributes,UpValues,OwnValues,DownValues,SubValues,NValues,FormatValues,Messages,Options}},ClearAll@new;
Set[#@new,#@org/.HoldPattern@org:>new]&~Scan~prop;]
(* Start copying*)
g~copy~A`f;
EndPackage[];

(using the great idea of the copy from above). Here I'll use A`f to avoid shadowing problems (and put this Sub.m in the subdirectory A/ of the A.m).

But if I now try to use that, say in a notebook (ATest.nb inside the same directory as A.m)

$Path = Join[$Path, {NotebookDirectory[]}];
Needs["A`"];

Of course f works as intended, but loading

Needs["A`Sub`"];

does not make g available as a copy of f (and does neither report any error). Though, executing the cells of Sub.m directly one by one does. Of course, i don't want to specify g::usage because that should be obtained by copying from f. I think it might depend on the context, but I can't see why and how.

What am I missing here?

Update

As Albert pointed out, rm -rf s solution below should be extended using a Private` area for the copy function. Then the sub package providing the old names looks like the following

BeginPackage["A`Sub`",{"A`"}];
Begin["Private`"];
    SetAttributes[copy,HoldFirst];
    new_~copy~org_:=
    With[{prop={Attributes,UpValues,OwnValues,DownValues,SubValues,NValues,FormatValues,Messages,Options}},
    ClearAll@new;
    Set[#@new,#@org/.HoldPattern@org:>new]&~Scan~prop;
    ];
    (* Start copying*)
    A`Sub`g~copy~A`f;
End[]
EndPackage[];

Then the right operand of copy (the original f) needs the context A` and the left one, g (in order not to be private) the context A`Sub` (due to the fact that ::usage should also be copied from f.

$\endgroup$
5
  • $\begingroup$ The package A`Sub` does not know about A` , so you should load Needs["A`"] immediately after BeginPackage["A`Sub`"]. I haven't run your code, but can you try the above and see if it works (use a fresh kernel, etc.). If it does, I'll post it as an answer. $\endgroup$
    – rm -rf
    Commented Mar 1, 2013 at 15:15
  • $\begingroup$ Oh. Didn't think of that one, because it's a sub package. I adapted your idea and used BeginPackage["A`Sub`",{"A`"}] (and learned about the double ticks), it works. Though my eyes hurt, that the subpackage has to load it's parent; but I'll have to get used to that I think. Works fine :) $\endgroup$
    – Ronny
    Commented Mar 1, 2013 at 15:20
  • 2
    $\begingroup$ @Ronny: I think for production code you would want to insert an additional pair of Begin and End: as it is written now, you'll also export copy, prop, new and org along with g... $\endgroup$ Commented Mar 1, 2013 at 16:00
  • $\begingroup$ Thanks, I think setting copy private is a good idea, but up to know, I'm not able to adapt the answer (which works fine for the MWE) to the production code - though I checked character by character - the function does not get copied :/ $\endgroup$
    – Ronny
    Commented Mar 1, 2013 at 16:06
  • 1
    $\begingroup$ Nice to see that my question was usefull. :) $\endgroup$
    – Murta
    Commented Mar 2, 2013 at 1:15

1 Answer 1

5
$\begingroup$

Mathematica packages are isolated, in the sense that A`Sub` need not necessarily be a "sub-package" of A`, unless you explicitly make it so. Note that when you call BeginPackage["A`Sub`"], the context path is temporarily changed to just {"A`Sub`", "System`"}, meaning it has no idea about functions in A`. To fix this, you need to load A` in your sub-package as:

BeginPackage["A`Sub`"]
Needs["A`"]
...
EndPackage[]

Now as you note, there is another way to load it and that is via BeginPackage["A`Sub`",{"A`"}]. These two ways of loading are not equivalent, as mentioned in this answer. Be sure to read that and use what best suits your needs.

$\endgroup$
7
  • $\begingroup$ Well for the MWE from above both approaches work fine - adapting that to my original package, nothing's changed there - but I'll check that slowly again now, whether i missed something (though there is not much to miss). $\endgroup$
    – Ronny
    Commented Mar 1, 2013 at 15:34
  • $\begingroup$ @Ronny Is A` (or the equivalent in your application) being loaded? Can you check the messages window for any warnings? Can you try inserting a Print@$ContextPath in places to see whether the contexts are being changed/loaded/etc correctly? $\endgroup$
    – rm -rf
    Commented Mar 1, 2013 at 16:13
  • $\begingroup$ Yes, it is loaded, there is only one minor difference. The function f is defined in A`Sub1 (which is also loaded and in the ContextPath), there are some Messages (because I still have to work on one unrelated ::usage), but none related to that. In A`Sub2` I set `copy` to private and copy a function (say `f` to `g`) and doing that manually (in the notebook corresponding to `TestA`) works fine, but using the Sub2`` package the problem persists as described above (though for the MWE it works and I transcribed the solution letter by letter checked three times). $\endgroup$
    – Ronny
    Commented Mar 1, 2013 at 16:18
  • $\begingroup$ My guess is that there's still some problem with getting the right contexts for the symbols depending on how you've set it up, because as is, it looks like it should work. Can you try this (in a fresh session) — After loading the parent as in my answer, do copy[A`Sub1`f, A`Sub2`g] i.e., with the full contexts (change the call to copy according to your definition of old/new) $\endgroup$
    – rm -rf
    Commented Mar 1, 2013 at 16:48
  • $\begingroup$ Running that after Loading A and at least Sub1 in a Notebook on a freshly started Kernel just works fine. With full contexts. But even if i define copy in Global context just Needs A`Sub2 won't work (neither does that work with a Sub2-local copy) to copy f to g in their contexts (tried putting none, one (each) and both in Context of their packages. $\endgroup$
    – Ronny
    Commented Mar 1, 2013 at 17:15

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.