:thumbsup:
Here we go @alexmiller https://github.com/clojure/clojure-site/pull/518 I included all content suggested by you and excluded all blog posts that I found except this: https://www.pixelated-noise.com/blog/2020/09/10/what-spec-is/ I included the post above because seems to be a good source of information, and excluded the remained because I think that maybe it's good to curate them and verify if it's a good source of information.
Is there a simpler way to write (rest (reductions conj [] (range 10)))
?
This isn't necessarily simpler, but it is slightly shorter: (reductions conj [0] (range 1 10))
Your version looks pretty simple to me.
Thanks, were you going to sign the Contrib Agreement so I can merge?
Yes, I'll sign
I will probably move it somewhere else but would like to merge your contribution first
alright, thanks
Done
thx
My first contribution, Yey!
😎
with that much nesting, an arrow macro or let block can help
ins)user=> (->> (range 10)
(reductions conj [])
(rest))
([0] [0 1] [0 1 2] [0 1 2 3] [0 1 2 3 4] [0 1 2 3 4 5] [0 1 2 3 4 5 6] [0 1 2 3 4 5 6 7] [0 1 2 3 4 5 6 7 8] [0 1 2 3 4 5 6 7 8 9])
(ins)user=> (let [numbers (range 10)
prefixes (reductions conj [] numbers)]
(rest prefixes))
([0] [0 1] [0 1 2] [0 1 2 3] [0 1 2 3 4] [0 1 2 3 4 5] [0 1 2 3 4 5 6] [0 1 2 3 4 5 6 7] [0 1 2 3 4 5 6 7 8] [0 1 2 3 4 5 6 7 8 9])
I moved the resources over to https://clojure.org/community/resources#spec and linked from the rationale and guide, I think that's better
(map take (range 1 11) (repeat (range)))
I've oddly never cared much for reductions (I'm also on my phone so I haven't checked if this works)
Maybe (iterate (fn [i] (range (inc (count i)))) [0])
Or even (for [i (range 1 11)] (range i))
Nice @alexmiller
Thank you!
Anyone aware of a tool that will help me to automatically remove the conditional in forms like (if true :a :b)
? I.e. turn this form into just :a
Not really, but tools.analyzer should get you most of the way (I just don't seem to find an actual constant folding pass).
https://github.com/jonase/kibit can probably do this
@martinklepsch I would recommend rewrite-clj to do this for you, if you actually want to rewrite the code. It now comes with babashka as a built-in library.
@martinklepsch https://gist.github.com/borkdude/77369ba1b2d0fbd2608a8d12f518ade3
I am trying to create some presentation slides with reveal-cljs and have clojure code on them.
I am struggling with coming up with a good way to re-present the example code in my slides code.
Using regular strings is tedious as I have to escape quotes etc.
I thought I’d be smart and use str
in combination with cljfmt
which works well so far but I can’t figure out
a way to force new lines with cljfmt
(ns reveal.slides
(:require [cljfmt.core :as cljfmt]))
(def default-options
{:project-root "."
:file-pattern #"\.clj[csx]?$"
:ansi? true
:indentation? true
:insert-missing-whitespace? true
:remove-multiple-non-indenting-spaces? false
:remove-surrounding-whitespace? true
:remove-trailing-whitespace? true
:remove-consecutive-blank-lines? true
:indents cljfmt/default-indents
:alias-map {}})
(def let-example
'(let [a 1]
(str "Hello " a)))
(defn code [c]
[:pre
[:code {:class "clojure"} (cljfmt/reformat-string (str c) default-options)]])
(def slide-1
[:section
[:h1 "My Presentation"]
(code let-example)])
This renders (let [a 1] (str "Hello " a))
where I’d like for it to be
(let [a 1]
(str "Hello " a))
Would anyone be able to help out or let me know if I am completely on the wrong path
Cheers 🙂@borkdude wow, that looks pretty cool!
I would just use strings, and put up with adding the escapes, to maintain the desired formatting. Or for bigger examples I’d put them in an external file and write a macro that slurp
s the file to a string at read time
Yeah maybe files + slurp is the way to go. That way I’d at least have syntax highlighting when I am typing the example code
@borkdude thanks for that snippet, admittedly the zipper API is something I haven’t really understood so far but with that example I was able to implement my thing!
I think this is relevant to the qualified keywords discussion happening earlier: What do you think is a good approach here?
(let [{:person/keys [id], :address/keys [id]} {:person/id "person-id"
:address/id "address-id"}]
;Bad. One of 'id' is shadowed.
id)
On one hand, it’s nice to be able to hold both :person and :address ID in the same map; On the other hand, you still run into shadowing issues when destructuring.I know this possible syntax but looking for better ideas:
(let [{person-id :person/id address-id :address/id} {:person/id "person-id"
:address/id "address-id"}]
;Not great. Feels too much like using unqualified keywords in practice...
[person-id address-id])
This is what I do, and I don't see any problem with it, but YMMV. :man-shrugging::skin-tone-2:
Yep
I’ve been writing this little REPL utility for the last couple of days. Generates at runtime all the de-structuring code that you never wanted to write by hand:
https://github.com/raspasov/alexandria-clj/blob/main/src/ax/auto_let/core.cljc#L78
(def m {:name :alice :favorite {:music [{:genre :rock} {:genre :trance}] :friends #{:bob :clara}}}) (de m) ;=> [{:keys [name favorite]} m {:keys [music friends]} favorite [{:keys [genre]}] music]
I'm having a bunch of problems with test.check, probably due to shrinking attempts - i.e. certain exceptions (like erroneously calling count on a fn returned by a generator) lead to infinite loops is there some way to just abort in such a case?
Hey, I'm a new clj dev and I wanted to do a quick sanity check on how I'm developing, see if I'm doing something obviously wrong or missing something that could increase my productivity:
1. Using IntelliJ with Cursive, Editing with Parinfer + Structural Movement Hotkeys
2. While writing code, I go to the namespace I'm editing in the REPL. I use clojure.tools.namespace to refresh the namespace on every save
3. When writing a fn, I call that function in the REPL, putting in reasonable args, when I want to check it
4. When I'm having trouble figuring out some syntax or how to get something working, I copy/paste that part of the code to a (let [fn-under-test (fn...
in the REPL and add a lot of debux.core/dbg
statements so I can see what state the data is along the way. I then make adjustments and copy/paste the adjustments back to the main code
5. I only do webdev stuff, so I'm mostly writing frontend components. I use shadow-cljs ... watch
to instantly see all changes I make hot-reloaded to get instant feedback
2. I have a shortcut for Tools > REPL > Sync Files in REPL
Other stuff sounds pretty good to me.
Nothing bad about your workflow, just one tip for something I've learned over time - you want a live REPL, you don't want to ever type in it.
It's better to have comment
blocks (aka Rich comments) where you set up mock arguments, call your function, etc
hey team, I have some text, where the text can contain a subset of html (<strong>, <br>, <em>, etc) Is there a “html to markdown” parser you’d recommend for clojure, that takes html like above and converts it to a safe markdown string?
I have something, it's not perfect but it got us pretty far when migrating from Markdown - let me dig it out
Oh wait, maybe I misread - you're looking for reverse operation
I think I’m indeed looking for reverse op: html->markdown
https://github.com/nathell/clj-tagsoup gets you halfway there
Yes, JSoup or hickory are super helpful here
Nice, thanks team!
Flexmark has a html->md converter https://github.com/vsch/flexmark-java#latest-additions-and-changes
Continuing in my spelunking into the guts of Clojure, I found this surprising:
(meta (empty (with-meta (range 100) {:x true}))) => nil
but
(meta (empty (with-meta (into () (range 100)) {:x true}))) => {:x true}
so a LazySeq
loses its metadata on empty
while every other PersistentCollection
(based on a quick sampling) does not. @jimmy, you were a big help yesterday, any thoughts on this one?
To compound the surprise:
(meta (empty (with-meta (range 0) {:x true}))) => {:x true}
(meta (empty (with-meta (range 1) {:x true}))) => nil
@cch1 range
returns a specialized Range type, it is not a generic LazySeq. I'd guess that the impl just drops the metadata since calling empty
for this particular seq is sort of useless. surprised it doesn't throw to be honest.
Everything has to preserve metadata separately and it is no wonder there are functions even in core where someone forgot
flexmark worked like a charm. Thanks team!
When my code preserves metadata it is by accident because I am just composing core functions that do. If I was making a collection utils library I would pay more attention to that but usually it is easy to forget. And I almost never use metadata, probably because of the suspicion that something will drop it
empty is defined by IPersistentCollection.empty(). ASeq (base class of many, but not all) seq impls does not preserve meta on empty. PersistentList (subclass of ASeq, but also a concrete collection) overrides that and preserves metadata on empty
in general, metadata is preserved on collection operations (assoc, conj, disj, etc) but not on seq operations
Is there any way to temporarily "turn off" binding conveyance for particular dynamic variables?
But isn't empty
a collection operation? It returns an empty collection of a similar type instead of always ()
or something.
Or would I have better luck manually creating thread local storage?
what are you trying to solve?
I'm binding thread-local values to allow me to introspect the stack and conditionally jump to parts of it with catch/throw. However whenever we cross a thread boundary, that breaks, because the existing value is copied.
This is further complicated by the fact that in core.async the thread that's invoking the code may be different between the part where the binding is created and when it is introspected.
My original thought was that I could just store the thread and filter the results I view based on what thread is currently introspecting.
But in order to do that I'd need to conditionally store some unique value to the current go block and use that instead of the thread value in the context of go blocks.
With those types of async systems I wouldn't trust thread locals or even the stack making any sense
it is, but seqs are such a weird beast - they are collections from type perspective, but they are also often not actual collections but views into them (or other things) and it's really up to the coll themselves how they handle these ops.
I mean I could just make it undefined behavior to use these ops inside the top-level of a go block.
maybe it's better to say that colls preserve metadata and seqs don't
I think of seqs as immutable iterators although lists blur the line
I don't think it's helpful to see them as iterators
Sounds like you would want full green threads instead of just inversion of control on a thread pool
I'd say more along the lines of I would need an abstraction that was aware of the code I'm writing.
because they are not stateful in the same way - you don't "lose" the head of the seq (necessarily) and can still use it again
The only reason I actually care about core.async here anyway is that I'm writing library code and don't want to mandate my users avoid anything.
logical or virtual lists is imo the best way to think about them
As it is, I'll just have to have a caveat in the documentation about not having anything go over a park boundary.
Question: the Reader reference says that symbols using /
or .
are said to be fully qualified. Could this somehow be an argument to not use a fully qualified symbol (e.g. foo.bar
as an alias (in (:require [foo.bar.baz :as foo.baz])
)
I'm asking this because of a potentially ambiguous case in clj-kondo CLJS linting where foo.bar
can refer to either a namespace property access or an alias. Related issue: https://github.com/clj-kondo/clj-kondo/issues/1248
It is very helpful to me. I also look at coroutines as mutable one-shot continuations -- kind of the reverse.
uncons :: [a] -> Maybe (a, [a])
(i.e. first
+ rest
) in Haskell vs. fn next(&mut self) -> Option<Self::Item>
in Rust where rest
is "returned" by mutating the iterator and incidentally making the iteration one-shot.
I know an old blog post of Stuart Sierra’s at least mentions multi-segment aliases https://stuartsierra.com/2015/05/10/clojure-namespace-aliases
> Keep enough trailing parts to make each alias unique. Did you know that namespace aliases can have dots in them? > [clojure.data.xml :as data.xml] > [clojure.xml :as xml]
Thread pools break this kind of stuff all the time
The thread pooling part doesn't really break it, it's just parking that does, since it might resume with a different thread than it started with. It is what it is though. Clear docs will help ensure it's not too big an issue.
And it won't be that inconvenient for users anyway, since you can just separate out all your parking ops onto their own expressions.
I have seen those dotty aliases in heavy use
Ah yes a more traditional thread pool usage could be fine
is foo.bar really valid syntax in cljs to lookup bar on foo?
But I think e.g. promesa
will have similar issues
I hate cljs so much
hate is a strong word to use for some convenience syntax. nobody expects you do use it, (.-bar foo)
is perfectly valid and fine too
I always thought it was an accidental feature and now it can't be prevented any more
yeah, and not ambiguous, and the entire reason the '.-' form was added (to both clj and cljs) was to make it unambiguous, and then cljs went ahead and added ambiguous syntax sugar anyway
Or manifold
Do those halt execution of threads before they complete execution?
Ah, I see. Yeah, stuff that has complex interactions between threads isn't really the place that I want this to be used. We have stuff like erlang for that.
Some clojure.set
functions work on non-set collections, not deliberately - just because of implementation. It's not documented.
People keep using them on non-set collections.
Same here.
a.b
works just because it was implemented that way. AFAIK, it's not documented. And people use it.
> is foo.bar really valid syntax in cljs to lookup bar on foo?
yes and clj-kondo recently added support for this, because people are depending on it, not really my call.
but you can have a namespace alias foo
and foo.bar
so now foo.bar
is ambiguous, probably the alias is preferred
@p-himik They "work" on non-set collections, only because those functions do not check the input. They merely happen to work for some non-set inputs but this is really not recommended as you can get unexpected results. They just "work" because conj
is polymorphic
Contrary a.b
is documented behavior in CLJS:
http://cljs.github.io/api/syntax/#dot
Then I was incorrect, I didn't know it was documented.
if you want to know some intricacies with clojure.set and non-set inputs, you can read some issues about it in this project: https://github.com/borkdude/speculative @andy.fingerhut can also tell you about this, as he made a project that has checked set functions
My point was not about clojure.set
though. :) I'm somewhat familiar with its issues, that's exactly why I mentioned it in my (apparently completely wrong) analogy.
In your defense, I did hear more than once that the foo.bar
syntax wasn't officially supported, so I was surprised to read this when someone posted a clj-kondo issue about it
“maybe it’s better to say that colls preserve metadata and seqs don;t” … (coll? (range 1)) => true
but (meta (empty (with-meta (range 1) {:x true}))) => nil
.
Is it possible to update ex-data
while keeping everything else the same, including the stacktrace?
the ex-data is an immutable field of the exception, so literally, no
it is possible to construct a new ex-info with the same string, modified data, chained throwable, and then setStackTrace on it to have the stack trace from the original exception
@frozenlock there is a sad exception in pedestal that I have a utility function to rewrite
user=> (def e ((fn mark-stack [] (ex-info "foo" {:a 1}))))
#'user/e
user=> (doto (ex-info (ex-message e) (update-in (ex-data e) [:a] inc) (ex-cause e)) (.setStackTrace (.getStackTrace e)))
#error {
:cause "foo"
:data {:a 2}
:via
[{:type clojure.lang.ExceptionInfo
:message "foo"
:data {:a 2}
:at [user$mark_stack__141 invokeStatic "NO_SOURCE_FILE" 1]}]
:trace
[[user$mark_stack__141 invokeStatic "NO_SOURCE_FILE" 1]
[user$mark_stack__141 invoke "NO_SOURCE_FILE" 1]
[clojure.lang.AFn applyToHelper "AFn.java" 152]
[clojure.lang.AFn applyTo "AFn.java" 144]
[clojure.lang.Compiler$InvokeExpr eval "Compiler.java" 3706]
[clojure.lang.Compiler$DefExpr eval "Compiler.java" 457]
[clojure.lang.Compiler eval "Compiler.java" 7186]
[clojure.lang.Compiler eval "Compiler.java" 7136]
[clojure.core$eval invokeStatic "core.clj" 3202]
[clojure.core$eval invoke "core.clj" 3198]
[clojure.main$repl$read_eval_print__9112$fn__9115 invoke "main.clj" 437]
[clojure.main$repl$read_eval_print__9112 invoke "main.clj" 437]
[clojure.main$repl$fn__9121 invoke "main.clj" 458]
[clojure.main$repl invokeStatic "main.clj" 458]
[clojure.main$repl_opt invokeStatic "main.clj" 522]
[clojure.main$main invokeStatic "main.clj" 667]
[clojure.main$main doInvoke "main.clj" 616]
[clojure.lang.RestFn invoke "RestFn.java" 397]
[clojure.lang.AFn applyToHelper "AFn.java" 152]
[clojure.lang.RestFn applyTo "RestFn.java" 132]
[clojure.lang.Var applyTo "Var.java" 705]
[clojure.main main "main.java" 40]]}
user=>
is there anything in slingshot that does this?