PHP-Related Letter to the Editor - Overload 66
I have recently found time to learn PHP, which is something I had been meaning to do for a while, and my response to the dynamic typing is the complete opposite of yours; I found it quite refreshing.
Provided that you have sufficient tests, such as those you get from doing TDD, the dynamic typing can be liberating rather than a cause for concern. After all, it's not as if it's something new - Smalltalk is dynamically typed, for example. In fact, I often find myself using templates in C++ precisely because the implicit interfaces give me some of the freedom of dynamic typing.
I found that the lack of explicit types made refactoring easier, as I did not have to change variable declarations or function signatures, and I didn't need to explicitly declare interfaces.
You only have to look over on comp.object to find a horde of people who swear by dynamic typing, including respectable authors such as Ron Jeffries and Robert (Uncle Bob) Martin.
I'm not yet willing to say I prefer dynamic typing to static typing, but I can certainly see the benefits. I had a discussion with someone recently about naming conventions, arguing that including information about the type in variable names (such as with Hungarian Notation) was a waste of bandwidth, as the name and usage of the variable should provide the necessary information. Dynamic typing goes a step further, and says that the actual type is really quite irrelevant, it's what you do with it that counts. Of course, it does mean that good naming and clean design are all the more important.
That's not to say that PHP is perfect, though - you can't overload functions, for starters, though I guess that's a consequence of them not having a fixed signature. The limiting factor here is that it can make it hard to extend existing code; I can't provide alternate implementations of functions that work on new classes.
Following this line of thought to its natural conclusion drives me to the "everything is an object" stance of Smalltalk - if everything is an object, then you can extend behaviour just by writing a new class which derives from, or even just wraps, an existing class (after all, we don't need inheritance for the interface), and passing instances of the new class instead of instances of the old one.
I guess that this actually demonstrates that C++ is evolving in a direction that works - if you're going to have non-member functions, then for maximum extensibility, you need static typing and overloading, including operator overloading. You also need generics (templates), and the ability to deduce types from expressions (the auto and typeof proposals). It's just a different way of providing freedom of expression, and the ability to write clean abstractions.
PHP therefore lives in a sort of never-never-land, where you can write code with everything being an object, but you can write free functions too, and the language ships with rather a lot of them. It is neither one thing, nor the other, which makes for plenty of opportunities for poor coding. That just makes it all the more important to strive for clean design, using refactoring and plenty of tests to help along the way. I welcome the addition of exceptions to PHP 5; having to work with error codes in PHP 4 reminded me how hard that can be. PHP 5 also adds destructors, which fire when the last reference to an object is destroyed; much better than Java's finalization mechanism.
That's my thoughts for now,
Anthony Williams < firstname.lastname@example.org >
And Terje's Reply:
Thanks for your thoughtful reply. Yes, I know that quite a few proponents of agile development like dynamic typing. Before going further, perhaps establishing some terminology might be in order. Often, dynamic and implicit typing is lumped together in discussions, while they are really orthogonal. One classification I've read and found useful is: implicit vs explicit typing (whether or not the type is declared), and static vs dynamic typing (whether or not types of values are determined and checked at compile time or run time). With this, we can classify some languages:
C++: Explicit and statically typed
Haskell: Implicit (with optional explicit) and statically typed
PHP: Implicit (with optional explicit for user-defined types in signatures) and dynamically typed
In languages like Haskell, if you don't specify the types, they are inferred from the expressions at compile-time. Something similar happens with C++ templates, as you mention. However, Haskell has type classes, giving a form of constrained genericity (as in Java 5.0 and C# generics, and the concept proposals for C++ [ proposals ]), whereas parameters to C++ templates, as they are today, are pretty unconstrained. This tends to lead to hard-to-understand error messages, when the program crashes deep inside a function (rather than giving an error at the call site about parameter mismatch). The same kind of problem, only worse, may happen in dynamically typed languages. Worse, because:
Being dynamically type-checked, you don't get any errors before that particular piece of code is executed with those particular parameters, and
Every variable and parameter is basically a variant - being able to take on a value of any type, including null
I find it interesting that in C++, unlike the current state of scripting languages (although there's some movement in that direction for them as well), there's a tendency towards getting a language where you can be more explicit about your intent . Proposals such as "Explicit Class" (N1702 - being able to decide which, if any, default member functions are provided), "Explicit Namespace" (N1691 - being able to turn ADL off, and selectively enable it), "Explicit Conversion Operators" (N1592 - having to explicitly request a conversion), as well as the mentioned concept proposals (N1758, N1782, et. al. - enabling you to state the concepts that the template parameters have to model), "Design by Contract" (N1773), etc. all facilitate expressing our intent more clearly in the code (and being able to be checked by the compiler/runtime).
By expressing our intent explicitly in the code (using type declarations, concepts, contracts, or whatever), the compiler is better able to sanity-check the code, and in the case of static type checking, even before the program is run. Moreover, it provides documentation for the users and maintainers of the code, as you can see what is expected, and what is guaranteed. In contrast, languages like PHP hardly lets you specify anything up front, and thus it defers any error detection to runtime (if even then). A couple of common arguments for dynamic typing are that a) static typing doesn't necessarily find all errors, and b) with unit tests, you can perform similar checks, and more. I feel that a) is a kind of straw man (as it can be said about most good practices), and it doesn't mean that static type checking isn't useful. As for b), yes, you can do it with tests (or using asserts in the function definition, checking the types manually), but why would you? By using static type checking (whether or not the types are declared) you're assured that the program makes at least some kind of sense. Why anyone would want to do manually what the compiler can do automatically is beyond me. Yes, unit tests are useful, and they complement type checking (and DbC, etc.) nicely, rather than displacing it.
Static checking (any kind of checking) enables you to detect any bugs early, and that's a good thing, as it gives rapid feedback. As mentioned, with a dynamically typed language, you risk getting type-related errors in code that is only executed rarely, perhaps a long time after it's written, or worse, you get silent errors, unless you're sure you've covered all the possible cases with tests, including values coming from outside.
If you want to be able to avoid specifying types, unless you explicitly want to, then, as you mentioned, type inference with templates (as well as the auto/decltype proposal (N1607)) enables this, and good generic code typically contains few explicit type declarations. This way, you get the "agility" of dynamic typing, with the type safety of statically checked types.
Terje Slettebø < email@example.com >