Squint Testing Lisps

· im tosti


It's no secret I'm a fan of lisps. I'm writing this in emacs right now!

Back in 2008, this blog post came out.

Now, almost 20 years later, we have more hindsight. Typical responses to it are to say that the squint test makes no sense for a programming language. Now a rejection of the premise is all fine and good, but I want to do more. This is because I think that lisps actually pass the squint test.

Thing is, the language being demonstrated here is actually scheme. I've been pretty vocal in my opinion that scheme sucks, it's the nothing-language with the XMPP problem.

Another common answer has been to say that the parens are coincidental. This perception also creates scheme, actually, where you end up with extra quantities because you don't consider things.

For me, the entire point of a lisp is actually s-expressions. However, s-expressions do not mean parens.

An s-expression (symbolic expression) is a way to represent trees. Traditionally, it means either an atom or a cons-cell of which both parts are also s-exprs. However, this doesn't need to be the case.

I want to argue that an s-expression is either an atom or any structure that cleanly translates into a machine-list. This is to say that [atom1 [atom2 atom3] atom4] is as much an s-expr as (atom1 (atom2 atom3) atom4). Put differently, it's not the parens that matter, but the parse structure : the ability to directly work in terms of AST.

Note: this also means that datum labels are not s-exprs, at least under my definition. Then again, that also applies to quote/quasiquote, which I'd like to keep, so this is an aside.

That said, you're now technically allowed arbitrary reserved openers and closers. In C type lisps (and clojure-inspired A and B lisps), this typically implies also having {} and []. The former serve to construct maps and the latter to mean non-function calls.

Let's now take an example of such a lisp and try to reproduce the squint test.

(xxxx xxx
  [x]
  (xxx [x (x xx)
        x (xx xxx)]
    (xxxx (> (* x x) x)
      (xxxxxx-xxxx x (x x x)))))

The structure of the program becomes immediately more recognizable. An (xxxx xxx [x] is obviously a function or macro definition. An (xxx [x (x xx) x (xx xxx)] looks like a let. An (xxxx (> (* x x) x) looks like a conditional in part due to the whitespace in the next line.

While remaining as orthogonal, by focusing on what matters, and having proper style guidelines (you wouldn't judge the squintability of a forced semicolon-ridden one-liner either, right?) lisp becomes just as squintable as any other language... While retaining orthogonality, code-as-data / macros, and having fewer symbols total.

If you're unconvinced, I don't care, I'm just sharing how I see things here. If you are convinced but don't know what the hell I'm talking about with all that "Type C" and so on and want to know more...

In short, Clojure (which appeared in 2007, so I'm not blaming the blog author at all!) had a few interesting ideas. One of them was this, another was to use maps for a lot of stuff. Languages that are inspired by Clojure or are even straight ports are called Type C lisps. Some languages aren't Type C lisps but still go for a similar syntax. A good one of those is Janet, you should try that one first if you've never used a lisp before.

last updated: