POLYGLOT PROGRAMMING

Better poly than sorry!

Clojure and Racket: Origins

Most programming languages are created with some goals in mind, which the new language tries to meet. Over time, the goals may (but don't have to) shift as languages evolve. Either way, knowing current goals and history of a language should make understanding the bad and good sides of the language easier. I prepared a short introduction to both Clojure and Racket, before getting to the main issue in the following posts.

Racket: in the beginning, there was (PLT) Scheme

Racket started its life in 1994, as an implementation of Scheme language[1]. Scheme is a rather minimal Lisp-1[2] dialect popular with Programming Languages researchers, Computer Science professors, and teachers because of its simplicity and elegance.

Matthias Felleisen initially created a research group with a goal of providing better teaching material for novice programmers. The group was called PLT, and after deciding to write a new language as part of their efforts, they decided to base it on Scheme, creating first versions of PLT Scheme. The initial goal was consequently pursued for many years, resulting in a couple of books and many papers, but there were other groups of users of the Scheme - the language proved to be a good platform for implementing novel PL concepts, so researchers took a liking to it. From that point, the design and implementation of what would become Racket tried to meet the two goals: to be a beginner-friendly pedagogical environment and to be a powerful, easily extendable general purpose language.

On the one hand, Racket included delimited continuations[3], more powerful macro systems[4] and soft typing and eventually statically typed dialect[5] were implemented in Racket, improving both the language and the state of research. On the other hand, the need for more beginner-friendly language features focused on making teaching and learning on different levels easier, resulted in a graphical IDE and support for restricted and contracted[6] subsets of the primary language. Between the two extremes was the often overlooked rest - a natively-looking, cross-platform GUI toolkit (used to write Racket's IDE), a lot of utility libraries distributed via a central repository[7] and the infrastructure like the JIT compiler, garbage collector and so on.

PLT Scheme changed its name to Racket sometime around 2010, when it became apparent that it evolved way past the Scheme specification and showed no signs of stopping its further development and evolution. Current Racket is a product of seven more years of cutting-edge language research, teaching material preparation[8], adding requested features and abstractions and implementing even more libraries.

If it sounds like a kitchen sink, that's because it is. Some parts of Racket are explicitly designed, but some others evolved on their own. When meeting all the challenges during the years Racket not only added tools for dealing with them but also perfected the meta-tools to create the tools, which made it into a perfect kitchen sink: one where implementing new linguistic features is not only possible but easy by design.

Clojure: quite a different story

Rich Hickey wanted a simple, powerful and successful Lisp for a long time. During the 00's such a Lisp arguably didn't exist. Common Lisp was in a decline which started a decade earlier[9]. Scheme was impractical (because of problems with portability between implementations, also Scheme's minimalist approach to stdlib didn't help). Other Lisps - other than the embedded ones, like Emacs Lisp or a dialect used in AutoCAD - hardly had any following at all.

Before writing Clojure, Rich was familiar with Common Lisp and tried integrating CL with Java on a couple of occasions, but ultimately decided to create an entirely new Lisp, using JVM as a runtime environment. His goals were: symbiotic relation with the hosting platform, Functional Programming, and concurrency. FP and concurrency implied immutability: almost all native data structures in Clojure are immutable and persistent[10], and the mutable ones only allow mutation in certain, safeguarded contexts. The desire for expressive power resulted in an extended syntax, used for data structure literals and pattern matching[11] and dereferencing shortcuts. The standard library is not too big because Clojure can use all of the Java stdlib directly; the largest part of the library are functions for dealing with collections, which are an abstraction similar to iterable in Python. The four basic data structures - string, vector, set and map - are all collections and may be operated by the same set of functions.

In the first couple of years after launch, Clojure marketing was focused on Java interop and concurrency. Clojure indeed offered a lot of options for safe concurrency and safe parallelism (leveraging JVM threads and other constructs, but hiding them behind novel APIs) and was definitely more productive than Java (but comparable to Groovy or Jython, I think). It was quickly recognized as a language well suited for server-side web development. Later, when ClojureScript appeared[12], Clojure steered even more towards web development, now promising performant servers and almost effort-free (i.e., reusing the server-side code) frontend. It didn't work that well at first: ClojureScript was lacking features and Clojure to ClojureScript interop wasn't easy to setup. Things improved over the years, and in 2015, according to Wikipedia, 66% of surveyed Clojure users also used ClojureScript.

Over the years, the language became one driven by the community, with Rich serving as its BDFL, analogous to Python's Guido van Rossum. The community produces new versions of the language in 1-2 year spans; the new features are generally few in number, but powerful, with a broad impact on how the code is supposed to be written[13]. The tooling and library ecosystem, initially dependent on Java, also improved and matured over the years, resulting - among other things - in a good build tool and package manager[14]. The core language remained relatively small and focused, but its expressivity allowed for many features to be added as libraries.

Cross-pollination and differences

As both languages were rapidly evolving around the same time (and still being developed), some features from one language were implemented in the other. For example, in Clojure, core.typed is based on TypedRacket, while Racket sequences idea is suspiciously similar to Clojure's collections (especially when using a helper library, because in stdlib support for sequences is very basic).

That doesn't really make the languages similar, though - they are based on completely different philosophies and were developed very differently. Honestly, I'm tempted to say that Lispiness-1 of the dialects is the only thing they have in common (I'm exaggerating a bit): they offer similar functionality but deliver it in different ways, and the general feel of using them is very different.

Of course, how the language feels when used is entirely subjective, but language designers and developers often optimize languages to "feel natural" or "feel right," and language users often evaluate them based on this vague notion. That is to say: even if two languages are technically equivalent, if they "feel" different, they attract different users, cover different use-cases and specialize in different things.

The differences between Racket and Clojure are just enough to make it worth learning both of them, I think... But, well, as a programming languages nerd I may be a bit biased. Still, it's worth seeing at least some of the capabilities and features they provide - I plan to present a few of them in the third post in the series. But before that, in the next post, I'll tell you how and why my work on a Clojure-based project was a nightmare.

Comments