Skip to content

Instantly share code, notes, and snippets.

@rauhs
Last active June 30, 2017 20:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rauhs/771fb0fc8746958ca6f473914eeb1128 to your computer and use it in GitHub Desktop.
Save rauhs/771fb0fc8746958ca6f473914eeb1128 to your computer and use it in GitHub Desktop.
;; No ticket yet. Idea:
(defn spread-n
"arr will be mutated!!
Puts n more items into arr and the rest of xs as the last element.
Returns the array arr."
[arr n xs]
(loop [s (seq xs)
i (dec n)]
(if (pos? i)
(do
(.push arr (-first s))
(recur (next s) (dec i)))
(do
(.push arr s)
arr))))
(spread-n #js[0] 2 [1 2 3])
(defn my-apply
([f x args]
(if (.-cljs$lang$applyTo f)
(let [fixed-arity (.-cljs$lang$maxFixedArity f)
bc (inc (bounded-count fixed-arity args))]
(if (<= bc fixed-arity)
;; TODO: Optimize apply-to similarlyto apply-to-simple:
(apply-to f bc (list* x args))
(.apply (.-cljs$core$IFn$_invoke$arity$variadic f)
nil
(spread-n #js[x] fixed-arity args))))
(apply-to-simple f x (seq args)))))
(my-apply (fn [a b & c] [a b c])
1 [1 2])
(defn pv-to-array
"Returns the (possibly internal) array for the given PersistentVector"
[v]
(let [cnt (.-cnt v)
arr (unchecked-array-for v 0)]
(if (< 32 cnt)
(let [arr (aclone arr)]
(loop [i 32]
(when (< i cnt)
(let [new-arr (unchecked-array-for v i)
len (alength new-arr)]
(loop [j 0]
(when (< j len)
(.push arr (aget new-arr j))
(recur (inc j))))
(recur (+ i len)))))
arr)
arr)))
;; DOnt use the above, instead use (seq [some-vec]). It's very efficient.
;; Basically, write a sub-seq for a few types.
;; LazySeq: 500x
;; => Walk first/next
;; IndexedSeq: 2000x
;; js/Array: 170x
;; => Very fast & easy
;; PersistentVector: 350x
;; =>
(defn pv-subseq
""
[pv start]
;; Invariant: start is not negative!
(let [cnt (.-cnt pv)]
(cond
(not (pos? (- cnt start))) nil
(<= cnt 32) (IndexedSeq. (.-tail pv) start nil)
:else (seq (subvec pv start)))))
#_(pv-subseq (vec (range 40)) 30)
;; Get MFA items out of a seq, + the rest of the seq (nil'd)
;; First do the generic fallback:
;;
;; Should we do apply-to-varidic be a JS array, then just
;; pluck out the values with args[0], args[1] + IndexedSeq(args,2,null)
;; Candidates:
#js[+ - * / < > = not=]
(defn spread-n
"Makes "
[arr n args]
(if (zero? n)
args
(cond
(instance? IndexedSeq args)
(let [arg-arr (.-arr args)
offset (.-i args)]
;; We have 3 cases:
;; 1. Push more items into arr + a new IndexedSeq with offset
;; 2. It's already good
;; 3. Pull items from arr into args (use cons)
()))))
(defn- maybe-count
"Counts in constant time or nil if that's not possible"
[coll]
(cond
(implements? ICounted coll) (-count ^not-native coll)
(array? coll) (alength coll)
:else nil))
#_(maybe-count #js[1])
#_(maybe-count (prim-seq #js[0 1 2]))
#_(maybe-count (lazy-seq [0 1 2]))
;; Current applyTo uses first/next!
;; Cases:
;; 1. We know count & can decide mfa or not => FAST
;; a) Call the proper exact arity, FAST by just dumping it all into an array
;; REUSE
;; b) Call the the variadic arity, FAST by spread-n
;; Jump to the arity and pluck out of array
;; 2. We dont know count, then
;; Walk first/next until we either:
;; a) Get nil as next, call the IFn
;; b) Passed MFA. Call the variadic fn.
;; What if we have a whole in the arities, like [a] [a b c & d] ??
;; And we pass 2 args? Go to the dispatcher and let it error?
;; But overall: Easy to code
(defn apply-to-variadic-walker
"Internal! DO NOT USE!
Applies to args to f using first/next"
[f args])
(defn apply-to-variadic
"Internal! DO NOT USE!
Invokes the variadic function"
;; We need the first MFA items out of args then call
;; the
[f mfa args]
(case mfa
0 (invoke-variadic* f args)
1 (invoke-variadic* f (aget args 0) (aget args 1))
2 (invoke-variadic* f (aget args 0) (aget args 1) (aget args 2))
;; Fall back with .apply at the end!
))
(defn into-array-fast
[coll]
(into-array coll))
(set! *unchecked-if* true)
(defn- apply-to-simple-new
[f arr args]
(let [IS? (instance? IndexedSeq args)
off (if IS? (.-i args) 0)
arr (if IS? (.-arr args) (into-array args))]
(case (alength arr)
0 (if (invokable? f 0) (invoke* f) (.call f f))
1 (if (invokable? f 1) (invoke* f (aget arr (+ 0 off)))
(.call f f (aget arr (+ 0 off)))))))
(defn apply-new
"Applies fn f to the argument list formed by prepending intervening arguments to args."
([f args]
(if (invokable? f "variadic")
(let [mfa (.-cljs$lang$maxFixedArity f)
bc (bounded-count (inc mfa) args)]
(if (<= bc mfa)
(apply-to f bc args)
(apply-to-variadic f mfa args)))
;; Need to handle offset
(apply-to-simple-new f #js[] args))))
(set! *unchecked-if* false)
;; Test:
;; 1. We need to handle nil:
(apply + 1 2 nil)
(apply (fn [& a] a) [])
@rauhs
Copy link
Author

rauhs commented Jun 30, 2017

create two apply-to-simple: apply-to-multi-arity, apply-to-js-fn. Monomorphic fns should be faster.

@rauhs
Copy link
Author

rauhs commented Jun 30, 2017

bounded-count can call slow native-satisfies when passed nil.

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