Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
什么时候应该在Clojure中使用临时-rebind-a-special-var习惯用法?_Clojure_Dynamic Rebinding - Fatal编程技术网

什么时候应该在Clojure中使用临时-rebind-a-special-var习惯用法?

什么时候应该在Clojure中使用临时-rebind-a-special-var习惯用法?,clojure,dynamic-rebinding,Clojure,Dynamic Rebinding,我注意到一些库,比如clojure twitter,使用特殊的变量(用于动态绑定的变量,用星号包围)进行oauth身份验证。将身份验证保存在一个var中,然后使用(与oauth myauth..)。我认为这是解决这类问题的一个非常好的解决方案,因为您可以为应用程序的每个用户重新绑定auth var 我在一个我一直在写的电子邮件客户端中也采用了类似的方法。我有一个名为session的特殊变量,我将它绑定到一个带有当前用户会话和用户信息的映射上,并且有各种重要函数使用该变量中的信息。我编写了一个宏,

我注意到一些库,比如clojure twitter,使用特殊的变量(用于动态绑定的变量,用星号包围)进行oauth身份验证。将身份验证保存在一个var中,然后使用(与oauth myauth..)。我认为这是解决这类问题的一个非常好的解决方案,因为您可以为应用程序的每个用户重新绑定auth var

我在一个我一直在写的电子邮件客户端中也采用了类似的方法。我有一个名为session的特殊变量,我将它绑定到一个带有当前用户会话和用户信息的映射上,并且有各种重要函数使用该变量中的信息。我编写了一个宏,使用session在传递给session的一组表单的上下文中临时重新绑定它。(对我来说)这是一个非常干净的解决方案


所以,我的问题是:我是不是在做这个仪式?这是一个糟糕的设计决策,还是特殊VAR的预期用途之一?

您似乎做得完全正确。事实上,有许多内置/contrib宏的工作原理类似,比如说
不带str
clojure.contrib.sql/带连接
。后者是当今Clojure基础设施中相当关键的一部分,因此它使用的任何习惯用法都受到了很多人的仔细研究


需要记住的一个重要问题是,在
绑定
/
与绑定
窗体的范围内启动的线程不会继承所讨论的变量的回弹值;相反,它们看到的是根绑定。如果要将绑定传播到工作线程/代理,可以显式地传递它们(例如,作为函数参数),或者在每次创建计划重新绑定的全局变量时使用
bound fn
,即为访问该变量的每个函数添加一个额外的隐式参数。与正确(显式)参数不同,此隐藏参数不会显示在函数的签名中,并且可能很少有迹象表明函数正在使用它。你的代码变得不那么“实用”;根据这些全局动态变量的当前状态,使用相同的参数调用相同的函数可能会导致不同的返回值

全局变量的好处是,您可以轻松地指定默认值,并且不必将该变量传递给使用它的每个函数,从而使您变得懒惰

缺点是您的代码更难阅读、测试、使用和调试。您的代码可能更容易出错;在调用使用var的函数之前,很容易忘记绑定或重新绑定var,但当它位于arglist中时,忘记传入
session
参数就不那么容易了

因此,最终会出现神秘的bug,以及函数之间奇怪的隐式依赖关系。考虑这种情况:

user> (defn foo [] (when-not (:logged-in *session*) (throw (Exception. "Access denied!"))))
#'user/foo
user> (defn bar [] (foo))
#'user/bar
user> (defn quux [] (bar))
#'user/quux
user> (quux)
; Evaluation aborted.  ;; Access denied!
qux
的行为隐式地取决于会话是否具有值,但除非深入研究每个函数
qux
调用以及这些函数调用的每个函数,否则您不会知道这一点。想象一个10或20层的调用链,底部有一个函数,具体取决于
*会话*
。祝你调试的愉快

如果你有
(defn foo[session]…)
(defn bar[session]…)
(defn qux[session]…)
,你会立刻明白,如果你调用
qux
,你最好准备好一个会话

就我个人而言,我会使用显式参数,除非我有一个强大的、健全的默认值,很多函数都使用这个值,我计划很少或永远不会重新绑定它。(例如,将STDOUT作为显式参数传递给每个想要打印任何内容的函数是愚蠢的。)

绑定函数非常适合测试代码

我在测试代码中广泛使用重新绑定包装器函数来模拟随机数生成器、使用固定块大小等。。。所以我可以根据已知的输出测试加密函数<代码>

(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]))
             com.cryptovide.modmath/mody 719
             com.cryptovide.modmath/field-size 10]
        ~@exprs))

(is (= (with-fake-prng (encrypt-string "asdf")) [23 54 13 63]))  
在使用绑定时,请记住它们仅为当前线程重新绑定,因此当您在使用线程池的pmap中触发某些内容时,您可能会丢失绑定。如果您有一些并行构建字符串的代码,如下所示:

(with-out-str
    (pmap process-data input))
在映射前使用该无辜循环\p将导致绑定消失,因为它将在线程池中的多个线程中运行process data函数


编辑:MichałMarczyk指出了绑定的fn宏,您可以使用它来防止在使用线程时丢失绑定

谢谢你的回答。信息量很大。我会记住的。:)当然,不客气,尽管我看到布赖恩过来为你挑起事端。。。事实上,我又回来发布了一个链接,链接到我突然想起的这个讨论:——这个帖子的标题是“戒指的会话”。当然,Ring在服务器端工作,但我认为那里的讨论可能仍然对您有所帮助。人们的观点似乎在将会话数据存储在原子中(每个会话一个)和功能纯度之间存在分歧。。。双方都有很好的论点,你可以选择。哦,还有,即使是带有原子的版本也会将它们放在一个显式传递的会话中,但是环会话与环的大部分代码相关,并且可能会有大量的环会话一直被传递。另一方面,使用电子邮件客户端,您很可能能够将所有相关信息放在一个数据结构中(可能包含多个服务器的登录凭据等,但所有这些服务器都属于同一个人),而该数据结构将始终存在。在我看来,这使它与网络应用程序有些不同。。。不过,YMMV。嗯,我想这意味着我将重新设计我的整个应用程序。当我得到两个非常自信但完全不相容的答案时,我非常喜欢谢谢你的回答,我去过那里。