每次您创建一个计划重新绑定的全局变量时,您都会为访问该变量的每个函数添加一个额外的隐式参数。与正确(显式)参数不同,此隐藏参数不会出现在函数的签名中,并且可能几乎没有迹象表明该函数正在使用它。你的代码变得不那么“实用”了;根据这些全局动态变量的当前状态,使用相同的参数调用相同的函数可能会导致不同的返回值。
全局变量的好处是你可以很容易地指定一个默认值,它可以让你变得懒惰,不必将这个变量传递给每个使用它的函数。
缺点是您的代码更难阅读、测试、使用和调试。而且您的代码可能更容易出错;在调用使用它的函数之前,很容易忘记绑定或重新绑定 var,但是session
当参数在 arglist 中时,忘记传递参数就不是那么容易了。
所以你最终会遇到神秘的错误,以及函数之间奇怪的隐含依赖关系。考虑这种情况:
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!
的行为quux
隐含地取决于具有值的会话,但是除非您深入研究每个函数quux
调用以及这些函数调用的每个函数,否则您不会知道这一点。想象一个 10 或 20 层深的调用链,底部有一个函数,取决于*session*
. 玩得开心调试。
相反,如果您有(defn foo [session] ...)
, (defn bar [session] ...)
, (defn quux [session] ...)
,那么您会立即明白,如果您调用quux
,则最好准备好会话。
就个人而言,我会使用显式参数,除非我有一个强大的、健全的默认值,大量函数使用,我计划很少或永远不会重新绑定。(例如,将 STDOUT 作为显式参数传递给每个想要打印任何内容的函数是很愚蠢的。)