How to Roll Your Own Future Macro Using Promises

Hello, I’m working through the Clojure From the Ground Up tutorial. I’m working on this problem from the state lesson:

“We can do the computation in a new thread directly, using (.start (Thread. (fn [] (sum 0 1e7))) –but this simply runs the (sum) function and discards the results. Use a promise to hand the result back out of the thread. Use this technique to write your own version of the future macro.”

I think I have something really close, but I’m not sure how to return a promise at the end. This is a combination of my thoughts and what someone else posted.

user=> (defmacro my-future [& args] ( let [p# promise] ((.start (Thread. (fn [] deliver p# (do ~args)))) p#)))
#'user/my-future
user=> (def x (my-future (prn "hi") (+ 1 2)))
Exception in thread "Thread-61" java.lang.IllegalStateException: Attempting to call unbound fn: #'clojure.core/unquote
        at clojure.lang.Var$Unbound.throwArity(Var.java:45)
        at clojure.lang.AFn.invoke(AFn.java:32)
        at user$my_future$fn__2063.invoke(NO_SOURCE_FILE:1)
        at clojure.lang.AFn.run(AFn.java:22)
        at java.base/java.lang.Thread.run(Thread.java:834)
Unexpected error (NullPointerException) macroexpanding my-future at (REPL:1:8).
null

user=>

Any thoughts? Am I way off course? Thanks!

The error message says:

Attempting to call unbound fn: #'clojure.core/unquote

unquote's sugar is ~.
Usually, we unquote things after having syntax-quoted them (with a backtick), but I couldn’t see one in the above macro. If that wasn’t a copy-paste typo, I’d investigate that!

If you want this anonymous function to call deliver, you must put deliver in parens… (deliver p#...).

I think you want the macro to produce the (let... form. In other words, the value of the macro will be a list whose first member is let. So you should put a backtick before that list.

args is a collection, such as ((prn "hi") (+ 1 2)). So (do ~args) would be something like (do ((prn "hi") (+ 1 2))) – too many parentheses! There is another tool that might be helpful, [email protected]

Remember you can use macroexpand-1 to look at the program code that a macro generates.

I defintely needed the backtick, to use to use unquote-splice ([email protected]) instead of unquote (~), and also I needed to move p# outside of the scope of the thread and into the scope of let.

This is a works:
(defmacro my-future [& args] `(let [p# (promise)] (.start (Thread. (fn [] (deliver p# (do [email protected]))))) p#))

Thank you aisamu and Phill for your help!