Lisp syntax considered insufficiently redundant

Kragen Sitaker kragen@pobox.com
Sat, 13 Apr 2002 03:24:36 -0400 (EDT)


I wrote this code in Elisp:

(defun isletter (char)
  (let (dcchar (downcase char))
    (and (>= dcchar ?a) (<= dcchar ?z))))

I needed a function that would tell me whether a character was a
letter or not.  There's probably a built-in elisp function that does
the same thing, but I don't know what it's called, and C-h a alpha RET
and C-h a letter RET didn't help.

The function is completely wrong in a way that was not obvious to me.
I think it should be obvious to experienced Lisp hackers, and I'd be
interested to hear from people who spotted it immediately.

When I tried to run it, I was getting this error (with 
(setq debug-on-error t)):

Signaling: (wrong-type-argument number-char-or-marker-p nil)
  >=(nil ?a)
  (and (>= dcchar ?a) (<= dcchar ?z))
)
  (let (dcchar (downcase char)) (and (>= dcchar ?a) (<= dcchar ?z)))
)
  isletter(?t)

Which meant I was comparing nil to the character a, which is illegal.
I couldn't figure out how dcchar got the value nil, though.

I stared at this for a minute or two and tried (downcase ?t) a few
times before I figured out what was wrong with the code.

The 'let' expression above is equivalent to 

(let ((dcchar nil) (downcase char))
	(....))

which wasn't what I meant at all.  I'd left out an extra set of
parentheses.

Lisp's syntax has very little redundancy in it, and constructs that
look very similar can have very different meanings, depending on
context.  The list (dcchar (downcase char)) would normally mean "call
dcchar with the result of calling downcase on char"; I wanted it to
mean "call downcase on char and save the result in dcchar"; and it
actually did mean "bind dcchar to nil and bind downcase to char".

While Lisp's syntax has its advantages, this is definitely a
disadvantage.  This kind of syntactic dependency on context requires
more mental effort to figure out what any particular piece of a
program is doing, although I'm sure it gets easier with time.  (No
doubt any experienced Lisp hackers reading this saw the bug
immediately.)

-- 
<kragen@pobox.com>       Kragen Sitaker     <http://www.pobox.com/~kragen/>
> Then the object is collected. The finalizer is not run a second time.
Can you cast a spell to resoul an undead object? Do the resouled undead
differ from the living? -- Charles Fiterman on gclist@iecc.com