使用“绑定”的好例子有哪些?在 Clojure 中?
据我了解,绑定
形式允许在 clojure 中重新绑定动态作用域。到目前为止,我看到它的唯一用途是 I/O,例如 print
,其中 *out*
反弹到您想要的任何写入器时间。
我希望看到真正利用绑定功能的示例,而其他工具确实无法使用。就我个人而言,我只在将用户提供的对象传递给所有函数非常乏味的情况下使用它。基本上是我试图创建辅助函数使用的上下文的情况。 (类似于这种情况 当应该在 Clojure 中使用temporary-rebind-a-special-var 习语吗?)更具体地说,我依靠用户创建一个动态绑定到*db*
var 允许数据库函数知道要操作的内容。当用户需要编写大量对数据库函数的嵌套调用时,这特别有用。通常,如果我需要编写宏来让自己的事情变得更容易,我没问题,但要求用户这样做似乎很糟糕。话虽这么说,我尽量避免这样做。
我可以复制并合并到我的代码中的“绑定”还有哪些其他好的用例?
I understand that the binding
form allows rebindable dynamic scoping in clojure. So far the only uses I've seen it used for is for I/O such as with print
where *out*
is rebound to what ever writer you would like at the time.
I would like to see examples that truly take advantage of the power of binding
where other facilities really don't work. Personally I've only used it in cases where passing around a user supplied object to all the functions was really tedious. Basically a situation where I am trying to create a context that the helper functions uses. (Similar to this case When should one use the temporarily-rebind-a-special-var idiom in Clojure? ) To be more specific, I was relying on the user to create a dynamic binding to the *db*
var to allow the database functions to know what to operate on. This was particularly useful when the user needs to write lots of nested calls to the database functions. Typically, I'm OK if I need to write macros to make things easier for myself, but to require the user to do so seems bad. That being said, I try to avoid doing so as much as possible.
What are some other good use cases for 'binding' that I can copy and incorporate into my code?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我使用绑定有两个原因:
测试
我正在使用多个组件的分布式系统通过消息交换发送消息进行通信。这些交换器具有全局名称,我对其进行了如下定义:
这些常量在许多地方使用,以将消息发送到正确的位置。为了测试我的代码,我的测试套件的一部分运行使用实际消息交换的代码。但是,我不希望我的测试干扰实际系统。
为了解决这个问题,我将测试代码包装在一个覆盖这些常量的绑定调用中:
在这个绑定函数内部,我可以调用任何使用常量的代码,并且它'将使用覆盖的值。
使用全局资源
我使用绑定的另一种方法是“修复”特定范围内的全局或单例资源的值。下面是我编写的 RabbitMQ 库的示例,其中我将 RabbitMQ
Connection
的值绑定到符号*amqp-connection*
以便我的代码可以使用它:
with-connection
的实现非常简单:我的 RabbitMQ 库中的任何代码都可以使用
*amqp-connection*
中的连接,并假设它是有效的、开放的连接
。或者使用(current-connection)
函数,当您忘记将 RabbitMQ 调用包装在with-connection
中时,该函数会抛出描述性异常:I use bindings for two reasons:
testing
I am working on a distributed system with several components that communicate by sending messages over message exchanges. These exchanges have global names, which I have defined like such:
These constants are used in a number of places to send messages to the right place. To test my code, part of my test suite runs code that uses the actual message exchanges. However, I don't want my testing to interfere with the actual system.
To solve this, I wrap my testing code in a
binding
call that overrides these constants:Inside of this
binding
function, I can call any code that uses the constants and it'll use the overridden values.using global resources
Another way I use bindings is to "fix" the value of a global or singleton resource inside a particular scope. Here's an example of a RabbitMQ library I wrote, where I bind the value of a RabbitMQ
Connection
to the symbol*amqp-connection*
so that my code can use it:The implementation of
with-connection
is quite simple:Any code in my RabbitMQ library can use the connection in
*amqp-connection*
and assume that it is a valid, openConnection
. Or use the(current-connection)
function, which throws a descriptive exception when you forgot to wrap your RabbitMQ calls in awith-connection
:在 VimClojure 后端中,您可能在同一个 JVM 中运行多个 repl。然而,由于 Vim 和后端之间的连接不是连续的,因此您可能会为每个命令获得一个新线程。因此您无法轻松保留命令之间的状态。
VimClojure 的作用如下。它为所有有趣的变量设置了一个
绑定
,例如*warn-on-reflection*
、*1
、*2
,等等。然后它执行该命令,然后将可能更改的变量从某些簿记基础设施中的绑定中存储起来。因此,每个命令都只是说“我属于 repl 4711”,并且它将看到所述 repl 的状态。不影响repl 0815的状态。
In VimClojure backend you might have several repls running in the same JVM. However since the connection between Vim and the backend is not continuous, you potentially get a new thread for each command. So you can't easily retain state between commands.
What VimClojure does, is the following. It sets up a
binding
with all interesting Vars like*warn-on-reflection*
,*1
,*2
, and so on. Then it executes the command and afterwards stores away the potentially changed Vars from thebinding
in some bookeeping infrastructure.So every command just says "I belong to repl 4711" and it will see the state of said repl. Without affecting the state of repl 0815.
绑定函数在测试代码中非常有用。这是在 var 中存储函数的巨大优势之一(就像 Clojure 默认情况下所做的那样)。
我编写的密码学程序的摘录。
如何对密钥生成器函数进行单元测试?它应该是不可预测的。您可以在任何地方使用线程
(如果测试...)
或使用某种模拟框架。或者您可以使用一个“动态模拟”随机数生成器的宏,并将此仅放在测试代码中,从而使您的生产端免受干扰。binding functions can be really helpful in test code. This is one of the great advantages of storing functions in vars (as Clojure does by default).
an excerpt from a cryptography program I wrote.
How do you unit test a key generator function? it's supposed to be unpredictable. You could thread
(if testing ...)
everywhere or use some sort of mocking framework. or you can use a macro that "dynamically mocks" the random number generator out and put this only in the test code leaving your production side free of cruft.