(CLJS) 'and' does not short circuit within go block in clojurescript
Description
Environment
core.async 0.1.338.0-5c5012-alpha
Activity
The issue is in ClojureScript
Possible fix in cljs to not optimize the and/or. ticket: https://clojure.atlassian.net/browse/CLJS-3260
another workaround is to split the check from the usage branch like so:
;; throws:
(let [x :nope]
(go (and (vector? x) (< (count x) 2))))
;; break up the vector? check with the usage of the fact that it is a vector
;; note if :deoptimization is first it will not work
(let [x :nope]
(go (and (vector? x) :deoptimization (< (count x) 2))))
;; use tag
(let [x :nope]
(go (and ^{:tag :not-boolean} (vector? x) (< (count x) 2))))
its hard to read the output, but i think
(cljs.pprint/pprint (macroexpand '(go (and ^{:tag :dont-optimize} (vector? cards) (< 0 (count cards))))))
provides a workaround. The and
macro checks to see if it can put all the logic into js with
(core/defn- simple-test-expr? [env ast]
(core/and
(#{:var :js-var :local :invoke :const :host-field :host-call :js :quote} (:op ast))
('#{boolean seq} (cljs.analyzer/infer-tag env ast))))
Is a good approach to this problem figuring out a good way to prevent this optimization? If so, what would that look like?
Seem to be three options
add information to env to prevent this optimization. Would require a change to cljs
Walk the form to be expanded and remove boolean tags and seq tags such that the macroexpander thinks it can’t be done in javascripts notion of truthiness. Might be heavy-handed? Is the only downside just emitting a bit slower code?
Prevent the boolean tag from being attached to the and forms? I’m not entirely sure
I have the following piece of ClojureScript code within a go block:
(cond
(and (vector? x) (= (first x) :some-key)) ...)
This generates the following piece of JavaScript code for the cond case:
if (40 === f) {
return
d = b[9],
f = cljs.core.vector_QMARK_.call(null, d),
d = cljs.core.first.call(null, d),
d = cljs.core.EQ.call(null, d, new cljs.core.Keyword(null, "some-key", "some-key", -978969032)),
cljs.core.truth_(f && d) ? b[1] = 42 : b[1] = 43,
new cljs.core.Keyword(null, "recur", "recur", -437573268);
}
This looks to me like both and arguments would actually get evaluated. As a result my code crashes whenever it hits this cond case and 'x' is not seqable.