Subscripts - Why do I see the error "only assignments to symbols are allowed" when using a Module and not otherwise?

15

4

When I type

Remove["Global`*"]
foo[] := Module[{Subscript[a, 1] = "x"}, 0]; 
foo[]

I get the expected error "only assignments to symbols are allowed". I understand the error.

enter image description here

But why I do not get the same error when I type the same assignment in the notebook?

Remove["Global`*"]
Subscript[a, 1] = "x"

No error and no beep.

enter image description here

What is the difference? I'm using version 8.04 on Windows.


Update

The non-localized trick shown by Szabolcs answer worked for me on a demo. Here is a screen shot of the result. What I was trying to do is to typeset some information about a solver, some matrix equation, and I thought why not take advantage of MatrixForm and Grid's nice way of typesetting things, and display the information right there in the demo screen as an option? This is easier than putting it in the text section below where it can hard to get to as I have so many such things.

Here is an example of the code and the result. You can see I did not localized these symbols in the module which builds this expression. I just started trying this idea, but so far I am happy that it worked. This below is only an example, not cleaned up, just wanted to see if the demo will work.

enter image description here

Nasser

Posted 2012-01-28T15:36:17.033

Reputation: 92 661

Answers

17

This is because only symbols can be localized by Module. It is not about assignment, but localization.

Subscript[a, 1] is not a symbol, but a compound expression, so:

Module[{Subscript[a, 1] = "x"}, 0] (* <-- not allowed *)

Module[{}, Subscript[a, 1] = "x"]  (* <-- allowed but not localized *)

I agree that the error you got may be a bit confusing.

A somewhat ugly workaround is Module[{Subscript}, Subscript[a, 1] = "x"] or you may try to use the Notation` package to create symbol names with subscripts in them. A word of warning though: in some cases, Module variables that have DownValues do not get destroyed when the Module finishes evaluating. For more information, see the end of the Module section in this answer by Leonid Shifrin, and the comments on that answer.

Szabolcs

Posted 2012-01-28T15:36:17.033

Reputation: 213 047

You probably meant Module[{a}, Subscript[a, 1] = "x"] in place of Module[{Subscript}, Subscript[a, 1] = "x"]? – Leonid Shifrin – 2012-01-28T16:30:10.880

@Leonid No, I meant what I wrote, and I agree that it is a hack. But it allows both displaying subscripted variables as such, localization, and usage in a Manipulate. If we write Module[{a}, Subscript[a, 1] = "x"], that'll be leaky. Module[{Subscript}, Subscript[a, 1] = "x"] renames subscript, and won't leak so easily. – Szabolcs – 2012-01-28T16:40:06.253

It won't be more leaky (assuming you meant garbage collection), in this case. Referencing symbols in UI components is enough to prevent their collection, since UI components by themselves are global objects (they are expressions interpreted in a certain way by the Front-End). – Leonid Shifrin – 2012-01-28T17:05:11.787

@Leonid Yes, I remember when we talked about Temporary symbols with DownValues not always getting collected. But I think in many use cases these would not need to be referenced. – Szabolcs – 2012-01-28T17:08:43.407

Perhaps. +1 either way. – Leonid Shifrin – 2012-01-28T17:17:07.747

@Leonid I should link to the post where you explain how Module is leaky, but I can't find it. Can you help me? – Szabolcs – 2012-01-28T17:27:18.610

This was done in the comments to this answer: http://mathematica.stackexchange.com/questions/559/what-are-the-use-cases-for-different-scoping-constructs/569#569. I will soon exctract the pieces of that conversation and add a small section to that answer.

– Leonid Shifrin – 2012-01-28T17:30:45.827

6

Module only works with Symbols.

Head[ Subscript[a, 1] ]
Subscript

Perhaps you can use Internal`InheritedBlock or Internal`LocalizedBlock.

Subscript[a, 1] = "x";
Subscript[a, 2] = "y";
foo := Subscript[a, 1]

Internal`InheritedBlock[{Subscript},
  Subscript[a, 1] = "new";
  DownValues[Subscript]
]

Subscript[a, 1]
{HoldPattern[a1] :> "new", HoldPattern[a2] :> "y"}

"x"

Note the different syntax and starting value within each Block:

Internal`InheritedBlock[{Subscript},
  DownValues[Subscript]
]

Internal`LocalizedBlock[{Subscript[a, 1]},
  DownValues[Subscript]
]
{HoldPattern[a1] :> "x",  HoldPattern[a2] :> "y"}

{HoldPattern[a1] :> System`Private`$Localized, HoldPattern[a2] :> "y"}
Internal`InheritedBlock[{Subscript},
 Subscript[a, 1] = "x";
 DownValues[Subscript]
]

Beware that neither function actually works like Module:

Internal`InheritedBlock[{Subscript},
 Subscript[a, 1] = "new";
 foo
]

Internal`LocalizedBlock[{Subscript[a, 1]},
 Subscript[a, 1] = "new";
 foo
]
"new"

"new"
a := "old"
b := a
Module[{a = "new"}, b]
"old"

Mr.Wizard

Posted 2012-01-28T15:36:17.033

Reputation: 259 163

3

It's just a mattern of Module's syntax. You'll see that the message was generated from Module and not from Set (=)

Note

In fact, Set doesn't even seem to be called internally when you run a regular Module (even though Trace suggests it does)

We disable Set but it still works

In[42]:= ClearAll[x, y];
Block[{Set},
 Module[{x = 8},
  y = 9;
  {x, y}
  ]]

Out[43]= {8, y}

Rojo

Posted 2012-01-28T15:36:17.033

Reputation: 40 993

3

In addition to the explanations above you can avoid this by using the Notations package. Cut and pasting code would be confusing I think -- things don't seem to paste as I would like them too -- so I'll paste the screen grab

enter image description here

So now the subscripted variable is recognized as a symbol and the problem goes away.

Mike Honeychurch

Posted 2012-01-28T15:36:17.033

Reputation: 36 211