Skip to content

Static Typing may not Scale

September 24, 2013

I program in both statically and dynamically typed languages (currently mostly D and JavaScript) and I tend to think that I appreciate the advantages of both. One of the main arguments made by the Haskell crowd in favor of static typing seems to be that developing large applications is hard, and really, as a programmer, you really should use all the help you can get, and that help should come in the form of an expressive static type system. I’d be inclined to agree. Developing large applications is in fact difficult, and static typing can help you discover many bugs that would otherwise remain hidden in your code for a long time. Static types give the compiler more semantic understanding of your code, which makes it better able to optimize the said code and detect bugs, both great advantages.

I recently got to thinking about the future of programming. I believe that as systems grow ever more complex and networked, we’re headed for a future where programming won’t be about editing code in text files, compiling them, and restarting programs (wash-rinse-repeat), but rather, incrementally building live systems, and performing microsurgery on running programs, without interrupting them. As we work on ever bigger systems, we’ll need all the debugging help we can get, and type systems sure would come in handy, but there’s just one issue with that: I think that static type systems, and type inference as implemented in languages like Haskell won’t work so well with such a model. I think the truth is that dynamic languages are much better suited to the incremental, organic model of software development we’re getting pushed into. Dynamic languages are here to stay. They’ve been gaining in popularity, and this trend isn’t going to reverse, it’s only going to grow stronger.

Imagine you’re working on a live system. You’re modifying the code for a program that’s currently running on some remote server. Now imagine this system is statically typed. You want to make a change to a method somewhere, but unfortunately, this small change would cause a type error. You need to modify several other functions elsewhere in the system, as well as schema/record declarations. The statically typed approach would probably involve making all of your code changes, changing the schema declarations, running some kind of type inference algorithm, verifying that everything checked, and then running some sort of “commit” that recompiles the needed code on the remote machine and translates your records to the new format, probably interrupting the program while this is happening (and who knows how long this might take, could be hours). The big issue here is that with current static type systems, you can only go from one statically valid program to another statically valid program, which seems poorly suited to the incremental modification of a running system.

The dynamically typed approach approach might be to write code to handle the new schema before it’s even introduced, then run some code to translate from the old schema format to the new (all on-the-fly), and watch the system adapt, translating your records without a pause. Once all records are translated, you can then remove the code that deals with the old schema. I think the great advantage here is that a dynamic language allows you more wiggle room to make things temporarily inconsistent and refactor the system in an incremental manner. With dynamic languages, you can have two record formats for the same data at the same time, and code that has typing errors won’t cause the system to fail so long as it’s not executed. It’s a more organic approach to the problem, and I think that programming live systems might actually be inherently organic in nature.

Now, I stated early in this post that I believe type systems and type inference are actually quite helpful. You could actually make the argument that organically modifying massive online systems on-the-fly without type checking is about as safe as riding a dirt bike through a Serbian minefield or playing Russian roulette everyday for a year. My answer would be that type inference isn’t fundamentally incompatible with dynamic typing. The unification-based type inference algorithms that Haskell and ML use don’t apply to dynamic languages, because they’re trying to solve a system of constraints where every variable has one single type, but unification isn’t the only type inference algorithm in existence. It’s quite possible to create a dynamic language VM that does type inference, and warns you about possible type errors. The difference is that the dynamic language VM will let you have temporary type errors, so long as you know you can make things eventually consistent.

What if you were modifying a live system, and introduced some crash bug? If you were working on a large production system, this could have terrible consequences. I would argue that if we’re going to modify live systems, we probably need to introduce live debugging tools. Things such as the ability to snapshot the system before introducing code changes, recording traces and being able to backtrack execution some large number of steps should a crash occur. It may also be interesting to be able to install monitoring on new pieces of code to keep track of what these specific pieces of code are doing after their introduction. Ultimately, however, with live systems, we should be prepared to have programmers directly intervening at the moment of a crash to repair the system on-the-fly and resume its execution. With the ability to backtrack execution and examine the live heap, this might actually turn out to be a rather comfortable way of debugging programs.

  1. anon permalink

    Have you read

    I am very excited about that work in large parts because of the reasons you mentioned

    • No, I wasn’t aware of this. Interesting work. I plan to eventually begin working on my own dynamic language. It will probably have to wait until I’m done with my PhD, however.

  2. This “big system with live updates” reminds me of Erlang. Apparently, the designers used a dynamic language. Historically though, they came from Prolog, which is also dynamically typed.

  3. Sergio permalink

    i agree with @qznc the big system with live updates already exists and its called Erlang/OTP the design axis has been faul-tolerance, embraces eventual consistency, changes code versions on the fly. The Erlang virtual machine can have loaded up to 2 versions of the same module. The whole point of Erlang/OTP is to have systems that run ‘forever’

  4. Isaac permalink

    1) “snapshot the system before introducing code changes … backtrack execution and examine the live heap”

    1980-1990s been there done that — Smalltalk.

    See Chapter 21 in

    See JPMorgan Kapital

    2) “you can only go from one statically valid program to another statically valid program”

    “Clean now offers a hybrid type system with both static as well as dynamic typing. An object (expression) of static type can be packed into an object of dynamic type (a “Dynamic”) and backwards. The type of a Dynamic can only be checked at run-time. An application can also check whether types of several Dynamics can be unified with each other, without a need to know what types are exactly stored into a Dynamic.”

    Click to access CleanLangRep.2.2.pdf

  5. Tom permalink

    Just found your blog, lots of interesting stuff. Wanted to comment on this.

    In short, live programming has the same issue wrt static/dynamic typing as other forms. Namely, dynamic is more convenient but runs the risk of inconsistency.

    If you can arrange to not hit any of the code that is inconsistent, then modifying the system by parts (dynamically) can be hugely advantageous. If we’re discussing a system that works and is merely being extended, I think that the assumption that you won’t hit inconsistent code is possibly safe. Conversely, if the system has already demonstrated an error, then that is evidence that the assumption is false and it is time to restrict your capabilities (by at least forcing type consistency.)

    To put this differently: Yes, with a static system there’s additional delay between live updates while you make the types work out. However, this provides additional time to think about what you’re doing and make sure the logic is sound. Ideally, you would do this verification regardless and reap the bonus of faster turn around on your accuracy, but you now need the discipline to actually do it and the temptation of just letting the system play out is very large.

  6. One thing bothers me: if you manually “upgrade” a live system by programming it while it’s running, how are you going to reproduce these changes on an another configuration? By configuration I mean both settings and code loaded into memory. Suppose you have server A with system in configuration X. You modify the system A live, and now it’s configuration is X’. Later you decide to add another server B with configuration Y. How would you modify B so that X -> X’ == Y -> Y’? Migration scripts?

    And the very idea of modifying a running production system live doesn’t sound like a good idea. Imagine losing important data. On-the-fly, yeah :) Developers should use special test infrastructure anyway.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: