Building Blocks

Building Software Better

Why dynamic-typing? (2)

Posted by Mark on August 27, 1997

Follow on topic “… actually, you could have both, couldn’t you? …”

Joern Janneck wrote:
> Mark Fussell wrote:
> >    In my own words, the main reason is that Smalltalk has a simple &
> > powerful concept of building software out of Objects that send Messages
> > to other Objects.  This is more powerful in both the small and the large
> > than a language that adds the additional concept (and constraints) of
> > "Types".
>
> actually, you could have both, couldn't you? many oo languages do, i see
> little reason to present them here as alternatives. the question really
> is, why only one of them is better than the two put together, isn't it?

Sure, there are lots of variations:

  1. Either you have compile-time types or you don’t
  2. Either those compile-time types are mandatory, they are optional,
    or there are loopholes [e.g. allow dynamic typechecks]
  3. Either types are explicitly declared or implicit
  4. … [equal or identical type matching, granularity, DBC enhancements,
    type/class separation]…

Some OO languages support multiple variations, but usually even if a
language supports semi-optional compile-time types they will have the
bulk of their code with mandatory compile-time checks and you can’t just
turn it off. Most common compile-time typed languages have mandatory
types with loopholes.

But the questions were:

>   (1) Why is Smalltalk dynamically typed?
>   (2) Why does dynamic typing (as done with Smalltalk) not negatively
>       affect the stability of large applications?

The part you quoted was the auxilary answer to (1): the main answer was
read HOPL-II and similar sources. And to discuss (2) we have to compare
dynamic (optimistic) typing to static (pessimistic) typing. It would
certainly be quite valid to consider them within a single language. I
did a bit of that in:

   http://www.chimu.com/publications/smallJava/index.html

And note that I would prefer not to be comparing relatively
weak/immature languages (in terms of static typing) like Java and C++.
I would much rather be talking in terms of Eiffel or Cecil. But the
reality is that Java is currently a common language to show examples. I
certainly would not make a point of a failing in Java (e.g. invariant
return type) as a failing in static typing. I tend to think primarily
in terms of Eiffel but with the additional separation of types from
classes (that Eiffel supports but does not enforce). This seems to be a
reasonable perspective considering commonly available languages.

So returning to the topic, I will add a little more explanation for a
few of the points.

Problems with type-checking

Note that there is nothing conceptually wrong with static
type-checking. It would be wonderful to create software that can be
completely verified before execution. That is provably impossible, but
getting closer to that goal would be nice. Static typing is a possible
approach: you at least make sure that all clients and suppliers agree to
a contract ahead of time before even attempting to run the program.
Some of the problems with static typing are that:

  1. Types are sometimes bound to Implementation Classes
  2. Types have poor granularity. Frequently a Type will be specified
    that has too many operations (is too specific) to be useful in multiple
    contexts even though subsets of those operations (a more general
    concept) is widely useful. Since it costs effort to name and create
    each Type, there is an impetus of reduction that again impedes reuse and
    generalization. Save now, pay later.
  3. Precise type information is lost when objects are fed through more
    generic structures.
  4. Types restrict future type-safe expansion to programs. Some
    programs that could have been written type-correctly if done in one
    “lump” are impossible to write given the actual historical growth of a
    program (many people, different companies, over time, with limited
    foresight). Choose now, pay later.

Of these (A) is actually easily fixable, and Java has helped with that.
And maybe people accept the workaround with (C): either massive
conceptual (and usually code) bloat for a myriad of homogeneous
structures or using a dynamic-typecheck loophole. But (B) and (D) are
pretty much intractable flaws in static typing for the near future (next
10 years of commercial software). I would love to be wrong, but it
seems unlikely at the current rate of change in programming languages
and research.

Someone could say somehow they avoid (B) and (D) but I would bet big
money that they are being bit by them all the time. I gave an example
from the Java collections. Everyone who programs in Java had the
opportunity to comment on the JDK 1.2 collections for maybe a year
before they were frozen. But the final choice was to have neither a
concept of an ‘Iterable’ object or a ‘Containing’ object. This caused
all of the following methods to have poor granularity:

    public boolean containsAll(Collection c)
    public boolean addAll(Collection c)
    public boolean removeAll(Collection c)
    public boolean retainAll(Collection c)

