From: Steve Haflich

Subject: Re: eval problem

Date: 1998-6-16 22:51

   From: Red Hat Linux User <h094.staff.tekotago.ac.nz at malcolm>
   
   I am wondering if anyone can help me with the following?
   This first bit gose OK:
   user(10): (setq line "S1   bob    gft")
   "S1   bob    gft"
   user(11): (with-input-from-string (s line)
               ( read s))
   S1
   However when I try to use eval I get:
   user(12): (setq read-s '(read s))
   (read s)
   user(13):  (with-input-from-string (s line)
               ( eval read-s))
   Error: Attempt to take the value of the unbound variable `s'.
     [condition type: unbound-variable]

The ANS dictionary entry for the EVAL function says this:

  Evaluates form in the current dynamic environment and the null lexical
  environment.

You variable S is a lexical variable and therefore only visible to
code lexically inside the body of the WITH-INPUT-FROM-STRING form.

There are several things you could do to make this work, but I'm not
going to tell you what they are because ultimately they just confuse
the underlying issue: There is a article of Common Lisp wisdom that
goes approximately: "If you think you need to use EVAL, you're wrong."
Calling EVAL is almost never the right solution to any programming
problem.  Common Lisp is a (primarily) lexically-scoped and compiled
language.  If you squirrel forms away inside structures or as the
value of variables with the intention of evaluating them later, you
are going against the normal language idiom.  This will cost you in
clarity and execution efficiency.

Much clearer and more idiomatic would be use of first-class functions.
Now, it would be silly to do this with your read-s, since it is about
the same as the standard function READ, but postulate alternative
versions of READ that, say, read in other numeric radixes:

(defun parse-a-string (string reader-function)
  (with-input-from-string (s string)
    (loop with eof = (cons nil nil)
      as x = (funcall reader-function s nil eof)
      until (eq x eof)
      collect x)))

(defun read-base-16 (stream eof-error-p eof-value)
  (ignore-errors (let ((*read-base* 16)) (read stream eof-error-p eof-value))))

;; Try these test forms:

(parse-a-string "foo bar 10" #'read-base-16)

(parse-a-string "foo bar 10" (lambda (&rest args)
                               (let ((*read-base* 8))
                                 (apply #'read args))))