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

Dylan type-safety



A discussion has come up in some other newsgroups (comp.lang.object a few
weeks ago, comp.lang.eiffel yesterday) about whether Dylan has strong static
type-checking.  My impression, as a relative newbie, is that it doesn't:
even if you scrupulously declare the types of all your variables and
methods, you can easily write code that causes run-time type errors with no
warning from the compiler, and with no indication in the code that you're
doing anything dangerous.

Now, most strongly-typed languages do give you some way to explicitly
circumvent type-checking (the "dynamic" type in Cecil, a downcast in Java).
These constructs can cause run-time errors if used carelessly -- but the
programmer has to make an explicit choice to use them.  Any code that avoids
these constructs will never cause a run-time type error.  It seems to me
that Dylan is much less explicit about identifying code that could cause
run-time type errors.

Of course, many people are perfectly happy with a dynamically-typed language
such as Smalltalk, and if Dylan sees itself as another language in that
category, I have no objection.  But Dylan programmers do seem to identify it
as a strongly-typed language -- or rather, as an optionally strongly-typed
language, in which you can prevent run-time type errors by following a
certain coding discipline.  I'm not convinced that is the case.

Here's a sample of some code that causes the kind of problem I'm talking
about:

  define abstract class <bird> (<object>) end;
  define class <sparrow> (<bird>) end;
  define class <penguin> (<bird>) end;

  define generic fly (b :: <bird>) => ();

  define method fly (s :: <sparrow>) => ()
    format-out("Sparrow flying!");
  end method fly;

  // don't define fly (p :: <penguin>) -- penguins can't fly!

  define method get-bird() => (bird :: <bird>)
    make(<penguin>);
  end method get-bird;

  define function do-it()
    let bird = get-bird();  // compiler doesn't know it's a penguin
    fly(bird);              // causes run-time dispatch failure
  end function do-it;

When I compile this sample with HD, in "production" mode, I don't get any
warnings.  But it causes a run-time dispatch failure.  Basically, the
<penguin> class fails to live up to its contract.  The failure should be
obvious to the compiler (or linker) in this case.  In other cases, it might
be harder to tell -- with multiple dispatch, the question of contractual
responsibility becomes much thornier.  But it seems that the HD compiler
doesn't make any attempt to catch this kind of error, and as far as I can
tell, the language definition doesn't require it to do so.

The Cecil language addresses these issues by defining a sophisticated type
system on top of the normal OO class system.  The purpose of Cecil's types
is precisely to define these sorts of contracts and let the compiler ensure
that they are fulfilled properly.  Dylan's types seem superficially similar
to Cecil's, but based on my experience I'd have to say that they don't
provide the same kind of safety that Cecil's do.

Another way to describe the problem is that Dylan's types define
implementations but not interfaces.  In Cecil, classes (actually objects)
define implementations, and types define interfaces.  In more traditional OO
languages like Eiffel (ignoring all the CAT problems for the sake of this
discussion), classes define interfaces and may define implementations as
well.  Real type-safety comes from having clearly-defined interfaces, which
Dylan doesn't support.

Can somebody prove me wrong, or shed more light on this issue?





Follow-Ups: