Purest Idiocy
It appears that once again, various people are discovering that writing webapps in Ruby with Rails requires fewer keystrokes than writing similar things in Java with Struts and Springs. While this is hardly a new realization (and is, one could mention, the entire justification for the existance of Rails), they then go on to conclude that the real culprit here is static typing.
I address this before when a different set of pundits came to the same conclusion (you know, roughly two years ago now), but this iteration's dynamic-typing proponents have come up with a couple of bold new ideas:
If you have pervasive testing, static typing == more typing. The static typing is nothing but a requirement to type extraneous code to satisfy a compiler that isn't telling you anything interesting anymore.
So, let's play the Hello, World game:
Ruby (dynamically typed): puts "Hello, World"
Haskell (statically typed): main = putStrLn "Hello, World"
Wow, look at all that extra typing.
The part that's particularly confusing to me, though, is the part where he concludes that if you already have pervasive testing, you don't need static type checking. The goal of static typing is to describe (and thus constrain) the behavior of your code, and thus reduce the number of cases in which failure can occur (and thus the number of things that need to be, incompletely I might add, checked with test cases).
It's not a simple trade-off, though. Consider a dynamically typed function:
(define (square x) (* x x))
compared to a statically typed:
square x = x * x
These both operate over the same domains, but in the case of the former, there's no static guarantee that the incoming values are within that domain. (square "this") or (square :that) compile just a nicely as (square 2). This means that to have complete testing coverage, you need to verify the behavior of square for all possible values in the language, not a constrained set.
Of course, things get more interesting once you get outside the world of printing hello world and squaring numbers. Recently, I've had some fun discovering which parts of the .Net libraries are threadsafe. While reading the documentation would have helped my quest, and testing did discover the non-threadsafe functions I'd missed (granted, four hours into batch runs), in Haskell I'd have been able to tell from the types whether or not the operations were threadsafe. That's a stronger assertion than I've seen any testing library be able to make.

3 diversions:
It seems quite clear to me that the "strong typing requires more touch-typing" stereotype [sic] is deeply ingrained from decades of programmers who learned their ABC's with explicit type annotations. These days the popular targets seem to be Java and C++. Before that, Pascal was big with the instructional crowd. Before that, I suppose it was probably PL/I. The languages people use without explicit type annotations tend to be high-level dynamic scripting languages, which gives yet another bias toward the idea that explicit type annotations are necessary for static type checking. The idea of static type inference has clearly not yet settled in the minds of your average programmer.
Of course, it is difficult to combat this stereotype for a number of reasons: One is that languages like Haskell and ML that do use static type inference are not widely used in undergraduate computer science curricula; you get maybe one course in functional programming, everybody writes it off as "that weird stuff where you have to think before you can write code," and they never think about it again. Rather than a curriculum oriented toward imperative languages with declarative techniques as a one-time alternative, I think we should treat declarative programming as the norm, and at most introduce imperative programming as a special-purpose technique for low-level systems programming---maybe in an operating systems course. This would initially produce some impedance matching difficulties with the existing workforce, but I suspect that could be ironed out in time.
While I think we can all agree there are no silver bullets, we would all be well served by upgrading from hand-shaped musket-balls manually fired out of an unrifled barrel with poorly-ground black powder, which seems to be the status quo at the moment.
Rah, rah!
Actually, some of the lovely terseness of Rails does depend on dynamic typing.
For a good example, compare ActiveRecord to HaskellDB.
class Employee < ActiveRecord::Base
has_many :projects
end
In this example, ActiveRecord creates a wrapper for the "employees" table in a hypothetical database, adding dozens of useful methods for querying, accessing fields, and so on, with only a single declaration needed to specify a foreign-key relationship.
This terseness is achievable because ActiveRecord connects to the database, extracts the "employees" table definition, and adds methods to "Employee" at runtime. Evil, but oh so pretty.
HaskellDB, on the other hand, forces us to declare all our tables and columns by hand.
The Ruby/Rails approach is interesting, because it gets us about 80% of the power of LISP macros, but in a form that (empirically) fewer library writers will botch in horrible ways.
Now, don't get me wrong. When I'm three layers deep inside a stack of monad transformers, I appreciate static typing as much as the next programmer.
But if (1) I'm doing test-driven design in the first place and (2) I'm sticking to object-oriented code, type errors are a mostly hypothetical problem. And in this case, there are some delicious tricks that can be performed with runtime class mutation, etc. So there's no reason not to jump the fence and enjoy the spirit of SmallTalk for a while.
Honestly, my favorite statically-typed language is Haskell. This is because Haskell focuses on Enabling Really Interesting Things, and not on Keeping Bozos from Messing Up. I'm not very sympathetic to the latter goal; it should have been solved by the HR department.
Post a Comment