Reading Associating Definitions With Different Symbols, I was intrigued by this:
You can think of upvalues as a way to implement certain aspects of object-oriented programming. A symbol like
quat
represents a particular type of object. Then the various upvalues forquat
specify "methods" that define howquat
objects should behave under certain operations, or on receipt of certain "messages".
To play with this idea, I attempted to implement a variation on List
called ContiguousOrderedIntegerMap
: like List
, but any attempts to get an index beyond its length return Null
; and any attempts to set an index beyond its length automatically fill intermediate indices with Null
. I started,
ClearAll[ContiguousOrderedIntegerMap, foo, bar];
SetAttributes[ContiguousOrderedIntegerMap, HoldAll];
ContiguousOrderedIntegerMap /:
Part[ContiguousOrderedIntegerMap[expr_], i_] := (
If[Length@expr < i, expr = PadRight[expr, i, Null]];
return = expr[[i]];
expr = ContiguousOrderedIntegerMap@expr;
return
);
foo = {100, 200, 300};
bar = ContiguousOrderedIntegerMap@foo;
Print@bar;
Print@bar[[2]];
Print@bar[[5]];
(* ContiguousOrderedIntegerMap[foo] *)
(* 200 *)
(* Null *)
This is however, of course, wrong, because I'm mistakenly modifying foo
instead of bar
and creating nonsense recursion, i.e.
foo = ContiguousOrderedIntegerMap[foo];
So, the first thing I'm stuck on is, How can I access the symbol passed into Part
? Scanning through all Mathematica's available operators, I came across Pattern (:), which seemed like exactly what I needed, and applied it like so:
ContiguousOrderedIntegerMap /:
Part[symbol : ContiguousOrderedIntegerMap[expr_], i_] := (
Print@symbol;
(* ContiguousOrderedIntegerMap[foo] *)
But wait, instead of bar
, I got back bar
's OwnValue. Why? Ah, because Part
evaluates its first argument:
Attributes[Part]
(* {NHoldRest, Protected, ReadProtected} *)
So, what's next? Do I find some way to set HoldFirst
on Part
only when applying to a ContiguousOrderedIntegerMap
? (If that's even possible.) Instead, is there a more obvious, idiomatic way to go about this all?
I have other questions in mind, e.g. "How do I set a separate UpValue for Set
given that attempting to TagSet
Part[Set[...]]
would push ContiguousOrderedIntegerMap
a level too deep?". However, I end my question here for now, since it's where I'm stuck, and I'll probably need to edit this question a few times as I receive help and make progress.
HoldFirst
working somehow, what would you do for aContiguousOrderedIntegerMap
that is not bound to any variable, like one that is returned by a function? $\endgroup$SubValue
orDownValue
that points to the actual list/data. Then yourTagSet
would copy the "function" - really, the object - by value, and it could alter the down/subvalue accordingly. $\endgroup$UpValue
from the integer map evaluates,bar
would have already been expanded into itsOwnValue
. All information regarding the symbol would have been lost. I don't thinkTagSet
is the solution here, unless the value the equivalent of some kind of pointer to its data as I described above. $\endgroup$ConstantArray[0, 10][[2]] = 7
—I'd expect an error, e.g. "[...] is not a symbol." $\endgroup$