Clojure 动态绑定 [英] Clojure Dynamic Binding

查看:15
本文介绍了Clojure 动态绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我意识到以下是一个坏主意,原因有很多.我还意识到,鉴于我的 stackoverflow 代表为 23,因此很自然地假设我是一个学习编程的新手.但是,请幽默一下我,并专注于我们如何做到这一点"而不是你为什么要这样做/你不想这样做"方面.

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 program. However, please humor me, and focus on the "how can we do this" rather than "why do you want to do this / you don't want to do this" aspect.

我想要的:

(def dog (Dog. ...))
(def cat (Cat. ...))

(with-animal dog
  (println (str "Dog: " (speak) "
")))
(with-animal cat
  (println (str "Cat: " (speak) "
")))

输出:

Dog: woof
Cat: meow

所以基本上,我希望 with-animal 成为一个宏 s.t.speak"函数调用的所有出现都映射到我正在调用块的对象.

So basically, I want with-animal to be a macro s.t. all occurences of the "speak" function call gets mapped to the object I'm calling the block with.

特别不想写:

(let-binding [speak (fn [] "woof")] ...)
(let-binding [speak (fn [] "meow")] ...)

相反,我希望 with-animal 使 speak 函数映射到我正在调用的对象的某个方法.

Rather, I want the with-animal to make the speak function map to some method of the object I'm calling with.

在 Clojure 中有没有一种干净的方法可以做到这一点?

Is there a clean way to do this in Clojure?

谢谢!

推荐答案

动态绑定的存在是有原因的,它有很多很好的用途,所以不用担心因为试图理解它而被抨击 :-) 有一些混乱漂浮围绕着许多较旧的 Clojure 教程,这些教程早于需要将 ^:dynamic 元数据添加到您希望动态重新绑定的变量.

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))

并测试它:

(with-animal dog  
  (println (str "Dog: " (speak) "
")))
Dog: wooooof                                                   

<小时>现在是高级版本",它只使用 let 将符号 speak 引入范围,无需动态绑定.这并不是说绑定在某些方面不好,它只是更符合你不写 (let-binding [speak (fn [] "meow")] ...) 的愿望maco 的类型被称为 anaphoric(如果你喜欢这样奇特的名字):


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):

重要的部分是 speak 符号之前的 ~' ,它明确地将非限定符号引入作用域:

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) "
")))
Dog: wooooof 

nil

<小时>我希望这两个示例之间的对比有助于回答您关于从对象到作用域的绑定行为的问题.第一个示例绑定了 maco 主体的值以及从该主体调用的任何内容.第二个示例只为宏的主体引入名称.


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.

这篇关于Clojure 动态绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