[To reply replace "Aaron.Sloman.XX" with "A.Sloman"]
There has been a certain amount of discussion on the possibility of
introducing typed identifiers into Pop-11, or rather adding more
types, since we already have procedure identifiers and a few other
oddities, like macros, syntax words, active variables.
1. First a little history. I don't know if Robert Rae in Edinburgh ever
reads this news group but if he does he may be able to expand on, or
correct, the following.
During the late 70's the WonderPop system (WPOP) which he developed in
Edinburgh, which ran on Dec-10 and Dec-20 machines, allowed one to
declare identifiers to be of type integer, or type real, and I think
also type array of integers (arbitrary dimensions) and array of reals
(ditto).
I can't recall the syntax, but the upshot was that first of all some
extra compile-time checking could be done (e.g. having a constant or
type-able expression on the left side of an assignment whose type did
not match that on the right side could cause a compile time error), and
in addition some compile time optimisation could be done.
I vaguely remember finding that for simple arithmetic functions WPOP ran
as fast as Pascal on the DEC-10.
My guess is that in principle for reasons of efficiency or increased
compile time error checking pop-11 could be extended with a number of
additional identifier type declarations. However, doing this in a
totally bug-free way would be very hard. The language is too soft
and flexible. (Which I see as one of its strengths.)
Moreover this extension might not go well with the existence of
functions which can be applied to many types of objects, e.g. <> which
can compose functions, can join two words, two strings, two vectors, two
lists. Purists might which to add specialised versions of <>, i.e.
word_<> string_<> vector_<> etc.
And don't you like being able to treat lists as if they were functions,
i.e. typing
list(3)
rather than
hd(tl(tl(list)))
??
2. Secondly it's worth reminding non-experts who may be reading this
discussion that the poplog virtual machine used by pop-11 is a strongly
typed system insofar as every object in the machine at run time has a
definite type which can be checked at run time.
They can be either built-in types or else vector-types or record-types
defined by the user. For each type there's a host of associated
information and procedures held in keys and at run time given any object
the associated key can be obtained via the procedure
datakey(item) -> key
The types available are summarised in REF data, which can be browsed
online at
http://www.cs.bham.ac.uk/research/poplog/doc/popref/data
and there is information about keys in
http://www.cs.bham.ac.uk/research/poplog/doc/popref/keys
This means that it is often possible to make programs very robust by
doing run-time type-checking even in cases where it is very difficult at
compile time to analyse what value will be returned by an expression
(e.g. because a program is interacting with a user who may type in a
number a word a string, or whatever).
But implementing this run-time checking requires NOT worrying about the
small percentage loss in speed that follows from the tests. You'll get
back far more speed than you've lost, in the next wave of CPUs on the
market.
But some will complain that run time is too late: we need to find errors
at compile time, before anyone runs the code in earnest. Fine if you
can: I believe that for many very complex applications, especially those
where programs are interacting with an external environment (e.g.
humans) that's not possible in principle, and where possible in
principle is not always tractable.
However, the number of places where run-time checks can be planted is
only a linear function of the size of the program text, so why not do
that and use good exception-handling/error-recovery mechanisms?
[Incidentally, it is also possible to plant code to check for stack
errors at run time. E.g. in the Sim_agent toolkit which is a very
flexible and extendable package for exploring agent architectures, there
are many places where user-defined procedures or methods are run.
After the toolkit had been used for a short time by other people as well
as by me, I quickly learnt that it was essential to check the length of
the stack before and after invoking user defined procedures and if the
lengths differed, to produce an informative error message. (In principle
these checks could be turned off with a compile-time switch, though I
have not implemented that.) Likewise in the event-handling components of
my RCLIB package. Most such stack errors will then show up during
normal systematic testing, even if it would be hard for a compiler to
detect them.]
3. Someone asked what would be lost by introducing one or other kind of
strong static typing. This topic turns up from time to time in relation
to lisp, in the comp.lang.lisp newsgroup, usually raised by a novice
lisp user, and there's then a flurry of discussion in which lisp
experts give examples of why they prefer lisp as it is.
From my viewpoint the main requirement is to permit a type of expression
which has no type, or is of the "universal" type, i.e. it can evaluate
to anything.
For instance you can then write programs like LIB SYSSORT, a merge-sort
procedure implemented by Jonathan Cunningham around 1981 which takes two
arguments, namely a list and a binary (ordering) predicate and returns a
list of items ordered in respect of that predicate. e.g.
syssort([ the cat sat on the mat], alphabefore) =>
** [cat mat on sat the the]
syssort([ 8 3 7 4 5 9 1], nonop < ) =>
** [1 3 4 5 7 8 9]
E.g. to sort words before numbers both in natural order:
define isbefore(x, y);
if isword(x) then
(isword(y) and alphabefore(x,y))
or isnumber(y)
elseif isnumber(x) then
isnumber(y) and x < y
else
mishap('Word or number needed',[^x ^y])
endif
enddefine;
Than
syssort([8 cat 7 bee 1 elephant 3 4 dog ant bat], isbefore) =>
** [ant bat bee cat dog elephant 1 3 4 7 8]
Syssort is an example of an enormously useful higher order function. It
was written on the assumption that there was no need for its author to
think about the types of the elements of the lists or the types of the
arguments of the predicate. That responsibility can be left to the
programmer who provides the ordering predicate, and if she wants the
list to contain an arbitrary mixture of types of entities, that's fine,
as long as the predicate supplied can handle any combination of them.
I understand you can't do that in ML, despite its support for
polymorphic types.
There are many such useful library procedures, e.g. various kinds of
list membership procedures, list, tree and graph searching procedures,
procedures for replacing one part of list or tree with another, and
many other list-manipulating procedures, where the algorithms used need
not pay any attention to the CONTENTS of the list.
I.e. the algorithms are totally generic. Why should such procedures have
to take any account of types that the *contents* of the lists may have.
It's completely irrelevant.
I would not want to use a language that interfered with the easy
production and use of such multi-purpose library procedures.
Of course, if a statically typed language includes a universal type
(along with all the apparatus required for run-time type-checking) then
one can use that type for such generic procedures.
4. Although it would be hard to change pop-11 to behave like a language
with strong static types (i.e. every expression has a well defined,
reliably computed type), it might be both easy and very useful to extend
the variable declaration syntax so that variables had types associated
with them which are checked at *run* time. E.g.
type_vars n:isinteger, w:isword, ....;
might cause the compiler to behave differently when it later meets
-> n;
or
-> w;
I.e. the above Pop-11 instructions would compile to calls of
, check_isinteger() -> n;
, check_isword() -> w;
where check_isinteger takes one argument and if it is an integer returns
it otherwise invokes an exception handler, and likewise check_isword.
This could be done by modifying the compiler procedure sysPOP, which
users can redefine to plant new code for assignments.
In fact, with a suitably modified sysPOP, would not be hard to define a
syntax word "type_vars" which behaved exactly as above. without changing
anything in the core pop-11 system. A property could associate typed
identifiers with the corresponding checking functions.
Implementing that is a nice little exercise for people wanting to
practice ways of extending the language?
Of course, doing this in such a way as to detect any inconsistent
re-declaration of the typed identifier using existing declaration
formats (e.g. lvars, vars), is much harder.
Even so, the above could be a useful syntactic extension, reducing the
need to plant checks all over code in programs that have to be robust.
CONJECTURE: languages with run-time type-checking support inherently
more robust software development than languages with compile time
checking where objects do not have recognisable types at run time.
(Except for trivial applications, of course.)
Enough for now.
Aaron
==
Aaron Sloman, ( http://www.cs.bham.ac.uk/~axs/ )
School of Computer Science, The University of Birmingham, B15 2TT, UK
EMAIL A.Sloman AT cs.bham.ac.uk (ReadATas@please !)
PAPERS: http://www.cs.bham.ac.uk/research/cogaff/
TOOLS: http://www.cs.bham.ac.uk/research/poplog/freepoplog.html
|