1
$\begingroup$

When writing packages I met the following problem. The way I name helper functions is like

BeginPackage["test`"];
ClearAll@@Names[$Context<>"*"];

test0[]:=0;
test0`helper1[]:=0;
test0`helper2[]:=0;
...

EndPackage[];

When Get["test`"] the public symbols under test` will be cleared by ClearAll@@Names[$Context<>"*"]; but the symbols under test0` are remained unchanged.

I need a fast way of searching and clearing all symbols under the contexts like symbol`*, where symbols are from some specific contexts.

To set up and test the problem, the following codes generate 10 symbols for each of the 200 sub-contexts:

BeginPackage["test`"];

ToExpression["
    test"<>#1<>"[]=0;
    test"<>#1<>"`a"<>#2<>"[]=0;
"]&@@@Flatten[
    Outer[List,ToString/@Range[200],ToString/@Range[10]],
    1
];

EndPackage[];
Names["test`*"]//Shallow
Names["test1`*"]
Names["test2`*"]

enter image description here

The direct way of clearing these helper functions is like

clearHelper[contexts__] :=
    (
        {contexts}//Map[Names[#<>"*"]&]//
            Map[ClearAll@Evaluate[#<>"`*",#<>"`*`*"]&,#,{2}]&;
    );

which is rather inefficient

test1`a1[]
clearHelper["test`"]//Timing
test1`a1[]

enter image description here

In some days earlier I used another method of managing helper functions: each symbol with helper functions is tagged by

test0//hasHelpers

where hasHelpers will store test0 into a public list $hasHelpers shared by all packages. Then I abandoned this method since frequently appending symbols to list will slow down Get and I'm tired of adding test0//hasHelpers.

Is there some clever way of solving this problem?


Or are there editors or IDEs that can auto-complete contexts? e.g. symbols like test0`helper1[] within BeginPackage["test`"] or Begin["`Private`"] will be parsed into *.wl as

test`test0`helper1[]

automatically?

$\endgroup$
7
  • 2
    $\begingroup$ Is Names["test*`*"] adequate? That syntax is also supported by Clear and Remove. $\endgroup$
    – WReach
    Commented Oct 27, 2022 at 3:39
  • $\begingroup$ @WReach No, the symbols in a package test` are certainly not fitted into test*`* . A feature I forgot to say is that the association from symbols like testn to the contexts testn` can be quite sparse since only a few functions need helper functions. And the naive inefficient function clearHelper just traverses this association one by one. $\endgroup$
    – Lacia
    Commented Oct 27, 2022 at 3:51
  • $\begingroup$ Maybe you dismissed the first comment too quickly? With Names["test*`*"] I get {test0`helper1, test0`helper2, test0}. Note the last one, test0, which is in the test` context. $\endgroup$
    – user293787
    Commented Oct 27, 2022 at 6:17
  • 3
    $\begingroup$ Is there a reason why you don't want to follow the standard way: BeginPackage["Test`"]; test0; Begin["`Private`"] test0[]:=helper1[] ... .... End[]; EndPackage[]. Then you could just do ClearAll["`*", "`*`*"] and still only test0 is exported from the package. $\endgroup$
    – Kuba
    Commented Oct 27, 2022 at 6:48
  • $\begingroup$ @user293787 the functions in test` are not named as test0 in REAL examples. $\endgroup$
    – Lacia
    Commented Oct 28, 2022 at 0:44

2 Answers 2

3
$\begingroup$

Start with what is a package.

Then you can do

ClearAll["`*`", "`*`*"]

"`*" is a shorthand for $Context<>"*" and "`*`*" will find even 'deeper' symbols like test1`a`b`c`foo

Related: 189660

If you insist on a private context specific to an exported function then the final code can be:

BeginPackage["Test`"]

  ClearAll["`*","`*`*"]

  test0::usage = ""
  test1::usage = ""

Begin["`test0`"]


  test0[]:= helper01[];

  helper01[]:=0;

End[]


Begin["`test1`"]


  test1[]:= helper11[];

  helper11[]:=2;

End[]

EndPackage[]
test0[]
test1[]

0 2

Notice that you could have helper01 and helper11 called both helper and it would work just fine because they are created in different contexts:

DownValues@test0

{HoldPattern[test0[]] :> Test`test0`helper01[]}

But I don't think this is a good idea because it could be confusing to have the same name within a single file.

$\endgroup$
1
$\begingroup$

For now I have changed the code styles, roughly following from https://github.com/WolframResearch/PacletCICD-Examples-AdvancedSample

For the original problem, the following function is faster

clearHelperFunction[contexts__,level_Integer:1] :=
    Module[ {helperList,helperContextList},
        helperContextList =
            Quiet[NestList[#<>"`*"&,#<>"`*",level-1],{StringJoin::string}]/.{all_:>Hold[all&]}//ReleaseHold;
        helperList =
            {contexts}//Map[Names[#<>"*"]&]//Map[helperContextList,#,{2}]&//Flatten;
        helperList = 
            DeleteCases[helperList,Alternatives@@`clearHelper`exceptionList];
        ClearAll@@helperList
    ];
clearHelperFunction[] :=
    StringCases[$ContextPath,StartOfString~~selfContext~~___]//Flatten//Apply[clearHelperFunction];
`clearHelper`exceptionList = {
    "FE`*"
};

enter image description here

There are quite tricky issues concerning Names, ClearAll, ..., e.g.

SeedRandom[1]

data=RandomWord[100];

data//Map[ClearAll]//Quiet//RepeatedTiming//First
data//Apply[ClearAll]//Quiet//RepeatedTiming//First

enter image description here

see also Why Names will search all subcontexts?


I'd also like to compare the different code editors and styles I have tried.

Editors

  • notebook, inconvenient for multi-file project;
  • vscode + Wolfram extension, the formatting functionality is a bit weird;
  • eclipse + Wolfram workbench, outdated, e.g. do not support |-> and non-English characters

Currently I use the last one.

Style

  • main function foo with helper functions foo`h1, foo`h2, ... - the approach in this question. This is really bad but the drawbacks can be cured:

    • symbols can collide. There can be different versions of foo, then the helpers collide.

    • hard to manage.

    • Solution: use $ContextAliases to resolve the context foo` to package`foo`Private`. But, it seems that in version 13.3 $ContextAliases is quite slow.

  • main function foo with helper functions `foo`h1, `foo`h2, .... This could be the most convenient approach. Drawback:

    • Wolfram workbench does not support renaming symbols starting from backtick like `foo`h1
  • multiple subcontext in single file by Begin, End. As pointed by @kuba, this can cause confusion and is hard to rename.

  • single subcontext for each file, see e.g. https://mathematica.stackexchange.com/a/176591/86893 https://github.com/WolframResearch/PacletCICD-Examples-AdvancedSample. This could be the best approach without undocumented functionalities or third-party paclets. see also another style What to be aware when using new-style package?

$\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.