Skip to main content
Fixed two subtle bugs
Source Link
Leonid Shifrin
  • 115.8k
  • 16
  • 341
  • 435
ClearAll[makeReference]; makeReference[sym_Symbol] := sym /: Set[sym[index_], rhs_] := With[{index = index},  With[{heldVal = Hold[sym[index]] /. DownValues[sym]}, If[heldVal === Hold[sym[index]], Module[{ref}, AppendTo[DownValues[sym], HoldPattern[sym[index]] :> ref] ] ]; Hold[sym[index]] /. DownValues[sym] /. Hold[s_] :> (s = rhs); ];]]; 
ClearAll[withModifiedPart]; SetAttributes[withModifiedPart, HoldAll]; withModifiedPart[code_] := Internal`InheritedBlock[{Part}, Unprotect[Part]; Part /: Set[Part[sym_Symbol[index_], inds__], rhs_] := With[{index = index}, Hold[sym[index]] /. DownValues[sym] /. Hold[s_] :> (s[[inds]] = rhs); ]; Protect[Part]; code]; 
ClearAll[makeReference]; makeReference[sym_Symbol] := sym /: Set[sym[index_], rhs_] := With[{heldVal = Hold[sym[index]] /. DownValues[sym]}, If[heldVal === Hold[sym[index]], Module[{ref}, AppendTo[DownValues[sym], HoldPattern[sym[index]] :> ref] ] ]; Hold[sym[index]] /. DownValues[sym] /. Hold[s_] :> (s = rhs); ]; 
ClearAll[withModifiedPart]; SetAttributes[withModifiedPart, HoldAll]; withModifiedPart[code_] := Internal`InheritedBlock[{Part}, Unprotect[Part]; Part /: Set[Part[sym_Symbol[index_], inds__], rhs_] := Hold[sym[index]] /. DownValues[sym] /. Hold[s_] :> (s[[inds]] = rhs); Protect[Part]; code]; 
ClearAll[makeReference]; makeReference[sym_Symbol] := sym /: Set[sym[index_], rhs_] := With[{index = index},  With[{heldVal = Hold[sym[index]] /. DownValues[sym]}, If[heldVal === Hold[sym[index]], Module[{ref}, AppendTo[DownValues[sym], HoldPattern[sym[index]] :> ref] ] ]; Hold[sym[index]] /. DownValues[sym] /. Hold[s_] :> (s = rhs); ]]; 
ClearAll[withModifiedPart]; SetAttributes[withModifiedPart, HoldAll]; withModifiedPart[code_] := Internal`InheritedBlock[{Part}, Unprotect[Part]; Part /: Set[Part[sym_Symbol[index_], inds__], rhs_] := With[{index = index}, Hold[sym[index]] /. DownValues[sym] /. Hold[s_] :> (s[[inds]] = rhs); ]; Protect[Part]; code]; 
Removed an erroneous statement that Part holds its first arg
Source Link
Leonid Shifrin
  • 115.8k
  • 16
  • 341
  • 435

However, the subtlety is that when we call Set[Part[s[ind],1,2],something], both Set and Part hold their holds its first argument, and therefore, s can not communicate to Set that this is special (UpValues won't work here since the s is too deep inside an expression - on level 2 - while UpValues are only looked at at level 1). To solve this problem, we will overload Part, but do it locally within a local dynamic environment, to make this operation safer. This is a dynamic environment:

However, the subtlety is that when we call Set[Part[s[ind],1,2],something], both Set and Part hold their first argument, and therefore, s can not communicate to Set that this is special (UpValues won't work here since the s is too deep inside an expression - on level 2 - while UpValues are only looked at at level 1). To solve this problem, we will overload Part, but do it locally within a local dynamic environment, to make this operation safer. This is a dynamic environment:

However, the subtlety is that when we call Set[Part[s[ind],1,2],something], Set holds its first argument, and therefore, s can not communicate to Set that this is special (UpValues won't work here since the s is too deep inside an expression - on level 2 - while UpValues are only looked at at level 1). To solve this problem, we will overload Part, but do it locally within a local dynamic environment, to make this operation safer. This is a dynamic environment:

added 201 characters in body
Source Link
Leonid Shifrin
  • 115.8k
  • 16
  • 341
  • 435

So, your original code will be changed to

ClearAll[matM]; makeReference[matM]; withModifiedPart[ matM[i] = ConstantArray[1, {10, 10}]; matM[i][[1, 10]] = 10 ] 

What about safety? I would argue that, for this particular case, modifying Part is safe enough. This is because, first, Part is overloaded softly, via UpValues. This means that, when Part is not inside some head which holds arguments, it will execute normally before it would even "think" of a new definition. Now, when it is inside some head which holds arguments, the new definition will only work if that head is Set. Note that no ne rules were added to the Set itself. And since normally, assignments to indexed variables are not allowed anyway, we don't risk breaking some internal behavior.

What about safety? I would argue that, for this particular case, modifying Part is safe enough. This is because, first, Part is overloaded softly, via UpValues. This means that, when Part is not inside some head which holds arguments, it will execute normally before it would even "think" of a new definition. Now, when it is inside some head which holds arguments, the new definition will only work if that head is Set. Note that no ne rules were added to the Set itself. And since normally, assignments to indexed variables are not allowed anyway, we don't risk breaking some internal behavior.

So, your original code will be changed to

ClearAll[matM]; makeReference[matM]; withModifiedPart[ matM[i] = ConstantArray[1, {10, 10}]; matM[i][[1, 10]] = 10 ] 

What about safety? I would argue that, for this particular case, modifying Part is safe enough. This is because, first, Part is overloaded softly, via UpValues. This means that, when Part is not inside some head which holds arguments, it will execute normally before it would even "think" of a new definition. Now, when it is inside some head which holds arguments, the new definition will only work if that head is Set. Note that no ne rules were added to the Set itself. And since normally, assignments to indexed variables are not allowed anyway, we don't risk breaking some internal behavior.

Source Link
Leonid Shifrin
  • 115.8k
  • 16
  • 341
  • 435
Loading