views:

513

answers:

6

I'm a newbie playing around with Lisp (actually, Emacs Lisp). It's a lot of fun, except when I seem to run into the same syntax mistakes again and again.

For instance, here's something I've encountered several times. I have some cond form, like

(cond
 ((foo bar)
  (qux quux))
 ((or corge
      (grault warg))
  (fred)
  (t
   xyzzy)))

and the default clause, which returns xyzzy, is never carried out, because it's actually nested inside the previous clause:

(cond
 ((foo bar)
  (qux quux))
 ((or corge
      (grault warg))
  (fred))
 (t
  xyzzy))

It's difficult for me to see such errors when the difference in indentation is only one space. Does this get easier with time?

I also have problems when there's a large distance between the (mal-)indented line and the line it should be indented against. let forms with a lot of complex bindings, for example, or an unless form with a long conditional:

(defun test ()
  (unless (foo bar
               (qux quux)
               (or corge
                   (grault warg)
                   (fred))))
  xyzzy)

It turns out xyzzy was never inside the unless form at all:

(defun test ()
  (unless (foo bar
               (qux quux)
               (or corge
                   (grault warg)
                   (fred)))
    xyzzy))

I auto-indent habitually and use parenthesis highlighting to avoid counting parentheses. For the most part it works like a breeze, but occasionally, I discover my syntax mistakes only by debugging. What can I do?

+1  A: 
Curt Sampson
+2  A: 

One easy thing to do would be to move your cursor to the start of each of your cond conditions and look at where the closing paren falls.

crashmstr
+2  A: 

The "best" way to write lisp is the emacs + slime combination It provides parentheses highlighting, tabcompletion, you can jump right to the lisp hyperspec doc, it provides the variablenames for functions (see below) and a lot more.

(defun foo (bar) ...)

When you start typing (foo it will show you that foo wants one argument named bar. This way you can easily "guess" what arguments a function takes. This is especially handy for functions that don't follow Lisp conventions.

Numlock
+24  A: 

Highlight sexps

First of all, turn on the builtin paren match highlighting (show-paren-mode) if not already did so. It always gives you an idea what indentation level you are in.

There are also some more sophisticated packages. For example, see TJ's answer on mic-paren. Or though I didn't find it suitable for me, but there is Schumacher's highlight-parentheses mode that highlights every expression block with different colors. Even Edward O'Connor has a mode that highlights the current sexp.

Paredit

Use paredit-mode which helps you writing sexps. You can easily navigate between sexps and re-structure the code. In addition, it ensures the parenthesis always balanced. When I first tried it, it was very annoying for me how Paredit constrains the way of coding, but since then I'm used to work with it, I'm a lot more productive and never get confused with opening and closing parenthesis.

Auto indenting

Use Emacs Starter Kit, which, by default, enables a lot of useful helpers for coding in Elisp like re-indent on newline.

ElDoc

emacs-lisp-mode has several useful extensions. For instance, I always use eldoc-mode that displays currently typing function's argument list or variable's docstring in the echo area. It also helps you easily recognize if you are in the wrong block.

Edebug

I've just nearly forgotten to mention Edebug which—as a last chance—always helps you figuring out what's going on in your code.

Török Gábor
Wow eldoc-mode is cool - save me a bunch of C-h v and C-h f when I'm reading someone else's code.
polyglot
+6  A: 

Here are three concrete things you can do to help with spotting Lisp syntax problems. In time, it'll become 2nd nature. But until then:

Parenthesis Matching

Parenthesis matching is the easiest way to check the grouping. My favorite package is mic-paren. And I like this particular configuration:

(setq paren-dont-touch-blink t)
(require 'mic-paren)
(paren-activate)
(setq paren-match-face 'highlight)
(setq paren-sexp-mode t)

That causes the sexp (matching parenthesis) to be highlighted when the point is at the beginning/end of the sexp. If the parenthesis don't match, the highlight color is different - and brighter. When the matching parenthesis is off-screen, it shows you what that end looks like in the minibuffer (and tells you how many lines away it is).

Compilation

For a slightly more involved method, you can run the elisp compiler with M-x comile-defun. for example, when I compiled this simple example:

(defun mytestfun ()
  (let ((cur (current-buffer)))
    )
  (set-buffer cur))

I got a buffer named *Compile-Log* which said:

Warning: reference to free variable `cur'

Which clued me into the fact that I was using cur outside of the let statement that defined it.

Change Indentation Level

If you want the indentation to be more prominent, you can customize the variable listp-body-indent:

(setq lisp-body-indent 4) ;# default is 2

You can also customize how various constructs are indented, but I don't advise that because it'll be non-standard and might lead to confusion when looking at most lisp code.

Trey Jackson
A: 

To add to everyone else's valuable pointers, I'd say "use the structure-based move commands", backward-sexp, forward-sexp and the like. That allows you to navigate around.

Vatine