It's well known to Lispers that Common Lisp's library is remarkably complete in some places, and remarkably incomplete in others. I ran into both cases yesterday. I needed to see what was behind some whitespace, so I did the obvious thing:
(defun skip-whitespace (stream &optional eof-error-p) "Skip leading whitespace on STREAM, and return the first non-whitespace character without removing it." (let ((c (peek-char stream eof-error-p))) (cond ((whitespacep c) (read-char stream eof-error-p) (skip-whitespace stream eof-error-p)) (t c))))
There are two problems with this code. The first is that
whitespacep is missing. Did I get the name wrong? No, it simply doesn't exist. Common Lisp has
lower-case-p, and even
both-case-p (which is not quite what it sounds like), but nothing for whitespace. And you can't even easily assemble it out of the available predicates.
Oh well, it's easy to fake.
(defun whitespacep (c) (member c '(#\ #\Tab #\Return #\Newline)))
The second problem was a mysterious type error:
peek-char didn't like
*standard-input*; it wanted a character or a boolean instead. Huh?
If I had bothered to read the Hyperspec (or even look closely at the argument list Slime displayed), I would have known that
peek-char's first argument isn't the stream, it's the whitespace setting. The function I wanted is already built in to the language:
(peek-char t stream) skips whitespace and returns the first non-whitespace character. Obviously I'm not the first person to want this.
peek-char may not be the most obvious place to put this feature, but this is one of those places where Common Lisp is almost absurdly complete. It may not be able to identify whitespace characters, but it knows what to do with them.
Edit: fixed an uninteresting third problem: I forgot to pass