I realize the following is a bad idea for many reasons. I also realize that given I have a stackoverflow rep of 23, it\'s nature to assume that I\'m a newb learning to progr
Dynamic binding exists for a reason and it has lots of great uses, so no worries about being flamed for seeking to understand it :-) There is some confusion floating around many older Clojure tutorials that pre-date the need for adding ^:dynamic metadata to vars that you expect to dynamically rebind.
This first example uses dynamic binding by rebinding an existing name. This removes the need for the macro to introduce a new symbol:
(def dog {:sound #(str "wooooof")})
(def cat {:sound #(str "mewwww")})
define the function we will be rebinding to be dynamic (which allows rebinding)
(defn :^dynamic speak [] (println "eh?"))
write a basic template macro to bind speak to the function in the animal:
(defmacro with-animal [animal & body]
`(binding [speak (:sound ~animal)]
~@body))
and test it:
(with-animal dog
(println (str "Dog: " (speak) "\n")))
Dog: wooooof
speak into the scope using a let with no need for dynamic binding. This is not to say that binding is bad in some way, it just more closely fits your desire to not write (let-binding [speak (fn [] "meow")] ...) This type of maco is called anaphoric (if you're into such fancy names):
the important part is the ~' before the speak symbol that explicitly introduces an un-qualified symbol into the scope:
user> (defmacro with-animal [animal & body]
`(let [~'speak (:sound ~animal)]
~@body))
#'user/with-animal
user> (with-animal dog
(println (str "Dog: " (speak) "\n")))
Dog: wooooof
nil
If you really want to make animal types talk idiomatically, use Clojure Protocols:
(defprotocol ISpeak
(speak [animal] "make the type say it's thing"))
(deftype Dog []
ISpeak
(speak [this] "Woof!"))
(deftype Cat []
ISpeak
(speak [_] "Meow!!")) ;;you can "drop" the item if not used using _
(def a-dog (Dog.))
(speak a-dog)
;;=>"Woof!"
(def a-cat (Cat.))
(speak a-cat)
;;=>"Meow!!"
Please note you can extend any type (class) with the speak method.
(extend java.util.Random
ISpeak
{:speak (fn [_] "I'm throwing dices at you!")})
(speak (java.util.Random.))
;;=>"I'm throwing dices at you!"
The syntax is a bit different for Java classes, please see the Protocols documentation for more information.