Question

I'm reading The Joy of Clojure, and in the section about paralellization, the functions pvalues, pmap and pcalls are explained, with a brief example of how each one is used. Below are the examples given for pvalues and pcalls:

(defn sleeper [s thing] (Thread/sleep (* 1000 s)) thing)

(pvalues
  (sleeper 2 :1st)
  (sleeper 3 :2nd)
  (keyword "3rd"))

(pcalls
  #(sleeper 2 :1st)
  #(sleeper 3 :2nd)
  #(keyword "3rd"))

I understand the technical difference between the two -- pvalues takes a variable number of "values" to be computed, whereas pcalls takes "an arbitrary number of functions taking no arguments," to be called in parallel. In both cases, a lazy sequence of the results is returned.

My question is essentially, when would you use one vs. the other? It seems like the only semantic difference is that you stick a # before each argument to pcalls, turning it into an anonymous function. So, purely from the perspective of saving keystrokes and having simpler code, wouldn't it make more sense to just use pvalues? -- and if so, why even have a pcalls function?

I get that with pcalls, you can substitute a symbol referring to a function, but if you wanted to use such a function with pvalues, you could just put the function in parentheses so that it is called.

I'm just a little confused as to why Clojure has these 2 functions that are so similar. Is there anything that you can do with one, but not the other? Some contrived examples might be helpful.

Was it helpful?

Solution

pvalues is a convienience macro around pcalls, though because macros are not first class it can't be composed or passed around in the same way a function can. Here is an example where only pcalls works:

user> (defn sleeper [s thing] (Thread/sleep (* 1000 s)) thing)
#'user/sleeper

user> (def actions [#(sleeper 2 :1st) #(sleeper 3 :2nd) #(keyword "3rd")])
#'user/actions

user> (apply pcalls actions)
(:1st :2nd :3rd)

user> (apply pvalues actions)
CompilerException java.lang.RuntimeException: Can't take value of a macro: #'clojure.core/pvalues, compiling:(NO_SOURCE_PATH:1:1)

It's important when designing APIs in clojure that you don't restrict the users to only interacting with your library through macros because then they get caught on limitations such as this.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top