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$ 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$ 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$ 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$ Commented Oct 27, 2022 at 6:48
  • $\begingroup$ @user293787 the functions in test` are not named as test0 in REAL examples. $\endgroup$ 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$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.