While you can't use map
with a macro, you could write a second macro to perform this function. This may, in turn, require writing a third macro, etc, which is the origin of the phrase "Macros All the Way Down" as described in Clojure for the Brave and True and other places.
A similar question was answered here by using Clojure's intern
function. Our problem is a little different than that question, since here we use intern
in two different ways:
- To create a global var like with
def
or defn
- To access the value of a global var using
var-get
To reduce typing, we will be using spy
and spy-let
from the Tupelo library. You will need the following in your project.clj
:
[tupelo "0.9.38"]
which allows us to write the following code:
(ns tst.clj.core
(:use clj.core )
(:require [tupelo.core :as t] ))
(t/refer-tupelo)
(def foo
{:able "Adelicious!"
:baker "Barbrallicious!"
:charlie "Charlizable"})
(def bar
{:able "Apple"
:baker "Berry"
:charlie "Kumquat"})
(defn generate-event-fn
[event-kw]
(spy-let [
event-str (name event-kw)
event-sym (symbol event-str)
fn-name (symbol (str "my-" event-str))
new-fn (fn fn-name [item]
(let [the-var (intern 'tst.clj.core event-sym) ; get the var the symbol 'event-sym' points to
the-map (var-get the-var) ; get the map the var is pointing to
the-str (get the-map item)] ; get the string from the map
(spyx the-var)
(spyx the-map)
(spyx the-str)))
]
(intern 'tst.clj.core fn-name new-fn) ; create a var 'fn-name' pointing to 'new-fn'
))
(newline) (println "*** generating functions ***")
(newline) (generate-event-fn :foo) ; creates and interns a function 'my-foo'
(newline) (generate-event-fn :bar) ; creates and interns a function 'my-bar'
(newline) (println "*** calling function ***")
(newline) (spyx (my-foo :able))
(newline) (spyx (my-bar :charlie))
When executed, we see these results:
*** generating functions ***
event-str => "foo"
event-sym => foo
fn-name => my-foo
new-fn => #object[tst.clj.core$generate_event_fn$fn_name__32477 0x1ebd2a1b "tst.clj.core$generate_event_fn$fn_name__32477@1ebd2a1b"]
event-str => "bar"
event-sym => bar
fn-name => my-bar
new-fn => #object[tst.clj.core$generate_event_fn$fn_name__32477 0x4824083 "tst.clj.core$generate_event_fn$fn_name__32477@4824083"]
*** calling function ***
the-var => #'tst.clj.core/foo
the-map => {:able "Adelicious!", :baker "Barbrallicious!", :charlie "Charlizable"}
the-str => "Adelicious!"
(my-foo :able) => "Adelicious!"
the-var => #'tst.clj.core/bar
the-map => {:able "Apple", :baker "Berry", :charlie "Kumquat"}
the-str => "Kumquat"
(my-bar :charlie) => "Kumquat"
So we see that we created the functions my-foo
and my-bar
which can access the global foo
and bar
maps, respectively. And we didn't need to use macros!
In Clojure, the var is somewhat of an invisible "middle-man" between a symbol like foo
or my-foo
and the values they point to (a map and a function, respectively, in this example). For more information please see the relate post:
When to use a Var instead of a function?