(This is a snapshot of my old weblog. New posts and selected republished essays can be found at raganwald.com.)

Friday, August 18, 2006
  Dynamic is the opposite of Static, not of Explicit

I recently read a post where the author said he does not care for Ruby's dynamic typing.

When people start talking about types in programming languages, the terms fly around in a rather fast and loose manner. Here's a rather extensive and balanced discussion: the article on Type Systems in Wikipedia.

Notice that Ruby is dynamically typed (it's types are determined at run time and can change at run time), strongly typed (it does not allow an operation to succeed on arguments which have the wrong type), and type-safe (it does not allow operations or conversions which lead to erronous conditions).

Stuck on Stupid

(There's another school of thought about how to name the properties of type systems. More on that at the bottom.)

Proponents of explicit types may say that Ruby is not type safe because it is possible to compile programs that contain errors which will not be detected until the program is run and the erroneous condition is detected.

Statically typed languages such as C# can detect a class of such errors at compile time. Some Ruby enthusiasts argue that they do not like the boilerplate associated with explicit typing and feel that the extra error checking does not outweigh the additional overhead.

Both arguments are mistaken. Sorry about that. If Explicit and Implicit were the issues, we could add Type Inference to Ruby and it would have the low overhead of implicit types as well as some extra error-checking (work on Soft Typing for languages like Scheme and Erlang aims to provide compile-time checking for implicitly typed languages). However, type inference is a feature of statically typed languages.

The trouble is that Ruby is dynamically typed. Specifically, Ruby contains extensive dynamic meta-programming constructs. Type inference works with statically typed languages. The compiler must be able to infer the type or set of types possible for any variable.

Consider a Ruby application that modifies classes and objects at run time. The simplest example is Ruby's built-in accessor methods: attr_accessor :foo is a class method that actually creates two instance methods at run time, foo and foo=. What happens when attr_accessor is called with a variable as its argument, like attr_accessor my_attribute?

If the type inference engine later looks at a call such as bar.blitz = 'bash', how does it know whether attribute_accessor was ever called with 'blitz' or :blitz as an argument? Dynamic meta-programming makes the type inference problem undecidable.

A lucid argument is that Ruby's dynamic typing makes it difficult to detect type errors statically. The correct counter-argument is that Ruby's style of dynamic typing makes it possible to use dynamic meta-programming, such as Ruby on Rails' ActiveRecord, not that Ruby's implicit typing is more productive.

Once you've made these arguments, you can decide for yourself whether the benefits of dynamic meta-programming do or do not outweigh the advantages of static type checking.

(Updated to use the expression "dynamic meta-programming" to distinguish features like define_method from static meta-programming features like macro systems, courtesy of the well-informed Lambda the Ultimate).


p.s. Ward's Wiki seems to use the term static typing to mean the same thing as explicit and strong typing. I prefer that the term static means that it doesn't change. The same wiki seems to use the term dynamic typing to mean the same thing as implicit and strong typing. It includes the possibility that a program is what I would call strongly, statically and implicitly typed: the checking is done at run time but the types of variables never change. The commentary suggests the phrase semantic dynamic typing for the quality of dynamic typing that encompasses dynamic meta-programming. If you like these terms, please substitute "semantic dynamic typing" for "dynamic typing" above.

p.p.s. And there's another argument that dynamic meta-programming, like macros, should be considered harmful. I'm saving my reply for a rainy day.

Labels: ,


Comments on “Dynamic is the opposite of Static, not of Explicit:
Any language can have a type inferencer applied to it. In some cases what you'd like to have happen is the infrencer determines a set of possible types, and the assigned type becomes a "variant record" where the actual type in any instance of one of the variants.

This doesn't help all type checking at compile time, but it can at least point out the "dangerous" cases without giving up entirely. This can also be used for code generation... the code generator can choose whether to produce the most general code, or to produce multiple paths of code for two or more of the variants, e.g. "if this instance is an integer then follow the code path for integers, otherwise follow the code path for the general case."
the infrencer determines a set of possible types, and the assigned type becomes a "variant record" where the actual type in any instance of one of the variants.

Yes, this is how Hindley-Milner deals with polymorphism. That being said, I do not see the obvious application of this technique to Ruby's define_method method.

I am opne minedd, however. I would love a language that lets me have my cake and eat it too!
Finally, a sensical argument. While the vernacular of terms describing programming languages is probably doomed to ambiguity, there still is serious discussion to be had.

This hits home to a point I've been trying to make: You can't have em all. For some situations, static, explicit code is the nicest most maintainable solution. In other situations, the associated boilerplate is needlessly verbose and actually gets in the way of a clean solution.

For an encore: Is there a middle, all-encompassing ground? A hypothetical programming language that is as explicit as you want it to be with no unfortunate limiting side-effects?

I don't think that holy grail exists. Simply allowing explicit types, or not, as a choice, results in such a mix and match language that someone well-versed in code probably won't understand code in the same language written by someone with a different style. That's no good.
Here's something to blur the lines -- MetaOCaml (http://www.metaocaml.org/) and other multi-stage thingies.
I'm puzzled by trust in type inference that many people who usually code in systems without it have.

Type inference works only with type systems too simple for the real world. Even the most advanced type inference systems like OCaml's (which is already provably *undecidable*, you can hang type inference engine with a 3-line expression) require a lot of type annotations and typecasts in real programs.

No matter how smart your type inference engine is, you couldn't type-infer even Java code (even without reflection). Mentioning type inference and Ruby in a single sentence is really weird.

<< Home
Reg Braithwaite

Recent Writing
Homoiconic Technical Writing / raganwald.posterous.com

What I‘ve Learned From Failure / Kestrels, Quirky Birds, and Hopeless Egocentricity

rewrite_rails / andand / unfold.rb / string_to_proc.rb / dsl_and_let.rb / comprehension.rb / lazy_lists.rb

IS-STRICTLY-EQUIVALENT-TO-A / Spaghetti-Western Coding / Golf is a good program spoiled / Programming conventions as signals / Not all functions should be object methods

The Not So Big Software Design / Writing programs for people to read / Why Why Functional Programming Matters Matters / But Y would I want to do a thing like this?

The single most important thing you must do to improve your programming career / The Naïve Approach to Hiring People / No Disrespect / Take control of your interview / Three tips for getting a job through a recruiter / My favourite interview question

Exception Handling in Software Development / What if powerful languages and idioms only work for small teams? / Bricks / Which theory fits the evidence? / Still failing, still learning / What I’ve learned from failure

The unary ampersand in Ruby / (1..100).inject(&:+) / The challenge of teaching yourself a programming language / The significance of the meta-circular interpreter / Block-Structured Javascript / Haskell, Ruby and Infinity / Closures and Higher-Order Functions

Why Apple is more expensive than Amazon / Why we are the biggest obstacles to our own growth / Is software the documentation of business process mistakes? / We have lost control of the apparatus / What I’ve Learned From Sales I, II, III

The Narcissism of Small Code Differences / Billy Martin’s Technique for Managing his Manager / Three stories about The Tao / Programming Language Stories / Why You Need a Degree to Work For BigCo

06/04 / 07/04 / 08/04 / 09/04 / 10/04 / 11/04 / 12/04 / 01/05 / 02/05 / 03/05 / 04/05 / 06/05 / 07/05 / 08/05 / 09/05 / 10/05 / 11/05 / 01/06 / 02/06 / 03/06 / 04/06 / 05/06 / 06/06 / 07/06 / 08/06 / 09/06 / 10/06 / 11/06 / 12/06 / 01/07 / 02/07 / 03/07 / 04/07 / 05/07 / 06/07 / 07/07 / 08/07 / 09/07 / 10/07 / 11/07 / 12/07 / 01/08 / 02/08 / 03/08 / 04/08 / 05/08 / 06/08 / 07/08 /