## Does Set vs. SetDelayed have any effect after the definition was done?

22

6

I was always assuming that the only difference between Set (=) and SetDelayed (:=) is that SetDelayed holds the right argument, so that a := b is effectively the same as a = Unevaluated[b]. Especially I assumed that after the assignment is done, there's no further difference for variables or functions assigned with Set and variables assigned with SetDelayed. Looking at OwnValues resp. DownValues seems to support that assumption.

However I now noticed that when writing ?a, Mathematica displays the type of assignment used for the definition, which means it has to store it somewhere. And I somehow doubt that it only stores it in order to show it with ?.

Therefore my question is: Is there any difference in the behaviour of values assigned with = and with := (apart from the different output of ?), assuming the actual assigned expression is the same (i,e, OwnValues/DownValues have the same value after both assignments)?

2

– Mr.Wizard – 2012-06-02T22:14:35.400

23

Yes, at least in one place.

x = {1, 2, 3}
x[[2]] = 8;


All right there, but

y := {1, 2, 3}
y[[2]] = 8


gives Set::noval: Symbol y in part assignment does not have an immediate value

Credit to this old comment by Leonid. Also note the point on memory usage:

[...] I'd guess that delayed definitions may use some intermediate internal variables, while immediate ones point straight to the memory where the data is stored.

Beat me to it - +1. – Leonid Shifrin – 2012-06-02T18:24:51.413

1I knew you were coming @LeonidShifrin – Rojo – 2012-06-02T18:25:05.683

1That's really interesting. Are there any other cases where there's a difference, esp. one involving DownValues? The obvious idea x[1]={1,2,3};y[1]:={1,2,3} doesn't work because x[1][[2]] already cannot be assigned. – celtschk – 2012-06-02T19:00:03.613

@celtschk, now that's an interesting question, because it seems to take 536 bytes to store a DownValue Set definition of an integer and 536 to store a SetDelayed one – Rojo – 2012-06-02T19:44:15.943

3

@celtschk While this is not directly related, you may find this discussion also interesting. In particular, RuleDelayed inside DownValues is not totally inert (contrary to a popular belief, it does evaluate the r.h.s. of the rule, albeit in a special way). Consider ClearAll[f]; f[x_] := Unevaluated@Unevaluated[x];, and contrast DownValues[f] with Block[{RuleDelayed = HoldComplete}, DownValues[f]], for instance.

– Leonid Shifrin – 2012-06-02T20:19:45.037

@LeonidShifrin: Indeed. Interestingly, f[1] still evaluates to Unevaluated[1], while Hold[f[1]] /. DownValues[f] gives Hold[1]. Obviously Mathematica does not really use DownValues for evaluation ... – celtschk – 2012-06-02T20:39:15.800

3@celtschk Not so simple: InternalInheritedBlock[{RuleDelayed}, SetAttributes[RuleDelayed, HoldAllComplete]; Hold[f[1]] /. DownValues[f]]. As I said, the whole point is not that DownValues are not used (which is in some sense true but not really the cause here), but that RuleDelayed in DownValues evaluates it's r.h.s. and strips any number of Unevaluated wrappers. In the above code, I prevented that, thus the result. – Leonid Shifrin – 2012-06-02T20:48:48.447

@LeonidShifrin, you could make your version of RuleDelayed without HoldAllComplete, by using my favourite "bug"(?): SetAttributes[rd, {HoldRest, SequenceHold}]; Module[{$guard = True}, rd[l_, s_Unevaluated] := rd[l, s]; rd[l_, s_] /;$guard := Block[{\$guard = False}, rd[l, s]]; ] – Rojo – 2012-06-02T21:01:32.147

@Rojo Perhaps. It wasn't my intent this time to play more with this, I was just making a point that it was RuleDelayed which was responsible for stripping Unevaluated wrappers. You can look at Alexey's answer in the cited question where he managed to reproduce the exact behavior of RuleDelayed with this code. I don't have time to test now if it is possible to also use your trick for this purpose, but it may be an interesting experiment. – Leonid Shifrin – 2012-06-02T21:13:38.463

@Rojo Oh, I see. I actually did not get it that you refer to that past answer of mine in that discussion. Yes, this is interesting. I don't have time right now, but will revisit this later. And, I did not know that Alexey's code is broken - or at least did not remember it. A fascinating topic, isn't it? I am however less interested in these details nowadays, there are more important things to learn or make myself busy with, for me. – Leonid Shifrin – 2012-06-02T21:26:24.860

2I was wondering how this information (:= vs =) is transferred to subkernels when doing parallel computations. AFAIK definitions are transferred using the (assignable) LanguageExtendedFullDefinitions function. It turns out that when using = we have OwnValues -> HoldPattern[x] :> {4, 5, 6} in the definition list while when using := we have OwnValues -> {HoldPattern[x] :> {4, 5, 6}}. Note that braces. Just an interesting bit to add and to confirm that this info is indeed transferred properly to subkernels. – Szabolcs – 2012-06-04T09:14:13.410

w.r.t. memory usage: a = 1; b := 1; DumpSave["a.mx", a]; DumpSave["b.mx", b]; {FileByteCount["a.mx"], FileByteCount["b.mx"]} – The Vee – 2016-03-07T12:04:29.593

6

Here is another difference

test[a_] = Unevaluated[1 /; a];
test2[a_] := 1 /; a
test[True]
test2[True]

1/;True
1


I think this has to do with different handling of RuleDelayed and Rule. I actually found this difference between Set and SetDelayed after finding the following difference between those.

True/.a_->  2/;a
True/.a_:>  2/;a
True/.a_:>  Evaluate[2/;a]

2/;True
2
2


Silly note on syntax highlighting

Note that the syntax coloring, which does not connect the as in the case of Rule, can be misleading. Of course (in the case where a does not have a value) the symbols a do correspond and we do not simply replace by the symbol a, but rather by what a_ matches.

True/.a_->  !a,
True/.a_:>  !a

False (*of course this is the right output, and not !a*)
False


So this has nothing to do with the difference.

Root of the difference

The code

True/.a_:> !a


is evaluated using the undocumented function RuleCondition (WReach explains that function nicely here). This can be seen by looking at the trace

Trace[True/.a_:>2/;a]


But in

Trace[True/.a_->2/;a]


no RuleCondition appears. A simple replacement is made, where the pattern is simply replaced by the expression with head Condition. So no "special handling" occurs here and whether we replace or not does not depend on the condition.

Another difference between Rule and RuleDelayed

First I thought the difference was responsible for the difference between Set and SetDelayed. I felt it was worth mentioning anyway.

Hold[2]/.a_Integer-> RuleCondition[a*2]
Hold[2]/.a_Integer:>  RuleCondition[a*2]

Hold[2 2]
Hold[4]


Another difference

Even another difference between Set and SetDelayed, also involving Condition, is given here

This had to be said, nice catch +1 – Rojo – 2014-11-05T15:43:51.990

@Rojo thanks again, I fixed the mistake I mentioned now :). – Jacob Akkerboom – 2014-11-05T17:54:49.207