The type system has always been a major feature in a programming language, to the extent that it is at the base of a primary classification of languages.
In fact, dynamically typed languages are often perceived as scripting languages, very productive and fun to work in but unsuitable for large-scale development.
Statically typed languages, on the other hand, are seen as the natural tool for a large project, at the cost of verbosity and complexity.
This article will explore a third approach, specifically aimed at getting the best of both worlds: optional type annotations.
Dynamic type systems: productivity
A language is said to be dynamically typed if it performs the type checking at runtime.
This means that one does not have to write explicit type signatures, resulting in less code to write and quicker development times.
However, this is not the main reason why dynamically typed languages are more productive. In fact, this misconception comes from comparing Ruby, Python et similia with Java and C++, which require an explicit type signature for every single variable.
However languages whose static type system performs type inference are just as concise as their dynamic counterparts, as one could easily see by comparing, for example, Python with Haskell.
What really favours productivity is that dynamically typed languages require much less design upfront to produce working software.
In fact the major designing issue with statically typed languages is modelling one’s problem domain in a way that satisfies the constraints imposed by the type system, no matter how expressive it is.
This really hinders productivity when coupled with the constant, fast paced requirements and design changes that every real world software project undergoes.
Furthermore, some features like reflection, meta-programming and dynamic dispatch are simpler and less cumbersome in a dynamically typed language, as anyone who’s written template meta-programming in C++ probably already knows.
Static type systems: scalability
So, why is everyone insisting on using Java and C++ for large scale software, instead of using dynamically typed languages?
Well, it turns out there is a problem with them: they don’t scale well.
As our system has grown, a lot of the logic in our Ruby system sort of replicates a type system, either in our unit tests or as validations on models. I think it may just be a property of large systems in dynamic languages, that eventually you end up rewriting your own type system, and you sort of do it badly. You’re checking for null values all over the place.
Alex Payne on why Twitter is switching to Scala for server-side code
Choosing the best tool for the job: optional type annotations.
All the flamewars on static vs. dynamic essentially boil down to this: choosing between productivity and flexibility vs stability and scalability.
The problem is that this choice is being made by the language, when instead it should be made by the programmer: she should be able to make a trade-off depending on the specific problem her code is trying to solve.
I believe that dynamic typing should be the default, as the advantages it brings are simply too valuable to give up on. However, there are cases in which enforcing the type a function expects at compile-time is simpler and sounder, without letting this additional guarantee getting in one’s way when it is not needed.
This can be fully achieved by using optional type annotations.
The most mature example of a successful application of this approach is Clojure. Clojure is a dialect of Lisp that runs on the JVM, and is receiving quite a lot of attention lately for its power and expressivity. Being a Lisp, it is dynamically typed, but its core.typed library adds an optional static type system using annotations. For example this function checks whether an integer is divided by another:
(ann div-by [AnyInteger AnyInteger -> Boolean])
(defn div-by [x y]
(== (mod y x) 0))
The interesting part is that the annotation (the top line) can be added later without modifying the actual code, letting the programmer decide which functions should be left generic and which should not.
Another very important point to be made is that this library has been originally written by an external developer, and added to the language core later, it was not necessary to have native support for this feature. This bodes well for the inclusion of such a feature in other languages like Python or Ruby.
Many dynamically typed languages are powerful and fun to work in, but they are often set aside when it comes to large scale software, due to the perceived unsuitability that the lack of a static type system implies.
An optional system based on annotations however, as we have seen, succeeds at bringing the best of both worlds, allowing programmers to do what is always required from them: choosing the best tool for the job.
Steve Yegge , Is Weak Typing Strong Enough?
Martin Fowler, Dynamic typing, 14 March 2005
Bill Venners,Twitter on Scala, 3 April 2009
Gilad Bracha, Pluggable type systems, 27 October 2004
Adam Bard , Clojure’s core.typed vs Haskell, 29 September 2013