2

Being drawn to and new to clojure and its macros I set myself the task of writing a macro that generates all strings of length 'n' from a list of chars such as "abc". So for n=2 the output should be "aa" "ab" "ac" "ba" "bb" "bc" "ca" "cb" "cc". I started with the following function as a template: (defn mkstr [n v] (for [i v j v] (str i j))). The repetition of 'v' in the for binding and the creation of how many vars should be a function of 'n'; in this specific case: 2.

After reading about 'quote' 'unquote' etc., then following an excellent online tutorial about macros, much trial and error, and some plain luck I managed to produce the following function and macro, which give the desired output whatever the value of 'n'. The really hard part was generating the variable amount of code needed in the 'for' bindings.

(defn mkvars [n] 
"Gives a list of 'n' unique symbols"
(let [vc (repeatedly n #(gensym ))] vc)) 

(defmacro mkcoms [n syms] 
"Generates a list of possible combinations of length 'n' from a string of symbols"
`(let [vs# (mkvars ~n) sy# ~syms  
    forarg# (vec (interleave vs# (repeat ~n sy#)))]
     `(for ~forarg# (str ~@vs#))))

Now my 'real' problem or lack of understanding is that to obtain the output I must do this: (eval (mkcoms len chars)). Why does this only work by using 'eval'? True, it is usable as is, but something feels wrong about it.

0

1 Answer 1

2

Your macros is returning a quoted form, which is why it works when you pass it to eval. I'm not understanding what the purpose of the macro is over the function, so I hope this explanation is what you're after.

A macro is supposed to generate the code it represents and return it. Your macro generates a quoted form. If you remove the outer layer of back-quoting, which looks like the intention is to be part of the code doing the expansion (the macro) and not the resultant code, you do get the execution at macro-expansion-time:

(defmacro mkcoms [n syms]                                                                                                      
  "Generates a list of possible combinations of length 'n' from a string of symbols"                                           
  (let [vs     (mkvars n)                                                                                                      
        sy     syms                                                                                                            
        forarg (vec (interleave vs (repeat n sy)))]                                                                            
    `(for ~forarg (str ~@vs))))

This sounds like what you're after, though I admit I don't understand why you want this to happen at 'compile time' vs run-time.

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

1 Comment

Thank you for this answer. It has removed some misunderstanding about macro writing, but shows also how to mix regular function code with generated code and vice-versa. After further experiments I realize how time consuming auto-gensym seems to be.

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.