How to reliably reload package after change?
There isn't a fully general way to reload packages because packages can do just about anything in principle, not just create definitions.
I recommend that you write your package in a way that simply using Get
again will properly re-set its state. Assuming that all your package does is issue definitions, you could do something like this:
BeginPackage["MyPack`"];
Unprotect["MyPack`*"];
ClearAll["MyPack`*"];
ClearAll["MyPack`Private`*"];
Foo::usage = "";
Begin["`Private`"];
helper[x_] := x^2
Foo[x_] := 2*helper[x]
End[];
Protect["MyPack`*"];
EndPackage[];
But in general there's a lot more a package can do than issue definitions. For example, it can cache results (MaTeX), it can load or save some persistent configuration (MaTeX, MATLink), it can create temporary files and directories to work with, or some other kind of state that persists throughout the session, such as connecting to other programs (MATLink), it may load LibraryLink functions that must be unloaded before they can be safely reloaded (IGraph/M), etc. My point is that most packages that I contributed to had something else than definitions in them that made reloading problematic. Thus not all of them will fully reset their state when using Get
a second time, and sometimes I just need to manually quit the kernel before loading them again. But I do try to build in some robustness against reloading to make development easier (and accidental double loading by users should definitely not break things).
I use these two regularly when developing packages. Not entirely bullet-proof, but enormously helps my workflow. For convenience, you should simply use Reload
instead of Needs
. Whatever is changed in the package file will be reloaded.
Attributes[Unload] = {Listable};
Unload::nonun = "The System`.` and Global`.` contexts cannot be removed.";
Unload["System`" | "Global`"] := Message[Unload::nonun];
Unload[form_String, fun_: Remove] :=
Module[{found = Cases[$Packages, _?(StringMatchQ[#, form<>"*"] &)], all},
all = # <> "*" & /@ found;
Unprotect /@ all;
Quiet[fun /@ all];
Unprotect@$Packages;
$Packages = DeleteCases[$Packages, Alternatives @@ found];
Protect@$Packages;
$ContextPath = DeleteCases[$ContextPath, Alternatives @@ found];
];
Attributes[Reload] = {Listable};
Reload[pkg_String] := (
If[MemberQ[Union[$ContextPath, $Packages], _?(StringMatchQ[#, pkg<>"*"] &)],
Unload[pkg, ClearAll]];
Get@pkg
);
Note, that Unload
removes symbols from other packages as well, e.g. Unload["Language`"]
also removes other system-Language`
related functions, not just user-Language`
ones.