[comp.lang.objective-c] Optional static typing limits

liberte@ncsa.uiuc.edu (Daniel LaLiberte) (04/17/91)

I wonder if we can raise the level of the dicussion 
on static vs dynamic types to consider more generally where
static typing fails to be sufficient or convenient, and why.

I am also interested in the various ways languages that support
*optional* static typing limit the extent of static typing one can do,
and why.  First of all which languages support optional static typing?

I would guess that static typing is made optional in some languages
because the designers recognized that static typing does not handle
all typing situations, and thus dynamic typing (or, alternatively,
C-like void typing) is a useful analog of the goto statement.
But given optional static typing, the language designers could then 
get lazy and not provide sufficient static typing capability for even
well known situations.

One case of optional static typing that I am aware of is in
Objective-C.  And one limitation I have come up against is the static
typing of mutually recursive instance variable types.  An instance
variable in each class is of type pointer to an instance of the other
class.  The problem comes in somehow declaring each class before the
other is declared.  Here is an example:

----- file A.h ----------
// interface of class A

#include "B.h"   // A needs to include B's interface

@interface A:Object
{
	B *b;   // instance variable b is a pointer to B
	...
}

----- file B.h ----------
// interface of class B

#include "A.h"   // B needs to include A's interface  <-- *ERROR*

@interface B:Object
{
	A *a;   // instance variable a is a pointer to A
	...
}

In C++ you could handle this situation by declaring "extern class A"
and "extern class B" instead of including A.h and B.h.  Objective-C
provides no such solution, that I could discover at least.  One is
forced to declare either a or b as just a generic "id" to break the
circularity.  This is simply a silly limitation of the language, but I
am curious to learn of other such examples.

I suppose the problem of hetrogeneous arrays (a realistic application
is where the types of elements and the functions to be applied to them
are not known until runtime) is another area where static typing
systems fall short of the ideal.  A union of types would be better
when it is appropriate than requiring either dynamic typing or
unwieldy static typing and (most likely) multiple inheritance.

Homogeneous generic types are not handled well in some languages,
in that the base type(s) may not be specified so that instances are
suitably constrained.

Here are some more examples:

From: brm@neon.Stanford.EDU (Brian R. Murphy)
> For example, I can't I write a function which returns
> either a boolean or an integer in a complex way.  I can't write my own
> Y combinator.  I can't write a function which allows either a sequence
> of functions which range over numbers or a sequence of numbers as an
> argument.

What are some other categories of problems that don't fit in existing
static typing systems?  Is it possible to come up with a "fix" for
these problems in the ideal language, or will dynamic typing always be
"necessary", like goto is.  Can we organize these and maybe learn
something about the problem?

Dan LaLiberte
National Center for Supercomputing Applications
liberte@ncsa.uiuc.edu

strom@watson.ibm.com (Rob Strom) (04/17/91)

In article <LIBERTE.91Apr16164831@babbage.ncsa.uiuc.edu>, liberte@ncsa.uiuc.edu (Daniel LaLiberte) writes:
|> I wonder if we can raise the level of the dicussion 
|> on static vs dynamic types to consider more generally where
|> static typing fails to be sufficient or convenient, and why.
|> 
|> I am also interested in the various ways languages that support
|> *optional* static typing limit the extent of static typing one can do,
|> and why.  First of all which languages support optional static typing?

I'll answer this question for the Hermes language.

Hermes provides both strong static typing and strong dynamic typing.
That is, it is never possible to apply an operation to an operand
of an inappropriate type.  This guarantee is sometimes enforced
by compile-time checking (static), and sometimes by run-time 
checking (dynamic).  Which technique is used is the programmer's
choice --- in this sense static typing is "optional".

However, I will candidly admit that Hermes is biased towards 
the static approach.  That is, the language design assumes that
most of the time, a variable will: (1) hold values of only a single
type, and (2) that type will be known at compile-time.  The cases
in which the type is not known statically are assumed to occur
less frequently.  The constructs for manipulating dynamically
typed data are more verbose.  This bias comes partially from
the historical tradition of the language, and partially from
the intended application domain:  large, long-lived, multi-application
systems, in which ease of maintenance is more important than
ease of initial writing.  In that domain, type declarations
serve as a form of machine-readable documentation.


|> 
|> I would guess that static typing is made optional in some languages
|> because the designers recognized that static typing does not handle
|> all typing situations, and thus dynamic typing (or, alternatively,
|> C-like void typing) is a useful analog of the goto statement.
|> But given optional static typing, the language designers could then 
|> get lazy and not provide sufficient static typing capability for even
|> well known situations.
|> 
|> [examples from Objective-C and C++]
|> 
|> What are some other categories of problems that don't fit in existing
|> static typing systems?  Is it possible to come up with a "fix" for
|> these problems in the ideal language, or will dynamic typing always be
|> "necessary", like goto is.  Can we organize these and maybe learn
|> something about the problem?
|> 
I agree that there will always be problems for which static typing
will be inadequate and therefore that an escape into dynamic typing
will be necessary.  I don't see the need for "language wars" between
proponents of static vs. dynamic typing.  When static typing is
possible, I prefer it, because I always prefer errors to be caught
before a program module begins executing rather than in the middle
of execution.  When static typing is impossible, I prefer dynamic typing
over escaping from type-checking altogether.  Had Hermes a different
application domain, we might have chosen to make dynamic typing
the default and static the exception, rather than the other way around.

