Wednesday, July 07, 2010

foolish usage of static and dynamic typing

Whenever I read the eternal sniping between the cheerleaders of static and dynamic typing, I'm often struck by the similarity of the arguments for each side (including identical words like "maintainable"; let's not even get started on the multifarious definitions of "scalability"). Today's example is "I admit that your complaints about [static, dynamic] typing are valid...but only if someone is doing it incorrectly. Don't do that." Assuming that sometimes the criticisms of a typing strategy are really targeted at specimens of its foolish usage, I've therefore paired common criticisms with preventive usage guidelines. The point is to neither tie yourself to the mast when you're static-typing nor wander overboard when you're dynamic-typing.

  • "I detest subjecting my fingers to the burden of type declarations." Use an IDE with type-completion and/or a language with type inference. 
  • "The types aren't helping to document the code." Stop reflexively using the "root object" type or "magic-value" strings as parameter and return types. Y'know, these days the space/speed/development cost of a short semantically-named class is not that much, and the benefit in flexibility and code centralization might pay off big later on - when all code that uses an object is doing the same "boilerplate" tasks on it, do you suppose that just maybe those tasks belong in methods? If a class isn't appropriate, consider an enumeration (or even a struct in C#).
  • "Branches on run-time types (and the corresponding casts in each branch) are bloating my code." Usually, subtypes should be directly substitutable without changing code, so the type hierarchy probably isn't right. Other possibilities include switching to an interface type or breaking the type-dependent code into separate methods of identical name and distinct signatures so the runtime can handle the type-dispatch.
  • "I can't reuse code when it's tightly coupled to different types than mine." Write code to a minimal interface type, perhaps a type that's in the standard library already. Use factories, factory patterns, and dependency injection to obtain fresh instances of objects.
  • "I need to change an interface." Consider an interface subtype or phasing in a separate interface altogether. Interfaces should be small and largely unchanging.
  • "When the types in the code all match, people don't realize that the code can still be wrong." Write and run automated unit tests to verify behavior. Mocks and proxies are easier than ever.
  • "It's impossible to figure out a function's expected parameters, return values, and error signals without scanning the source." In comments and documentation, be clear about what the function needs and how it responds to the range of possible data. In lieu of this, furnish abundant examples for code monkeys to ape.
  • "Sometimes I don't reread stuff after I type it. What if a typo leads to the allocation of a new variable in place of a reassignment?" This error is so well-known and typical that the language runtime likely can warn of or optionally forbid assignments to undeclared/uninitialized variables.
  • "Without types, no one can automatically verify that all the pieces of code fit together right."  Write and run automated unit tests that match the specific scenarios of object interactions.
  • "Beyond a particular threshold scripts suck, so why would I wish to implement a large-scale project in that manner?" A language might allow the absence of namespaces/modules and OO, but developers who long to remain sane will nevertheless divide their code into comprehensible sections. Long-lived proverbs like "don't repeat yourself" and "beware global state" belong as much on the cutting-edge as in the "enterprise".
  • "My head spins in response to the capability for multiple inheritance and for classes to modify one another and to redirect messages, etc." Too much of this can indeed produce a bewildering tangle of dependencies and overrides-of-overrides, which is why the prudent path is often boring and normal rather than intricate and exceptional. Mixin classes should be well-defined and not overreach.
  • "In order to read the code I must mentally track the current implied types of variables' values and the available methods on objects." Descriptive names and comments greatly reduce the odds that the code reader would misinterpret the intended variable contents. Meanwhile, in normal circumstances a developer should avoid changes to an object's significant behavior after the time of creation/class definition, thus ensuring that the relevant class definitions (and dynamic initialization code) are sufficient clues for deducing the object's abilities.

No comments:

Post a Comment