[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RE: dynamic vs. static typing

Steve Dekorte wrote:
> Ok, now let's take a simple and common example such as a List that can
> hold any collection of objects (of different types). How do we
> implement such a thing in a statically typed system? For example, let's
> say our List object has an "at(index)" method which returns the object
> at the specified index. What would the return type for such a method
> be? - without being the equivalent of a void *, in which case, we can
> once again get "object does not respond to this message" errors, which
> is what the static type system was supposed to prevent.

In an ideal system, you should be able to express whatever type constraints
about the list that you expect to always hold true.  Even in the most
dynamically typed language, you have some expectations of the list's
contents - perhaps all the objects are expected to respond to the 'print'
message, for example, i.e. they all support a common interface, even if that
interface consists of just a single message.  So, the standard way to type
such a list is to say that all the elements support interface X, or
typeclass X, depending on the language and typesystem you're using.  Even
the oft-maligned C++ and Java support this capability.

In fact, a question would arise if you *didn't* want to statically type such
a list.  What on earth would you want to do with it that involves knowing
literally nothing about the types of the elements?  If your answer is that
you'll use introspection to figure out what to do with the objects, then
that presumably implies that the objects support an introspection interface,
and you can specify that as a type constraint.

In DT languages, operations like 'print' or introspection are typically
supported through a single standard interface, such as that of the Object or
Class classes in Smalltalk.  (I'm talking in terms of class-based systems,
but similar logic applies to prototype systems.)  Since such languages
usually don't explicitly separate their class interfaces into independent
sub-interfaces, there's a tendency to think of the kind of list we're
discussing as lists of Objects.

This can lead to some muddled thinking about types: if the only explicit
types are in fact composites of multiple interfaces, like 'Object',
factoring problems are created which *require* dynamic typing to solve: for
example, if two otherwise unrelated classes share some interface, the only
common explicit type they can be both be said to share is Object, even if
Object doesn't actually contain the interface in question.  The result is an
apparent quandary from a type perspective, and the erroneous conclusion may
be that you need dynamic typing to deal with these situations.  In fact,
"all" you need is to fix the object system in question (throwing it out
might be a good start ;-)

Note that I'm not saying there are no problems with static type checking -
I'm pretty much in the same camp as Matthias and Shriram (although with less
theoretical knowledge!).  I think good static typesystems, as in ML and
Haskell, are useful, powerful, and can be fun to work with - but using them
can have consequences which we often may want to avoid.  The point of it
all, though, is to be able to use automation to tell us things about our
programs that may be tedious or expensive or undesirable to find out in
other ways.  I don't think it can reasonably be argued that we don't want to
use automation to tell us things about our programs; the only real argument
we can make is that existing automated analysis tools can incur too great a
cost in terms of the way code has to be written to support them.

Note that the costs in question have nothing to do with having to annotate
"all the variables, arguments and return values" with types.  In modern
type-inferencing languages, the need for explicit annotation is minimal, and
you can write hundreds of lines of code without needing a single type
annotation.  The costs arise when you find that the types are constraining
your program in ways that you'd like to be able to circumvent.  It's in this
area that work needs to be done.  One promising direction are the PLT type
analysis tools like MrSpidey and MrFlow, which are run on a program
independently of compiling and running that program.  They won't stop you
from running a program that's not well-typed, but they can warn you that
there might be a problem.

Summary: there are a bunch of issues which currently confuse the type system
landscape.  A few of these which I've touched on are:

* Mainstream DT languages tend to have simplistic objects systems which are
broken from a type perspective.
* Mainstream ST languages have broken type systems which in some ways offer
the worst of both worlds - they constrain without offering safety in return.
* The idea that you can't run a program that doesn't typecheck at compile
time is an assumption built into many languages, it's not a hard and fast
* Most languages are either "all DT" or "all ST", there's precious little
middle ground.  I think this has more to do with what's easy to design and
implement than what's ultimately desirable for use.

We're talking about language design here, let's not let the existing
typesystem landscape cloud our judgement too much.