0

I am trying to use esrecurse in my ClojureScript code, I have a working JavaScript snippet that looks like this

esrecurse.visit(ast, {
  Literal: console.log
});

and I'm trying to come up with the equivalent for ClojureScript, but whenever I run it esrecurse.visit is not called

#(.visit esrecurse % (js-obj "Literal" (fn [node] node)))

where % is the AST. I am not sure what I'm doing wrong honestly, I've also tried passing the second argument in a different way, like

#(.visit esrecurse (first (vals %)) {:Literal (fn [node] node)})

without success.

I'm invoking mutate? like this

(defn mutate?
  "applies a mutation to the ASTs,
  returns the mutated, or non mutated, ASTs"
  [asts]
  (map
   #(.visit esrecurse % #js {:Literal print})
   asts))

(mutate? (reader/to-ast (reader/read "./test/example-project/lib")))

But with both this version and mine I still don't get anything, I also tried passing identity instead of print but I just got (nil) as result.

2
  • If esrecurse is available globally then I believe you need to specify it as js/esrecurse Commented Feb 23, 2017 at 19:29
  • I imported it above that code. Commented Feb 23, 2017 at 21:32

1 Answer 1

1

This works fine for me with a mocked implementation of esrecurse.visit.

(defn visit [ast]
  (.visit esrecurse ast #js {:Literal print}))

(visit #js {:type "Literal"})

;; prints #js {:type "Literal"}

And the mocked code:

var esrecurse = {
  visit(node, visitor) {
    visitor.Literal(node);
  }
}

Here's a less verbose version of the same idea.

 #(.visit esrecurse % #js {:Literal print}))

Verify that you're calling the function correctly, because your first implementation looks fine to me and works in place of either of the above functions.

At a guess, I'd say the reason you're not seeing anything happen in your second example is because Clojure's sequences are lazy—meaning your computation won't be applied until you ask for the result.

These lazy sequences (take returns one too) are only evaluated when something calls doall, dorun or doseq on them.

This happens under the hood if you call print or evaluate an expression which returns a lazy sequence in a repl.

You can try forcing the evaluation of your lazy expression with doall.

(doall
  (mutate? (reader/to-ast (reader/read "./test/example-project/lib"))))

This doesn't feel natural and that's the point. As a reasonably pure functional programming language, Clojure expects you to mostly write functions without side effects.

This is an example of where JavaScript interop can be painful, as esrecurse.visit doesn't actually return a value, making the call to map return a sequence of nil values. I would be tempted to pull the anonymous function out and redefine it to always return the AST it was passed.

(defn visit [ast]
  (.visit esrecurse ast #js {:Literal print})
  ast)

(defn mutate?
  "applies a mutation to the ASTs,
  returns the mutated, or non mutated, ASTs"
  [asts]
  (map visit asts))

(doall
  (mutate? (reader/to-ast (reader/read "./test/example-project/lib"))))

And just as a heads up, mutate? would be the convention for naming a function which returns a boolean. Maybe you meant mutate! instead, which would be the convention for a function that may perform side effects?

Sign up to request clarification or add additional context in comments.

2 Comments

Hey Dan thanks for your answer! I tried your solution but still without results, I've edited my question with more informations.
Thanks a lot for your help! In the end I was assuming I was passing a certain data structure, but esrecurse was receiving another one, hence my problem. Your answer helped a lot in isolating the error :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.