This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-04-20
Channels
- # announcements (3)
- # babashka (7)
- # beginners (36)
- # calva (71)
- # cider (25)
- # clj-commons (5)
- # cljdoc (19)
- # cljs-dev (5)
- # clojure (223)
- # clojure-austin (2)
- # clojure-bay-area (1)
- # clojure-europe (31)
- # clojure-france (6)
- # clojure-nl (2)
- # clojure-norway (19)
- # clojure-spec (13)
- # clojure-uk (7)
- # clojurescript (127)
- # core-logic (2)
- # cursive (21)
- # datalevin (53)
- # datomic (9)
- # emacs (37)
- # events (1)
- # graphql (8)
- # jobs (12)
- # lsp (8)
- # off-topic (92)
- # pathom (49)
- # pedestal (1)
- # polylith (3)
- # re-frame (25)
- # releases (2)
- # sci (11)
- # shadow-cljs (13)
- # vim (10)
How would I go about using performance.now from the NodeJS standard lib: https://nodejs.org/api/perf_hooks.html#perf_hooksperformance
It looks like: (-> js/performance (.now))
works, but I'm not sure I understand why. The NodeJS doc says the object is perf_hooks and performance is on that, so why is performance exposed as a global?
Hum, ok that explains it. If I couldn't access it through js/performance, how would I go about accessing it through perf_hooks? I couldn't figure it out
performance
is a standard global, so maybe the node docs are just outdated or forget to mention that part. its also available in the browser
Oh, maybe docs were wrong, cause when I tried js/perf_hooks it said can't find JS for that name
@U0K064KQV What are you using this for? I recently made an async version of time
- wondered if you also needed that maybe :)
user=> (nbb.core/time (p/delay 1000 :hello))
"Elapsed time: 1000.643083 msecs"
:hello
That was my use case ya. Wanted to time how long two go blocks took to run to completion. I'm surprised promesa doesn't have its own async time, might be a good addition to it
which closure compiler version should we be using? I am getting errors with v20220301
@roklenarcic generally you should never alter the version of Google Closure that ClojureScript depends on, this was reliable in the old days, but simply not possible in the past 4-5 years
hm cljs dep doesn’t seem to bring in closure compiler as a dep
it should, both deps.edn and project.clj has it defined as a dependency: https://github.com/clojure/clojurescript/blob/0d5b1baddacc590b60979c77c2353f8ae32ed351/deps.edn#L3 & https://github.com/clojure/clojurescript/blob/0d5b1baddacc590b60979c77c2353f8ae32ed351/project.clj#L19
Ok then why is this happening?
internal.js:114 Uncaught TypeError: str.trim is not a function
at Object.goog.string.internal.trim (internal.js:114:18)
at Object.clojure$string$trim [as trim] (string.cljs:199:4)
Seems like goog library doesn’t have trim function that cljs expects it to have?looks like you are calling (clojure.string/trim not-a-string)
when it expects a string
oh that is one confusing type error then
thanks, I had a regression in my code
Usually when you come across Uncaught TypeError: X is not a function
in JS (not just CLJS) and you're sure you're calling the right function, it's because you're calling the function on a unexpected object (`nil/null` instead of String
for example), first thing to check would be that the type in runtime is what you expect it to be
Can I use unicode characters (japanese words specifically) as keys in a map in cLJS?
I am under the impression any [ol'] string will work ... But I am getting null
when (.log js/console (get @atom "もの"))
It doesn't matter what the key is. Let alone what the contents of a string key are.
=> (def m {"もの" 1})
#'cljs.user/m
=> (m "もの")
1
So your @atom
doesn't have that key or its associated value is nil
.Okay thanks for confirming that @U2FRKM4TW
Also, I didn't know we could do (m k)
that's a mind explode for me
Yep, described here: https://clojure.org/guides/learn/hashed_colls#_looking_up_by_key
And, in a bit more wordy way, here: https://clojure.org/reference/data_structures#Maps
-_- there was an invisible character at the end of the key
😅 brain melt 🫕
is there some diff in behavior between clojure and clojurescript with respect to lazy sequences? i'm getting call stack size exceeded errors for both of the following common definitions:
(def primes (remove
(fn [x]
(some #(zero? (mod x %)) primes))
(iterate inc 2)))
(def primes
(lazy-seq
(filter (fn [i] (not-any? #(zero? (rem i %))
(take-while #(<= (* % %) i) primes)))
(drop 2 (range)))))
$ lumo
Lumo 1.10.1
ClojureScript 1.10.520
Node.js v11.13.0
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Exit: Control+D or :cljs/quit or exit
cljs.user=> (def primes (remove
#_=> (fn [x]
#_=> (some #(zero? (mod x %)) primes))
#_=> (iterate inc 2)))
#'cljs.user/primes
cljs.user=>
cljs.user=> (take 1 primes)
Maximum call stack size exceeded
cljs.core.Iterate.cljs$core$ISeq$_rest$arity$1 (NO_SOURCE_FILE <embedded>:1125:318)
Object.cljs.core._rest (NO_SOURCE_FILE <embedded>:361:87)
Object.cljs.core.rest (NO_SOURCE_FILE <embedded>:504:396)
cljs.core.LazySeq.fn (NO_SOURCE_FILE <embedded>:1147:176)
cljs.core.LazySeq.sval (NO_SOURCE_FILE <embedded>:792:151)
cljs.core.LazySeq.cljs$core$ISeqable$_seq$arity$1 (NO_SOURCE_FILE <embedded>:798:255)
Object.cljs.core._seq (NO_SOURCE_FILE <embedded>:388:89)
Object.cljs.core.seq (NO_SOURCE_FILE <embedded>:503:180)
cljs.core.some (NO_SOURCE_FILE <embedded>:927:52)
(evalmachine.<anonymous>:3:23)
cljs.user=>
$ clj
Downloading: com/cemerick/clojurescript.test/0.2.3-SNAPSHOT/maven-metadata.xml from clojars
Clojure 1.10.3
user=>
user=>
user=>
user=>
user=>
user=>
user=>
user=> (def primes (remove
(fn [x]
(some #(zero? (mod x %)) primes))
(iterate inc 2)))
#'user/primes
user=>
user=>
user=>
user=> (take 1 primes)
(2)
was just looking over http://ask.clojure.org and ended up here
Ok well that blog post is just one of the two provided implementations with this problem and even if his reasoning is off (which I'm not saying it is since I didn't look into it), the problem that this works on Clojure and doesn't work on cljs still remains
Although yes I agree that if he says that primes is closed over, then he's wrong on that
It did happen to be the shortest implementation I found and I like brevity (despite whatever misunderstandings its author apparently seems to be under)
for example, in clj you can do
(def primes (remove (fn [x] (some #(zero? (mod x %)) @(resolve 'primes))) (iterate inc 2)))
where it will resolve and take the value of primes over and over, obviously nothing being closed over or cleared, and it works fine@U0NCTKEV8 i just came across this — if your explanation is that some
short circuits, then why would this terminate on prime numbers? for a prime number, the predicate function is never true.
A lazy seq is basically a pair, the first element is a function that when called returns a new seq, the second cell is nil, and forcing the seq checks if the function cell is nil, and if it is it returns the value of the second element, other wise it takes the function from the function cell, sets the function cell to nil and invokes the function, setting the second cell to the result
Your primes definition causes a read of the value cell in the middle of the caching logic, so it reads a nil from the second cell, so it thinks that the sequence has ended
If the caching implementation in lazy seqs used a proper sentinel value instead of re-using nil (a valid empty sequence) then you would get an error
I don't know, but I would guess that the exact caching mechanism inside lazy seqs is considered an implementation detail
that makes sense. it seems like this works with clojure and not with clojurescript because the caching implementation is different
https://ask.clojure.org/index.php/11787/lazy-clojurescript-does-allow-self-referential-sequences?show=11787#q11787 <- the other answer on the ask is verry good
My answer is about the caching is incorrect, it isn't the caching in lazy seqs, it is the way locals clearing for :once fns works
But it is a similar sort of confusion, a once fn being called reentrantly (but they are :once fns because you promise to only call them once) and in the second call the nil'ed out value is being confused for an empty seq
Yeah, found that article in the annals of Slack, reading it now. "Supposed" is a bit of a strong word though given that Clojure doesn't have a standard. And while the JVM implementation is definitely a reference one, this particular tidbit might easily end up being an implementation detail. But I could be wrong.
Is it possible that mod
and zero?
are producing different values on cljs due it using floating point instead of integers?
It doesn't even get to calling those function - it falls into the first iteration of some
time and time again.
So it seems that it works in Clojure due to a {:once true}
function being used.
It's unclear to me whether it's actually intended or not, as I can easily see how {:once true}
could be introduced only as an optimization due to the promise that the inner fn
of LazySeq
will be called only once (which is not true in this case).
In any case, CLJS doesn't have {:once true}
, so anything that relies on it in CLJ must either be written in a different way in the core of CLJS itself or it will not work.
NM, in lazy-seq
It's from the linked article. But remove
uses lazy-seq
which clears its inner function's locals on the first call - so the second call unconditionally returns nil
. In CLJS, it doesn't.
@alexmiller Could you please confirm whether the above example with (def primes ...)
is something that works intentionally and not by accident?
If it's intentional, then something should be done on the CLJS side - either a change to how it works or an addition to the "Differences from Clojure" page. In this case, I'll create an Ask summarizing the discussion.
If it's not intentional, then... Not even sure.
Sorry, what's the question?
This code
=> (def primes (remove
(fn [x]
(some #(zero? (mod x %)) primes))
(iterate inc 2)))
=> (take 1 primes)
works without issues on CLJ but fails with stack overflow on CLJS.
It works on CLJ only because remove
uses lazy-seq
which, in turn, uses fn*
with {:once true}
.
CLJS doesn't have any analog to {:once true}
, so lazy-seq
doesn't work that way.
The question is, whether the behavior observed on CLJ is something intentional or a happenstance.The docstring of lazy-seq
in both implementations says:
> yields a Seqable object that will invoke the body only the first time seq is called
The difference is, in CLJ entering a function counts as an invocation in this case, and in CLJS exiting a function does.
So in CLJ, a lazy seq can refer to itself as above, but in CLJS it cannot.
how can it not be intentional with the special metadata handled by the compiler?
but I'm not sure you've described this properly. the once is a hint that allows reference clearing (because it will only be invoked once), not sure if that's the difference you're actually describing
since the head of primes is held in a var here regardless, I don't think that actually matters
the outer primes var and the inner primes reference to the var refer to the same sequence instance, guarded by the same locks, this is all inherent in how seqs are implemented. I don't know how CLJS differs from this.
note that there were two reference implementations provided. the other one also works on clj, fails on cljs and perhaps does not rely on {:once true} (I haven't drilled into those details to understand it yet or not)
but given that we have two completely independent and quite different implementations that both "work in clj" and "fail in cljs", i am inclined to think nothing here is "by accident"
If you mean the other way of implementing primes
you mentioned above, then it also seems to rely on :once
because it also uses lazy-seq
.
I don't think either "relies" on :once - that's just allowing local clearing to clear the head and avoid head holding, but you're head-holding regardless, so don't understand how that matters
I have implemented the above code without using :once
. It started to fail with stack overflow on CLJ as well.
the reason this stack overflows has nothing to do with the problem being discussed, it is because you swapped remove with filter a not around some
it is :once that makes the difference, but primes is not closed over, so :once doesn't matter for it, :once only applies to the sequence produced by iterate
(defmacro my-lazy-seq
[& body]
(list 'new 'clojure.lang.LazySeq (list* 'fn* [] body)))
=> #'dev/my-lazy-seq
(defn my-filter [pred coll]
(my-lazy-seq
(when-let [s (seq coll)]
(if (chunked-seq? s)
(let [c (chunk-first s)
size (count c)
b (chunk-buffer size)]
(dotimes [i size]
(let [v (.nth c i)]
(when (pred v)
(chunk-append b v))))
(chunk-cons (chunk b) (filter pred (chunk-rest s))))
(let [f (first s) r (rest s)]
(if (pred f)
(cons f (filter pred r))
(filter pred r)))))))
=> #'dev/my-filter
(def primes (my-filter (fn [x]
(not (some #(zero? (mod x %)) primes)))
(iterate inc 2)))
=> #'dev/primes
(take 1 primes)
Error printing return value (StackOverflowError) at clojure.core/seq (core.clj:139).
null
CLJS' implementation of filter
and lazy-seq
is almost identical. Except for :once
. Given that, and the above demonstration, I fail to see how the difference is not caused by :once
.
I dunno, don't have time to think about it any more rn, sorry
The inner primes
becomes a local, right? It's not the var, it's a local binding.
On the second call of the :once
function (which happens only when we go beyond the realized part of the lazy-seq
), it becomes nil
- there's nothing to iterate over. That's why this pseudo-recursive iteration over a lazy-seq never unrolls itself from inside out.
In any case, I'll create a question on http://ask.clojure.org then.
Created https://ask.clojure.org/index.php/11787/lazy-clojurescript-does-allow-self-referential-sequences for the above.
i just read the ask and your explanation regarding :once but I think that's kind of missing the point. Laziness is broken if this doesn't work as written. Try it in a language like Haskell where laziness is also default and it will work fine.
So, sure, if you wanna say that the fix is to add :once to CLJS, that's fine. It's an implementation detail that really doesn't matter from the perspective of the user however. All the user wants is for laziness to work.
I'm describing the cause in the post, I'm not offering any solutions let alone implementation details.
As @U0NCTKEV8 stated, the reason this works is the same reason the user expects it to work, namely the short-circuiting nature of some
. So I think I was correct in objecting to your explanation & trying to persuade you away from the pontificating (which did not sit well with my gut).
The stackoverflow is the result of once, but the only thing once actually effects is the sequence created by iterate
I still consider that an implementation detail though. I'll bet this works in Haskell without the :once keyword appearing anywhere (Haskell doesn't have keywords so this is a sure bet) 😂
Yes but how is that sequence different between an impl containing :once and one that doesn't ?
They behave differently with or without once, and the difference is in regard to the sequence passed in as argument, the one created with iterate in this case
There is a closure in that code though: the lambda inside the anon fn closes over ‘x’
My entire reason for wading in on this is just people kept repeating the thing about closing over primes
But if that fn doesn't close over primes, then it isn't a closure as there's nothing else for it to close over
Ok then why is this happening?
internal.js:114 Uncaught TypeError: str.trim is not a function
at Object.goog.string.internal.trim (internal.js:114:18)
at Object.clojure$string$trim [as trim] (string.cljs:199:4)
Seems like goog library doesn’t have trim function that cljs expects it to have?