Skip to content

Instantly share code, notes, and snippets.

@favila
Last active March 29, 2017 08:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save favila/589b3369b984cad1c37c063b96d79424 to your computer and use it in GitHub Desktop.
Save favila/589b3369b984cad1c37c063b96d79424 to your computer and use it in GitHub Desktop.
See how js output adapts to boolean hinting and tidy vs messy predicates. JS code emitted for predicates is much better if you explicitly test for nils and thus convert to bools.
;; Using clojurescript master 1.9.512 uberjar
;; Commit 52ff7a2bdcaacf840f7aae72f4be4575297f7db1
(require 'cljs.build.api)
(cljs.build.api/build "src" {:optimizations :advanced
:pretty-print true
:pseudo-names true
:output-to "out/main.js"})
(ns booltest.main)
(defn qualified-keyword-messy? [x]
(and (keyword? x) (namespace x) true))
(defn qualified-keyword-tidy? [x]
(and (keyword? x) (some? (namespace x))))
(defn ^boolean qualified-keyword-messy-hinted? [x]
;;This is what cljs.core does right now
(and (keyword? x) (namespace x) true))
(defn ^boolean qualified-keyword-tidy-hinted? [x]
(and (keyword? x) (some? (namespace x))))
(if (qualified-keyword-messy? :a/b)
(.log js/console "messy qualified")
(.log js/console "messy not qualified"))
(if (qualified-keyword-tidy? :a/b)
(.log js/console "tidy qualified")
(.log js/console "tidy not qualified"))
(if (qualified-keyword-messy-hinted? :a/b)
(.log js/console "messy-hinted qualified")
(.log js/console "messy-hinted not qualified"))
(if (qualified-keyword-tidy-hinted? :a/b)
(.log js/console "tidy-hinted qualified")
(.log js/console "tidy-hinted not qualified"))
// For brevity, we have deleted about 450k of cljs.core code.
// Define :a/b
$cljs$core$cst$0kw$0a_SLASH_b$$ = new $cljs$core$Keyword$$("a", "b", "a/b", 1482224565), $cljs$core$cst$0kw$0alt_DASH_impl$$ = new $cljs$core$Keyword$$(null, "alt-impl", "alt-impl", 670969595);
// Results below may be confusing due to inlining
// (if (qualified-keyword-messy? :a/b) ...)
var $JSCompiler_inline_result$jscomp$70$$;
// (keyword? x)
var $and__3470__auto__$jscomp$inline_312$$ = $cljs$core$cst$0kw$0a_SLASH_b$$ instanceof $cljs$core$Keyword$$;
// (namespace x)
// Note that the compiler cannot eliminate this block (generated by the logic of the `and` macro)
// Nor can it eliminate the cljs.core.truth_ call.
if ($and__3470__auto__$jscomp$inline_312$$) {
// Note $cljs$core$namespace$$() is zero-arg here because closure has replaced its arg with the constant :a/b
// This fn is never called with anything else, so no point passing it in as an arg!
// Snippet:
/*
function $cljs$core$namespace$$() {
var $x$jscomp$285$$ = $cljs$core$cst$0kw$0a_SLASH_b$$; // former argument 'x' on left now assigned to constant :a/b
if (null != $x$jscomp$285$$ && ($x$jscomp$285$$.$cljs$lang$protocol_mask$partition1$$ & 4096 || $cljs$core$PROTOCOL_SENTINEL$$ === $x$jscomp$285$$.$cljs$core$INamed$$)) {
return $x$jscomp$285$$.$ns$;
}
throw Error([$cljs$core$str$$.$cljs$core$IFn$_invoke$arity$1$("Doesn't support namespace: "), $cljs$core$str$$.$cljs$core$IFn$_invoke$arity$1$($x$jscomp$285$$)].join(""));
}
*/
var $and__3470__auto____$1$jscomp$inline_313$$ = $cljs$core$namespace$$();
// inner call to `truth_` (around (namespace x)) not eliminated
$JSCompiler_inline_result$jscomp$70$$ = $cljs$core$truth_$$($and__3470__auto____$1$jscomp$inline_313$$) ? !0 : $and__3470__auto____$1$jscomp$inline_313$$;
} else {
$JSCompiler_inline_result$jscomp$70$$ = $and__3470__auto__$jscomp$inline_312$$;
}
// Outer call to truth_() from the `if` is not eliminated.
$cljs$core$truth_$$($JSCompiler_inline_result$jscomp$70$$) ? console.log("messy qualified") : console.log("messy not qualified");
// (if (qualified-keyword-tidy? :a/b) ...)
// Note the (and ...) becomes a simple js &&
// However the outer truth_ call for if() is still there.
// Seems like the analyzer is not propagating the boolean type inference from the
// `(and ...)` expression out the the function as a whole.
$cljs$core$truth_$$($cljs$core$cst$0kw$0a_SLASH_b$$ instanceof $cljs$core$Keyword$$ && null != $cljs$core$namespace$$()) ? console.log("tidy qualified") : console.log("tidy not qualified");
// (if (qualitified-keyword-messy-hinted? :a/b) ...)
// This is what cljs.core/qualified-keyword? does today. Even though the function does not actually just
// return true/false, it still has the boolean hint on it.
// In this case, where we know the only other possible value is nil, it is unlikely to cause any correctness problems....
var $JSCompiler_inline_result$jscomp$71$$;
var $and__3470__auto__$jscomp$inline_316$$ = $cljs$core$cst$0kw$0a_SLASH_b$$ instanceof $cljs$core$Keyword$$;
if ($and__3470__auto__$jscomp$inline_316$$) {
var $and__3470__auto____$1$jscomp$inline_317$$ = $cljs$core$namespace$$();
// inner truth_() call still there
$JSCompiler_inline_result$jscomp$71$$ = $cljs$core$truth_$$($and__3470__auto____$1$jscomp$inline_317$$) ? !0 : $and__3470__auto____$1$jscomp$inline_317$$;
} else {
$JSCompiler_inline_result$jscomp$71$$ = $and__3470__auto__$jscomp$inline_316$$;
}
// But ^boolean hint guarantees outer truth call is eliminated
$JSCompiler_inline_result$jscomp$71$$ ? console.log("messy-hinted qualified") : console.log("messy-hinted not qualified");
// (if (qualified-keyword-tidy-hinted? :a/b:) ...). No truth_() calls anywhere.
$cljs$core$cst$0kw$0a_SLASH_b$$ instanceof $cljs$core$Keyword$$ && null != $cljs$core$namespace$$() ? console.log("tidy-hinted qualified") : console.log("tidy-hinted not qualified");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment