Why we’re supporting Typed Clojure, and you should too!
tl;dr Typed Clojure is an important step for not just Clojure, but all dynamic languages. CircleCI is supporting it, and you should too.
Typed Clojure is one of the biggest advancements to dynamic programming languages in the last few decades. It shows that you can have the amazing flexibility of a dynamic language, while providing lightweight, optional typing. Most importantly, this can make your team more productive, and it’s ready to use in production.
Why optional typing?
Dynamic languages have long been criticised for being hard to maintain at scale. When you grow to a large team or a large code base, it becomes more difficult to refactor a code base, to understand how it works, and to make sure it does what it should.
The standard solution is great testing, and obviously we at CircleCI are big fans of great automated testing. However, what we’re really about is productivity, and optional typing can make you much more productive, because you it can can provide guarantees that testing can’t. This is amazing for hard-to-test features, and to increase your coverage without spending a massive amount of time doing so.
Not your parents’ typing (unless your parents hacked in ML)
This isn’t typing as you expect from the Java world. Typed Clojure is much much more powerful than that. Most Clojure code, like other scripting languages, is about manipulating hashes and lists. Typed Clojure allows you type-check duck-typed languages.
Typed Clojure is based on important advancements in combining typed and untyped languages. Based on Typed Racket, it further proves that optional (or gradual) typing is practical to use in production code.
Technically, Typed Clojure is a different form of type-checking than Java and older languages. Those languages use nominal typing, where you type-check the names or classes of a value. Although that is still used in Typed Clojure, the major feature is about structural typing, where you can type-check the structure of an object or hash or list.
This allows you to have incredibly powerful type-checks, while staying lightweight and flexible. For example, you can guarantee that:
- nil isn’t allowed
- a hash must have a defined set of keys (each with their own types!)
- a hash must have only known keys
- a value must be one of a set of types
- a vector has a known size
- many other properties about set, lists, vectors, and object instances.
Not only that, but because it’s optional, adding type-checking can be done incrementally, providing only value where you need it. You can annotate a single library, a single namespace, or a small set of functions, and you can use libraries that aren’t typed.
Does it work?
Oh yes! CircleCI has been using it in production for 3 months. We started by using it in areas that are hard to test, such as code which allocates AWS machines and VMs. That’s right, we’re type-checking devops!
We now using it in 45 namespaces, about 20% of our codebase. Those are covered using only 300 type annotations. And obviously, this has made us more productive, by finding bugs before we ship them.
We’ll go into using more detail in a later blog post, but obviously we’re very excited.
All of this already works, and is used in production now. But Ambrose, the Typed Clojure author, isn’t stopping there. He’s launched an Indiegogo campaign to increase the scope of Typed Clojure, including:
- good support with major IDEs/editors
- significant speed improvements
- support for checking Clojurescript
- a tool to “guess” type annotations, useful for converting lots of legacy code
- ensure support for popular libraries (eg. core.async, ring, compojure)
- annotate the rest of clojure.core
We’ve supported him, and you should too!