Common Lisp vs Racket
I'm a heavy user of Common Lisp, and I only dabble in Racket from time to time. While Common Lisp is my tool of choice for a lot of reasons stated in the post, since the post largely skews favorably toward Common Lisp, I'll offer two things that Racket shines at:1. DocumentationRacket has a beautiful documentation system called Scribble [0]. Almost all documentation written for Common Lisp is ugly, inconsistent, and doesn't follow any sort of standard. Common Lisp has a gazillion "documentation generators" which do little more than slurp up all the documentation strings and barf them back out as e.g. HTML. Common Lisp users tend to just rely on READMEs + jump-to-definition to learn how a project works. A few Lisp programmers (e.g., Shinmera, Edi Weitz) have made their own documentation systems that look better than average, but are still at least an order of
magnitude less capable than Racket's offering.Racket's documentation system is aesthetically pleasing, very well organized, and makes the programmer want to write long-form documentation. Moreover, Racket (the language) sets a superlative example
of what its users can strive for in terms of quality of clear, no-nonsense writing.2. Languages and DSLsOne of Lisp's (any dialect) defining features is the ability to build domain-specific languages, syntactic extensions, and the like by way of a special kind of function that can run prior to the code's execution. These special functions are called "macros", and across Lisp dialects, there are lots of different takes on them.Common Lisp gives you a grab bag of facilities for writing code that lets you change syntax at different levels: DEFMACRO (scoped variant: MACROLET), DEFINE-COMPILER-MACRO, DEFINE-SYMBOL-MACRO (scoped variant: SYMBOL-MACROLET), SET-MACRO-CHARACTER, and a few others. At best, I'd consider these only loosely related, and they all work on really "raw" data representations (parsed S-expressions and character streams). In part because of this, ANSI Common Lisp doesn't provide access to any notion of environment, source location, etc. when writing new macros.Macros defined by the above operators "live" in different places and are compartmentalized in different ways. There are some Lisp libraries that help you tame definitions and keep them fenced in, like NAMED-READTABLES which helps you organize where reader macros (macros which change how Lisp is parsed at the character-level) live and how to turn them off and on in a sane way.Contrast with Racket which has explicit documentation on how to write macros [1,2], how to debug them [3], and how to encapsulate them a new DSL embedded in the system [4]. Racket—as a technical system—clearly defined the phases of compilation, what objects are manipulated during the phases (e.g., "syntax objects"), how to provide debugging information, and so on.In the end, Common Lisp's system for making DSLs feels a little runny. It's supremely hacker-friendly, but it's a bit more difficult to deliver, in one fell swoop, a language as a library. Racket, on the other hand, at the expense of having increased rigidity and needing to read two or three complete chapters of a manual, you get a way to neatly bundle up a language. This is why Racket had nice DSLs like Typed Racket, Hackett, Scheme RnRS, etc.I use Common Lisp and I develop relatively large DSLs in the language, and even if I want to, it's hard—if not impossible—to write large DSLs that have nice error messages, source locations, etc. without writing an entirely new "reader" (lisp's term for a code parser) myself every time. Smaller DSLs where user code doesn't exceed more than a few lines? Examples like:- regex syntax- string interpolation syntax- a pattern matching featureCommon Lisp is great, and you get a ton of mileage out of the facilities it provides. But larger ones? Examples like:- an embedded language for writing documentation with markup- a complete language with lazy evaluation semantics- a language for non-deterministic programming (like SCREAMER, Prolog)- a language for strictly typed functional programming (like Coalton, Hackett)Tackling these in Common Lisp requires ambition and a very steady hand, whereas Racket's overall system (language, implementation, documentation) makes that a comparatively painless exercise.[0] https://docs.racket-lang.org/scribble/[1] https://docs.racket-lang.org/guide/macros.html[2] https://www.greghendershott.com/fear-of-macros/[3] https://docs.racket-lang.org/macro-debugger/index.html[4] https://docs.racket-lang.org/guide/languages.html