Symex.el: Edit Lisp Code in a Vim-like Way

A quick movement out of the corner of your eye, but it’s gone as you turn to look. A rustle in the trees, or did you imagine it? A shadow passing overhead, and a blinding flash as you look up. Was that the Sun, or the gleam in the eye of Symex the Squirrel, as it soared above the treetops?

You’ll never know. The legendary Squirrel is said to possess startling agility and speed, capable of executing mind-bending maneuvers in the trees, and traversing impossible distances in a single leap. So they say. Yet, no one has ever seen Symex. Some say that it is much too quick for our eyes to see. Others say that no one has seen Symex because, you fool (they say), Symex isn’t some squirrel out there — it’s the squirrel in you.

And of course, there’s an Emacs extension for that!

Symex is many things:

  1. It is an abbreviated way of saying “symbolic expression.” I remember when I first learned about Lisp, I thought “S-expressions” must be something very advanced and sophisticated, far beyond my meagre imagination to conceive. But symexes are the simplest things in the world! Why shouldn’t they have a nice and simple, even fun, name? One that’s more welcoming to your friends who may be considering learning about Lisp?

And, as an Emacs extension, it’s also:

  1. A DSL for describing, traversing, and manipulating symexes, allowing it to provide complex and useful features you won’t find anywhere else.
  2. A full-featured Vim-style modal interface (implemented as an evil state, just like Normal) for structural editing using the DSL, allowing the keybindings to be minimal and composable, and also supporting quantifiers and expressive macros just like Vim.

You would use it if:

  1. You write code in dialect of Lisp, like Emacs Lisp, Racket, Clojure, Common Lisp, etc.
  2. If you’re an evil user you would be right at home, but you don’t need to be one in order to use Symex.
  3. Er, that’s it. There’s no other reason. Well, since you asked, at some future point the hope is to have Symex be language-agnostic, so that it would work with Python and Ruby and C++ and what have you, but, that’s a pipe dream for the moment.

Now that Symex has hit 1.0, I thought it was a good time to write about it a little more than I have (which is, almost not at all) and, at the encouragement of one of my erudite users (Hi, Tommy!), prepare an animated tutorial for it. After all, the responsibility for writing the documentation lies with the core developers (but help is always appreciated!).

First, I should mention that Symex owes its existence to (1) my partner Ariana, who named it and gave it life, (2) my friend Soumya, who chided me (very sweetly) on my lack of output and motivated me to put some work out there, and (3) Jeff who organizes EmacsSF, who, in response to my feeble “I tried” message saying I could maybe mention Symex at an upcoming meetup, warmly encouraged me and helped make that happen and made it easy[1]. It just goes to show that a little help or a good kick in the butt can be all that’s needed sometimes, and may be appreciated more than you think. So, er, help somebody out today 😉

You can find the original EmacsSF video here, but that’s more of a deep dive than a tutorial. Without further ado, here, in the next post, is the long-overdue ✨ animated tutorial ✨.


[1] Oh, and (4) Oleh Krehel, whose creative mind gave us the hydra package which made it easy to prototype things like Symex. Although Symex is based on evil now by default (for interoperability reasons), it may not have existed at all if I had to write an evil state that early in my ELisp dabblings. Thanks Oleh!

2 comments

  1. stt

    I use spacemacs and I put

    (use-package symex
    :config
    (symex-initialize))

    in my user-config, and type in M-x symex-mode-interface and nothing happened, while symex related commands are listed when I pressed M-x.

    Any suggestion?

    1. sid

      Hey, thanks for reporting that! I’ve created an issue to track it here. What if you run symex-mode-interface again, a second time? Does that fix it? It might be a transient issue at startup. Also, would you mind following up on the GitHub ticket I linked to? It will be easier to track and address it there.

      Update: it should be fixed now. Feel free to reopen the issue if you’re still running into problems 👍

Leave a Reply

Your email address will not be published. Required fields are marked *