This is obviously an annoying problem - you can't easily keep the flexibility to reload the package with Get
during the development and at the same time keep certain functions Protected / Locked
. Just Protected
by itself can be dealt with, as explained by halirutan in his answer, but if you add Locked
, you are out of luck.
Perhaps, the easiest way out is to have some build process, during which the code to Protect
/ Lock
some functions will be automatically generated and added to the production version of your package (this is how I'd do it). But in case you really want to be able to test the Protect
/ Lock
part during the development (for whatever reason), here is some alternative.
Here is the suggested change to your test package:
BeginPackage["VolumeReserves`"];
(*---------- Protection - related functionality ------------ *)
Begin["`Private`"];
ClearAll[lockProtect, $developmentMode, def];
$developmentMode = False;
SetAttributes[def, HoldAll];
def /: (s : (Set | SetDelayed))[def[lhs_], rhs_] :=
s[lhs, rhs] /; $developmentMode || ! TrueQ[$executed] ;
lockProtect[sym_] :=
SetAttributes[sym, {Protected, Locked}] /; ! TrueQ[$executed || $developmentMode]
End[];
(*---------- Your actual code starts here ------------ *)
`Private`def @ startInterface::usage = "...";
`Private`def @ detailRock::usage = "...";
(*Implementation of functions*)
Begin["`Private`"];
def @ startInterface[___] := "Start interface"
def @ detailRock[___] := "DetailRock"
def @ f1[] := 1
def @ f2[] := 2
def @ f3[] := 3
(* Actual code to protect your symbols *)
Scan[lockProtect, {startInterface, detailRock, f1, f2, f3}];
$executed = True;
Update[def];
End[];
EndPackage[]
Now, the def
thing is entirely optional, and is used so that you (and the users of your package) can use Get
several times, even when running your package in production mode. When you set the variable $developmentMode
to True
, then def
reduces to Set
and SetDelayed
, as usual, and lockProtect
does nothing. If, however, $developmentMode
is set to False
, then def
only acts as an assignment for the first time (when the $executed
variable has not yet been defined), and so does lockProtect
(which in this case adds Protected
and Locked
attributes to your symbols of choice). All subsequent times, in production mode, both def
and lockProtect
do nothing.
Once again, you can remove all calls to def
, if you don't like them - all they affect is whether or not you get a bunch of error messages when using Get
second time on your package in production mode. The main point of def
is that you only need it for those symbols which you protect, while for other definitions you can use the usual assignments. Therefore, you can then reload your package and pick up changes to other symbols, even in production mode with some symbols locked, without the annoying error messages.
Note that def
as implemented above, won't cover some cases such as certain conditional definitions, and also definitions given by UpSet
, UpSetDelayed
, TagSet
and TagSetDelayed
- so this is more of an illustration of how such syntactic sugar can be added than the actual thing.
Locked
symbols, but there really are very few symbols that should beLocked
, and locking symbols is likely to be annoying for the user as well. Very few of the built-in symbols areLocked
, and it's mostly system-dependent stuff like$SystemWordLength
or really fundamental constants likeTrue
,False
andI
. $\endgroup$