(CLJS) 'and' does not short circuit within go block in clojurescript

Description

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.

Environment

core.async 0.1.338.0-5c5012-alpha

Activity

Show:
import
November 7, 2016, 10:29 AM

Comment made by: raspasov

This happened to me as well with the latest version of core.async on ClojureScript 0.2.395.

I would bump this to not minor since it severely breaks the promise that (and ...) makes.

(def cards :loading)
(go (and (vector? cards) (< 0 (count cards))))

This throws an exception like this:

No protocol method ICounted.-count defined for type cljs.core/Keyword: :loading

I was able to go around this by nesting the checks like this:
(go (if (vector? cards)
(if (< 0 (count cards))
(println "counting cards..."))))

Mike Fikes
September 14, 2018, 4:15 PM

If you

you will see that arguments to the js* special form are lifted out and let-bound, causing evaluation of code that the special form would never evaluate.

Daniel Sutton
June 27, 2020, 3:53 PM

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

 

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

  1. add information to env to prevent this optimization. Would require a change to cljs

  2. 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?

  3. Prevent the boolean tag from being attached to the and forms? I’m not entirely sure

Daniel Sutton
June 27, 2020, 5:48 PM
Edited

another workaround is to split the check from the usage branch like so:

 

 

Daniel Sutton
July 18, 2020, 10:57 PM

Possible fix in cljs to not optimize the and/or. ticket:

Assignee

Unassigned

Reporter

import

Labels

None

Approval

None

Patch

None

Priority

Critical