D-Expressions: Lisp Power, Dylan Style (1999) [pdf]
I&#x27;ve been working on a Lisp for the last couple of years. There&#x27;s one major downside to reader macros &#x2F; syntax extension: Your editor probably won&#x27;t understand it, and in extreme cases won&#x27;t know how to indent or read it.<p>Note that Lisp macros do not suffer this problem because it still uses the standard `&#x27;(,&quot;foo&quot;) syntax. (In fact it&#x27;s somewhat remarkable that Lisp syntax can be described in shorthand so compactly.)<p>Reader macros are a different beast. Let&#x27;s say you want to add support for [] as indexing. You can do this:<p><pre><code>  &gt; (let l &#x27;(a b c)
     l[1])
  &quot;b&quot;
</code></pre>
And this:<p><pre><code>  &gt; (let l &#x27;(a (b) c)
      l[1][0])
  &quot;b&quot;
</code></pre>
And since the reader is under our control, it&#x27;s easy to use a dynamic expression as the index:<p><pre><code>  &gt; (let (l &#x27;(a (b) c)
          i 0)
      l[(+ i 1)][i])
  &quot;b&quot;
</code></pre>
If you try this, you&#x27;ll find most Lisp editors work okay-ish with it.<p>Now let&#x27;s say you want to be able to do this:<p><pre><code>  &gt; (if x) {
      (print &quot;hello&quot;)
    }
</code></pre>
This is entirely possible since we control the reader. The rule is, if the following expression is a curly-body, it gets converted into `(do ,@body) and inserted as the last argument of the previous expression. So it becomes:<p><pre><code>  &gt; (if x (do (print &quot;hello&quot;)))
</code></pre>
However, this fails <i>hard</i> inside of standard Lisp editors. You&#x27;ll lose indentation right away. Nothing understands curly notation. For example if you turn on Clojure mode and try to auto-indent, here&#x27;s what you get:<p><pre><code>  &gt; (if x) {
            (print &quot;hello&quot;)
            }
</code></pre>
The alternative curly style is just as broken:<p><pre><code>  &gt; (if x) 
    {
     (print &quot;hello&quot;)
     }
</code></pre>
So if you expose this power to users, suddenly you have an editor problem: How can the editor know what to do with tokens, short of running the program?<p>Even in Emacs, which is Lisp-based, it&#x27;s not really possible to execute other Lisp variants. (I actually got both Scheme and Arc to work inside of emacs, which was amusing. No external programs; literally scheme, but running in elisp.)<p>But even if it were possible, you wouldn&#x27;t want to. An infinite loop would freeze your editor. Not to mention it should always be safe to open a file for editing, yet this breaks that safety guarantee.<p>I think the solution is something like:<p><pre><code>  &gt; (define-reader (&quot;{&quot; s curly-body)
      ...)
</code></pre>
Within the define-reader block, `s` is the stream, and you can read tokens from it and return a result, or stuff the result into the previous token&#x27;s last argument, or throw an error, etc. And since it ends with &quot;curly-body&quot;, Lisp editors can be designed to detect this token and turn on &quot;curly-body syntax&quot; for any file that imports it.<p>It&#x27;s worth it. Would you believe this is Lisp?<p><pre><code>  &gt; require(&quot;fs&quot;).existsSync(&quot;&#x2F;tmp&quot;)
  true
</code></pre>
It&#x27;s equivalent to:<p><pre><code>  &gt; ((get (require &quot;fs&quot;) &quot;existsSync&quot;) &quot;&#x2F;tmp&quot;)
</code></pre>
But ten billion times more readable. And Lisp!<p>I have no idea why Lisp doesn&#x27;t let you call functions like<p><pre><code>  foo(1 2 3)
</code></pre>
instead of<p><pre><code>  (foo 1 2 3)
</code></pre>
It&#x27;s an unambiguous transformation in every case. Nobody smashes an atom into a left-paren, ever. If there&#x27;s an atom followed by a left-paren, or a right-paren left-paren combo, it means you want to call it as a function.<p>It leads to some amusing situations:<p><pre><code>  (list 1 2 3)
  list(1 2 3)
</code></pre>
But there&#x27;s an easy solution: If it looks confusing, don&#x27;t write it that way. Just like every other language feature.<p>Even though Lisp is such an old language, I feel like we&#x27;ve barely scratched the surface. Clojure was a promising demo of what&#x27;s possible. It&#x27;s time for the next step.