joyride

pez 2023-10-30T16:27:39.737959Z

I want a macro like time. Tried to copy time from Clojure core and adapt it like so:

(defmacro timer
  "Evaluates expr and prints the time it took.  Returns the value of
 expr."
  [expr]
  `(let [start# (js/performance.now)
         ret# ~expr]
     (prn (str "Elapsed time: " (/ (double (- (js/performance.now) start#)) 1000000.0) " msecs"))
     ret#))
Getting Function.prototype.apply was called on undefined, which is a undefined and not a function when trying to use it. Am I supposed to be able to do something like this?

pez 2023-10-30T16:28:51.515289Z

And maybe Joyride should come with time defined?

pez 2023-10-30T16:30:26.606589Z

No, I was being stupid. My macro works if I feed it a valid expression. picard-facepalm

pez 2023-10-30T16:32:12.458869Z

For some definition of “works”. 😃 I’ll need to adapt it to whatever it is I get from performance.now()

borkdude 2023-10-30T16:32:58.441939Z

time is already available in nbb, just copy it from there

borkdude 2023-10-30T16:33:07.583229Z

actually, isn't time standard in SCI?

borkdude 2023-10-30T16:33:14.883329Z

maybe it should be

pez 2023-10-30T16:35:45.723769Z

We lack cljs.core/system-time in Joyride. Time for some issues!

borkdude 2023-10-30T16:36:30.215639Z

I add this to a lot of my JS projects too: https://github.com/babashka/scittle/blob/71c224baa737ae33fb18b8eca064a75dfdeb2a68/src/scittle/core.cljs#L14-L22 I think we should just add it to SCI

borkdude 2023-10-30T16:39:33.484249Z

I'll just add it now

1
borkdude 2023-10-30T17:20:50.313079Z

https://github.com/babashka/sci

borkdude 2023-10-30T17:20:53.735779Z

added

borkdude 2023-10-30T17:20:57.340939Z

wow 1111 stars ;)

1
pez 2023-10-30T20:07:11.504669Z

I did some benchmarking on some different ways to convert acorn’s AST (which is in some funny JS instance) to plain data. Might as well share.

(ns bench.object-to-data
  (:require ["acorn-loose" :as acorn]
            [util :as util]
            ["./ast-util.js" :as ast-util]
            ["fs" :as fs]))

(defn ast->data [node]
  (cond
    (array? node) (mapv ast->data (into [] node))
    (and node (instance? js/Object node)) (into {} (for [k (js-keys node)
                                                         :let [v (aget node k)]]
                                                     [(keyword k) (ast->data v)]))
    :else node))

(comment
  ; wc test-files/large.js
  ; 7662   18943  169040 test-files/large.js
  (def text (fs/readFileSync "/Users/pez/.config/joyride/test-files/large.js"))

  (util/time ; Elapsed time: 8.087583065032959 msecs
   (def ast (acorn.parse text #js {:allowAwaitOutsideFunction true})))

  (util/time ; Elapsed time: 5.1631669998168945 msecs
   (def js-ast (ast-util/objectAstToData ast)))

  (util/time ; Elapsed time: 41.782166957855225 msecs
   (def clojure-ast-1 (js->clj js-ast :keywordize-keys true)))

  (util/time ; Elapsed time: 201.6672089099884 msecs
   (def clojure-ast-2 (ast->data ast)))

  (util/time ; Elapsed time: 16.00624990463257 msecs
   (def js-ast-2 (-> ast
                     js/JSON.stringify
                     js/JSON.parse)))

  (util/time ; Elapsed time: 41.43733310699463 msecs
   (def clojure-ast-3 (js->clj js-ast-2 :keywordize-keys true)))

  :rcf)
The ->js-data converter looks like so:
function objectAstToData(node) {
  if (Array.isArray(node)) {
      return node.map(objectAstToData);
  } else if (node && typeof node === 'object') {
      const result = {};
      for (const key of Object.keys(node)) {
          result[key] = objectAstToData(node[key]);
      }
      return result;
  } else {
      return node;
  }
}
So: • 8ms: parsing the file • 5ms: converting the AST JS data with a JS function • 16ms: converting the AST to JS data via JSON • 40ms: js->clj • 200ms: converting the AST to Clojure data with a SCI CLJS function

pez 2023-10-30T20:08:45.467489Z

It’s not exactly criterion benchmarking. I just hammered on the evaluation buttons a few times per form and took some sample I thought look representative.