I was wondering what is going on in the snippet below. Why isn't the function properly redefined without forcing the evaluation of the sequence?
user> (defn foo [] (map vector (range 3)))
#'user/foo
user> (defn bar [] (map #(vector %) (range 3)))
#'user/bar
user> (foo)
([0] [1] [2])
user> (bar)
([0] [1] [2])
user> (with-redefs [vector (fn [_] "what does the fox say?")] (foo))
("what does the fox say?" "what does the fox say?" "what does the fox say?")
user> (with-redefs [vector (fn [_] "what does the fox say?")] (bar))
([0] [1] [2])
user> (with-redefs [vector (fn [_] "what does the fox say?")] (vec (bar)))
["what does the fox say?" "what does the fox say?" "what does the fox say?"]
user>
Thanks!
The difference is that when you call foo
, vector
, as an argument to map
, is evaluated (which in this case means resolving it to a function object) once and doesn't need to be resolved again. That same function object is used even after your code has exited with-redefs
.
In bar
, however, it isn't vector
that's an argument to map
, but instead an anonymous function which references vector
by name. The result is that while the anonymous function is only evaluated once, vector
will be resolved every time the anonymous function is invoked. Because map
is lazy, that's happening after the code has already exited with-redefs
(except for when your force evaluation).
The key point is that in a function call - like (map vector (range 3))
- each of argument is evaluated and the calling function gets the result of those evaluations. That means the map
call in foo
gets the redefined vector
, whereas the map
call in bar
gets a function which will still need to look up vector
by name when it's invoked.
The Clojure.org page on evaluation provides some details on how symbols are resolved to objects. This is also an example of late binding .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.