Search code examples
clojureclojurescriptlisp-macros

macros in clojurescript - does not expand properly


I have created a macro in clojure

(ns macro-question.map)

(defmacro lookup [key] (list get (apply hash-map (range 1 5)) key))

in the clojure repl it works as expected

$ clj
Clojure 1.9.0
user=> (require 'macro-question.map)
nil
user=> (macro-question.map/lookup 1)
2

So I create a clojurescript module like this to try to use it:

(ns macro-question.core (:require-macros [macro-question.map]))

(defn lookup1 [] (macro-question.map/lookup 1))

and when I try that in the repl, I get this

$ clj -m cljs.main  --repl-env node
ClojureScript 1.10.520
cljs.user=> (require 'macro-question.core)
Execution error (ExceptionInfo) at cljs.compiler/fn (compiler.cljc:304).
failed compiling constant: clojure.core$get@3df1a1ac; clojure.core$get is not a valid ClojureScript constant.

meanwhile back in clojure, there is a clue why this might be

user=> (macroexpand '(macro-question.map/lookup 1))
(#object[clojure.core$get 0x101639ae "clojure.core$get@101639ae"] {1 2, 3 4} 1)

I can create macros that start with '( instead of (list. However, I want the map to be expanded at build time.

What is going on? And what do I have to do to get something like the following:

user=> (macroexpand '(macro-question.map/lookup 1))
(get {1 2, 3 4} 1)

or what should I be doing to use this macro in clojurescript?


Solution

  • (list get (apply hash-map (range 1 5)) key)

    Creates a list where the first position is the function object that the var get refers to. What you actually want to return is a list with the fully qualified symbol for get as the first position. Change your macro definition to

    (defmacro lookup [key] 
      `(get ~(apply hash-map (range 1 5)) ~key))
    
    (macroexpand '(lookup 1))
    => (clojure.core/get {1 2, 3 4} 1)
    

    The reference guide for the reader is helpful here https://clojure.org/reference/reader