9
$\begingroup$

Why does assigning a DownValue using Apply, e.g.,

Remove[a]
index={3,4};
(a @@ index) = 5;
a @@ index
(*Set::write: Tag Apply in a @@ {3, 4} is Protected. >>*)
(*a[3,4]*)

not work, while an assignment such as

Remove[a]
a[Sequence @@ index] = 5
a @@ index
(*5*)
(*5*)

does?

$\endgroup$
  • 2
    $\begingroup$ Set holds its first argument, so you need to force the evaluation inside Set. To work in all cases (including the case when a has been already defined on that index), I'd do something like Hold@@index/.Hold[inds__]:>Set[a[inds],5]. This seems to be a duplicate of at least one similar question, but I can't find it. $\endgroup$ – Leonid Shifrin Oct 13 '14 at 13:34
5
$\begingroup$

The behaviour we see here is due to Set performing limited evaluation of its left-hand-side prior to the assignment.

Set Evaluates the Left-Hand-Side

Despite the fact that Set has the attribute HoldFirst, it performs a special kind of evaluation upon its left-hand-side prior to performing the assignment. Specifically, if the left-hand-side has a head and parts, then that head and those parts are evaluated -- but no further rules are applied to the result.

Here is a generic example illustrating this behaviour:

ClearAll[f1, f2, a1, a2]
f1 = f2;
f2[___] := Print@"f2 evaluated!"
a1 = 1; a2 = 2;
f1[a1 + a2] = 999;
??f2

INFORMATION screenshot

Observe how even though the Set expression was assigning to f1[a1 + a2], the resulting definition is actually attached to f2[3]. f1 was evaluated to f2 and a1 + a2 was evaluated to 3. But also note that the pre-existing definition that matches f2[3] was not invoked, and nothing was printed.

In contrast, if the left-hand-side has no parts, it is not evaluated:

ClearAll[a, b]
a = b;
a = 3;
??a

INFORMATION screenshot

??b

INFORMATION screenshot

The Case At Hand

Let us now apply this knowledge to the case at hand.

For (a @@ index) = 5, Set performs the limited evaluation upon the left-hand-side. index is replaced with {3, 4} to produce a @@ {3, 4} which is to say Apply[a, {3, 4}]. As noted above, the normal definition of Apply is not expanded so this is the final left-hand-side. Next, Set attempts to assign 5 to this expression. Since Apply is protected, the attempt fails with an error message.

Now, a[Sequence @@ index] = 5. By construction, the head a is inert so it evaluates to itself. Sequence @@ index is evaluated to produce Sequence[3, 4]. Since a does not have the SequenceHold attribute, the Sequence head is stripped yielding a final left-hand-side of a[3, 4]. This is a valid and unprotected assignment target, so the assignment succeeds.

Why Does Set Behave Like This?

It is necessary for Set to perform at least some evaluation of the left-hand-side for otherwise expressions like x[i, j-1] = y would be far less useful. We want to have the indices i and j-1 evaluated. On the other hand, full evaluation of the left-hand-side would mean that we could never reassign a variable after its initial assignment because it would disappear from the left-hand-side by evaluation. Thus, we have these arcane rules of partial evaluation. Most of the time, these rules just "do the right thing". But occasionally, like for the present question, the subtleties of the rules are observable.

Workaround

One way to work-around the present problem is as follows:

(a[##] = 5) &@@ index

a @@ index
(* 5 *)

(a[##] = 6) &@@ index

a @@ index
(* 6 *)

The technique works even if a prior assignment has been performed.

$\endgroup$
  • 1
    $\begingroup$ +1. I think I will point to a similar discussion I had in my post answering this question. $\endgroup$ – Leonid Shifrin Oct 18 '14 at 0:45
  • $\begingroup$ Also somewhat related: (5846756) $\endgroup$ – Mr.Wizard Feb 1 '15 at 2:30
6
$\begingroup$

Evaluation happens too late, but you can fix it by forcing evaluation first:

Remove[a]
index = {3, 4};

Evaluate[a @@ index] = 5;
a @@ index

5

This can only be done on the initial definition though. After that

a[Sequence @@ index] = 6;
a @@ index

6

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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