Here are some applications for which Hermes uses dynamic typing rather than static:

o On-the-fly program creation:  Hermes has a data type "program" whose value
  is a syntax-free representation of a Hermes program.  Program values
  can be built at run-time and then instantiated as processes
  with a particular initialization interface.  It is checked at instantiation
  time that the program you have built is type consistent with the
  initialization interface you expect.

o Name servers:  In Hermes, the right to access a service is encoded as
  a value of a particular "output port" type.  A name server is a process
  which, in its simplest form, owns a table of pairs <name, port>.
  This table is typically heterogeneous since the "port" components
  will be of different types, representing the different services
  being named.

o File systems:  This is a situation similar to name servers.  A file
  system is a table of pairs <name, thing>, but the <thing>s are of
  different types.

Here is a situation in which Hermes can use static typing where some other
languages cannot:  Suppose I am managing a collection of operator consoles.
These operator consoles have heterogeneous protocols:  some are ASCII
glass teletypes, some are bitmap displays, some are "windows", etc.
However, all support a standard *interface* for printing and reading
lines, prompting, etc.  In Hermes these console "objects" (called
"processes" in Hermes) are visible only through their ports.  Since
the ports have the same interface, they appear to the console
manager as a homogeneous collection.   The fact that these console
objects have different internals does *not* make them different types.
In some other languages, they *would* look heterogeneous.  For
example, they would be different task types in Ada, and different
classes in Smalltalk.  Hermes would not need dynamic typing to
handle a case like this.

Your Objective-C problem has no analog in Hermes.

Please note that followups are directed to comp.lang.misc.
-- 
Rob Strom, strom@ibm.com, (914) 784-7641
IBM Research, 30 Saw Mill River Road, P.O. Box 704, Yorktown Heights, NY  10958

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (04/18/91)

In article <LIBERTE.91Apr16164831@babbage.ncsa.uiuc.edu> liberte@ncsa.uiuc.edu (Daniel LaLiberte) writes:
> What are some other categories of problems that don't fit in existing
> static typing systems?  Is it possible to come up with a "fix" for
> these problems in the ideal language, or will dynamic typing always be
> "necessary", like goto is.  Can we organize these and maybe learn
> something about the problem?

Amazing! Someone else is actually trying to solve problems and make
programming easier, instead of arguing about exactly how hard it is!
I like this guy's attitude.

I propose we focus on just one problem: What should a type system look
like in order to allow everything people want to do with types? In other
words, what kinds of constraints should the programmer be able to
express upon objects (in the general sense, not the OO sense) in order
to achieve both safety and generality?

Let's not argue whether dynamic typing is necessary. Let's just do what
we have to so that it's obviously unnecessary. (Is that too upbeat for
comp.lang.misc readers?)

As a starting point: Ben, could you describe the Russell type system in
more detail, or point us to references? I think the basic idea makes
sense. If you apply certain operations to a value inside function f,
then f should specify the value as anything subject to those operations.

And, as a first sample problem to be solved by a typing system: How do
we write a generic, type-safe sorting routine? What's the best that
available languages can do to make this work? Remember, type-safe means
that the sort routine will never tell you ``can't apply < to glorps'' at
run-time, as a dynamically typed system would.

We may have to separate the issues of typing for storage management and
typing for typechecking/security/etc.

Followups to comp.lang.misc.

---Dan

lerman@stpstn.UUCP (Ken Lerman) (04/18/91)

In article <LIBERTE.91Apr16164831@babbage.ncsa.uiuc.edu> liberte@ncsa.uiuc.edu (Daniel LaLiberte) writes:
|>
|>----- file A.h ----------
|>// interface of class A
|>
|>#include "B.h"   // A needs to include B's interface
|>
|>@interface A:Object
|>{
|>	B *b;   // instance variable b is a pointer to B
|>	...
|>}
|>
|>----- file B.h ----------
|>// interface of class B
|>
|>#include "A.h"   // B needs to include A's interface  <-- *ERROR*
|>
|>@interface B:Object
|>{
|>	A *a;   // instance variable a is a pointer to A
|>	...
|>}
|>
|>Dan LaLiberte
|>National Center for Supercomputing Applications
|>liberte@ncsa.uiuc.edu

A (not very nice) way of handling this in Stepstone's Objective-C
compiler is to write struct __objcA *a and struct __objcB *b to
declare the instance variables.

A better way would be for the compiler to permit @interface A {} @end
to be an incomplete definition of class F which could be completed
later on.

A further deficiency of Objective-C regarding automatic and static
instances of objects (those that are declared Object foo, instead of
Object *foo) is that the compiler doesn't automatically call an
initializer for them so that only the isa pointer is set.  This
implies that the writer of the class must know that it will be used
this way.

I personally don't use them and suggest that others avoid them, also.


Ken

Disclaimer: In case you haven't noticed, I work for Stepstone.  I
don't speak for them in this matter.

lerman@stepstone.com