-
Notifications
You must be signed in to change notification settings - Fork 7
Rationale
I've found Clojure to be a beautiful language to work with. In fact, it's probably my favourite language right now.
However it has, in my humble opinion, two notable weaknesses:
- The lack of static typing
- A tricky mutable namespace system
I believe these issues can be addressed without compromising the otherwise excellent design of Clojure. So Kiss is an experiement to try and create a mini-language that addresses these two issues, while otherwise being pretty much exactly like (and bootstrapped on top of!) the Clojure that we know and love.
There are many reasons to want the benefits of static typing, most notably:
- Performance - with knowledge of static types, the compiler can eliminate runtime checks and produce more efficient code. Sometimes performance matters, sometimes it doesn't - but it's better to have the performance there for when you need it.
- Reliability - static typing will tell you about many bugs at compile time. If your code doesn't type check, then it probably isn't logically correct (the converse is of course not true: code that type checks can still be very incorrect, although at least you know that you have eliminated a large class of typing bugs).
- Refactoring - refactoring dynamically typed code is much harder. In part this is because you don't find out about errors until much later (at runtime or at least test time). In addition, static typing makes it much easier to create useful code analysis and refactoring tools.
- Less work - To compensate for reliability and flexibility issues, programmers in dynamic langauges often write large test suites that do something equivalent to type checking of various cases (but such tests are still probably nowhere near exhaustive). There is also a tendency to create various schema lannguages / DSLs. With static typing, the compiler just does all this work for you, saving considerable effort.
All of the above can be worked around in various ways, but it's generally messy and unidiomatic to do so.
Clojure is founded on the idea that immutable data is good. Indeed, the use of immutable data (and the functional programming style that this enables) is one of the most compelling features of the language.
So why are namespaces mutable? Do they need to be, or is this just an accident of design derived from Clojure's Lisp heritage?
Mutable namespaces present a number of significant problems:
- Performance is impacted by the fact that every single Clojure function call has to go through dynamic var lookups, just in case the var has changed. 99.99999% of the time this isn't true, but it's still a significant cost that you pay on every function call in Clojure
- Tools are hard to get right: Code analysis / compilers / other code-aware tools are very hard to get right, because they can assume very little about the state of namespaces. Namespaces can be mutated arbitrarily, with no transactional protection.
- Circular dependencies are very tricky in Clojure, in a large part because namespaces are constructed in an incremental, imperative style. Other languages which take a declarative approach to code can handle circular dependencies just fine (even Java!).
- Expansion Passing style - http://cs.au.dk/~hosc/local/LaSC-1-1-pp53-75.pdf