Each of the above only needed a single operation: ‘contains’ or
‘iterator’. But Javasoft rejected:

    public boolean containsAll(Container c)
    public boolean addAll(Iterable c)
    public boolean removeAll(Container c)
    public boolean retainAll(Container c)

Why? Because it increased the number of interfaces, which would be a
pain (consider their design notes and the analyses of Doug Lea’s
collection classes). A perfect example of both (B) and (D): save now,
choose now, pay later. And the examples are all over the place in Java,
C++, and Eiffel library code.

The arguments presented against this existing problem actually reinforce
the examples of (B) and (D):

Changing an existing class/interface

Davorin Mestric wrote:
>  Yes you can:
>     ...
>     public interface IContains{ public boolean contains( Object o);}
>     ...
>     public void AbstractCollection::removeAll( IContains c) {

No, *I* can’t change ‘AbstractCollection’. And Javasoft chose not to do
it that way. They may have chosen unwisely, but that actually means a
whole community of Java programmers chose unwisely because of other
forces solely attributable to static typing. The problem was putting
these forces on their decision in the first place.

Joern Janneck wrote:
> ... now
> assume that for a given class of collection (hashed collections are a
> good example) it is more efficient to implement removeAll by iterating
> over the argument (when it is smaller, maybe) instead of over the
> original collection:
[...]

OK, so the original contract/type should have been ‘IterableContainer’.
How should I know that ahead of time? Why did Javasoft not choose that
interface instead? Collection is definitely too big
a requirement because it dramatically limits the usability of the method
‘removeAll’. I would argue that ‘IterableContainer’ is also too big.
Why can’t I just support the ‘contains’ method? As I gave an example:

   new Container() { public boolean contains(Object o) {
       ... person is < 6ft...
   }}

I can’t iterate over all the people under six feet tall, or it would at
least be very painful. Why should iterability be a requirement to a
method like ‘removeAll’? Intuitively this seems extremely limiting and
I know it to be so in actually building systems. But the bigger problem
isn’t just the choices in contract for the ‘removeAll’ method, but that
out of simplification that that contract was lumped in with a whole
bunch
of other contracts for Collections in general.

Summary

Within just this one simple class (AbstractCollection):

  • 4 are too specific (they only require an ‘Iterable’ or ‘Container’)
  • 3 are non-typed (typed to Object)
  • 4 are non-typed (no parameters)
  • 1 ‘toArray(Object[])’ is redundant (and is effectively dynamically
    typed)

So 7 out of 12 are identical with the dynamic version (considering only
parameters), 1/3 are “incorrect” caused by static typing disincentives,
and 1 is simply redundant.

Joern Janneck wrote:
> ...let me again point out that i think that code size
> as such is not the issue in big systems. it is design, and code
> complexity. these don't differ in principle between type systems, and
> arguably static type systems might encourage more structure. or not, who
> knows.

In principle, with a perfect static type system, there is no
difference. But that type system does not exist and current languages
are very far away from it. The problems (A)-(D) are not just affecting
intra-method complexity (semi-harmless code bloat) they are affecting
inter-object and inter-package complexity. In just the example
presented we will have to increase (potentially many) clients effort to
use a Collection for their goals, or we will have to provide additional
methods somewhere else (a Collection helper) that a client will need to
know of and again increase the clients’ efforts.

And the argument can be more informed than just personal opinion.
We have plenty of examples of the various languages to compare[1]:
The libraries of Smalltalk, C++, Perl, Java, Eiffel, etc.; Design
Patterns in various languages; and so on. If you review all these
languages in depth you will find that static typing is certainly harming
scalability. It may be helping in certain ways against mistakes in the
small, but it is interfering with good code (code that will execute
properly at runtime, is easy to understand, is easy to maintain, and is
useful to many clients) that helps grow systems in the large and over
time.

But great software can be written in any language in spite of each’s
flaws. It is just important to keep the great ideas in your team’s
heads … and the bad ideas too so you can avoid them (or work around
them) when possible.

--Mark
mark.fussell@chimu.com

[1] A multi-year immersive experience in each language would be the best
approach, but some of the following might be easier to catch up on:

  • Design Patterns: Elements of Reusable Object-Oriented Software
  • The Design Patterns Smalltalk Companion
  • Reusable Software: The Base Object-Oriented Component Libraries.
  • Smalltalk-80: The Language and its Implementation
  • JDK 1.2 & JGL (www.objectspace.com)
  • The C++ ANSI/ISO Standard Template Library
  • CPAN

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: