在Clojurescript中如何使用"->"?

4
在我的Clojure共享源代码中,我有以下内容(这是我无耻地偷来的):
(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代码中,我有以下内容(我:需要宏,如c)
(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

很不幸,当我编译时,出现了以下错误:
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时,弹出“嘿,Stephen”的对话框,但是在按下“嘿,Stephen”对话框上的“确定”后立即出现了普遍存在的“Uncaught TypeError: Cannot call method 'call' of undefined”错误。确实,在查看生成的JavaScript代码时,我的js / alert变成了:
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"))

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


不要创建一个宏(从defnilsafe宏)来实现nil-safe-name,你能尝试创建一个函数吗? - Ankur
1
@Ankur 不,它必须是一个宏,因为表达式需要在编译/宏展开时进行排列以便于评估。 - rplevy
@rplevy:我的意思是宏正在创建另一个宏,而不是宏可以创建一个函数。 - Ankur
1
@Ankur 那是一个修辞问题。正确的答案是,你做不到。 - Cubic
这里是来自未来的一条注释,现在这个 some-> 宏已经存在了,所以不需要重新实现。 - porglezomp
显示剩余2条评论
3个回答

2

-?> 在使用clojure 1.5.1和cljsbuild 0.3.0时对我有效,但只有当我使用 :use-macros 而不是 :require-macros 时它才有效。当我使用 :require-macros 时,clojurescript 尝试将宏解析为本地命名空间中的变量,这是不正确的。我认为你已经发现了clojurescript中的一个bug,为什么不报告一下呢?

(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))

我确信这是一个 bug 的原因是,即使我不从变量中删除 tn/ 范围,取消注释 :use-macros 将使文件正确编译。


0

->宏是简单的。

(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)))

编写一个 nil 安全的函数应该很简单(下面的示例没有经过测试),但你应该能理解概念。

(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

也许从宏中创建函数而不是另一个宏可以帮助您解决这个问题:

(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#))))

1
尝试使用(-?> nil (println "hello world"))。这并不能像原始宏一样完成同样的事情。这又怎么可能呢? - Cubic
@Cubic,你是正确的,它不能用于表达式,但可以用于函数,例如:(-?> nil println) - Ankur
但是这不是 -> 宏的作用,因此也不应该是 -?> 宏的作用。 - Cubic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接