Web 何时使用变量而不是函数?

Web 何时使用变量而不是函数?,web,clojure,var,Web,Clojure,Var,我正在阅读这本书,它告诉我将处理程序(定义如下)作为Var对象而不是函数本身传递,因为函数可以动态更改(这就是wrap-reload所做的) 书上说: “请注意,我们必须从处理程序中创建一个var,以便使用此中间件 这是确保包含当前 返回处理程序函数。如果使用处理程序,则应用程序将 只看到函数的原始值,不会反映更改。” 我真的不明白这意味着什么,变量和c指针相似吗 (ns ring-app.core (:require [ring.adapter.jetty :as jetty]

我正在阅读这本书,它告诉我将处理程序(定义如下)作为Var对象而不是函数本身传递,因为函数可以动态更改(这就是wrap-reload所做的)

书上说:

“请注意,我们必须从处理程序中创建一个var,以便使用此中间件 这是确保包含当前 返回处理程序函数。如果使用处理程序,则应用程序将 只看到函数的原始值,不会反映更改。” 我真的不明白这意味着什么,变量和c指针相似吗

(ns ring-app.core
  (:require [ring.adapter.jetty :as jetty]
            [ring.util.response :as response]
            [ring.middleware.reload :refer [wrap-reload]]))

(defn handler [request]
  (response/response
   (str "<html>/<body> your IP is: " (:remote-addr request)
        "</body></html>")))

(defn wrap-nocache [handler]
  (fn [request]
    (-> request
        handler
        (assoc-in [:headers "Pragma"] "no-cache"))))

是的,Clojure中的Var类似于C指针。这方面的记录很差

假设您创建一个函数
fred
,如下所示:

(defn fred [x] (+ x 1))
这里实际上有三件事。首先,
fred
是一个符号。符号
fred
(无引号)和关键字
:fred
(由前导的
字符标记)与字符串
“fred”
(两端用双引号标记)之间存在差异。在Clojure看来,每个字符由4个字符组成;i、 e.关键字的冒号和字符串的双引号均不包含在其长度或组成中:

> (name 'fred)
"fred"
> (name :fred)
"fred"
> (name "fred")
"fred"
唯一的区别是如何解释它们。字符串表示任何类型的用户数据。关键字用于以可读的形式表示程序的控制信息(与1=左、2=右等“幻数”相反,我们只使用关键字
:left
:right

符号意味着指向事物,就像在Java或C中一样

(let [x 1
      y (+ x 1) ]
  (println y))
;=> 2
然后,
x
指向值1,
y
指向值2,我们可以看到打印的结果

(def…
表单引入了一个不可见的第三个元素,变量

(def wilma 3)
<>我们现在有3个对象要考虑。<代码>威尔玛<代码>是一个符号,它指向<代码> var >代码>,它指向值<代码> 3 <代码>。当我们的程序遇到符号<代码>威尔玛< /代码>时,发现< <代码> var var >代码> <强> > Value>强>评估< /强>以产生值3。因此,这就像C中指针的两级间接定向。由于符号变量都是“自动计算”的,因此这会自动且不可见地发生,您不必考虑变量(事实上,大多数人甚至都没有意识到不可见的中间步骤的存在)

对于上面的函数
fred
,存在类似的情况,除了var指向匿名函数
(fn[x](+x1))
,而不是值
3
,如
wilma

我们可以“短路”var的自动评估,如:

> (var wilma)
#'clj.core/wilma

其中读卡器宏
#'
(磅引号)是调用
(var…)的简写方式
特殊形式。请记住,像
var
这样的特殊形式是一个内置的编译器,类似于
if
def
,并且与常规函数不同。
var
特殊形式返回附加到符号
wilma
的var对象。clojure REPL打印
var
对象使用相同的速记,因此两个结果看起来相同

一旦我们有了
Var
对象,自动求值将被禁用:

> (println (var wilma))
#'clj.core/wilma
如果我们想要得到wilma所指向的值,我们需要使用
var get

> (var-get (var wilma))
3
> (var-get    #'wilma)
3
弗雷德也是这样:

> (var-get #'fred)
#object[clj.core$fred 0x599adf07 "clj.core$fred@599adf07"]
> (var-get (var fred))
#object[clj.core$fred 0x599adf07 "clj.core$fred@599adf07"]
其中,
#object[clj.core$fred…]
是Clojure将函数对象表示为字符串的方式

对于web服务器,它可以通过
var?
函数或其他方式判断提供的值是处理函数还是指向处理函数的var

如果键入以下内容:

(jetty/run-jetty handler)
双重自动求值将生成handler函数对象,该对象被传递到
runjetty
。如果您键入:

(jetty/run-jetty (var handler))
然后,指向处理函数对象的
Var
将被传递到
runjetty
。然后,
runjetty
必须使用
if
语句或等效语句来确定它接收到了什么,并调用
(Var get…)
如果它收到了一个
Var
而不是一个函数。因此,每次通过
(Var get…
将返回
Var
当前指向的对象。因此,
Var
就像C中的全局指针,或者Java中的全局“引用”变量

如果将函数对象传递给
运行jetty
,它会保存指向函数对象的“本地指针”,外部世界无法更改本地指针所指的内容

您可以在此处找到更多详细信息:


更新

AS <代码> OlegTheCat < /代码>指出,Culjule在Voice对象上有另一个关于指向Culjule函数的技巧。考虑一个简单的函数:

(defn add-3 [x] (+ x 3))
; `add-3` is a global symbol that points to
;     a Var object, that points to
;         a function object.

(dotest
  (let [add-3-fn  add-3           ; a local pointer to the fn object
        add-3-var (var add-3)]    ; a local pointer to the Var object
    (is= 42 (add-3 39))           ; double deref from global symbol to fn object
    (is= 42 (add-3-fn 39))        ; single deref from local  symbol to fn object
    (is= 42 (add-3-var 39)))      ; use the Var object as a function 
                                  ;   => SILENT deref to fn object
  (let [wilma-long  wilma         ; a local pointer to the long object
        wilma-var   (var wilma)]  ; a local pointer to the Var object
    (is (int? wilma-long))        ; it is a Long integer object
    (is (var? wilma-var))         ; it is a Var object

    (is= 4 (inc wilma))          ; double deref from global symbol to Long object
    (is= 4 (inc wilma-long))     ; single deref from local  symbol to Long object
    (throws? (inc wilma-var))))  ; Var object used as arg => WILL NOT deref to Long object
(defn unvar
  "When passed a clojure var-object, returns the referenced value (via deref/var-get);
  else returns arg unchanged. Idempotent to multiple calls."
  [value-or-var]
  (if (var? value-or-var)
    (deref value-or-var) ; or var-get
    value-or-var))
如果我们将一个Var对象视为一个函数,Clojure将静默地将其释放到函数对象中,然后使用提供的参数调用该函数对象。因此,我们看到
add-3
add-3-fn
add-3-Var
三者都将工作。这就是Jetty中发生的情况。它从未意识到我给了它一个Var对象而不是函数,但是Clojure神奇地修补了这个不匹配,而没有告诉你

侧栏:请注意,这只适用于我们的“码头”实际上是 Clojure包装器代码
ring.adapter.jetty
,而不是实际的。如果您试图使用 实际的Java函数而不是Clojure包装,它将失败
  (let [wilma-long  wilma         ; a local pointer to the long object
        wilma-var   (var wilma)]  ; a local pointer to the Var object
    (is (int? wilma-long))        ; it is a Long integer object
    (is (var? wilma-var))         ; it is a Var object

    (is= 4 (inc wilma))          ; double deref from global symbol to Long object
    (is= 4 (inc wilma-long))     ; single deref from local  symbol to Long object
    (throws? (inc wilma-var))))  ; Var object used as arg => WILL NOT deref to Long object
(defn unvar
  "When passed a clojure var-object, returns the referenced value (via deref/var-get);
  else returns arg unchanged. Idempotent to multiple calls."
  [value-or-var]
  (if (var? value-or-var)
    (deref value-or-var) ; or var-get
    value-or-var))
(is= 42 (+ 39 (unvar wilma))
        (+ 39 (unvar wilma-long))
        (+ 39 (unvar wilma-var)))
(ns tst.demo.core
  (:use tupelo.core tupelo.test))

(def wilma 3)
; `wilma` is a global symbol that points to
;     a Var object, that points to
;         a java.lang.Long object of value `3`

(dotest
  (is= java.lang.Long (type wilma))

  (is= 3 (var-get (var wilma)))
  (is= 3 (var-get #'wilma))

  ; `deref` and `var-get` are interchangable
  (is= 3 (deref (var wilma)))
  (is= 3 (deref #'wilma))

  ; the reader macro `@xxx` is a shortcut that translates to `(deref xxx)`
  (is= 3 @(var wilma))
  (is= 3 @#'wilma)) ; Don't do this - it's an abuse of reader macros.
> (defn your-handler [x] x)
#'your-handler

> (defn wrap-inc [f]
    (fn [x]
      (inc (f x))))
> #'wrap-inc

> (def your-app-with-var (wrap-inc #'your-handler))
#'your-app-with-var

> (def your-app-without-var (wrap-inc your-handler))
#'your-app-without-var

> (your-app-with-var 1)
2

> (your-app-without-var 1)
2

> (defn your-handler [x] 10)
#'your-handler

> (your-app-with-var 1)
11

> (your-app-without-var 1)
2
(defn f [] 10)
(defn g [] (f))
(g) ;;=> 10
(defn f [] 11)

;; -Dclojure.compiler.direct-linking=true
(g) ;;=> 10

;; -Dclojure.compiler.direct-linking=false
(g) ;;=> 11
(defn g [] (#'f))