Well, reliable programs aren't written by the compiler;
programs become more reliable if the programmers working on
them are able to understand them and adapt them to changing
requirements without having to introduce gruesome ad-hoc hacks
or having to redesign the entire thing (which usually is
prohibitively expensive, and therefore not done.)
My experience is that flexibility which isn't designed in
doesn't work anyway. You can't derive from a class if it counts
on all of its functions working in a specific way. You need
some sort of a contract. Static typing is just a part of the
contract which is enforced by the compiler. It's certainly not
sufficient, and doesn't eliminate the need for readable
documentation, good code review and extensive testing, but it
does help reduce costs when slip-ups are caught immediately by
the compiler, rather than later in the development cycle.
From my experience, programs written in a "dynamically" typed
language are simply more malleable and also more concise and
hence easier to understand.
My experience is that regardless of the language, some things
end up poorly documented, and some things slip through code
review. The only real difference I see in well written code is
that static type checking forces you to make the changes that
you should make anyway, where as with dynamic type checking,
they're only comments, and tend to be forgotten.
Add to that interactive development (like in Lisp, or other
languages that provide an interactive toplevel), and the whole
development process shows a much faster turnaround because one
doesn't have to wait for the compiler and a new test run all
the time. At least that's my experience. Faster turnaround
means more testing of new ideas, and more testing of code and
debugging; in the end resulting in better code.
That's an excellent choice for prototypes, or applications where
you need ultra-rapid turn around regardless of reliability.
It's not a good choice when the application has to work.
The code also won't work for any kind of logics errors, which
no compiler could guard against.
Certainly. As I said above, you still need extensive code
review. But since programmers, including the code reviewers,
the authors of the test suite, and everyone else is human, it's
nice to know that at least a minimum can be guaranteed.
There's simply no substitute for testing.
I'd say the good code review is even more important. Some
things simply cannot be tested, period.
The situation is simple:
-- If the compiler indicates an error, you know right away
where it is, and can correct it immediately.
-- Ditto with code review, but you do have to wait until the
code review takes place; you can't do it on your own.
-- If there's an error in the tests, you know that something's
wrong, but you still have to figure out where. Well
designed tests can make this easier, but still not to the
degree of having a concrete indication as to the exact line.
If we could design a language in which the compiler would catch
all of the errors, that would be ideal. That's not possible, of
course, but the more it catches, the less development costs.
After testing, runtime type errors seem to be rather rare and
unproblematic compared to more serious program flaws which are
sometimes hard to detect, and even harder if the program logic
is further obfuscated by having to accomodate a complicated
type hierarchy.
Used correctly, the type hierarchy makes the program logic
considerably clearer. In dynamically typed languages, you have
to explicitly document all of what the type system manages
anyway.
Not every application can be properly specified before
programming starts, in some cases, what the application
actually ought to do in detail might be unclear in the
beginning and only become manifest during an exploratory
development process, and in addition, many applications change
quite a lot over their lifetime.
Sure. But that doesn't mean that you can write working code
without some sort of a design. Random changes don't work.
A complete up-front design is often not possible, or even
desirable. I think a lot more software suffers from a too
rigid specification and implementation process than from a too
loose one.
I don't know. You can create a lot of useless rules, but on the
whole, the companies I've seen which have produced the most
reliable software, and been the most flexible with regards to
modifications in it, have also been the ones with a fairly
strict development process, which required programmers to
actually do some design before writing code, and have that
design reviewed, to ensure that their changes didn't break
something elsewhere.
Unfortunately, exploratory development is a recipe for
disaster with inflexible languages like C++ or Java because
you'll end up with a horrible type mess.
I've never encountered this. In the end, objects do have a
type, regardless. And if you ignore the type, you get into
trouble very quickly.
Of course, I don't do much exploratory development---the
programs I write generally have to work, and have to be
developed within a fixed budget. I'm not a research scientist,
I'm an engineer.
IMHO, using a dynamically typed language can make a difference
here. The idea is not to restrict the programmer at all in
what he/she can do, while giving him/her as much expressive
power as possible. C++ is quite powerful (albeit in a
sometimes very awkward and verbose way) but also restrictive,
while Java only restricts and gives very little in the way of
expressive strength. Languages like ML have a more powerful
type system that is somewhat more expressive than the
primitive ones of C++ and Java but the main problem remains:
Once the program stands, no matter how beautifully designed it
is, it is hard to make substantial changes to it because of
the rigidity of the type structure.
Well, I more or less agree with your characterizations of C++
and Java. But I would content that the type structure is there,
regardless. The difference is that C++ and Java make you be
explicit about it, and declare it up front; languages like
Smalltalk leave it up to the documentation and programmer
discipline. In either case, poor design with regards to type
will cause problems in the long run, and violating the type
system will not work.