The pymdl book
15. Locatives
There is in MDL a facility for obtaining and working directly with objects
which roughly correspond to "pointers" in assembly language or "lvals" in BCPL
or PAL. In MDL, these are generically known as locatives (from "location")
and are of several TYPEs, as mentioned below. Locatives exist to provide
efficient means for altering structures: direct replacement as opposed to
re-copying.
Locatives always refer to elements in structures. It is not possible to
obtain a locative to something (for example, an ATOM) which is not part of
any structure. It is possible to obtain a locative to any element in any
structured object in MDL -- even to associations (chapter 16) and to the values
of ATOMs, structurings which are normally "hidden".
In the following, the object occupying the structured position to which you have obtained a locative will be referred to as the object pointed to by the locative.
12.1. Obtaining Locatives
12.1.1. LLOC
<LLOC atom env>
returns a locative (TYPE LOCD, "locative to iDentifier") to the LVAL of
atom in env. If atom is not bound in env, an error occurs. env is optional,
with the current ENVIRONMENT used by default. The locative returned by
LLOC is independent of future re-bindings of atom. That is, IN (see
below) of that locative will return the same thing even if atom is re-bound to
something else; SETLOC (see below) will affect only that particular binding of
atom.
Since bindings are kept on a stack (tra la), any attempt to use a locative to an
LVAL which has become unbound will fetch up an error. (It breaks just like a
TUPLE....) LEGAL? can, once again, be used to see if a LOCD is valid.
Caution: <SET A <LLOC A>> creates a self-reference and can make PRINT very
unhappy.
<SET A 1> ⇒ 1
<IN <LLOC A>> ⇒ 1
<TYPE <LLOC A>> ⇒ LOCD
And "it breaks just like a TUPLE" is exact:
<DEFINE F (X) <LLOC X>> ⇒ F
<SET L <F 5>> ⇒ #LOCD #OBJECT
<LEGAL? .L> ⇒ #FALSE ()
<IN .L> ⇒ *ERROR* ILLEGAL-LOCATIVE
Found along the way. That banner was pymdl's own English until 2026-09-10, and the era's name for it was found where you would hope: the interpreter's own
mudsys/primitcarriesILLEGAL-ARGUMENT-BLOCK,ILLEGAL-FRAMEandILLEGAL-LOCATIVEtogether, one for each of the three things that die when a Function returns -- theTUPLEof section 9.2.1, theACTIVATIONof section 9.8, and theLOCDhere. pymdl already said the first two correctly, so the third was simply a gap.<AT struc n>out of range moved toOUT-OF-BOUNDSin the same pass, which is the bannerNTHalready gave for the identical mistake.
12.1.2. GLOC
<GLOC atom pred>
returns a locative (TYPE LOCD) to the GVAL of atom. If atom has no GVAL
slot, an error occurs, unless pred (optional) is given and not FALSE, in
which case a slot is created (chapter 24). Caution: <SETG A <GLOC A>> creates
a self-reference and can make PRINT very unhappy.
<SETG G 5> ⇒ 5
<TYPE <GLOC G>> ⇒ LOCD
<IN <GLOC G>> ⇒ 5
<TYPE <GLOC NEVER-SET T>> ⇒ LOCD
The last line is the pred argument doing its work: NEVER-SET has no global
value at all, and asking for a locative to it would be an error without the T.
12.1.3. AT
<AT structured N:fix-or-offset>
returns a locative to the Nth element in structured. N is optional, 1 by
default. The exact TYPE of the locative returned depends on the PRIMTYPE of
structured: LOCL for LIST, LOCV for VECTOR, LOCU for UVECTOR, LOCS
for STRING, LOCB for BYTES, LOCT for TEMPLATE, and LOCA for TUPLE.
If N is greater than <LENGTH structured> or less than 1, or an OFFSET with
a Pattern that doesn't match structured, an error occurs. The locative is
unaffected by applications of REST, BACK, TOP, GROW, etc. to structured.
<TYPE <AT '(1 2) 1>> ⇒ LOCL
<TYPE <AT '[1 2] 1>> ⇒ LOCV
<TYPE <AT '![1 2] 1>> ⇒ LOCU
<TYPE <AT "ab" 1>> ⇒ LOCS
<AT '(1 2) 9> ⇒ *ERROR* OUT-OF-BOUNDS
Seven locative TYPEs for seven structured PRIMTYPEs, and each one is its own
PRIMTYPE -- there is no PRIMTYPE LOCATIVE, which is what the next section
is about.
12.1.4. GETPL and GETL
<GETPL item:any indicator:any default:any>
returns a locative (TYPE LOCAS) to the association of item under indicator.
(See chapter 16 for information about associations.) If no such association
exists, GETPL returns EVAL of default. default is optional, #FALSE () by
default.
GETPL corresponds to GETPROP amongst the association machinery. There also
exists GETL, which corresponds to GET, returning either a LOCAS or a
locative to the indicatorth element of a structured item. GETL is like AT
if item is a structure and indicator is a FIX or OFFSET, and like GETPL if
not.
<PUTPROP FOO BAR 1> ⇒ FOO
<TYPE <GETPL FOO BAR>> ⇒ LOCAS
<IN <GETPL FOO BAR>> ⇒ 1
<GETPL FOO NO-SUCH> ⇒ #FALSE ()
<GETPL FOO NO-SUCH DEFAULT> ⇒ DEFAULT
<TYPE <GETL '(1 2) 1>> ⇒ LOCL
<TYPE <GETL FOO BAR>> ⇒ LOCAS
The two GETL answers are the whole of its description: given a structure and a
FIX it behaved like AT, and given anything else it behaved like GETPL.
12.2. LOCATIVE?
This SUBR is a predicate that tells whether or not its argument is a locative.
It is cheaper than <MEMQ <PRIMTYPE arg> '![LOCD LOCL ...]>.
<SET A 1> ⇒ 1
<LOCATIVE? <LLOC A>> ⇒ T
<LOCATIVE? 1> ⇒ #FALSE ()
<LOCATIVE? '(1)> ⇒ #FALSE ()
12.3. Using Locatives
The following two SUBRs provide the means for working with locatives. They
are independent of the specific TYPE of the locative. The notation locative
indicates anything which could be returned by LLOC, GLOC, AT, GETPL or
GETL.
12.3.1. IN
<IN locative>
returns the object to which locative points. The only way you can get an error
using IN is when locative points to an LVAL which has become unbound from an
ATOM. This is the same as the problem in referencing TUPLEs as mentioned in
section 9.2, and it can be avoided by first testing <LEGAL? locd>.
Example:
<SET A 1> ⇒ 1
<IN <LLOC A>> ⇒ 1
12.3.2. SETLOC
<SETLOC locative any>
returns any, after having made any the contents of that position in a structure
pointed to by locative. The structure itself is not otherwise disturbed. An
error occurs if locative is to a non-LEGAL? LVAL or if you try to put an
object of the wrong TYPE into a PRIMTYPE UVECTOR, STRING, BYTES, or
TEMPLATE.
Example:
<SET A (1 2 3)> ⇒ (1 2 3)
<SETLOC <AT .A 2> HI> ⇒ HI
.A ⇒ (1 HI 3)
Through a LOCD it reaches a variable rather than a structure element, and the
type restriction is real:
<SET A 1> ⇒ 1
<SET L <LLOC A>>
<SETLOC .L 99> ⇒ 99
.A ⇒ 99
<SET U '![1 2]> ⇒ ![1 2!]
<SETLOC <AT .U 1> "s"> ⇒ *ERROR* TYPES-DIFFER-IN-UNIFORM-VECTOR
SETLOC through a LOCD did what <SET A 99> would have done, with no mention
of the name A anywhere in the call. That is the point of the next section.
12.4. Note on Locatives
You may have noticed that locatives are, strictly speaking, unnecessary; you can
do everything locatives allow by appropriate use of, for example, SET, LVAL,
PUT, NTH, etc. What locatives provide is generality.
Basically, how you obtained a locative is irrelevant to SETLOC and IN; thus
the same program can play with GVALs, LVALs, objects in explicit structures,
etc., without being bothered by what function it should use to do so. This is
particularly true with respect to locatives to LVALs; the fact that they are
independent of changes in binding can save a lot of fooling around with EVAL
and ENVIRONMENTs.
pymdl's own. Two things about this chapter are worth setting down, because a reimplementation could easily get either wrong.
First, a locative is a reference into a live container, not a copy of what it found. pymdl represents one as the container plus a key, so
SETLOCwrites where the manual says it writes and a locative obtained before aRESTstill points at the same element afterwards, exactly as 12.1.3 requires. Chapter 10'sRESTbox is the same fact seen from the structure's side.Second, the "hidden structurings" of the opening paragraph are the ones that make locatives interesting and are also the ones that carry the most era detail: a
LOCDreaches into the binding stack, which means it has to know when its frame has died (henceILLEGAL-LOCATIVE), and aLOCASreaches into the association table of chapter 16. Chapter 34's compiler declines to compile aFUNCTIONthat takes anLLOCof one of its own unspecial locals, for the same reason it declines aFUNCTIONliteral that closes over one -- a Python local has no address the interpreter can hand out. TheSPECIALdeclaration of chapter 17 is what makes it compilable, and that is not a pymdl restriction: the era's compiler needed the same declaration, for the same reason, about a machine register.