Building Blocks

Building Software Better

Why dynamic-typing? (3)

Posted by Mark on August 27, 1997

Java Collection

Joern Janneck wrote:
> Mark Fussell wrote:
> > If you need a specific example, consider the [completely randomly
> > selected] JDK 1.2 Collection code of:
> >     public boolean AbstractCollection::removeAll(Collection c) {
[snip]
> >
> > The static-typing problem in this extremely simple code is that 'c' only
> > needs to respond to 'contains', not be a Collection.  This means I can't
> > just use any containment concept I want to (say, remove all people who
> > are 6' tall)
>
> ... which is something that could be defined as a collection, couldn't
> it? at least in a good design, it should be able to.

I don’t think you really mean that. Of the 13 operations for a Collection:

    public boolean contains(Object o);
    public boolean containsAll(Collection c);
    public Iterator iterator();
    public int size();
    public boolean isEmpty();
    public Object[] toArray();
    public Object[] toArray(Object a[]);
    public boolean add(Object o);
    public boolean remove(Object o);
    public boolean addAll(Collection c);
    public boolean removeAll(Collection c);
    public boolean retainAll(Collection c);
    public void clear();

The concept of “contains a person 6′ tall” could only reasonably be considered to have the first operation at its core with the second as a helper (really an “augmentation” from Collection’s point of view). _Maybe_ the third through seventh operation if we insist on the set being preknown and finite. All of the rest imply serious mutability which it would be unreasonable to cause all clients of ‘removeAll’ to support for the parameter ‘c’. I can’t believe you consider this to be a good design and good code. Again, my definition of good code is code that will execute properly at runtime, is easy to understand, is easy to maintain, and is useful to many clients. This problematic restriction (all 13 operations instead of 1, 2, or maybe 7) makes this code much less useful, harder too understand (“why do we need a full heavyweight Collection for a simple predicate-like test”), and the system less maintainable because we have not specified what we really, precisely, wanted from the parameter ‘c’.

The argument that HashedCollection would like more from the parameter ‘c’ is really a symptom of this imprecision and a weighting towards implementers over clients. Clients are the important ones and need to be considered first. There will be many more clients of a particular operation than implementers of it, so the quality/usefulness of the contract to the client is much more important to the scalability of the application. Client-punishing contracts are the hobgoblins of static typing.

--Mark
mark.fussell@chimu.com

Augmenting existing Types

Mike Anderson wrote:
[snip]
> ...If, when implementing the client
> of a preexisting class, I could create a new interface and declare that (in the
> context of my client) the preexisting class implements that interface, problem D
> would be solved (wouldn't it?).  Is such a feature feasible?  Are there
> compile-time-checked languages that support something like this?

You might look into BeCecil as one example:

   http://www.cs.washington.edu/research/projects/cecil/cecil/www/www/Papers/BeCecil.html

Augmenting existing Types

Joern Janneck wrote:
> Markus Kohler wrote:
[snip]
> > Here's a new example
>
> i'd still be interested in your answer to my objection to the first
> example. after all, it was originally supposed to show the virtues of st
> for _large_ sw development. i am still waiting to be answered on that
> issue.

If I gave the first example you are referring to (as the originator of this particular title of thread), I am not sure what answer you are looking for. I think multiple people have showed the problem that static typing has with scaling (in both space and time) because of certain limitations [‘A’-‘D’] fairly well. Nothing that I and others presented as problems are _unknown_ to static typing research and people are busily working on solving these types of problems. Language families of Cecil, Haskell, ML, and so on are trying to solve these problems because they *are* problems. And they affect scalability.

Dynamic OO languages have benefits that cause (well designed
applications) to scale extremely well in size and space because they
allow components to be reused and pieced together in a more optimal way,
and so reduce overall system complexity. The reason is that dynamic OO
languages simply do not artificially enlarge a contract between two
parties to include irrelevant details from:

  1. Other parties (by being lumped together in a single named type).
  2. Other times (because types were frozen by a compile at one
    moment in time)
  3. Implementation

Current static languages unfortunately encourage or require the
inclusion of these artificial restrictions in a system, which impedes
scalability (space and time).

Specific example

My example was:

    public boolean AbstractCollection::removeAll(Collection c)

No, this is not large programming yet, but it is headed that way:
Collections are a core library in any programming language. If there
are serious restrictions and overhead in dealing with them, this is a
strong indicator of what will come in many areas as the system scales
(in size and over time). Simply consider the quality and lifetime of
the Smalltalk-80 collections. They are better (more capable, more
useful, and more maintainable) than anything that has come from the C++
and Java languages over their unstable lifetimes — even though both
languages could have leveraged this existing work and its published
improvements. The Smalltalk libraries have been amazingly stable over a
20 year period of use and growth: Smalltalk code I have from 1986 is
still CORRECT and is cleaner than (hopefully much more skillfully
written) Java code from 1999.

My other example included looking at the complexity of all the Design
Patterns in dynamic OO vs. static-typed OO languages. If you seriously
think that the patterns are more elegant, scalable (size and time), and
maintainable in static-typed OO languages — Java, for example, needed
three *totally different* implementations of the Observer/Listener
pattern to deal with _primarily_ different typing issues — there is
unfortunately little to talk about. If you simply think the tradeoff is
worth it, there is also little to talk about because I accept that
perspective. Note that I was answering a specific question:

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

for someone who had never built a large application in Smalltalk or a
similar language.

Tradeoffs

The one aspect I do consider very useful with static typing for building
systems is that it *forces* developers to think formally about
interfaces between objects or their code breaks quickly. This is a very
good training experience and should be repeated off and on to make sure
people aren’t getting sloppy. But if you are a good designer, the
realities/restrictions of static typing cause you to produce somewhat
less scalable (over size and time) applications[1] than for a dynamic OO
language with similar effort in documentation of protocols and test
suites. So the idea of switching back and forth between an optional
statically-checked and a dynamic [with documented protocols] OO language
is certainly a good one. Better than having casting/type-checking
loopholes (a sort of strange intermediate).

Most large dynamic OO projects do have this characteristic
(static-oriented documentation) because UML and most other notations
require a static-type based perspective. And well-defined
types/contracts exist all throughout good Smalltalk code, but some of
the best contracts (Valuable, Observer, Iterable, etc.) are just too
fine-grained, too pervasive, or too generic to be represented in a
statically-typed program. Unfortunately, a dynamically simple concept
like “augmentations”[1] is also impossible to correctly represent in the
core UML because of its simplistic (C++ ish) static-typing origin.

--Mark
mark.fussell@chimu.com

[1] Just the one feature of Envy extensions (the ability to “augment”
existing classes with new behavior without modifying the original code)
is incredibly powerful for building large applications [especially
dealing with layering] and is only possible if the original compiler did
not statically freeze the types.

Amount of static typing

patrick@c837917-a.potlnd1.or.home.com wrote:
> : David Jenkins wrote:
> : > One of my problems with Java is that it pretends to be statically
> : > typed, but is not--you can always cast your way out of a type.
> : > Eiffel, I've found, is a very stern taskmaster when it comes to
> : > typing, but I've learned to appreciate the lessons it teaches.
>
> Java is no less statically typed than Eiffel, i.e. you cannot cast a
> Java object to anything that it was not defined to be (as could be
> done with C++, at least in years past).

I think “more” or “less” statically typed was referring to “amount of
code that is verifiably type-correct at compile-time” not whether the
type system is safe. I don’t think unsafe type systems are in any way
interesting to those reading these threads. I agree with David Jenkins
that Java code will be far less compile-time verified than Eiffel
because of weaknesses in the Java type system (especially from the lack
of covariant return types and some form of parametric types). A Java
typecheck cast (a “type attempt”) is just as dynamic as standard
Smalltalk message sends; the Java version just has a larger granularity
and a slightly different timing (before the message as opposed to at
message time). The amount of “type attempts” within Java code is
enormously larger than the amount of “assignment attempts” within Eiffel
code. So the Java code is really “much less statically typed” although
we are want for nice short phrases that a precise and accurate: “much
less compile-time type-verified”.

Interestingly, the Eiffel code will be additionally and more precisely
“dynamically object-verified” through the preconditions, postconditions,
and invariants. So a stronger verification applied to the Objects
themselves is coming from dynamic checks (just like Smalltalk) as
opposed to compile-time checks. Smalltalk with Eiffel-like DBC
capabilities would definitely muddle the linearity of type safety[1]:
Smalltalk would be more precisely verified at runtime than a Java
program but less precisely verified at compile time. This would be
without the problems of compile-time typing (problems with granularity,
evolution, genericity, etc.). Which is safer?… no I don’t want to go
down that topic’s path…

Actually, this Smalltalk augmentation is pretty easy [I previously did a
prototype and I also recall the concept being published in Smalltalk
Report] since we could: (1) allow DBC anotations in methods or
categorize special unit tests into pre, post, and invariant conditions,
(2) augment the compiler to generate the calls on method entry and exit,
(3) provide the same behavior as Eiffel [no check on intra-object
calls], and (4) throw reasonable exceptions depending on who is
responsible. The main issues are the exact annotation form (although
UML’s OCL pretty much solves that problem) and
standardization/portability across Smalltalk platforms.

--Mark
mark.fussell@chimu.com

[1] Type safety: Amount the program is verified to behave correctly

Other topic continuations

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: