为什么我可以设置!内置动态(?)Clojure vars?

时间:2016-08-10 15:13:05

标签: clojure dynamic-variables

为什么我可以这样做:

> (set! *unchecked-math* true)
true
> (set! *warn-on-reflection* false)
false

但不能这样做:

> (def ^:dynamic *x*)
#'user/*x*
> (set! *x* 1) ;; no luck, exception!

3 个答案:

答案 0 :(得分:5)

内置动态是否可能被运行时隐式包装在绑定形式中?因为这有效,例如:

user=> (def ^:dynamic *x*)
user=> (binding [*x* false] (set! *x* true))
true
user=> 

有一点需要注意的是,文档明确表示尝试通过set!修改根绑定是错误的,请参阅:

http://clojure.org/reference/vars

也可能会异常处理内置函数,例如,如果您查看x的元数据:

user=> (meta #'*x*)
{:dynamic true, :line 1, :column 1, :file "/private/var/folders/8j/ckhdsww161xdwy3cfddjd01d25k_1q/T/form-init5379741350621280680.clj", :name *x*, :ns #object[clojure.lang.Namespace 0x6b8f00 "user"]}

它标记为动态,而*warn-on-reflection*未标记为动态,但仍然以绑定形式运行:

user=> (meta #'*warn-on-reflection*)
{:added "1.0", :ns #object[clojure.lang.Namespace 0x377fc927 "clojure.core"], :name *warn-on-reflection*, :doc "When set to true, the compiler will emit warnings when reflection is\n  needed to resolve Java method calls or field accesses.\n\n  Defaults to false."}
user=> (binding [*warn-on-reflection* true] (set! *warn-on-reflection* false))
false
user=>

据推测,这是为了向后兼容,因为在早期版本的具有耳罩(每侧的星星)的clojure变量中,按惯例是动态的。但无论如何,这只是表明内置物的处理方式略有不同。

现在,我决定更进一步,并grep clojure的源代码,寻找warn-on-reflection,这导致我到常数WARN_ON_REFLECTION,这导致我RT.java中的代码行:

https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java#L467

Var.pushThreadBindings(
        RT.mapUniqueKeys(CURRENT_NS, CURRENT_NS.deref(),
               WARN_ON_REFLECTION, WARN_ON_REFLECTION.deref()
                ,RT.UNCHECKED_MATH, RT.UNCHECKED_MATH.deref()));

这让我相信我最初的假设是正确的,某些特殊的全局变量被隐式包装在线程局部绑定中。

编辑:

如评论中所述,您可以使用clojure.core/push-thread-bindings,但请务必遵循文档的建议,并在finally块中使用pop-thread-bindings包装try / catch / finally。此时您将重新实现binding(例如,在repl处运行(source binding)),这可能是文档明确警告push-thread-bindings是低级函数的原因,{ {1}}应该是首选。

答案 1 :(得分:4)

仔细阅读文档,您将找到

user=> (doc thread-bound?)
-------------------------
clojure.core/thread-bound?
([& vars])
  Returns true if all of the vars provided 
  as arguments have thread-local bindings.
  Implies that set!'ing the provided vars will succeed.  
  Returns true if no vars are provided.

特别是:

  

意味着设置!提供的变量将成功

因此,这意味着您可以按如下方式检查set!是否可行:

user=> (thread-bound? #'*x*)
false
user=> (thread-bound? #'*unchecked-math*)
true     

这意味着您只能set!线程绑定的变量,而*x*尚不存在。

PS:在Kevins回答中,您会看到Var.pushThreadBindings,如果您不想深入挖掘,那么clojure.core/push-thread-bindings实际上可以提供let key = "CrawlerDistance" let request = NSFetchRequest<CrawlerOne>(entityName: "CrawlerOne") request.returnsObjectsAsFaults = false; let resultPredicate = Predicate(format: "formUUID = %@" , key) var compound = CompoundPredicate.init(andPredicateWithSubpredicates: [resultPredicate]) var results:NSArray = try context.executeFetchRequest(request, error:nil)

答案 2 :(得分:1)

根据reference on Vars,您只能在线程绑定的变量上使用set!赋值:

  

目前,尝试设置var的根绑定是错误的   使用set!,即var赋值是线程局部的。在所有情况下   返回expr的值。

但是,像*warn-on-reflection*这样的内置动态变量具有线程局部绑定,因此您可以在它们上自由使用set!

(thread-bound? #'*unchecked-math*)
=> true

(def ^:dynamic *x*)仅创建根绑定:

(thread-bound? #'*x*)
=> false

binding宏为动态Var创建一个新范围;在低级别,它临时'pushes'将给定Vars的值绑定到当前线程,然后在退出宏的主体时'pops'它们。

(binding [*x* 1]
  (thread-bound? #'*x*))
=> true

(binding [*x* 1]
  (set! *x* 2))
=> 2