Clojure Dynamic Binding

后端 未结 2 1385
无人及你
无人及你 2020-12-16 21:58

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

2条回答
  •  萌比男神i
    2020-12-16 22:14

    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:


    first make some animals, I just use maps in this example, many people will use some other type of Object:

    (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                                                   
    


    and now the "advanced version" which just introduces a symbol 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
    


    I hope that the contrast between these two examples serves to answer your question about binding behavior from an object into a scope. The first example binds the value for the body of the maco AND anything that is called from that body. The second example introduces the name ONLY for the body of the macro.

提交回复
热议问题