关于clojure:在Clojurescript中使用-?>

using -?> in Clojurescript?

在我的Clojure共享源中,我有以下内容(我无耻地偷走了):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
(defmacro hey-str [name]
  `(str"hey" ~name))

{:author"Laurent Petit (and others)"
  :doc"Functions/macros variants of the ones that can be found in clojure.core
            (note to other contrib members: feel free to add to this lib)"}

(defmacro- defnilsafe [docstring non-safe-name nil-safe-name]
  `(defmacro ~nil-safe-name ~docstring
     {:arglists '([~'x ~'form] [~'x ~'form ~'& ~'forms])}
       ([x# form#]
         `(let [~'i# ~x#] (when-not (nil? ~'i#) (~'~non-safe-name ~'i# ~form#))))
     ([x# form# & more#]
         `(~'~nil-safe-name (~'~nil-safe-name ~x# ~form#) ~@more#))))

(defnilsafe
 "Same as clojure.core/-> but returns nil as soon as the threaded value is nil itself (thus short-circuiting any pending computation).
   Examples :
   (-?> "foo" .toUpperCase (.substring 1)) returns "OO"
   (-?> nil .toUpperCase (.substring 1)) returns nil
  "
  -> -?>)

(defnilsafe
 "Same as clojure.core/.. but returns nil as soon as the threaded value is nil itself (thus short-circuiting any pending computation).
   Examples :
   (.?. "foo" .toUpperCase (.substring 1)) returns "OO"
   (.?. nil .toUpperCase (.substring 1)) returns nil
  "
   .. .?.)

(defnilsafe
 "Same as clojure.core/->> but returns nil as soon as the threaded value is nil itself (thus short-circuiting any pending computation).
   Examples :
   (-?>> (range 5) (map inc)) returns (1 2 3 4 5)
   (-?>> [] seq (map inc)) returns nil
  "
  ->> -?>>)

在我的Clojurescript代码中,我有以下内容(I:require-macros as c)

1
2
3
4
5
6
7
8
(def a nil)
(def b [])
(def c [{:a 23}])

(js/alert (c/hey-str"Stephen")) ;; should display"hey Stephen"
(js/alert (c/-?> a first :a)) ;; should display nil
(js/alert (c/-?> b first :a)) ;; should display nil
(js/alert (c/-?> c first :a)) ;; should display 23

不幸的是,当我编译时,我得到:

1
2
3
WARNING: Use of undeclared Var webstack.client/-?> at line 56 cljs-src/webstack/client.cljs
WARNING: Use of undeclared Var webstack.client/-?> at line 57 cljs-src/webstack/client.cljs
WARNING: Use of undeclared Var webstack.client/-?> at line 58 cljs-src/webstack/client.cljs

当我在浏览器中打开javascript时,出现"嘿斯蒂芬"警报对话框,但是在"嘿,斯蒂芬"警报。果然,查看生成的javascript代码,我的js /警报变成了:

1
2
3
4
alert([cljs.core.str("hey"), cljs.core.str("Stephen")].join(""));
alert(webstack.client.__QMARK__GT_.call(null, webstack.client.__QMARK__GT_.call(null, webstack.client.a, cljs.core.first),"\\ufdd0'a"));
alert(webstack.client.__QMARK__GT_.call(null, webstack.client.__QMARK__GT_.call(null, webstack.client.b, cljs.core.first),"\\ufdd0'a"));
alert(webstack.client.__QMARK__GT_.call(null, webstack.client.__QMARK__GT_.call(null, webstack.client.c, cljs.core.first),"\\ufdd0'a"))

很明显,我可以使用宏,但是有关-?>(及相关)宏的编写方式的某些信息会导致它们无法编译。为了使用这些-?> .?.和`-?>>'宏,我需要做什么?


-?>使用clojure 1.5.1和cljsbuild 0.3.0对我有效,但前提是我使用:use-macros而不是:require-macros。当我使用:require-macros时,clojurescript尝试将宏解析为本地名称空间中的var,这是不正确的。我认为您在clojurescript中发现了一个错误,为什么不报告呢?

1
2
3
4
5
6
7
8
9
10
11
12
(ns test.test
  ;(:use-macros [test.nilsafe :only [hey-str -?>]]) ;uncomment and everything works!
  (:require-macros [test.nilsafe :as tn]))

(def a nil)
(def b [])
(def c [{:a 23}])

(.log js/console (tn/hey-str"Stephen")) ;; should display"hey Stephen"
(.log js/console (tn/-?> a first :a)) ;; should display nil
(.log js/console (tn/-?> b first :a)) ;; should display nil
(.log js/console (tn/-?> c first :a))

让我确定这是一个错误的事情是,即使我没有从变量中删除tn /范围,取消注释:use-macros也会导致文件正确编译。


-> marco很简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(defmacro ->
 "Threads the expr through the forms. Inserts x as the
  second item in the first form, making a list of it if it is not a
  list already. If there are more forms, inserts the first form as the
  second item in second form, etc."
  {:added"1.0"}
  [x & forms]
  (loop [x x, forms forms]
    (if forms
      (let [form (first forms)
            threaded (if (seq? form)
                       (with-meta `(~(first form) ~x ~@(next form)) (meta form))
                       (list form x))]
        (recur threaded (next forms)))
      x)))

编写一个零安全的代码应该很简单(下面的示例未经测试),但是您可以理解这个概念。

1
2
3
4
5
6
7
8
9
10
(defmacro -?>
  [x & forms]
  (loop [x x, forms forms]
    (if (and forms (some? x)) ; check nil to exit eariler
      (let [form (first forms)
            threaded (if (seq? form)
                       (with-meta `(~(first form) ~x ~@(next form)) (meta form))
                       (list form x))]
        (recur threaded (next forms)))
      x)))

可能正在创建函数而不是从宏中创建另一个宏可以帮助您解决此问题:

1
2
3
4
5
6
(defmacro defnilsafe [docstring non-safe-name nil-safe-name]
  `(defn ~nil-safe-name ~docstring
     ([x# form#]
         (let [i# x#] (when-not (nil? i#) (~non-safe-name i# form#))))
     ([x# form# & more#]
         (apply ~nil-safe-name (~nil-safe-name x# form#) more#))))