I followed the whole „Clojure vs. the static typing world“ discussion with great interest. And I like the general tone (aside from the inevitable sporadic haters), especially the people on r/Haskell who are very open, rational and willing to discuss. This has shown me that the community is a good one. It thereby has reinforced my intention to learn Haskell sometime. Thanks for keeping it constructive!
Type systems are important
There is an aspect of Rich Hickey’s talk that I haven’t seen discussed anywhere, and it is this one:
„I think that this verification and whatnot is incredibly important. But it should be à la carte. Depending on what you need to do. Depending on the amount of money you have to spend. Depending on what you want to express. You should be able to pull different kinds of verification technology off the shelf and apply it. It should not be built-in. There’s a diversity of needs, there’s a diversity of approaches for doing it and a diversity of costs.“ – Rich Hickey (https://youtu.be/2V1FtfBDsLU?t=4169)
Rich seems really critical about static typing for most of his talk but he is not. He’s critical about integration static typing strongly into a language. And he explains why he didn’t do that in Clojure given the drawbacks he sees. Separating the type layer from the data layer certainly makes the language simpler. Clojure is all about that.
Gradual typing
À la carte means being able to use the type system when you choose to, aka gradual typing. Good examples of this are Racket’s type system, Clojure’s own core.typed and Spectrum. Clojure.spec’s approach is also opt-in (but not a type system).
The drawbacks of an opt-in type system are numerous. Clojure’s type systems are not as sophisticated as Haskell’s. They probably have a harder time achieving the same degree of performance optimizations as in other languages. Also gradual type systems tend not to be as concise nor as convenient as integrated ones. So this is a tough trade-off. The question then is: How often does one benefit from a dynamic approach and does this outweigh the drawbacks?
Conversely, one benefit is architectural flexibility, as we’ll see later. The other is that users are not forced to use the system and can choose to work without types where it suits their needs.
This is the point where a lot of static type fans go: „but I can choose to use maps in my language“. And that’s true. But do you? In Java for example using maps is extremely cumbersome because there is so little language support for them. I suspect it is similar in other languages. When I suggested using „just maps“ to my teammates a mixture of fear and disgust was written all over their faces. And I can’t blame them. In Java we only use maps when there is no other way. E.g. if we can’t know the shape of the data beforehand.
This is why all languages I know of support basic data structures like maps. And it shows that they are useful in all languages, otherwise there would be no support for them at all. They’re just not idiomatic.
But Clojure is proof that using maps doesn’t have to be painful. And that we can make use of maps for their great properties of being open, dynamic and flexible. For example if we tackle inherently dynamic problems. And if we choose to use a type system like core.typed or Spectrum, we can have type-safety. All at the same time. À la carte type systems mean having the best of both worlds. The flexibility of untyped-land where it suits us and type checking where it is beneficial.Clojure vs. the static typing world
Pole Home Programming
My minds-eye picture for this is pole home programming. Languages with type systems tend to optimize for the higher-level floors (types) and keep the lower level floors (data structures) as sparse as possible. This means that we can be working in the comfy upper floors most of the time. But problems that don’t fit the upper floors force us to grudgingly work in the windy, cold ground floor part of the building with almost no comfort.
Clojure’s approach is different. It builds a simple, stable and practical ground floor. It may not be as comfy in comparison, but it is very usable and at the same time acts as groundwork for several upper floors. We can live in those upper floors when it suits us, but we don’t have to. Moreover there is never a windy, cold ground floor that we have to put up with.
„Logic should be your tool, it shouldn’t be your master. You should be underneath the logic system and be applying the logic system when it works out for you.“ – Rich Hickey (https://youtu.be/2V1FtfBDsLU?t=3869)
Flexibility
And then there’s the benefit of architectural flexibility of opt-in approaches. The type system is independent of the language core, which means there can be multiple ones – which there are already in Clojure land. Imagine you could download a library (not a compiler) providing a second type system for Java if you found the original one lacking for some reason…
And to drive this point home let’s think about what happens when some language decides that it does not provide the pinnacle of type systems. The answer in most cases is that the core language, i.e. compiler has to change. But with opt-in approaches the core language can remain unchanged, because it is independent of the type system. I like that.
P.S.:
So do I use one of the Clojure type systems? No I don’t. But I use spec to spec my functions and I keep instrument on at dev time. That way I can catch type errors akin to the ones Java can detect, without the language forcing me to make an API that protects from reuse. That’s all I want.