11
$\begingroup$

I'm writing a Mathematica Package with many functions, but only two can be seen/public. I have:

BeginPackage["VolumeReserves`"];

startInterface::usage = "...";

detailRock::usage = "...";

(* Implementation of functions *)

Begin["`private`"];

global private and initialized variables;

startInterface[___] := ...

detailRock[___] := ...

f1[] := ...

f2[] := ...

f3[] := ...


(* And finally I do: *)

SetAttributes[startInterface, {Protected, Locked}];
SetAttributes[detailRock, {Protected, Locked}];
SetAttributes[f1, {Protected, Locked}];
SetAttributes[f2, {Protected, Locked}];
SetAttributes[f3, {Protected, Locked}];

End[];
EndPackage[];

When I open a new notebook and I load the package for the first time with:

SetDirectory[StringJoin[NotebookDirectory[]]];
<< VolumeReserves`

Everything is OK, but if I evaluate the notebook one more time (and so on) I get:

Set::write: Tag startInterface in startInterface[___] is Protected. >>
Set::write: Tag detailRock in detailRock[] is Protected. >>
Set::write: Tag f1 in detailRock[] is Protected. >>
...
General::stop: Further output of SetDelayed::write will be suppressed during this calculation. >>

So this is a little strange... Why can't I reload/re-evaluate the notebook/package? Should I quit the kernel every time? I've tried to evaluate some of Mathematica Packages, such ComputerArithmetic, more than once, and it doesn't show that error...

$\endgroup$
2
  • $\begingroup$ As Leonid mentions in his answer, it can become really annoying when you work with Locked symbols, but there really are very few symbols that should be Locked, and locking symbols is likely to be annoying for the user as well. Very few of the built-in symbols are Locked, and it's mostly system-dependent stuff like $SystemWordLength or really fundamental constants like True, False and I. $\endgroup$
    – Pillsy
    Commented Apr 10, 2014 at 17:58
  • $\begingroup$ @Pillsy I guess, locking here is used in the context of protecting the intellectual property. This is how I interpreted this question anyway. $\endgroup$ Commented Apr 10, 2014 at 18:59

3 Answers 3

9
$\begingroup$

When you don't restart the Kernel (by using Quit[] or restarting Mathematica) then you will always get this behaviour because (1) you have protected the functions yourself and (2) you try to redefine them by reloading the package.

It is like evaluating the following twice:

f[x_]:=x^2;
Protect[f]

During evaluation of SetDelayed::write: Tag f in f[x_] is Protected. >>

What you have to do is to Unprotect (and maybe ClearAll) your functions at the beginning of your package.

Edit:

If you want to peak how this is done in packages which come with Mathematica, you could for instance look at the following:

FilePrint@
 FileNameJoin[{$InstallationDirectory, "AddOns", "Packages", 
   "VectorAnalysis", "VectorAnalysis.m"}]

This package uses a similar concept.

$\endgroup$
1
  • $\begingroup$ Thanks, this is very illuminating. Our teacher gave us a big lesson about how to built packages and protect functions and she never mentioned that :( $\endgroup$
    – Lory Lory
    Commented Apr 10, 2014 at 15:48
10
$\begingroup$

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.

$\endgroup$
5
$\begingroup$

Using << (or Get) always attempts to evaluate the package. As you have protected your definitions you get the error messages unless you start a new Mathematica kernel.

Better is to use Needs to load your packages (when you are not actively developing the package in question). This checks to see if the package context is already known and only evaluates the package if if is not.

$\endgroup$
3
  • 4
    $\begingroup$ Which is unfortunately very bad if you actively developing and testing a package and need to really reload the complete code. Maybe you can add this information to your answer. $\endgroup$
    – halirutan
    Commented Apr 10, 2014 at 15:44
  • $\begingroup$ mmm...this is very unfunyy! thank you very much, it's much clearer now and I think I'll use Needs from now on. $\endgroup$
    – Lory Lory
    Commented Apr 10, 2014 at 15:47
  • 1
    $\begingroup$ @LoryLory The problem with Needs, as halirutan just pointed out, is that this does not help you during the development, where you do want your package to be reloaded to pick up the changes, rather than just its context brought up to the $ContextPath - which is what Needs does for already loaded packages. $\endgroup$ Commented Apr 10, 2014 at 17:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.