Skip to content

Instantly share code, notes, and snippets.

@city41
Last active June 7, 2018 04:13
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 city41/12099b91e0fbb526b0dc to your computer and use it in GitHub Desktop.
Save city41/12099b91e0fbb526b0dc to your computer and use it in GitHub Desktop.
set! vs let

From this discussion: https://news.ycombinator.com/item?id=8854844

and related to this code: https://github.com/city41/bookends/blob/master/site/src/cljs/demo/knex.cljs#L6

Here is the relevant chunk of advanced compiled js when using set!

the ClojureScript:

(ns demo.knex
  (:require [cljs.core.async :refer [put! chan]]))

(def knex (js/Knex. #js {:client "websql" :debug true}))

(defn init-chan []
  (let [out (chan)]
    (set! (.. knex -client -Runner -prototype -debug)
          (fn [obj]
            (put! out {:sql (.-sql obj)
                       :bindings (.-bindings obj)})))
    out))

and the chunk of the JS that corresponds

(function $h(b) {
        "undefined" === typeof lh && (lh = function(b, d, e) {
            this.f = b;
            this.Ob = d;
            this.uc = e;
            this.A = 0;
            this.n = 393216
        }, lh.prototype.$b = function() {
            return !0
        }, lh.prototype.ac = function() {
            return this.f
        }, lh.prototype.J = function() {
            return this.uc
        }, lh.prototype.M = function(b, d) {
            return new lh(this.f, this.Ob, d)
        }, lh.cb = !0, lh.bb = "cljs.core.async/t21578", lh.ub = function(b, d) {
            return A(d, "cljs.core.async/t21578")
        });
        return new lh(b, $h, new s(null, 5, [ng, 20, Ug, 16, zg, 3, Jg, 13, kg, "/Users/matt/dev/bookends/site/target/cljsbuild-compiler-1/cljs/core/async.cljs"], 
        null))
    })(function() {
        return null
    });
    var ai = new Knex({debug: !0,client: "websql"});
    var bi;
    function di(a) {
        var b = S.h(a, 0, null);
        a = S.h(a, 1, null);
        if (t(a))
            throw a;
        return b
    }

Here is the same function, but using let instead.

(ns demo.knex
  (:require [cljs.core.async :refer [put! chan]]))

(def knex (js/Knex. #js {:client "websql" :debug true}))

(defn init-chan []
  (let [out (chan)
        client (.-client knex)
        Runner (.-Runner client)
        proto (.-prototype Runner)] 
    (set! (.-debug proto)
          (fn [obj] 
            (put! out {:sql (.-sql obj)
                       :bindings (.-bindings obj)})))
    out))

and the resulting JS

        function a(a, b, c, d) {
            a = mh(a, b, ai(c));
            return t(a) ? (b = O.d ? O.d(a) : O.call(null, a), t(d) ? c.d ? c.d(b) : c.call(null, b) : Hh(function(a) {
                return function() {
                    return c.d ? c.d(a) : c.call(null, a)
                }
            }(b, a, a)), b) : !0
        }
        function b(a, b, c) {
            return d.r(a, b, c, !0)
        }
        function c(a, b) {
            var c = mh(a, b, ci);
            return t(c) ? O.d ? O.d(c) : O.call(null, c) : !0
        }
        var d = null, d = function(d, f, g, k) {
            switch (arguments.length) {
                case 2:
                    return c.call(this, 
                    d, f);
                case 3:
                    return b.call(this, d, f, g);
                case 4:
                    return a.call(this, d, f, g, k)
            }
            throw Error("Invalid arity: " + arguments.length);
        };
        d.c = c;
        d.h = b;
        d.r = a;
        return d
    }();
    var ei = new Knex({debug: !0,client: "websql"});
    function fi() {
        var a = bi.v(), b = ei.client, c = b.Runner, d = c.prototype;
        d.debug = function(a) {
            return function(b) {
                return di.c(a, new s(null, 2, [Gg, b.sql, eg, b.bindings], null))
            }
        }(a, b, c, d);
        return a
    }
    ;
    var hi;
    function ii(a) {
        var b = S.h(a, 0, null);
        a = S.h(a, 1, null);
        if (t(a))
            throw a;
        return b
    }

You can see in the second chunk after the creation of the knex object, it's successfully digging into it. But there is no equivalent to that in the set! version.

ADDED

As Skinney requested, here is no optimizations and pretty printed

using set!

// Compiled by ClojureScript 0.0-2665 {}
if (!goog.isProvided_('demo.knex')) {
  goog.provide('demo.knex');
}

goog.require('cljs.core');
goog.require('cljs.core.async');
demo.knex.knex = (new Knex({
  "debug": true,
  "client": "websql"
}));
demo.knex.init_chan = (function init_chan() {
  var out = cljs.core.async.chan.call(null);
  demo.knex.knex.client.Runner.prototype.debug = ((function(out) {
    return (function(obj) {
      return cljs.core.async.put_BANG_.call(null, out, new cljs.core.PersistentArrayMap(
        null, 2, [new cljs.core.Keyword(null, "sql", "sql", 1251448786),
          obj.sql, new cljs.core.Keyword(null, "bindings", "bindings",
            1271397192), obj.bindings
        ], null));
    });
  })(out));

  return out;
});

using let

// Compiled by ClojureScript 0.0-2665 {}
if (!goog.isProvided_('demo.knex')) {
  goog.provide('demo.knex');
}
goog.require('cljs.core');
goog.require('cljs.core.async');
demo.knex.knex = (new Knex({
  "debug": true,
  "client": "websql"
}));
demo.knex.init_chan = (function init_chan() {
  var out = cljs.core.async.chan.call(null);
  var client = demo.knex.knex.client;
  var Runner = client.Runner;
  var proto = Runner.prototype;
  proto.debug = ((function(out, client, Runner, proto) {
    return (function(obj) {
      return cljs.core.async.put_BANG_.call(null, out, new cljs.core.PersistentArrayMap(
        null, 2, [new cljs.core.Keyword(null, "sql", "sql", 1251448786),
          obj.sql, new cljs.core.Keyword(null, "bindings", "bindings",
            1271397192), obj.bindings
        ], null));
    });
  })(out, client, Runner, proto));

  return out;
});

(the only added changes I did was to format it even more than what :pretty-print did)

Addition 2

And for the heck of it, here is advanced mode, with pseudo-names turned on

(function $fn_handler$$2$$($f$$458$$) {
  "undefined" === typeof $cljs$core$async$t21578$$ && ($cljs$core$async$t21578$$ = function($f$$458$$, $fn_handler$$3$$, $meta21579$$) {
    this.f = $f$$458$$;
    this.$fn_handler$ = $fn_handler$$3$$;
    this.$meta21579$ = $meta21579$$;
    this.$cljs$lang$protocol_mask$partition1$$ = 0;
    this.$cljs$lang$protocol_mask$partition0$$ = 393216;
  }, $cljs$core$async$t21578$$.prototype.$cljs$core$async$impl$protocols$Handler$active_QMARK_$arity$1$ = function() {
    return!0;
  }, $cljs$core$async$t21578$$.prototype.$cljs$core$async$impl$protocols$Handler$commit$arity$1$ = function() {
    return this.f;
  }, $cljs$core$async$t21578$$.prototype.$cljs$core$IMeta$_meta$arity$1$ = function() {
    return this.$meta21579$;
  }, $cljs$core$async$t21578$$.prototype.$cljs$core$IWithMeta$_with_meta$arity$2$ = function($f$$458$$, $meta21579__$1$$) {
    return new $cljs$core$async$t21578$$(this.f, this.$fn_handler$, $meta21579__$1$$);
  }, $cljs$core$async$t21578$$.$cljs$lang$type$ = !0, $cljs$core$async$t21578$$.$cljs$lang$ctorStr$ = "cljs.core.async/t21578", $cljs$core$async$t21578$$.$cljs$lang$ctorPrWriter$ = function($f$$458$$, $writer__4331__auto__$$78$$) {
    return $cljs$core$_write$$($writer__4331__auto__$$78$$, "cljs.core.async/t21578");
  });
  return new $cljs$core$async$t21578$$($f$$458$$, $fn_handler$$2$$, new $cljs$core$PersistentArrayMap$$(null, 5, [$cljs$core$constant$0keyword$08$$, 20, $cljs$core$constant$0keyword$09$$, 16, $cljs$core$constant$0keyword$010$$, 3, $cljs$core$constant$0keyword$011$$, 13, $cljs$core$constant$0keyword$012$$, "/Users/matt/dev/bookends/site/target/cljsbuild-compiler-1/cljs/core/async.cljs"], null));
})(function() {
  return null;
});
var $demo$knex$knex$$ = new Knex({debug:!0, client:"websql"});
var $cljs_promises$async$t25234$$;
function $cljs_promises$async$consume_pair$$($err$$3_p__25216$$) {
  var $val$$113$$ = $cljs$core$nth$$.$cljs$core$IFn$_invoke$arity$3$($err$$3_p__25216$$, 0, null);
  $err$$3_p__25216$$ = $cljs$core$nth$$.$cljs$core$IFn$_invoke$arity$3$($err$$3_p__25216$$, 1, null);
  if ($cljs$core$truth_$$($err$$3_p__25216$$)) {
    throw $err$$3_p__25216$$;
  }
  return $val$$113$$;
}
var $cljs_promises$async$pair_port$$ = function $pair_port$$($promise$$10$$) {
  "undefined" === typeof $cljs_promises$async$t25234$$ && ($cljs_promises$async$t25234$$ = function($promise$$10$$, $pair_port$$1$$, $meta25235$$) {
    this.promise = $promise$$10$$;
    this.$pair_port$ = $pair_port$$1$$;
    this.$meta25235$ = $meta25235$$;
    this.$cljs$lang$protocol_mask$partition1$$ = 0;
    this.$cljs$lang$protocol_mask$partition0$$ = 393216;
  }, $cljs_promises$async$t25234$$.prototype.$cljs$core$async$impl$protocols$ReadPort$take_BANG_$arity$2$ = function($promise$$10$$, $handler$$10$$) {
    this.promise.then(function($promise$$10$$) {
      return function($_$$225$$) {
        return $cljs$core$async$impl$dispatch$run$$(function() {
          return function() {
            return $cljs$core$async$impl$protocols$commit$$($handler$$10$$).call(null, new $cljs$core$PersistentVector$$(null, 2, 5, $cljs$core$PersistentVector$EMPTY_NODE$$, [$_$$225$$, null], null));
          };
        }($promise$$10$$));
      };
@robinheghan
Copy link

Would you throw up the relevant lines from optimizations none and pretty printing?

@city41
Copy link
Author

city41 commented Jan 8, 2015

@Skinney -- I added them above.

David Nolan has said the cause of this is an improper externs file for Knex. I was originally using the Knex js file itself as the extern file. I have been trying to create my own Knex externs file that makes the problem go away but so far no luck.

I also find it odd that in the optimized set! version, the channel went away too (ie the out variable), the only thing left is the instantiation of the knex object. But I'm still a cljs newb, so very open to suggestions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment