Amogh Talpallikar
Amogh Talpallikar

Reputation: 12184

How to make this macro work as expected?

 (defmacro switch [choices choice] '(do (choices choice)))
 (macroexpand-1 '(switch {1 (print 1) 2 (print 2)} (+ 1 1)))

 gives: (do (choices choice))

Just to learn macros, I wanted to emulate switch case where I will give a dictionary having case as keys and code to execute as values.

I wanted (+ 1 1) to get evaluated as 2 and then be used as key to get codde to execute with do.

However the expanded macro gives us code which doesn't resolve choices and choice both.

I tried unquoting choice and choices, doesn't work. What am I missing here ?

Upvotes: 1

Views: 76

Answers (1)

Michał Marczyk
Michał Marczyk

Reputation: 84341

Unquoting does work, but you need to switch from the regular quote to syntax-quote (backtick) for your quoting:

(defmacro switch [choices choice]
  `(do (~choices ~choice)))

Note that the do here is unnecessary.

Note that with this version of switch, both print calls in your example will be evaluated, because they will simply appear as value expressions in a map literal in switch's output. To print conditionally, you'll have to use some conditional construct (if, case or something which expands to one of them). I'll also point out that the choices argument must be a literal map, or else the macro won't be able to get at the keys and values (and in any case the values would have been evaluated by the outer context).

Here's a version written with the above in mind:

(defmacro switch [choices choice]
  `(case ~choice ~@(apply concat choices)))

Example from the REPL:

user=> (switch {1 (print 1) 2 (print 2)} (+ 1 1))
2nil

(2 is printed by the print call, nil is the return value.)

Upvotes: 3

Related Questions