Trouble with advanced macros

75 views
Skip to first unread message

Nathan Rogers

unread,
Mar 28, 2019, 11:58:25 PM3/28/19
to Clojure
(def dict                                                      
 
{:key `(str "obj isn't defined in this scope" (:blah ~'obj))})    
 
(defmacro my-macro [obj & args]                      
  `
(print ~(:key dict) ~@args))


(macroexpand '(my-macro {:blah "thingy"} "test string"))  



I have a need to inline arbitrary code, but that code needs to know about some things in the context of the macro. In the minimal example, you can see that I need to get the key :blah from some symbol obj, in this context one :key might be one set of code, another :key in a list of dicts might have a completely different bit of code. I want to avoid using functions here, because despite the simple appearance, calling functions will bee horrible and messy. Is there any way to accomplish this? 

The above example appears to give me what I need, but obj isn't in scope. I've tried something like the following, but it still isn't correct

(def dict                                                      
 
{:key `(str "obj isn't defined in this scope" (:blah ~'obj))})  

(defmacro my-macro [my-obj & args]                      
  `
(let [obj my-obj]
     
(print ~(:key dict) ~@args))

(macroexpand '(my-macro {:blah "thingy"} "test string"))  


If it can be done, how. If not, please let me know your thoughts on how to best accomplish what I need. 

Nathan Rogers

unread,
Mar 29, 2019, 2:30:08 AM3/29/19
to Clojure
 (def dict                                                                
   
{:key `(str "obj isn't defined in this scope" (:blah ~'obj))})        
                                                                         
 (defmacro my-macro [obj & args]                                          
   `(let [o# ~obj
          a
# ~args]
     
(apply (fn [obj args] (print ~(:key dict) args)) o# a#)))  


This approach looks even better and still doesn't work :(

peter...@gmail.com

unread,
Mar 29, 2019, 4:54:15 AM3/29/19
to Clojure
How about:
(def dict                                                      
 {:key `(str "obj isn't defined in this scope" (:blah ~'obj))})    
(defmacro my-macro [inobj & args]                      
 `(let [~'obj ~inobj]
   (print ~(:key dict) ~@args)))


(macroexpand '(my-macro {:blah "thingy"} "test string"))


I think this might have 'macro hygiene' issues though.

Alexander Yakushev

unread,
Mar 29, 2019, 5:25:50 AM3/29/19
to Clojure
Looks like you are missing a few unquotes. Is this what you expected?

(def dict
  {:key `(str "obj isn't defined in this scope" (:blah ~'obj))})

(defmacro my-macro [my-obj & args]
  `(let [~'obj ~my-obj]
     (print ~(:key dict) ~@args)))

(my-macro {:blah "thingy"} "test string")
;; obj isn't defined in this scopethingy test string

Nathan Rogers

unread,
Mar 29, 2019, 4:03:24 PM3/29/19
to clo...@googlegroups.com
Thanks everyone. That's what I needed. 


--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Nathan Rogers

unread,
Mar 30, 2019, 12:26:16 PM3/30/19
to Clojure
Ok, so it turns out I have other issues. Args is (:keys dict) and that doesn't evaluate

 (def dict                                                                            
   {:key `(str "obj isn't defined in this scope" (:blah ~'obj))}) 
                                                                                      
 (defmacro encode [my-obj body]                                                        
   `(let [~'obj ~my-obj]                                                              
      ~body))                       

(macroexpand '(encode {:blah "thingy"} (:key dict)))  =>    (let* [code (first json)] (:key dict))       

(encode {:blah "thingy"} (:key dict)))
(clojure.core/str "obj isn't defined in this scope" (:blah obj))



The macro doesn't have the key there, I need to pass it in. So it appears that I need to evaluate the body?what am I doing wrong here. 

Nathan Rogers

unread,
Mar 30, 2019, 12:30:59 PM3/30/19
to Clojure
 (def dict                                                                            
  {:key `(str "obj isn't defined in this scope" (:blah ~'obj))})
                                                                                                       
(defmacro encode [ncode get-key]          
  (let [body (eval get-key)]              
    `
(let [~'code ~ncode]                  
     ~body)))                        
(macroexpand '
(encode {:blah "thingy"} (:key dict)))


This looks like it does the trick, but can I do this without eval? 

Alexander Yakushev

unread,
Mar 30, 2019, 12:38:46 PM3/30/19
to clo...@googlegroups.com
Honestly, it looks to me that you are concocting something overly complicated. Are you sure that a combination of anonymous functions and dynamic variables won't suffice? Can you, in broad strokes, describe what you want to achieve?

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/exOJKNWC-a4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.

Nathan Rogers

unread,
Mar 30, 2019, 12:58:29 PM3/30/19
to Clojure
                                 
(defmacro encode [get-key]      
 
(let [body (eval get-key)]    
   
`(~@body)))                  


It turns out this is actually the macro I'm looking for, but I still don't want to use eval :(

Honestly, it looks to me that you are concocting something overly complicated. Are you sure that a combination of anonymous functions and dynamic variables won't suffice? Can you, in broad strokes, describe what you want to achieve?

Imagine I have a config of strings and ints. I'm building some strings based on the config and it can change. But now I have a list of different kinds of configs and I need to do parsing with them. 

For example:
(def config {:delimiter "_"})

;;;...elsewhere in code

(map #(s/join (:delimiter config) %)  strings)



Looks well and good, but happens when I have a list of configs and the strings don't join together the same way at all, and they all depend on different parts of state of the function in which you're joining them? 

Well, that's why I chose to do something like this


(def config1                                                                            
  {:key `(str "do something horrible complicated here" (:blah ~'state) (:key ~'other-state) (:something ~'evenmorestate)
})
                                                                                                       
(defmacro encode [ncode get-key]           
  (let [body (eval get-key)]               
    `(~@body)))       

so that I can 
(encode  (:key config1))



instead of something like this
(encode ((:key config1) args args args args args args))

because I want to inline the code to be interpreted once it's inline. 

So is there a way to do this new encode macro without eval?
(let [body (eval get-key)]...)

Alexander Yakushev

unread,
Mar 30, 2019, 2:57:26 PM3/30/19
to Clojure
No, I still don't get it. Why wouldn't this work?

(def config1
 
{:key (fn []
(str "do something horrible complicated here" (:blah state) (:key other-state) (:something evenmorestate)))})

;; To invoke:
((:key config1))
Reply all
Reply to author
Forward
0 new messages