Why I Don't Like Smalltalk
For software engineering purposes, I think statically typed programming languages are better suited than dynamically typed programming languages. I use Smalltalk as the prime example for my rant, but most arguments apply just as well to programming languages such as Python. Note that this text is about software engineering; I am indeed well convinced of the merits of dynamic typing for small scripting purposes. My reasons for claiming this:
Understanding Code
When a programmer is presented a piece of source code he did not write himself, it is not a trivial task to fully understand the code. In a dynamically typed system, variables are not typed in the source code, so the information that would be given there if the system were statically typed, is lost to the programmer. To identify the type of a variable, the programmer has to browse through often hundreds of lines of code, to understand where the variable has been given a value.
Dynamic-typing proponents claim this problem can be solved by documenting your code when writing it. This may be true in theory, but in practice this documentation is often incomplete, and my claim is that a formalised documentation (such as type signature) is always better than an informal documentation (a text in english) because it is guaranteed to be correct.
Also, dynamic-typing proponents say that static typing suffers from the same problem, because the source code will only specify the static source-level type, and not the dynamic type that is given at run time (see polymorphism). This is partly true, but knowing more specific information about a variable (i.e. seeing the object is an instance of a subclass of a known type) is always better than no information (i.e. knowing only that the variable refers to an object).
Design by Contract
I personally believe in the merits of design by contract. As previously stated, formal documentation (and thus also formal contracts) are always preferrable over informal documentation (informal contracts, i.e. English text). If your contract changes, the formal specification of your function (through its type signature) might change, and the changes will be reflected throughout the program and typechecked everywhere where used by the compiler. I see something like a statically typed formal parameter as a way of formalising a precondition in a design contract.
Efficiency
I will not give much attention to this point, because nowadays there are so many specific optimization techniques, that the issue of efficiency is debatable. However, statically typed languages can usually be compiled to more efficient code than their dynamic counterparts.
Static Typechecking
Static typechecking validates the usage of typing information in your program. Everyone will agree that this is an advantage.
Automatic Refactoring
Static typing information aids automatic refactorings. Try this: open your Smalltalk VisualWorks, create a class, and give it a method at:put:. Use this class a few dozen times in your program, and then you decide that in fact the method should have been differently. You ask VisualWorks to rename the method, and it presents you with this question: "Do you want to update all 3487372 occurences of the message?". Not realising your fault, you click yes, and then you notice that VisualWorks has renamed every' occurance of the at:put: in your image, even the ones that have nothing to do with the class you created. I'm sure I need not tell you what problems could occur when combining this with name collisions. Automatic refactorings require static typing. Software engineering is all about refactoring, so software engineering requires static typing.
Error Handling and Abstraction
In modern programming languages for software engineering, programmers don't want their applications to crash when an error is encountered. They don't want a C++ program to crash producing the text "segmentation fault", but they want a NullPointerException and precise information on where the error occurred. Likewise, they want to get a TypeMismatchException (at compile-time, or at run-time) when they pass an object of an incompatible type. Instead, Smalltalk gives them an obscure doesNotUnderstand exception, thrown from several levels deeper in the method implementation by some object often not related to the one they're in charge of (see double dispatch). This requires the programmer to examine the inner workings of the method. When the internal implementation is exposed unnecessarily to the programmer, abstraction rules are broken. Producing a nonsensical error in Smalltalk is just the same as producing a segmentation fault in C++. So what if, like me, you want your Smalltalk source code to be typesafe? See the next section.
Longer Code
This is my most controversial argument against dynamic typing in software engineering. I believe that dynamic typing requires longer code to produce code equivalent to statically typed code. As previously mentioned, I (try to) write code using the design by contract methodology. Example:
int square(int x) { return x * x; }
This concise piece of C/C++/Java/Whatever code specifies two types in its (unmentioned) contract: x has to be an integer, and the return value is an integer. The compiler's type-checker will give me an error if the contract is violated. The Smalltalk equivalent:
square: x (x isKindOf: Number) ifTrue: [^x * x] ifFalse: [self error: 'square: expects an integer argument']
This code is much, much longer and everyone will agree that it is less readable. If the method becomes bigger, this code will become longer and longer, requiring 2 extra lines per parameter. Of couse Smalltalk programmers don't write code like this, so I must conclude their code uses less abstraction, encapsulation, and is unsuited for software engineering purposes because they reject the concept of design by contract. Advocates of dynamic typing often argue that they replace typechecks with more extensive unit-testing. If this is true, they are forced to write even more code.
Static Typing and Polymorphism
Moreover, statically typed languages that use a single-rooted object hierarchy (such as Java) are capable of the same level of polymorphism, only they encourage you to think about type completeness because they require type checking. The only features that Smalltalk allows over, say, Java, are its far-stretching reflection and metaprogramming mechanisms. Even if these are usefull in software engineering (which I doubt, I think their purpose is mainly academic), their merit cannot possibly outweigh the arguments given above.
Critique
I welcome critique to this page, and will try to defend my arguments when challenged. This list is far from complete, so I will add more over time.