What is the accessibility of a package's `Private` context variables?

So how does MyFunction know the value of abc at the delayed function call (when it is called) if the Private` context isn't on the $ContextPath

There is a misunderstanding here. You are assuming that abc is searched for in some context only when MyFunction[something] is evaluated. This is not the case.

$Context and $ContextPath only affect how source code is parsed (not how expressions are evaluated). In other words, they only affect how the text you write in the package file is interpreted and converted into in-memory expressions. Once the package has been loaded with Get, this interpretation has already happened. MyFunction has been interpreted as the symbol CustomPackage`MyFunction and abc has been interpreted as CustomPackage`Private`abc, according to the value of $Context and $ContextPath at the time each was read. These are the full names of these symbols and this is how they exist in memory.

Load the package and try this:

Block[{$ContextPath},
 Print@Definition[MyFunction]
]

You'll see the following printed:

CustomPackage`MyFunction[CustomPackage`Private`arg1_] := 
  CustomPackage`Private`arg1+CustomPackage`Private`abc

As you can see, a context is always associated with every symbol.


All symbols are created at load time, so when you do:

BeginPackage["X`"];

x::usage="Declaring x as an exported symbol in the X` context";

Begin["`SomePrivateContext`"];

x[a_]:=b

End[];

EndPackage[];

x was created as X`x but the DownValues of x reference X`SomePrivateContext`a and X`SomePrivateContext`b which were created at the time the function was defined. These symbols are unique, so that reference only ever points that a single object.


Begin["`Private`"]; sets the current $Context to "CustomPackage `Private`". This causes two things:

  • The symbol abc will be searched in the current context first, thus in"CustomPackage`Private`". Only if it is not found there, the search goes on along $ContextPath.

  • If no matching symbol is found this way, a new symbol abc is created, namely in the current $Context which is "CustomPackage`Private`". So the full symbol name is "CustomPackage`Private`abc".

For example, running your code in a fresh kernel and executing

??MyFunction

reveals that the full definition of MyFunction is

MyFunction[CustomPackage`Private`arg1_]:=CustomPackage`Private`arg1+CustomPackage`Private`abc

Moreover, with

 ?*`abc

you see that the only symbol in all contexts that matches abc is CustomPackage`Private`abc and has the value 5 assigned to it.