This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-03-02
Channels
- # announcements (25)
- # babashka (76)
- # beginners (74)
- # biff (36)
- # calva (11)
- # cider (5)
- # clerk (43)
- # cljs-dev (4)
- # cljsrn (12)
- # clojure (111)
- # clojure-austin (14)
- # clojure-europe (82)
- # clojure-nl (2)
- # clojure-norway (5)
- # clojure-uk (1)
- # clojurescript (36)
- # core-async (13)
- # cursive (30)
- # datomic (12)
- # fulcro (6)
- # honeysql (9)
- # hyperfiddle (73)
- # instaparse (3)
- # introduce-yourself (1)
- # membrane (40)
- # nbb (2)
- # off-topic (6)
- # other-languages (9)
- # polylith (33)
- # reagent (2)
- # reitit (7)
- # rum (7)
- # shadow-cljs (47)
- # tools-deps (10)
- # vim (11)
- # xtdb (16)
I guess this thing has been done many times before right? An unnested let-like macro, which lets you define vars step by step in a REPL, like in a test scenario, but expands into a nested let so it doesn't actually define a bunch of global vars?
Scheme has this idea so this is nice for folks coming from that world for sure!
@U017QJZ9M7W can you link to any of the scheme stuff that does this?
I can find more examples from scmutils but the first answer here is the sort of thing I’ve seen a lot: https://stackoverflow.com/questions/56568358/racket-scheme-nested-define/56568468
(define (func n)
(define (n1! f)
(if (= f 0) 1
(* f (n1! (- f 1)))))
(define (iter i res)
(if (= i n) (+ 1 res)
(iter (+ i 1) (/ i (n1! i)))))
(iter n 0))
basically letfn
, these don’t create globals
but if you execute each sub-expression in the REPL, it still defines a global, right?
correct
once you’re inside that first define
you’re all local, but yes if you go statement by statement you get global
would be nice to check for defn
too in there
or is that handled already by macroexpansion? probably no since, duh, the form’s not expanded before it hits your macro…
for sure, I can just imagine someone assuming defn
works if def
does and writing (translation of the above)
(defn func [n]
(deflet
(defn n1! [f]
(if (zero? f)
1
(* f (n1! (dec f)))))
(defn iter [i res]
(if (= i n)
(inc res)
(iter (inc i) (/ i (n1! i)))))
(iter n 0)))
@U04V15CAJ but recommending scope-creep to someone’s macro is usually not a good idea so ignore my suggestion if it feels wrong!! a very predictable “only def
is handled here” is nice too
other maybe-you-want-to-handle-vs-ignore-cases I can think of….
(deflet
(def cake 1)
(let [x 10]
(def face x))
(do
(def z 100)))
clojure.tools.analyser.jvm
fails to analyze the following (def ^double PI 3.14)
I believe this is proper primitive tagging, am I right?
(clojure.tools.analyzer.jvm/analyze '(def ^double PI 3.14))
Execution error (ExceptionInfo) at clojure.tools.analyzer.passes.jvm.validate/eval16782$fn (validate.clj:189).
Wrong tag: clojure.core$double@4e0d541b in def: PI
You can use ^{:tag 'double}
. It won't affect any types but it will be used for method resolution in interop.
^double
is not wrong, but it won't work for vars specifically. You can still use it in other places.
yeah, I know, I was pretty sure that it works also in def
but it's resolved to a function
Var meta is resolved, so you can’t use the primitive symbol hints there unless you quote, but as Ghadi said, it won’t actually be a primitive anyways. One other case is for true constants, using a (properly quoted) double or long hint AND ^:const can result in primitive inlining
thanks @U064X3EF3, one more question about constants, do constant and primitive expression will be treated as constant, eg will (def ^{:tag 'double :const true} HALF_PI (/ PI 2.0))
be inlined or not?
of course, I know that, I'm just not sure when and how knowledge about :const
is applied.
why is this happening?
(merge {} {:foo 'bar}) ;; => {:foo bar}
why is it removing the quote?What does quote do?
what happens if you evaluate just 'bar
?
If I want to merge two maps with some quoted forms in them, merge
shouldn't be manipulating them
The '
is an instruction to not evaluate the symbol. It's much like "\n"
- you wouldn't expect the string to contain two characters after this.
or ''bar
, but it's pretty unlikely that's what you want
> (= ''bar (list 'quote 'bar))
true
user=> (-> (merge {} {:foo 'bar}) :foo (type))
clojure.lang.Symbol
'bar
produces the symbol bar
and that's what's in your result.user=> (into [] (comp cat (map (juxt identity type))) {:foo 'bar})
[[:foo clojure.lang.Keyword] [bar clojure.lang.Symbol]]
🙂I've been trying to merge some config data with another hardcoded config map that gets passed to another function that expects things quoted, and it was failing
For a problem like that you may want a tool like rewrite-clj
that can preserve information that will be discarded by the reader
How come this doesn't work:
=> (realized? (repeat 'foo))
Execution error (ClassCastException) at user/eval1 (REPL:1).
class clojure.lang.Repeat cannot be cast to class clojure.lang.IPending
According to docs, repeat
should return a lazy seq, and realized?
should operate on a lazy seq:
user=> (doc repeat)
-------------------------
clojure.core/repeat
([x] [n x])
Returns a lazy (infinite!, or length n if supplied) sequence of xs.
nil
user=> (doc realized?)
-------------------------
clojure.core/realized?
([x])
Returns true if a value has been produced for a promise, delay, future or lazy sequence.
nil
I can even do
(defn my-repeat [x] (lazy-seq (cons x (my-repeat x))))
(realized? (my-repeat 'foo))
=> false
it doesn't return a lazy seq, it returns a clojure.lang.Repeat which doesn't implement IPending
ClojureScript 1.11.60
cljs.user=> (realized? (repeat 10 'foo))
false
cljs.user=> (realized? (repeat 10))
false
user=> (realized? (for [i (range 10)] i))
false
user=> (realized? (cons 1 (for [i (range 10)] i)))
Execution error (ClassCastException) at user/eval156 (REPL:1).
class clojure.lang.Cons cannot be cast to class clojure.lang.IPending (clojure.lang.Cons and clojure.lang.IPending are in unnamed module of loader 'app')
user=> (realized? (lazy-seq (cons 1 (for [i (range 10)] i))))
false
user=> (realized? (seq (lazy-seq (cons 1 (for [i (range 10)] i)))))
Execution error (ClassCastException) at user/eval190 (REPL:1).
class clojure.lang.Cons cannot be cast to class clojure.lang.IPending (clojure.lang.Cons and clojure.lang.IPending are in unnamed module of loader 'app')
user=> (realized? (seq [1 2 3]))
Execution error (ClassCastException) at user/eval208 (REPL:1).
class clojure.lang.PersistentVector$ChunkedSeq cannot be cast to class clojure.lang.IPending (clojure.lang.PersistentVector$ChunkedSeq and clojure.lang.IPending are in unnamed module of loader 'app')
user=>
repeat used to be a lazy seq so this seems to be a breaking change https://github.com/clojure/clojure/commit/ee69dc46fc81ab2cee12fe69cc14679ea222585f
user=> (realized? (lazy-seq (seq [1 2 3])))
false
user=> (realized? (rest (lazy-seq (seq [1 2 3]))))
Execution error (ClassCastException) at user/eval214 (REPL:1).
class clojure.lang.PersistentVector$ChunkedSeq cannot be cast to class clojure.lang.IPending (clojure.lang.PersistentVector$ChunkedSeq and clojure.lang.IPending are in unnamed module of loader 'app')
user=>
basically, realized? for a seq only sort of returns something useful if literally looking at a value constructed by a call to lazy-seq
but the rest of any lazy-seq value might not be constructed using lazy-seq (it only needs to be a seq)
and calling seq on someting constructed with lazy-seq is by contract going to return something not constructed by lazy-seq
IPending was originally a thing for delays and promises (maybe futures?), and then someone came along and tried it on a lazy-seq, and it didn't work, and said "hey, it should work on lazy-seqs" and some how they get their patch merged, despite the implementation of it for lazy-seqs being pretty useless
> repeat used to be a lazy seq so this seems to be a breaking change
repeat
still returns a lazy seq (it's just not a LazySeq)
there is an existing ticket to make realized? return true for anything that doesn't implement IPending, which would make it return true for the seq returned by repeat (https://clojure.atlassian.net/browse/CLJ-1752)
https://clojure.atlassian.net/browse/CLJ-1751 about realized? and LongRange has some interesting commentary
it was a long point of discussion
if anything here, I would want realized?
to say more nuanced things about when it can be used
and what you should expect about it
there are pretty much no cases where I would use realized? with a sequence
I meant, breaking as in (realized? (repeat ..))
probably used to work but not anymore, but 🤷
well that was 8 years ago. if you're the first to notice ...
but I don't really consider it breaking given that realized? "doesn't work" on lots of stuff and it's about a property of the thing. I don't think repeat promises to return something that works with realized?
I didn't even notice it, it was grav and I don't really care given that I've never really used realized? on seqs either 😆
> repeat
still returns a lazy seq (it's just not a LazySeq)
Uh 😓 this hit me! 😅 So, to rephrase:
• there are sequences lazy as, strictly speaking, plain clojure.lang.LazySeq;
• others are lazy as (logically?) infinite, not clojure.lang.LazySeq.
Correct?
I'd have never attempted to call (realized? (repeat ...)
but I wasn't aware of this nuance.
I went through the first 5 pages on http://grep.app and couldn't find realized?
on lazy seqs, mostly future/promise stuff
@U017QU43430 The type differences are usually relevant when implementing a protocol on seqs like this one: https://github.com/borkdude/finitize/blob/master/src/finitize/core.cljc
well you probably shouldn't do that anyways. that code is missing Cycle for example.
and LongRange
well, I guess LongRange is finite
but cycle can take an infinite seq and is then infinite
the best thing with sequences is to assume that you don't know anything about the size, finite/infinite, or how much of it is realized
@U04V15CAJ sure, in general I'm well aware of that; this particular case was a blind spot to me, I was taking for granted people intended type LazySeq when speaking generically of lazy sequences (nevertheless I find the doc string in repeat
clear enough to avoid the confusion).
(that library never got further than 0.0.1-SNAPSHOT anyway and is now 4 years old 😆 )
probably more efficient to implement something like that with the new partitionv
in 1.12 too :)
if you did (first (partitionv n coll)), you'd get a realized vector (built with into using transients and reduce). and the lazy next partition utilizes the new fast IDrop implementation for colls that support it under the hood (via nthrest).
I used that lib in https://borkdude.github.io/re-find.web/ to limit the amount of things that were being compared to not freeze the browser, in e.g. (= (range) (range))
. that was 4 years ago. maybe today I would find a different solution