[OT] Strong/Weak/Dynamic/Static typing (was Re: Python Strings)
Jonadab the Unsightly One
jonadab at bright.net
Fri Sep 22 11:41:44 EDT 2000
m.faassen at vet.uu.nl (Martijn Faassen) wrote:
> > Actually, this discussion has me trying to classify Inform's
> > typing system, and I'm having trouble. Perhaps if I describe
> > its typing system you can tell me how to classify it...
>
> Trying to figure it out now..
>
> > Everything is an integer. I'll explain. If you assign an
> > integer to a variable, that integer is literally stored in
> > the variable itself -- so the variable actually holds the
> > integer.
>
> All right. So, in Pythonic syntax:
>
> a = 1
>
> Should behave similar to Python, though the implementation in Inform
> seems different as no integer object is created.
Right. There's no integer object; the variable just literally
holds the integer. This happens whether the variable is a
declared global, a local belonging to a routine, or a property
of an object.
> > If you assign anything *else* to a variable,
> > the item itself is not stored. An integer is stored. For
> > some kinds of things (strings, routines) the stored value
> > is a packed address.
>
> Okay, so in that case, variables are references to these objects.
Yes, essentially (although Inform parlance normally reserves
the term "object" for an object specifically, but I understand
what you mean). However, variables don't hold references
to objects in exactly the same way; notably, they do not hold
the address. (It is in theory possible to get the address of
an object from the object table using zasm instructions[6],
but you would not normally every have any reason to do that.)
The difference here is often transparent to the programmer,
but not *quite* always. Since objects are numbered sequentially,
and since you normally include the parser at the top of your
code before defining any objects, and since the last object
the parser defines is selfobj, the cannonical way to loop
through all of your own objects is like this:
for (i=selfobj+1:i<=top_object:i++) {
! Do stuff
}
(top_object is a constant the compiler defines for you,
IIRC.) So you see that there are *occasions* when it
is exposed that these references to objects are actually
integers. Usually you don't think about it, but it
crops up from time to time. (The packed addresses of
routines and strings almost *never* crop up, however.)
> How does Inform know whether a variable contains an
> integer or an address?
It doesn't. However, if the variable contains a packed
address, you can (via a very odd and contrived library
routine called ZRegion -- the language author wanted to
make it more straightforward but couldn't really given
the limitations of the VM to which Inform compiles,
which he didn't create[2]) determine whether it refers
to a string or a routine or something else. (The only
something else it normally would be is an object number.
If it were, for example, a property or dictionary word,
you who wrote the code would *know* that you made such
an assignment.) Furthermore, if the integer in question
is an object number (for example), it's guaranteed by
the structure of the VM to be less than the smallest
packed address[7], so it could never be confused with
a string or routine.
> Does the variable name have a type associated too, or
> is some other magic going on?
That which cannot be determined by ZRegion[8], the
programmer is responsible to know. In practice, this is
seldom a problem, because generally the only three things
you assign in an interchangeable manner are strings,
routines, and objects. Anything else, while the variable
does not hold a specific type as far as the language is
concerned, in practice would be assigned to a variable
that the programmer knows contains that kind of thing.
> > For other kinds of things (attributes,
> > properties, dictionary words, objects, classes, ...) the
> > value stored is a unique ordinal number. (So, the first
> > dictionary word that appears in the code will be 1, and
> > so on. The first property declared will also be 1. And
> > the first object. Et cetera.)
>
> I'm not sure I understand this. Does this mean you have addresses here
> as well, but in a fixed range (or array indices, if you like).
Indices is closer. If you have 879 objects in your code,
they will be numbered 1 to 879. This is part of the
structure of the VM, the z-machine, which has an object
table hardwired IIUC. Similar for attributes, properties,
and dictionary words. (I'm stone cold certain the VM
has the dictionary hardwired, since if you use zasm (the
assembly language for this VM) there are instructions
for tokenising.)
> > So, if you have an object...
>
> > Object foo "foo";
>
> What's the "foo" bit about?
That's the object's hardware short name. It is required
unless you include a short_name property and is a good
idea anyway. Without delving too much into the way
Inform handles user I/O (which puts many other languages
to shame in a number of ways, but nevermind), the program
will call the object a foo when it talks to the user.
The non-quoted foo is the internal name, which the
program code uses to refer to the object. If the user
needs to be able to refer to the object, then you use
either a name property or, if you need to do more
complicated stuff, a parse_name routine.
> > And you give the object some attributes...
>
> > give foo openable open container;
>
> > You can copy those attributes to another object...
>
> > for (i=1:i<=last_attribute:i++)
> > if (foo has i) give bar i;
> > else give bar ~i;
>
> What does the 'give bar ~i' part do?
It clears the i attribute of bar. The ~ reads as "not".
Attributes, I should clarify, are strictly boolean.
(They are literally stored as one bit per attribute
per object in the compiled binary.) This is really
the only static typing in Inform. Properties, globals,
and locals can hold anything, as discussed, but attributes
are just either set or not. Again, this is hardwired
into the VM, which was created LONG before the language.
(This is where Inform gets its portability[3].)
> > It's very different from the type system of,
> > say, Perl... maybe it's yet some other kind
> > of typing? Or are "weak typing" and "dynamic
> > typing" sufficiently general terms to include
> > this system?
>
> I think that is Perl's type system is similar to Python's you could
> pull off some of this copying as well.
My point is not that you can copy attributes. I realise both
Perl and Python are far more powerful than Inform (unless you
happen to be doing natural language processing...) I was
pointing out the manner in which it can be done, which
says something about the kind of typing -- I'm just not
sure what.
> What Inform seems to do is expose some implementation details (addresses),
> though I'm not quite sure how it could work internally yet..
Actually, they're not really exposed to the casual programmer;
the Designer's Manual doesn't talk about them really. I know
about them because I've read the technical manual and the
specification of the z-machine and no small number of related
technical discussions on rec.arts.int-fiction. As far as the
casual programmer is aware, you can simply do something like
the code below. Notice that the description property of one
object holds a string, and the description property of the
very next object holds a routine (which executes a rather
lousy joke I shamelessly ripped off from Graham Nelson for
the sake of the example, but nevermind). When the library
is ready to print the description of the first object, it
discovers a string and just prints it. When it is ready
to print a description of the second object, it discovers
a routine and runs it. Since the routine returns true[1]
rather than false (which would be the default) it assumes
that the routine has printed something. If the routine
had not returned true, the library would have printed
a default "You see nothing special about the spade."
There are similar cases where you can substitute a
routine for an object. And there are library routines
in case the programmer needs to do this kind of thing
himself. Calling PrintOrRun on an object property will
print it if it's a string and run it if it's a routine,
returning the return value of the routine (or true if
it was a string). ValueOrRun similarly will run
it if it's a routine (and return its return value)
or otherwise will simply return its value if it is
not a routine. Here's an example of substituting
a routine for a string...
Object lawnmower "lawn mower" garage
with name 'lawn' 'mower' 'lawnboy' 'green',
description "It's a green lawnboy. This isn't an example
of exemplary writing, but I'm demonstrating
a point.",
! ... insert more stuff here...
has switchable;
Object spade "implement" garage
with name 'spade' 'shovel' 'garden' 'implement',
description [;
if (self.short_name == "garden implement")
{
self.short_name = "spade";
"On second thought, let's just call a
spade a spade.";
}
"It looks like any other spade you've ever seen.";
],
! ... insert more of the shovel's code here...
short_name "garden implement";
And here's an example of substituting a routine for an object...
Room kitchen "KITCHEN"
with description "Some lavish description of
the kitchen goes here.",
s_to diningroom,
! More properties here...
has light;
Room diningroom "DININGROOM"
with description "Some lavish description of
the dining room goes here.",
n_to kitchen,
s_to [;
if (livingroom has general)
{
"There's nothing but a heap of rubble
in that direction.";
! Presumably we would have explained
! why when we set the general attribute,
! but perhaps that code is contained in
! the livingroom object itself, which
! handily allows me to evade explaining
! it here..
}
return livingroom;
],
has light;
[1] By way of implicit print_ret; see the Inform Designer's
Manual or just take my word for it.
[2] The z-machine was created by Infocom, originally
for Zork. These days most z-machine emulators
(which we usually call interpreters) are by some
third-party or another, but at the time Infocom
ported one to every platform for which they
released any of their games, so when GN created
Inform to compile to this VM, there was already
wide portability; there is much better portability
for the z-machine now than there was then.
[3] MUCH more portability[4] than Python, but of
course it has a cost in power. There is no
dynamic memory allocation; there is no real file
I/O... OTOH, Inform is sufficiently powerful that
there have been a least three interpreters written
in it (one for Befunge, one for a subset of Lisp,
and one for a subset of Scheme). It's PLENTY
powerful for its intended purpose of creating
text-based interactive games. There is also
now a newer VM (glulx) which kicks out all the
size limitations a few orders of magnitude
and supports sound and graphics and I think
limited file I/O; it will still be more portable
and less powerful than Perl or Python.
[4] z-code can be run on the old rusty mainframes
and 8-bit micros from the seventies[5], the
palmtops of the nineties, the PDP11, Nintendo
Gameboy, Emacs, and certain brands of handheld
calculators, among other things. The only
platform I know about that does not have a
z-machine is the AS/400. Of course, major
platforms like DOS have a choice of dozens of
z-machines, all mutually interchangeable
(although some offer nice features (like
command-line editing, a choice of colours and
fonts, and multiple levels of undo) that are
simply not possible on some of the minor
platforms).
[5] Kaypro, TRS80, Commodore, ... things that
make the Apple //c look like the state of
the art.
[6] zasm as in "z assembler", which the Inform
compiler understands, but it's not considered
part of the Inform language, and the casual
programmer need never use these instructions.
Also, they're meaningless if you're compiling
to glulx instead of the z-machine.
[7] The low addresses (which are not packed) are
RAM, and the packed addresses are ROM.
So all strings and routines are in ROM, but
all objects reside in RAM. Since an object
takes up a number of bytes minimally, there can
never be nearly as many objects as the amount
of RAM, and thus there can never be an object
with a number as high as the lowest packed
address. So ZRegion checks the integer that
a given variable holds, and if it's lower
than the lowest packed address it says "this
is something else" (i.e., not a routine or
a string). If you assigned an object,
ZRegion will never think it is a string or
a routine. Nor can it confuse strings and
routines, because the compiler segregates
them in ROM and keeps track of the address
ranges that can refer to each for ZRegion's
benefit. It's a kludge, but it works, and
it lets the compiler, library, and indeed the
program see distinctions that apparently the
VM was not designed to see. There are also
classes, which are an object as far as the
VM is concerned; you can always tell whether
an object is a class or a real object (because
of some under-the-surface magic the compiler
puts into the property table for the object,
the details of which I don't really know)
by checking metaclass(foo).
[8] or metaclass.
[9] I know these footnotes don't appear in order.
I didn't write the post in order. I usually
don't write straight from beginning to end.
Besides, there is no footnote 9.
- jonadab
More information about the Python-list
mailing list