Group Abstract Group Abstract

Message Boards Message Boards

5
|
9.3K Views
|
8 Replies
|
19 Total Likes
View groups...
Share
Share this post:

How to setup and organize larger paclets as of v13+

8 Replies
POSTED BY: Bob Sandheinrich

Thanks. I replied in GitHub.

POSTED BY: Bob Sandheinrich

Hi Jason,

my bad, of course, during development, one needs to start out with:

PacletDirectoryLoad[ paclet_directory ]
PacletDataRebuild[]

before Needs[...] is used to load the paclet. When I tested my setup, I did not have to add the other contexts, but I may have forgotten, that I in fact did. :)

EDIT

Using Get puts contexts on $ContextPath, which is what the recommendation given by Todd Gayley wants to avoid, e.g., we would like to refer to paclet functions using alias`funcname[ ].

EDIT2:

I just tested again on a MacBook Pro M1 Max (ARM64) using v13.1 and it should work as I described without having to provide additional "Context" information.

I tested using Eclipse and it does indeed not work without providing all context information.

Using Get puts contexts on $ContextPath

Using Get or Needs in the private portion of a package does not pollute the user's context path:

In[5]:= cpath = $ContextPath;
In[6]:= Needs["PublisherID`PacletName`"];
In[7]:= Complement[$ContextPath, cpath]

Out[7]= {"PublisherID`PacletName`"}
POSTED BY: Jason Biggs

Interim Conclusion

After having done a bit more testing and updating of my code (see Revisions section in my original post), the following picture emerges:

Using full-references with Needs["context' " -> None] as proposed by OP

  • aliases in main package serve as a “lookup-table”, i.e., it is immediately clear where a function is implemented and can thus serve as a central reference for development
  • full-name access to functions is possible, e.g., we may call PuiblisherID`PacletName`Special`Private`specialFunc directly from a notebook that has loaded the paclet
  • Using full-name external function calls in the sub-packages is a bit cumbersome, albeit (typically) just the main paclet context needs to be called
  • In Workbench/Eclipse we can use F3 from the main package to immediately jump to the code that is implementing the function in a sub-package
  • This implementation is completely compatible with Workbench/Eclipse, i.e., for development all you need to do is to comment out the Needs[ ] in the main package. Run as Wolfram will then work (if you have gotten rid of things like <<init.m and what have you in the run configurations).

Loading sub-packages into the main private context as proposed by Jason Briggs

  • it is not clear where a function has been implemented from looking at the main package
  • full-name access to functions is not possible, e.g., we cannot call PuiblisherID`PacletName`Special`Private`specialFunc from a notebook any more
  • short name references in the sub packages for external functions are more convenient
  • In Workbench/Eclipse it is not possible to use F3 to immediately jump to a functions implementation
  • The implementation is not compatible with Workbench/Eclipse (at least I have not managed to get it running with Run as Wolfram).

So far, these points make this a "matter of taste". Am I missing something? Is there something else that will clearly tilt the scales to one side?

EDIT

I added Workbench/Eclipse specific detail (F3) I addressed Workbench/Eclipse compatibility regarding runs during development

I get an error when loading the paclet using the structure you've laid out, the line

Needs["PublisherID`PacletName`Core`" -> None]

because the package can't be found

In[3]:= FindFile["PublisherID`PacletName`Core`"]
Out[3]= $Failed

If I modify the kernel extension in the paclet info file to use

"Context" -> {
    {"PublisherID`PacletName`", "PacletName.wl"},
    {"PublisherID`PacletName`Core`", "Core.wl"},
    {"PublisherID`PacletName`Special`", "Special.wl"}
}

then it works as you describe.

I don't know if the method I lay out below is the 'best' practice but it works for me. I added another .wl file with package-internal functions that can be used by other subpackages that aren't exported to the user.

PacletName.wl

BeginPackage["PublisherID`PacletName`"]

(* implemented in Core.wl *)
coreFunc::usage = "..."

(* implemented in Special.wl *)
specialFunc::usage = "..."

Begin["`Private`"]

(* load all sub-packages here in the private context *)
Get["PublisherID`PacletName`Internal`"]
Get["PublisherID`PacletName`Core`"]
Get["PublisherID`PacletName`Special`"]

End[]

EndPackage[]

Core.wl

BeginPackage["PublisherID`PacletName`Core`"]
(* Exported symbols added here with SymbolName::usage *)

Begin["`Private`"]
Needs["PublisherID`PacletName`"] (* so that coreFunc is on the context path *)

coreFunc[k : (_Integer | _List)] := 2 * k (* simple example *)

End[]
EndPackage[]

Special.wl

BeginPackage["PublisherID`PacletName`Special`"]
Begin["`Private`"]

Needs["PublisherID`PacletName`"]
Needs["PublisherID`PacletName`Internal`"] (* for helperFunc *)

specialFunc[ arg_Integer ] := coreFunc @ helperFunc @ arg

End[]
EndPackage[]

Internal.wl

BeginPackage["PublisherID`PacletName`Internal`"]
(* introduce internal symbols of the paclet *)
helperFunc::usage = "..."

Begin["`Private`"]

helperFunc[x_] := {x, x^2}

End[]
EndPackage[]

Using this structure I get the same results,

In[5]:= Needs["PublisherID`PacletName`"]

In[6]:= coreFunc[4]
Out[6]= 8

In[7]:= specialFunc[4]
Out[7]= {8, 32}

But it avoids the function aliases you are using, which I find confusing

POSTED BY: Jason Biggs
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard