1
votes

Goal: I'm trying to make a macro which takes as an input something like the following:

(cb-chan (.readFile "/path/to/file" "utf8" _))

and returns as an output something like the following:

(go (let [c (chan 1)
          rep (.readFile "path/to/file" "utf8" (>? c)]  ; >? is a function that I defined elsewhere that jams the result of a callback into a channel
     rep
     (<! c))))

Notice that the _ in the original input is being replaced by special callback (defined elsewhere). This callback jams its result into a channel, which is then retrieved and returned at end of the go block.

Attempt:

(defmacro cb-chan [func]
   `(cljs.core.async.macros/go 
      (let [~'c    (cljs.core.async/chan 1)]
           ~'rep  (replace (quote {~'_ (cljs-async-patterns.core/>? ~'c) }) (quote ~func))

       ~'rep
       (~'<! ~'c))))

Result: This fails since rep is just ends up being a literal, unevaluated list. If I were able to type (eval rep) on the second to last line instead of just rep, my problem would be fixed, but I cannot since I'm working in ClojureScript (where there is no eval). How do I get around this?

1
What you want here is quite unclear. Strings don't have a .readFile method, so (.readFile "/path/to/file") can never be correct. There's no reason at all to (let [rep (foo)] rep x), instead of just writing (do (foo) x). You probably just want to write ~(replace ...) instead of (replace ...), but it's hard to be sure since your desired input and outpot don't entirely make sense.amalloy

1 Answers

1
votes

first of all, what do you need is probably a bit different. look at your desired code

(go (let [c (chan 1)
          rep (.readFile "path/to/file" "utf8" (>? c)]
      rep
      (<! c))))

do you really need to bind a var rep? what you want is probably this:

(go (let [c (chan 1)]
      (.readFile "path/to/file" "utf8" (>? c)
      (<! c))))

because there is no need for rep

however, you should consider rereading some articles about macros, because here you have a mess of random qoutes and unquotes.

the macro generating your code would look like this:

(defmacro cb-chan [func]
  (let [c (gensym "c")]
    `(cljs.core.async.macros/go 
       (let [~c (cljs.core.async/chan 1)
             rep# ~(replace {'_ `(cljs-async-patterns.core/>? ~c)} func)]
         rep#
         (cljs.core.async/<! ~c)))))

it will expand (cb-chan (.readFile "/path/to/file" "utf8" _)) to this:

(cljs.core.async.macros/go
  (let [c19307 (cljs.core.async/chan 1)
        rep__19301__auto__ (.readFile
                             "/path/to/file"
                             "utf8"
                             (cljs-async-patterns.core/>? c19307))]
    rep__19301__auto__
    (cljs.core.async/<! c19307)))

for my variant (without rep):

(defmacro cb-chan [func]
  (let [c (gensym "c")]
    `(cljs.core.async.macros/go 
       (let [~c (cljs.core.async/chan 1)]
         ~(replace {'_ `(cljs-async-patterns.core/>? ~c)} func)
         (cljs.core.async/<! ~c)))))

expands to:

(cljs.core.async.macros/go
  (let [c19313 (cljs.core.async/chan 1)]
    (.readFile
      "/path/to/file"
      "utf8"
      (cljs-async-patterns.core/>? c19313))
    (cljs.core.async/<! c19313)))