State of Clojure language features
I found the results of the 2013 State of Clojure & ClojureScript survey to be very interesting.
Tonight I dug in a little deeper to the answers to the specific question “Name *one* language feature you would like to see added to Clojure.” There were 610 answers to this question (of 1061 survey responses). Many did not actually seem like language features but I’ll take all the answers as generally requests for something lacking in the use of Clojure.
I assigned a category (and in some cases sub-category) to all the responses and wound up with this summary…
None (60) + 451 non-responses
None was the second biggest category with answers, but that does not take into account the 451 responses that did not answer the question. In other words, around half of respondents could not think of a language feature they needed. Clearly, the existing language is getting it done for most people.
I found it very interesting to see type-related requests at the top of the list. To some degree I see this dovetailing with a number of talks at last week’s Clojure/conj. Stuart Sierra tweeted re the Conj: “2013 summary: We made everything data. Now what’s it all mean?”.
Common kinds of responses were “core.typed”, “optional or gradual static typing”, “schema”, “typechecker”, “type inference”, and “algebraic data types”. Both Scala and Haskell were called out either by name or feature several times.
Clearly there are many people that enjoy Clojure but wish they had ways to describe and verify the effective types they are using in their programs. I presume this also has some interaction with the success of the recent Typed Clojure Indiegogo campaign in terms of awareness.
Fortunately, Typed Clojure exists and can be used right now for optional static type checking. Similarly, Prismatic’s Schema library is another useful tool for specifying and validating your types at runtime. Seems like many of the requests can actually be satisfied now with no additional language changes.
There were a handful of specific requests beyond those, mostly around using types to affect performance via optimizations.
There were a handful of sub-categories within the error category – things split pretty evenly between error messages, stack traces, and desires for a Lisp style condition system. Currently, there are a number of external tools that provide parts of this – slingshot, dire, clj-stacktrace, io.aviso:pretty, etc.
Probably the biggest chunk of those were requests for Clojure in Clojure (or steps along the way). While that may itself be a ways off, some of the prerequisite pieces are being worked on via the Typed Clojure campaign (reader, analyzer, emitter, etc).
There were several requests for removing the need for forward declaration of vars, multi-pass compilation, and optimization (tree-shaking was mentioned twice).
Another strong response was the 30 responses related to requests for debugger or in a few cases, tracing facilities. In particular, many people mentioned the ability to breakpoint, inspect locals, restart, etc. There are a number of debuggers for Emacs, Eclipse, IntelliJ, etc but clearly the status quo is not satisfying the demand.
Interestingly there seem to be two totally separate vectors for this request – the Java camp used to the great debuggers available in Java IDEs and Lisp developers used to the excellent error handling and debugging facilities available since well it’s kind of embarrassing how long.
Cross compilation / feature expressions (26)
I’ve lumped together some things here that may not necessarily be exactly the same thing but the intent of them seems to be the ability to write (one) library that works in both Clojure and ClojureScript. Currently, the leading answer to this issue is the cljx preprocessor.
Feature expressions are a feature from Common Lisp that has been considered for a while to allow the inclusion of platform-specific code. We hope to focus on feature expressions in the release following 1.6 (presumably 1.7).
Pattern matching (26)
There were 26 responses mentioning pattern matching, of which about half specifically mentioned or seemed to refer to predicate dispatch on function arguments. The existing core.match library provides pattern matching but several people mentioned wanting it more tightly embedded.
Several responses mentioned reworking the implementation of namespaces to make them transactional, or immutable, or to overhaul the transaction lifecycle. Others mentioned wanting to make namespaces a first-class entity with a more rigorous API.
About half of the namespaces request had to do with implementing some kind of module system (with shout-outs to Racket Units and Newspeak).
And finally, there were several requests to overhaul the use of namespaces (the syntax used in ns declarations and the syntax used typically at the REPL).
Tail call optimization (25)
Many requests here for true tail-call optimization without the need for loop/recur. Obviously, this is a tricky thing on Java so unlikely to happen soon. I assume that most of these requests are coming from people with backgrounds in Lisp, Scheme, etc.
The bulk of the performance requests are around faster startup time with a handful around numeric performance. Beyond those, there were requests for more options, more transparency, and better guidance around tuning performance and I think that is an area where more feedback and docs would certainly be helpful.
Lots of micro suggestions here. There were several suggestions around currying / allowing automatic partial application for every function (I guess Haskell influence?). There are some helper macros in Poppea that can do some of this now.
Other comments were around full laziness, primitive types in HOFs, Pythonic yield, regexes that implement IFn (intriguing), overloading on type hints, multiple return values, Common Lisp named args, labeled recur points for loops, etc.
These were small suggestions that didn’t seem to need language changes but someone was requesting support in core libs – contains-value, count for > max int items, Datomic queries as lib, more def-forms, helper macros for chunking and transients, monad support, EDN writing, xml, etc.
There were a number of requests for a standard code formatter tool (like gofmt). Requests for better IDE support. Several requests for a linter (Kibit seems pretty good for this).
These requests were about half for reader macros (not happening) and the other half were around support for multi-line comments/strings/heredoc, etc.
The bulk of these requests were for compilation to native targets (many suggested LLVM as the path for that).
Java interop (12)
Couple of repeated calls here: ways to alias Java imports and wildcard imports from Java, improvements to gen-class, better support for generics, simplified reflective calls.
Most of these comments lacked enough info to be actionable. There were a couple of requests for the ability to write docs with executable examples (like Elixir).
There were a number of requests for pods, which Rich has worked on in the past, a new kind of ref type. There were also calls for GPU integration, message passing, dataflow variables, and CAS. Some of those are actually available now in libraries.
Some other things that were mentioned were: actors, advice support, contracts (enhanced pre/post conditions, assertions, etc), distributed system support (distributed async, OTP-style constructs), CLJS self-hosting, continuations, more built-in tagged literals (#uri), Haskell lenses, ordered sets and maps, better hashing for collections, better support for I/O resources (scopes), matrix support (core.matrix?), mobile support, etc.
Thanks for all the ideas!! I hope to look more deeply into the question on weaknesses and blind spots later this week as well.