rainbow-delimiters
or similar to show matching parenthesis in the same color.
The default color scheme uses so many colors I cannot always see them clearly, so I simplify it like this:
;; Customize rainbow-delimiters to cycle through three colors. (with-eval-after-load 'rainbow-delimiters (setq rainbow-delimiters-max-face-count 3) (let ((light ["#6276ba" "cyan2" "#A09183"]) (dark ["yellow1" "red3" "sky blue"])) (dotimes (depth rainbow-delimiters-max-face-count) (face-spec-set (intern (format "rainbow-delimiters-depth-%d-face" (1+ depth))) `( (((class color) (background light)) :foreground ,(elt light depth)) (((class color) (background dark)) :foreground ,(elt dark depth)) ) 'face-defface-spec) )))Adjust colors to taste (and according to the colors available on your system).
Lisp code can be formatted to taste in terms of where to put newlines. Compare the following styles:
;; Style 1: "cuddled" (let ((var1 val1) (var2 val2)) ((exp1) (exp2)) ;; Style 2: lines with code have balanced parens (let ( (var1 val1) (var2 val2) ) ( (exp1) (exp2) ) )The cuddled style 1 seems to be the most standard for Lisp programming.
Style 2 is similar to the gnu style of formatting {} blocks in C. It wastes screen area with lots of nearly empty lines, but has the advantage of making nicer looking diffs when editing. For example if the (var2 val2) clause in the example above is deleted the cuddled style will show it as a two lines change.
;; Style 1: "cuddled" ;; Old version (let ((var1 val1) (var2 val2)) ((exp1) (exp2)) ;; ;; New version; deleted (var2 var1) (let ((var1 val1)) ((exp1) (exp2)) ;; diff --- Two lines changed 1,2c1 < (let ((var1 val1) < (var2 val2)) --- > (let ((var1 val1)) ;; Style 2: lines with code have balanced parens ;; Old version (let ( (var1 val1) (var2 val2) ) ( (exp1) (exp2) ) ) ;; New version; deleted (var2 var1) (let ( (var1 val1) ) ( (exp1) (exp2) ) ) ;; diff --- only one line changed. 4d3 < (var2 val2)
;; Style 3: intermediate (let ((var1 val1) (var2 val2) );; Colored here since an isolated ) looks funny to me in black & white. ((exp1) (exp2) )); extra closing parens get their own line.Before I got used to programming in Lisp, I often forgot some parens with the
let
special form,
so for a while I would always add a comment after the paren closing the let
form.
(let ((var1 val1) (var2 val2) ); let ((exp1) (exp2) ))You may adopt whatever style works best for you. In any event, consistent use of white space (spaces and newlines) may help you avoid or detect mistakes involving too many or two few parenthesis.
;; Goldilocks and the three pairs. ;; too few paren pairs ☹ (let (x 7) (y 8) (+ x y) ) ;;--> error: (wrong-type-argument listp 7) ;; too many paren pairs ☹☹ (let ((x 7) (y 8) ) ( (+ x y) )) ;;--> error: (invalid-function (+ x y)) ;; Just right (let ((x 7) (y 8) ) (+ x y) )This was rather frustrating. The good news is that the correct number of paren pairs now comes to me naturally 99% of the time.
Why?
1. Practice. I became accustomed to common constructs such as let
and cond
.
2. Understanding. After some practice writing macros (e.g. until
), I now understand how macros (and special forms) which read in variable number of clauses work, and can
see the logic behind their syntax.
That being said, I still sometimes feel that all those parens visually clutter code a bit.
The following section give some tips for reducing them when using let
.
let
is very common, as it is the usual way both to create local variables and temporarily overwrite
the value of global variables.
If you just want to use one variable, and don’t mind if that (or you require that) it is initialized to nil
, you can use one fewer pair of parens.
;; Longhand style, case insensitive matching (let ((case-fold-search nil)) (string-match-p "cat" "CAT");; returns nil ) ;; Shorthand style (let (case-fold-search) (string-match-p "cat" "CAT");; returns nil )Where
case-fold-search
which controls whether search commands are case sensitive or not.
If you only need one variable and you want to initialize it with a non-nil value,
you might try let1
; a simple macro I wrote
for that situation.
;; Standard style, case sensitive matching (let ((case-fold-search t)) (string-match-p "cat" "CAT");; returns 0 (start position of the match) ) ;; Shorthand style using let1 (let1 case-fold-search t (string-match-p "cat" "CAT");; returns 0 (start position of the match) ) ;; let1 can do simple error checking as well, since it knows you want to bind *exactly* one variable to a non-nil value. (let1 case-fold-search;; Oops, forgot the value. (string-match-p "cat" "CAT") ) ;; --> pops to *Warnings* buffer with warning: Warning (emacs): let1 warning; attempting to give variable ’case-fold-search’ a nil value or atom body makes no senseNo big deal, just a couple fewer paren pairs to look at.