在 clojure 中使用 'binding' 的好例子有哪些?



我理解binding形式允许在clojure中可重新绑定的动态作用域。到目前为止,我看到它的唯一用途是用于I/O,例如print,其中*out*被反弹到您当时想要的任何写入器。

我想看到真正利用binding的力量的例子,而其他设施确实不起作用。就我个人而言,我只在将用户提供的对象传递给所有函数非常繁琐的情况下使用它。基本上是我试图创建辅助函数使用的上下文的情况。(类似于这种情况,什么时候应该在Clojure中使用临时重新绑定一个特殊变量的习惯用法?)更具体地说,我依赖于用户创建到*db*变量的动态绑定,以允许数据库函数知道要操作什么。当用户需要编写大量对数据库函数的嵌套调用时,这一点特别有用。通常情况下,如果我需要编写宏来简化自己的工作,我可以接受,但要求用户这样做似乎很糟糕。话虽如此,我还是尽量避免这样做。

还有什么好的"绑定"用例,我可以复制并合并到我的代码中?

我使用绑定有两个原因:

  1. 运行覆盖常量或其他符号的其他值的测试
  2. 使用"全局"资源,如数据库连接或消息代理通道
测试

我正在研究一个分布式系统,其中有几个组件通过消息交换发送消息进行通信。这些交换具有全局名称,我将其定义为:

(ns const)
(def JOB-EXCHANGE    "my.job.xchg")
(def CRUNCH-EXCHANGE "my.crunch.xchg")
;; ... more constants

这些常量在许多地方使用,以便将消息发送到正确的位置。为了测试我的代码,我的测试套件的一部分运行使用实际消息交换的代码。但是,我不希望我的测试干扰实际的系统。

为了解决这个问题,我将测试代码封装在覆盖这些常量的binding调用中:
;; in my testing code:
(binding [const/CRUNCH-EXCHANGE (str const/CRUNCH-EXCHANGE (gensym "-TEST-"))
          const/CRUNCH-TASK-QUEUE (str const/CRUNCH-TASK-QUEUE (gensym "-TEST-"))]
  ;; tests here
)

在这个binding函数中,我可以调用任何使用常量的代码,它将使用覆盖的值。

使用全局资源

我使用绑定的另一种方式是在特定范围内"固定"全局或单例资源的值。以下是我编写的RabbitMQ库的一个示例,我将RabbitMQ Connection的值绑定到符号*amqp-connection*,以便我的代码可以使用它:

(with-connection (make-connection opts)
  ;; code that uses a RabbitMQ connection
)

with-connection的实现非常简单:

(def ^{:dynamic true} *amqp-connection* nil)
(defmacro with-connection
  "Binds connection to a value you can retrieve
   with (current-connection) within body."
  [conn & body]
  `(binding [*amqp-connection* ~conn]
     ~@body))

我的RabbitMQ库中的任何代码都可以使用*amqp-connection*中的连接,并假设它是有效的,打开Connection。或者使用(current-connection)函数,当你忘记在with-connection中包装你的RabbitMQ调用时,它会抛出一个描述性异常:

(defn current-connection
  "If used within (with-connection conn ...),
   returns the currently bound connection."
  []
  (if (current-connection?)
    *amqp-connection*
    (throw (RuntimeException.
      "No current connection. Use (with-connection conn ...) to bind a connection."))))

在VimClojure后端中,您可能在同一个JVM中运行多个repls。但是,由于Vim和后端之间的连接不是连续的,因此您可能会为每个命令获得一个新线程。因此,您不能轻易地在命令之间保留状态。

VimClojure的作用如下。它设置了一个binding,其中包含所有有趣的变量,如*warn-on-reflection**1*2等。然后它执行命令,然后将binding中可能更改的var存储在某些bookeeping基础结构中。

所以每个命令都说"我属于repl 4711",它会看到所述repl的状态。不影响repl 0815的状态

绑定函数在测试代码中非常有用。这是将函数存储在变量中的一大优点(Clojure默认就是这样做的)。

摘自我编写的一个密码程序。

(defmacro with-fake-prng [ & exprs ]
  "replaces the prng with one that produces consisten results"
  `(binding [com.cryptovide.split/get-prng (fn [] (cycle [1 2 3]))]
     ~@exprs))

如何对键生成器函数进行单元测试?它应该是不可预测的。您可以在任何地方执行(if testing ...)或使用某种mock框架。或者,您可以使用一个"动态模拟"随机数生成器的宏,并将这个仅放在测试代码中,从而使您的生产端免受影响。

(deftest test-key-gen 
   (with-fake-prng 
         ....))

相关内容

  • 没有找到相关文章

最新更新