Reputation: 14544
In my Clojure shared source, I have the following (which I shamelessly stole):
(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
"
->> -?>>)
In my Clojurescript code I have the following (I :require-macros as 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
Unfortunately, when I compile, I get:
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
When I open the javascript in a browser, I get the "hey Stephen" alert dialog, but the ubiquitous "Uncaught TypeError: Cannot call method 'call' of undefined" error comes up immediately after pressing "ok" on the "hey Stephen" alert. Sure enough, looking at the generated javascript code, my js/alert's became:
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"))
So clearly, I can use macro's, but something about the way the -?> (and related) macros are written causes them to fail to compile. What do I need to do in order to use these -?>
.?.
and `-?>>' macros?
Upvotes: 4
Views: 730
Reputation: 571
The ->
marco is simple.
(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)))
write a nil safe one should be simple (example below is not tested), but you get the concept.
(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)))
Upvotes: 0
Reputation: 939
-?> works for me using clojure 1.5.1 and cljsbuild 0.3.0, but only if I use :use-macros instead of :require-macros. When I use :require-macros clojurescript tries to resolve the macro as a var in the local namespace, which is incorrect. I think you've found a bug in clojurescript, why don't you report it?
(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))
The thing that makes me sure this is a bug is that uncommenting the :use-macros will cause the file to compile correctly, even if I don't remove the tn/ scope from the variables.
Upvotes: 2
Reputation: 33657
May be creating function rather than another macro from the macro can help you solve this issue:
(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#))))
Upvotes: -1