8
$\begingroup$

In the ideas shared in my answer to this post Struct equivalent in Mathematica? at the end I propose a solution that changes the way Set works in a particular case that can be expressed in plain words

If I see something like x[u][key]=value where u is a symbol then assign value to u[key] (DownValues of u) instead of assigning value as a SubValues of x.

The corresponding code

ObjectSet[(_[symbol_Symbol]|symbol_),key_,value_]:=symbol[key]=value;

Unprotect[Set];
Set[symbol_[key_],value_]:=
   Block[{$inObjectSet=True},
      ObjectSet[symbol,key,value]
   ]/;!TrueQ[$inObjectSet];
Protect[Set];

This is needed for what I want to do as Set has a HoldFirst attribute, so I pass the left hand side to a function that will evaluate it.

An example after having executed the code above

x[u]["key"]=3
DownValues[u]

Leonid expressed his concerns to this solution of modifying Set in case it becomes widespread.

What could be alternatives to this way of doing ?

$\endgroup$
6
  • $\begingroup$ Can you make your question self-contained, so people can understand it without reading other posts? As it stands, it is not clear at all what your question is (at least without reading and understanding a multi-page answer and the corresponding question --- and the other answer is full of links to yet another answers as well) $\endgroup$
    – Szabolcs
    Commented Feb 2, 2012 at 10:58
  • $\begingroup$ Thanks for adding details. But you still didn't describe the problem you are trying to solve. Quoting: "expressed his concerns to this solution of modifying Set". What problem are you trying to solve? You are asking "[what is an] alternative to overloading Set", but this question only makes sense if you say why you want to overload Set. Otherwise it's of course impossible to provide an alternative. If the problem is [this question](mathematica.stackexchange.com/questions/990), then answers should go in that thread instead, right? Do you see my point? $\endgroup$
    – Szabolcs
    Commented Feb 2, 2012 at 11:20
  • $\begingroup$ If you change your set, you will post code and not know that what you see is due to your changes to set or not, nor will anyone else. Really think twice of what you want to do here! $\endgroup$
    – user21
    Commented Feb 2, 2012 at 11:27
  • $\begingroup$ @Szabolcs I think by now the amount of supplied details is more than enough. This is a frequently discussed topic, and lots of people start trying this stuff once they learn that this is possible (I don't mean the OP, lots of even much less advanced or beginner users). The topic is wide enough and important enough to warrant a separate discussion in general, on this site, to be sure. $\endgroup$ Commented Feb 2, 2012 at 11:42
  • 1
    $\begingroup$ @Leonid I don't know what OP means, it probably means the person who asked the question, but OP is the acronym of what ? $\endgroup$
    – faysou
    Commented Feb 2, 2012 at 12:02

1 Answer 1

20
$\begingroup$

Reasons why adding rules to Set is a really bad idea

First, let me list the reasons why I think that adding rules to Set globally is a very bad practice:

  • This is a hugely non-local system modification. We have no idea which parts of the system will be affected, but we can be sure that there will be many.
  • Set is a very frequently used command (see first point)
  • Set is fundamental to the system, and in some ways more low-level command than most others.
  • It could have been already overloaded internally for certain purposes. We may even break that internal code, and in this case, there would be absolutely no way to know,
  • We may degrade the performance, in unpredictable ways.

Some possible ways out

Now, here are some suggestions of what one could do.

  • Overload Set via UpValues, when you can. One example can be found here. It is not always possible however, due to the limitation on the depth-1 UpValues search.
  • Define yout own custom assignment operator, like mySet, and use that. This is the option I use most frequently myself. A bit more typing and less syntactically pleasing, but saves a lot of hassle in the long term. Besides, custom assignment operators are a very powerful programming tool, because you can do some extra stuff along with making assignments.
  • Create local environments. These can be lexical or dynamic. I will illustrate with a dynamic environment, for a simple example of a type point, that will hold a list of 2-dimensional coordinates. Our goal is that if some variable var is of type point (i.e. holds an expression like point[{x,y}], then if I make an assignment like var = {3,4}, I should have now point[{3,4}] stored in var.

Here is the code:

ClearAll[withCustomSet];
SetAttributes[withCustomSet, HoldAll];
withCustomSet[code_] :=
   Internal`InheritedBlock[{Set},
     Unprotect[Set];
     Set[var_Symbol, {x_, y_}] /;
        MatchQ[HoldComplete[var] /. OwnValues[var], HoldComplete[_point]] :=
            var[[1]] = {x, y};
     Protect[Set];
     code];

Let us see:

a = b = point[{1,2}]

a = {3,4};
a

(*
   ==>{3,4}
*)

while

withCustomSet[b = {3,4}];
b

(*
   ==>  point[{3,4}]
*)

In practice, you can execute arbitrary code inside withCustomSet, and the new redefinition of Set will take effect all the way down the execution stack. This is powerful but at the same time dangerous, however, much less dangerous that an analogous but global redefinition.

A lexical environment is also easy to construct:

ClearAll[withCustomSetLex];
SetAttributes[withCustomSetLex, HoldAll];
withCustomSetLex[code_] :=
  Unevaluated[code] /.
     HoldPattern[
       Set[var_Symbol, {x_, y_}] /; 
           MatchQ[HoldComplete[var] /. OwnValues[var], HoldComplete[_point]]] :>
             (var[[1]] = {x, y});

You can test that it works fine with the same simple test as above. However, it will only affect the instances of Set explicitly present in the code inside it. OTOH, this is yet much safer, since the stack is not affected.

Summary

  • Don't ever add rules to Set globally, if you want predictable behavior from Mathematica
  • There are plenty of ways to go around this problem, so this is not that serious of a limitation, really.

EDIT

To address the specific question (added in the update): the lexical environment would look like

ClearAll[withCustomSetLex];
SetAttributes[withCustomSetLex, HoldAll];
withCustomSetLex[code_] :=
  Unevaluated[code] /. 
      HoldPattern[Set[symbol_[key_], value_]] :>  ObjectSet[symbol, key, value]

A dynamic environment is trivial to implement: replace the code I used above in between Unprotect[Set] and Protect[Set] with your code.

$\endgroup$
3
  • $\begingroup$ +1 changing Set is really a bad thing to do! $\endgroup$
    – user21
    Commented Feb 2, 2012 at 11:28
  • $\begingroup$ @ruebenko Oh yes. And what a great appeal this idea had to me some years ago! $\endgroup$ Commented Feb 2, 2012 at 11:34
  • $\begingroup$ The "local environment" method you have recommended a number of times is really starting to appeal to me. (+1) $\endgroup$
    – Mr.Wizard
    Commented Feb 3, 2012 at 23:33

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.