An answer to a similar question regarding package dependencies described the directory structures needed to implement sub-functions but here the details of the function structure are illustrated. To first backtrack; an ideal package framework should enjoy the following properties with function names inserted from this example but which apply more generally:
- The end-user's name-space should not be polluted by
foo
.
- The
mypkg`
package should "see" the unadulterated foo
without needing a full context specification.
- There should be no potential conflicts in
mypkg`
and mypkg`sub`
.
- To seamlessly scale, there should be no need for (multiple)
Needs
's calls given the introduction of a new "Developer's DeclarePackage
" idiom.
The OP's implementation satisfies 1) The accepted answer satisfies 1) and 2); WL's "idiomatic method" (to be illustrated shortly) satisfies 1), 2) and 3); and finally, I will mention the syntax for an extended package framework which satisfies 1), 2), 3) & 4).
Explanations for the above claims:
1) An end-user's namespace is not polluted because an unadulterated foo
(one without full context specification) is still available for use in the end-users environment (foo
's full contextual name is mypkg`sub`Private`foo
but mypkg`sub`Private`
doesn't appear on $ContextPath
).
2) In the OP's example, any appearance of the full context form mypkg`sub`foo
means accessing its definition wherever it is placed but what may initially seem surprising here is that an apparently encapsulated definition has attached itself to an apparently external symbol. Consider however, the following snippet:
mypkg`sub`foo
BeginPackage["mypkg`sub`"];
Begin["`Private`"];
foo[n_] := Module[{}, Print["in mypkg`sub`foo[]"];
n + 1]
End[]; (*End Private Context*)
EndPackage[];
mypkg`sub`foo[1]

One might have expected foo
to be created (and hence encapsulated) as mypkg`sub`Private`foo
with the final mypkg`sub`foo[1]
remaining unevaluated but a previous creation of the mypkg`sub`foo
symbol (anywhere) ensures that the internal foo
now instead refers to mypkg`sub`foo
and hence attaches its subsequent definition to this symbol. As noted, this is because mypkg`sub`
remains on $ContextPath
when the control flow passes through Begin["`Private`"]
with new symbols only being created (and hence encapsulated) in mypkg`sub`Private`
if they don't already appear in $ContextPath
. But we are now, at least, in a position to see why the idiomatic practice works:
The idiomatic way to define this sub-package
BeginPackage["mypkg`sub`"];
usage::foo = "foo[n] adds one to n";
Begin["`Private`"];
foo[n_] := Module[{}, Print["in mypkg`sub`foo[]"];
n + 1]
End[]; (*End Private Context*)
EndPackage[];
ensures firstly that all symbols created in foo
's definition are encapsulated in mypkg`sub`Private`
(and hence don't pollute elsewhere, either in end-users' namespaces or in developers' other package namespaces). In terms of the foo
symbol itself; it is defined in mypkg`sub`
and hence accessible in the private context given that mypkg`sub`
is the only other context whose symbols the private context "sees" (we will cover foo
's exposure shortly). This similarly applies to any other symbols defined in usage commands placed between BeginPackage["mypkg`sub`"]
and Begin["`Private`"]
;
Hence following idiomatic practice we see that usage commands serve a dual purpose; ostensibly to provide documentation but actually much more importantly, to create the symbols in mypkg`sub`
that are to be seen in the following private context (when being defined) and in other packages (when exported).
For years I used these idioms without giving a second thought to the actual context manipulations occurring behind the scenes that give them effect. It was only when I came to extend this BeginPackage/EndPackage
framework (to implement (4) described later) that I came to appreciate the superb design that has underpinned their implementation (I'd like to know who came up with it?) over all these years. In fact, IMO this is the very marker and epitome of brilliant language design; end-users do things that just seem natural (place to-be-public symbols in idiomatic positions; idiomatically wrap to-be-encapsulated code in Begin
/End
etc.) while the design takes care of the rest. It does also mean though, that you deviate from designed idioms at your peril.
To be continued ...
Query 1: Why is the accepted answer (satisfying OP's specific request) risky?
Query 2: Why doesn't WL's current package idiom "seamlessly scale"?
Returning to the example at hand, our idiomatic definition of foo
in mypkg`sub`
has, while encapsulating foo
's definitions (in mypkg`sub`Private`
) apparently also made the symbol foo
public as it is exported as a symbol from mypkg`sub`
. But therein lies the horns of an apparent dilemma, a tension between the imperatives of end-user and developer--on the one hand, foo
needs to be private from an an end-users perspective but on the other, public from a developer's perspective in exporting it for servicing the over-arching package mypkg`
. In fact, it is actually this tension that lies at the root of a design challenge, one eventually confronted by all developers.
It is readily met idiomatically (but IMO ultimately unsatisfactorily) by the following practice of internally placing Needs["mypkg`"]
BeginPackage["mypkg`"]
main::usage = "main[]"
Begin["`Private`"]
Needs["mypkg`sub`"];
main[] := foo[1] // moreThanfoo;
End[] (*End Private Context*)
EndPackage[]
This "solves" our dilemma because it makes foo
"temporarily public" in the service of main
's definition but then returns foo
to its encapsulated, private status as the control flow exits EndPackage[]
in preparation for end-user application. It is natural in the sense of mimicking an interactive session by, in a developer's environment (within the package definitions), making available functionality in the same familiar way Needs
imports functionality in an interactive session.
Let's see the Context
manipulation going on behind the scenes to give this effect--Needs
puts mypkg`sub`
on $ContextPath
thereby giving access to all of mypkg`sub`
's exported functions but what Needs
giveth EndPackage
taketh as the latter removes mypkg`sub`
from $ContextPath
and with it end-users access to any of mypkg`sub`
exported symbols as desired.
We can immediately see why this is preferable to the accepted answer and the approach initially tried by the OP: in this idiomatic approach, within mypkg`
only foo
is exposed from mypkg`sub`
and not any of the symbols involved in foo
's definition which is what occurs when mypkg`sub`Private`
is added to $ContextPath
as in the accepted answer (Note that Needs
also ensures the package is a priori loaded in addition to adjusting $ContextPath
and it turns out that de-coupling this action will key to a new "Developer's DeclarePackage
").
Now there can be a bit of a trade-off between encapsulation and convenience. For example, I'm not a big fan of routinely including helper functions within the body of a Module
/Block
/Let
as I feel that the loss of readability is rarely offset by any (local) encapsulating gains particularly when backed by the encapsulation still taking place at the package level. Modularization therefore, not only occurs at run-time but also occurs at the code organization level so I prefer to have helper definitions tucked away in other collapsible cells within a package preventing them from clogging up the logic of my main function (but see other viewpoints and this is restricted to functions not variables).
I've also never actually stumbled across a naming/variable conflict within a single "standard" package/file although of course when mission-critical functionality arises "total encapsulation" can be adopted in a similar way that a helper function can command its own package if it morphes into something more general.
Potential naming conflicts with helper functions/etc across packages however, is a very different matter and IMO real risks accrue in scaling a code-base by file encapsulation alone (effectively the approach of the accepted answer) and in particular, without including the context encapsulation provided by fine-grained package disaggregation.
Ok, so the described idiomatic method seems fit-for-purpose, can handle the OP's request for a single subsidiary function, so what about our earlier claim that it doesn't "seamlessly scale" or that it is in some sense inherently unsatisfactory. Well, first, this needs to be put in perspective; clearly the idiomatic method is an elegant, bullet-proof and proven base and it evidently does scale given the numerous large, successful packages/systems that have already been built on top of it. Hence for someone starting out in package development you can indeed go a long way before any of the current limitations start to make their presence felt---for most package development therefore, what follows may not be relevant for a considerable amount of time.
Nonetheless, it is nice to at least be aware of the existence of frameworks for managing the burgeoning complexity that inevitably accompanies a package's growth, a quest, to which we now turn.
To be continued ... (or not as this is becoming inappropriately saga-like for SE)
Needs["mypkg`sub`"]
afterBeginPackage
of the main package. Or have nestedBeginPackages
if all of them export to your common context. There is a lot of discussion in 176434 but I think that thread misses simple approach. Start reading from Ronald Monson answer, then b3m2a1's and then the rest. $\endgroup$mypkg`sub`private`foo[1]
will always work. As for your problem: I think the problem is the following: You firstGet
mypkg`mypkg
, which declares the symbolmypkg`sub`foo
. After this, youGet
mypkg`sub
, which now sees this symbol on the$ContextPath
, which is why the definition inside thePrivate`
context is still attached to the "public" symbol. In short, your exporting statement is the call inmypkg`
. $\endgroup$For which purpose do you want to hide these symbols?
that is the whole purpose of data and implementation hiding. So that your are free to modify them later as you wish without breaking client code. If Client to your package had access to your package private data and functions, and written code based on these, then if you later change the private data or the private functions API, then the client application will no longer work when you release new version of the package. $\endgroup$