The Author Online Book Forums are Moving

The Author Online Book Forums will soon redirect to Manning's liveBook and liveVideo. All book forum content will migrate to liveBook's discussion forum and all video forum content will migrate to liveVideo. Log in to liveBook or liveVideo with your Manning credentials to join the discussion!

Thank you for your engagement in the AoF over the years! We look forward to offering you a more enhanced forum experience.

C.G. (1) [Avatar] Offline
(def-logged-fn daily-report [the-day]
;; code to generate a report here

throws an exception, just like

(def-logged-fn printname [name]
(println "hi" name))


(let [now "2009-10-22"]
(daily-report now))

cannot be successfully executed.
amitrathore (132) [Avatar] Offline
Re: Section 7.1.3 : (def-logged-fn daily-report also throws an Exception
It's an illustration of what would happen if Clojure didn't expand names inside macros with their fully-qualified-namespaces (and if you didn't use gensym). The code isn't meant to work, and I've made that clearer in the text. Thanks for the feedback!
Somoza (7) [Avatar] Offline
Page 175. I don’t think the following statement is correct. “This doesn’t work as expected, because the value of now that the daily-report function sees isn’t “2009-10-22” but a number like 1259828075387.” In the context of (let [now “2009-10-22”] (daily-report now)), I don’t see why now will not evaluate to “2009-10-22.”

Since daily-report is a function, the arguments that are passed to the function will be evaluated, then, the function will be evaluated using the values of the arguments (consistent with #1 & #2 at the bottom of page 169). It seems that the only way that now will be an issue is if now is used in the function body (i.e. in the section that says “;; code to generate a report here” on page 175).

Great book!

Francis Avila (16) [Avatar] Offline
(Previous and this message are about the second edition; earlier messages are about the first edition.)

You are correct, the only way variable-capture will mess you up in this particular case is either if you use an argument named now, or if you reference a var or let-bound name now at definition time. The thought experiment is also not executable so this is hard to see!

Here is a better illustration. First we redefine def-logged-fn in an unsafe (i.e., variable-capturing) way that will run:

(defmacro def-logged-fn [fn-name args & body]
  `(defn ~fn-name ~args
     (let [~'now (System/currentTimeMillis)]
       (println "[" ~'now "] Call to" (str (var ~fn-name)))

Note the added unquote-quote before now: ~'now to capture the name "now", i.e. turn off the automatic namespace-qualification done by the backtick (`) that is done to prevent accidental variable capture.

Now here are the two cases where variable-capture (or variable-shadowing) is a problem.

First, an argument named now will be captured by the now the def-logged-fn macro will define:

(def-logged-fn daily-report [now]
  (str "Report for " now))
;=> #'user/daily-report
(let [now "2009-10-22"]
  (daily-report now))
[ 1459281294140 ] Call to #'user/daily-report
;=> "Report for 1459281294140" ;; Oops!

Second, a variable with the same name at definition time will be captured:

(let [now "2009-10-22"]
  (def-logged-fn daily-report [the-day]
    (str "Using `now`: " now "\nRun for " the-day "\n")))
;=> #'user/daily-report
(let [now "2009-10-22"]
  (daily-report now))
[ 1459281467318 ] Call to #'user/daily-report
;=> "Using `now`: 1459281467318\nRun for 2009-10-22\n" ;; Oops!

In both the problem is the same: the let-now binding done by the macro introduced an invisible lexical scope.