     :hierarchy  the isa? hierarchy to use for dispatching
                 defaults to the global hierarchy"
   [mm-name & options]
-  (let [docstring   (if (core/string? (first options))
-                      (first options)
-                      nil)
-        options     (if (core/string? (first options))
-                      (next options)
-                      options)
-        m           (if (map? (first options))
-                      (first options)
-                      {})
-        options     (if (map? (first options))
-                      (next options)
-                      options)
-        dispatch-fn (first options)
-        options     (next options)
-        m           (if docstring
-                      (assoc m :doc docstring)
-                      m)
-        m           (if (meta mm-name)
-                      (conj (meta mm-name) m)
-                      m)
-        mm-ns (-> &env :ns :name core/str)] 
+  (core/let [docstring   (if (core/string? (first options))
+                           (first options)
+                           nil)
+             options     (if (core/string? (first options))
+                           (next options)
+                           options)
+             m           (if (map? (first options))
+                           (first options)
+                           {})
+             options     (if (map? (first options))
+                           (next options)
+                           options)
+             dispatch-fn (first options)
+             options     (next options)
+             m           (if docstring
+                           (assoc m :doc docstring)
+                           m)
+             m           (if (meta mm-name)
+                           (conj (meta mm-name) m)
+                           m)
+             mm-ns (-> &env :ns :name core/str)]
     (when (= (count options) 1)
       (throw
         #?(:clj (Exception. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)")
            :cljs (js/Error. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)"))))
-    (let [options   (apply core/hash-map options)
-          default   (core/get options :default :default)]
+    (core/let [options   (apply core/hash-map options)
+               default   (core/get options :default :default)]
       (check-valid-options options :default :hierarchy)
       `(defonce ~(with-meta mm-name m)
          (let [method-table# (atom {})

    (bar [x y] ...)
    (baz ([x] ...) ([x y & zs] ...))"
  [type-sym & impls]
  (let [env &env
        _ (validate-impls env impls)
        resolve (partial resolve-var env)
        impl-map (->impl-map impls)
        [type assign-impls] (if-let [type (base-type type-sym)]
                              [type base-assign-impls]
                              [(resolve type-sym) proto-assign-impls])]
    (when (core/and (:extending-base-js-type cljs.analyzer/*cljs-warnings*)
                    (js-base-type type-sym))
      (cljs.analyzer/warning :extending-base-js-type env
          {:current-symbol type-sym :suggested-symbol (js-base-type type-sym)}))
    `(do ~@(mapcat #(assign-impls env resolve type-sym type %) impl-map))))

(defn- prepare-protocol-masks [env impls]
  (let [resolve  (partial resolve-var env)
        impl-map (->impl-map impls)
        fpp-pbs  (seq
                   (keep fast-path-protocols
                     (map resolve
                       (keys impl-map))))]
    (if fpp-pbs
      (let [fpps  (into #{}
                    (filter (partial contains? fast-path-protocols)
                      (map resolve (keys impl-map))))
            parts (as-> (group-by first fpp-pbs) parts
                    (into {}
                      (map (juxt key (comp (partial map peek) val))
                        parts))
                    (into {}
                      (map (juxt key (comp (partial reduce core/bit-or) val))
                        parts)))]
        [fpps (reduce (fn [ps p] (update-in ps [p] (fnil identity 0)))
                parts
                (range fast-path-protocol-partitions-count))]))))

  print the result. expr's string representation will be produced
  using pr-str in any case."
  [bindings expr iterations & {:keys [print-fn] :or {print-fn 'println}}]
  (core/let [bs-str   (pr-str bindings)
             expr-str (pr-str expr)]
    `(let ~bindings
       (let [start#   (.getTime (js/Date.))
             ret#     (dotimes [_# ~iterations] ~expr)
             end#     (.getTime (js/Date.))
             elapsed# (- end# start#)]
         (~print-fn (str ~bs-str ", " ~expr-str ", "
                      ~iterations " runs, " elapsed# " msecs"))))))

(def cs (into [] (map (comp gensym core/str core/char) (range 97 118))))

(defn- gen-apply-to-helper
  ([] (gen-apply-to-helper 1))
  ([n]
   (core/let [prop (symbol (core/str "-cljs$core$IFn$_invoke$arity$" n))
              f (symbol (core/str "cljs$core$IFn$_invoke$arity$" n))]
     (if (core/<= n 20)
       `(let [~(cs (core/dec n)) (-first ~'args)
              ~'args (-rest ~'args)]
          (if (core/== ~'argc ~n)
            (if (. ~'f ~prop)
              (. ~'f (~f ~@(take n cs)))
              (~'f ~@(take n cs)))
            ~(gen-apply-to-helper (core/inc n))))
       `(throw (js/Error. "Only up to 20 arguments supported on functions"))))))

(core/defmacro gen-apply-to []
  `(do
