Fork me on GitHub
#clojurescript
<
2022-07-28
>
john04:07:48

Well, not traditionally. But if you're in a webworker, there's ways to claw back synchrony, such as with https://github.com/johnmn3/inmesh:

(defn post [url headers data]
  (let [response-post @(future (-> got (.post url (clj->js {:headers headers :json data})) .json yield))]
    (println (js->clj response-post :keywordize-keys true))))
I'm still polishing up the beta, so no formal announcements yet, but it's possible. There's caveats too, like serialization overhead, etc, but if sync control flow is what you want, it's possible. Not sure what got is there though.

john04:07:06

And I presume you might have to wrap the .json call in a .then if it's a js/fetch like thing, but yeah

john04:07:06

Like:

(->> @(future (-> (js/fetch "")
                  (.then #(.json %))
                  (.then #(yield (js->clj % :keywordize-keys true)))))
     :iss_position
     (println "ISS Position:"))
;ISS Position: {:latitude 44.4403, :longitude 177.0011}

Hankstenberg13:07:13

@thheller yea, I'm actually not used to it. I'm writing a lambda right now on nbb that is supposed to do some business logic depending on the state of stuff that is behind an API. It's surprisingly different from developing for the browser, where the state-propagation-rules are usually taken care of by a framework and you're always working with some implementation of redux. Maybe I need some half-assed version of that for backend-logic. @john Oh that looks interesting! But I think right now using workers of any kind would add too much complexity for too little gain. But I'll check it out as soon as I see a use case for it!

borkdude13:07:12

@roseneck If it helps, nbb has a REPL(!) helper called nbb.core/await which lets you pull out the resolved value. It works only in the top level.

(def foo (nbb.core/await (async-fn)))
or in the REPL:
user> (nbb.core/await (async-fn))

borkdude13:07:00

Even in JS you cannot "pull out" the resolved value if you have async await: functions will always return promises once you use async/await.

borkdude13:07:22

But the above is a bit analogous to const foo = await async_fn()

skylize14:07:00

@roseneck The general idea behind a "promise" is that the promise itself is a thing which you can return and then pass around, even while the underlying value may not exist yet.

(defn post [url headers data]
  ;; ... Wrap invocation of post request inside promise creation,
  ;;      or invoke post with a lib that returns promise for you...
  ;; ... probably attach some minor transforms to result ...
  ;; ... return promise
)

;; The "return value" of `post` is now a promise to provide
;;  some real value eventually, i.e. when the request succeeds. 

(def my-post-response (post url headers data))

;; Do something with the underlying value whenever it actually exists.

(p/then my-post-response some-side-effect)

(def my-transformed-post (p/then my-post-response transform-fn))
(p/then my-transformed-post some-other-side-effect)
The conceptual model of promesa is captured by p/deferred , p/resolve!, and p/reject! to create promises (p.deferred creates an unresolved promise, which you can return to consumers, and then resolve/reject when data becomes available), and p/then, p/catchto consume them. Everything else the lib offers is essentially just sugar.

Richie23:07:28

(/ 1 "2")
;; cljs.core//, all arguments must be numbers, got [number string] instead
(/ 1 (deref (atom "2")))
;; 0.5
Is this a bug?

Richie23:07:59

Also, why does this happen?

tomd23:07:09

Seems strange that dereffing to a string doesn't raise the same error, but I don't know if that's a bug exactly.

dominicm10:07:31

The compiler can't statically analyze to catch you out in that case

💡 1
Richie14:07:26

Oh. Ok, so it's not a runtime exception it's failing static analysis. That makes me a little sad.

(defn ^number /
  "If no denominators are supplied, returns 1/numerator,
  else returns numerator divided by all of the denominators."
  ([x] (/ 1 x))
  ([x y] (cljs.core/divide x y)) ;; FIXME: waiting on cljs.core//
  ([x y & more] (reduce / (/ x y) more)))
(core/defmacro ^::ana/numeric divide
  ([x] `(/ 1 ~x))
  ([x y] (core/list 'js* "(~{} / ~{})" x y))
  ([x y & more] `(/ (/ ~x ~y) ~@more)))
Yea, it looks like it just expands into javascript divide.

Richie14:07:56

(zero? (deref (atom "0")))
;; false
(core/defmacro ^::ana/numeric zero? [x]
  `(== ~x 0))
"0" == 0
true

Richie14:07:34

Clojurescript is giving me javascript behavior for division such that 1 / "2" and (/ 1 (deref (atom "2"))) give 0.5 but clojurescript isn't giving me javascript behavior for equality since "0" == 0 gives true while (= (deref (atom "0")) 0) gives false . I had code in my app that checks for zero in the denominator but it was blowing up since I was passing a string "0". That's how I found this.

(let [zero (atom "0")]
  (when-not (zero? @zero)
    (/ 1 @zero)))
;; ##Inf

dominicm15:07:21

That's correct. I believe cljs offers == or something like that for js equality.

dominicm15:07:33

Math in cljs uses js semantics, by design.

Richie15:07:01

(== (deref (atom "0")) 0) also gives false

dominicm15:07:43

Apparently this is what you're after

Richie15:07:19

Oh. zero? uses == which is js === strict equality. TIL that https://clojuredocs.org/clojure.core/== exists in clojure.

Richie15:07:09

Ok, and then = only returns true if they're identical.

(defn ^boolean =
  "Equality. Returns true if x equals y, false if not. Compares
  numbers and collections in a type-independent manner.  Clojure's immutable data
  structures define -equiv (and thus =) as a value, not an identity,
  comparison."
  ([x] true)
  ([x y]
    (if (nil? x)
      (nil? y)
      (or (identical? x y)
        ^boolean (-equiv x y))))
  ([x y & more]
     (if (= x y)
       (if (next more)
         (recur y (first more) (next more))
         (= y (first more)))
       false)))
(extend-type number
  IEquiv
  (-equiv [x o] (identical? x o)))
Thanks!

Richie21:08:21

(swap! (atom "1") inc)
;; "11"
Bit me again...

JoshLemer23:07:05

Sorry if this is a really dumb or confused question but I have heard that the ClojureScript compiler is/can be made to be self-hosting, and so I was just wondering why is it not common practice to compile ClojureScript only with itself (i.e., forego use of the JVM, and compile using something like node)? I also don't understand why something like nbb is needed which runs on the SmallClojureInterpreter, if there's also just the regular ClojureScript compiler which could also compile Clojure to run on node.

seancorfield00:07:28

My understanding is that the self-hosted compilers produce pretty inefficient code because they're a literal translation to JS -- and cljs really benefits from all the optimizations that are part of the (hosted) compiler. (but I don't do cljs at work yet, only clj, so my voice is not at all authoritative! 🙂 )

thheller06:07:01

the self hosted compiler does produce the same JS that the regular compiler does. there is no or very little difference there. however the self-hosted compiler has certain restrictions that prohibit certain post-compile optimizations.

thheller06:07:06

the main concern here is build size

thheller06:07:47

while a regular fully optimized build starts out at about 130kb (~30kb gzip) a self-hosted build starts out of several megabytes

thheller06:07:04

since you have to include the entire compiler and supporting data it needs

thheller06:07:35

in most setups for browser builds where you care about build size this is a problem

thheller06:07:51

the CLJ compiler is also much faster

zimablue11:07:30

@UEH6VEQQJ just to say that if it helps I have asked similar questions in the past so it's not more stupid than the random sample of "me"

zimablue11:07:42

I don't really know what I'm talking about so don't take this as gospel but I think the clojure(script) world has a larger centre of gravity towards the (clojure==java) side, there are definitely people using only cljs or cljs and arbitrary backend but my guess would be that most (cljs) is talking to (clj), so most people already have a java dependency, and their best developers are most familiar with java

zimablue11:07:14

so the idea of eliminating a java dependency isn't as interesting for those people as it would be to people who usde cljs-only, and there's more of the first type

JoshLemer18:07:19

So then it sounds like the only missing link is a JS optimizer that runs on Node? Maybe there is such an optimizer out there

john05:07:31

There was a JS version of the Google Closure compiler (used by CLJS) but it looks like they discontinued it: https://github.com/google/closure-compiler-npm/blob/master/packages/google-closure-compiler-js/readme.md

john05:07:02

You could use nbb though

john05:07:29

Closure does produce stuff that the v8 engine really likes though

john05:07:15

At least, IME, some hotloops in CLJS will run 2 to 3 times faster in chrome than in firefox and safari