clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
alexmiller 2021-04-06T00:27:24.053500Z

:thumbsup:

marciol 2021-04-06T01:04:28.053700Z

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.

wombawomba 2021-04-06T01:12:01.054500Z

Is there a simpler way to write (rest (reductions conj [] (range 10)))?

2021-04-06T01:28:42.054600Z

This isn't necessarily simpler, but it is slightly shorter: (reductions conj [0] (range 1 10))

2021-04-06T01:29:15.054800Z

Your version looks pretty simple to me.

alexmiller 2021-04-06T01:41:12.055300Z

Thanks, were you going to sign the Contrib Agreement so I can merge?

marciol 2021-04-06T01:47:41.055500Z

Yes, I'll sign

alexmiller 2021-04-06T01:48:15.055700Z

I will probably move it somewhere else but would like to merge your contribution first

wombawomba 2021-04-06T01:54:48.055900Z

alright, thanks

marciol 2021-04-06T01:54:57.056100Z

Done

alexmiller 2021-04-06T01:55:23.056300Z

thx

marciol 2021-04-06T01:55:47.056500Z

My first contribution, Yey!

2
🎉 1
alexmiller 2021-04-06T02:28:50.056700Z

😎

2021-04-06T02:29:08.056900Z

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])

alexmiller 2021-04-06T02:36:13.057300Z

I moved the resources over to https://clojure.org/community/resources#spec and linked from the rationale and guide, I think that's better

2021-04-06T02:54:47.057800Z

(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)

➕ 2
2021-04-06T03:00:15.058Z

Maybe (iterate (fn [i] (range (inc (count i)))) [0])

2021-04-06T03:03:59.058200Z

Or even (for [i (range 1 11)] (range i))

➕ 1
marciol 2021-04-06T03:08:45.058400Z

Nice @alexmiller

marciol 2021-04-06T03:08:50.058600Z

Thank you!

martinklepsch 2021-04-06T07:13:46.061600Z

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

nilern 2021-04-06T08:14:52.062700Z

Not really, but tools.analyzer should get you most of the way (I just don't seem to find an actual constant folding pass).

yuhan 2021-04-06T08:29:34.063600Z

https://github.com/jonase/kibit can probably do this

borkdude 2021-04-06T08:44:16.064100Z

@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.

Jakob Durstberger 2021-04-06T09:43:04.068400Z

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 🙂

martinklepsch 2021-04-06T09:55:42.068700Z

@borkdude wow, that looks pretty cool!

jkxyz 2021-04-06T09:56:51.068900Z

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 slurps the file to a string at read time

Jakob Durstberger 2021-04-06T10:04:59.069100Z

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

martinklepsch 2021-04-06T10:47:40.069300Z

@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!

🎉 5
raspasov 2021-04-06T12:17:17.073500Z

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.

raspasov 2021-04-06T12:19:22.074600Z

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])

flowthing 2021-04-06T12:38:31.074900Z

This is what I do, and I don't see any problem with it, but YMMV. :man-shrugging::skin-tone-2:

👌 3
alexmiller 2021-04-06T12:40:57.075700Z

Yep

raspasov 2021-04-06T13:00:23.076300Z

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:

raspasov 2021-04-06T13:01:15.076900Z

(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]

Elso 2021-04-06T13:07:33.081400Z

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?

Azzurite 2021-04-06T13:14:45.086300Z

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

raspasov 2021-04-06T14:14:39.089700Z

2. I have a shortcut for Tools > REPL > Sync Files in REPL

raspasov 2021-04-06T14:15:20.089900Z

Other stuff sounds pretty good to me.

Ben Sless 2021-04-06T15:56:27.092600Z

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.

☝️ 2
Ben Sless 2021-04-06T15:56:55.092800Z

It's better to have comment blocks (aka Rich comments) where you set up mock arguments, call your function, etc

☝️ 1
2021-04-06T17:56:53.094800Z

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?

lukasz 2021-04-06T17:57:40.095300Z

I have something, it's not perfect but it got us pretty far when migrating from Markdown - let me dig it out

lukasz 2021-04-06T17:58:18.095500Z

Oh wait, maybe I misread - you're looking for reverse operation

2021-04-06T17:59:00.095700Z

I think I’m indeed looking for reverse op: html->markdown

nilern 2021-04-06T18:00:08.095900Z

https://github.com/nathell/clj-tagsoup gets you halfway there

lukasz 2021-04-06T18:01:52.096200Z

Yes, JSoup or hickory are super helpful here

2021-04-06T18:03:51.096400Z

Nice, thanks team!

nilern 2021-04-06T18:05:57.096600Z

Flexmark has a html->md converter https://github.com/vsch/flexmark-java#latest-additions-and-changes

cch1 2021-04-06T19:41:51.100400Z

Continuing in my spelunking into the guts of Clojure, I found this surprising:

(meta (empty (with-meta (range 100) {:x true}))) =&gt; nil
but
(meta (empty (with-meta (into () (range 100)) {:x true}))) =&gt; {: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}))) =&gt; {:x true}
(meta (empty (with-meta (range 1) {:x true}))) =&gt; nil

thheller 2021-04-06T19:46:02.101500Z

@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.

nilern 2021-04-06T19:47:42.102800Z

Everything has to preserve metadata separately and it is no wonder there are functions even in core where someone forgot

2021-04-06T19:49:26.104300Z

flexmark worked like a charm. Thanks team!

🎉 1
nilern 2021-04-06T19:51:47.106100Z

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

alexmiller 2021-04-06T20:07:47.107700Z

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

alexmiller 2021-04-06T20:09:45.108800Z

in general, metadata is preserved on collection operations (assoc, conj, disj, etc) but not on seq operations

2021-04-06T20:13:52.110400Z

Is there any way to temporarily "turn off" binding conveyance for particular dynamic variables?

nilern 2021-04-06T20:14:05.110600Z

But isn't empty a collection operation? It returns an empty collection of a similar type instead of always () or something.

2021-04-06T20:14:11.110800Z

Or would I have better luck manually creating thread local storage?

ghadi 2021-04-06T20:15:03.110900Z

what are you trying to solve?

2021-04-06T20:16:28.111100Z

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.

2021-04-06T20:17:00.111400Z

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.

2021-04-06T20:19:03.111600Z

My original thought was that I could just store the thread and filter the results I view based on what thread is currently introspecting.

2021-04-06T20:19:43.111800Z

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.

nilern 2021-04-06T20:19:48.112Z

With those types of async systems I wouldn't trust thread locals or even the stack making any sense

alexmiller 2021-04-06T20:20:27.112200Z

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.

2021-04-06T20:21:03.112400Z

I mean I could just make it undefined behavior to use these ops inside the top-level of a go block.

alexmiller 2021-04-06T20:21:12.112600Z

maybe it's better to say that colls preserve metadata and seqs don't

nilern 2021-04-06T20:21:48.112800Z

I think of seqs as immutable iterators although lists blur the line

alexmiller 2021-04-06T20:23:37.113Z

I don't think it's helpful to see them as iterators

nilern 2021-04-06T20:23:55.113200Z

Sounds like you would want full green threads instead of just inversion of control on a thread pool

2021-04-06T20:24:43.113400Z

I'd say more along the lines of I would need an abstraction that was aware of the code I'm writing.

alexmiller 2021-04-06T20:24:56.113600Z

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

2021-04-06T20:25:02.113800Z

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.

alexmiller 2021-04-06T20:25:18.114Z

logical or virtual lists is imo the best way to think about them

2021-04-06T20:25:19.114200Z

As it is, I'll just have to have a caveat in the documentation about not having anything go over a park boundary.

borkdude 2021-04-06T20:27:45.115300Z

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

nilern 2021-04-06T20:29:21.115900Z

It is very helpful to me. I also look at coroutines as mutable one-shot continuations -- kind of the reverse.

nilern 2021-04-06T20:31:56.116100Z

uncons :: [a] -&gt; Maybe (a, [a]) (i.e. first + rest) in Haskell vs. fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; in Rust where rest is "returned" by mutating the iterator and incidentally making the iteration one-shot.

Derek Passen 2021-04-06T20:32:42.117100Z

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

Derek Passen 2021-04-06T20:33:10.117500Z

> 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]

nilern 2021-04-06T20:34:23.117600Z

Thread pools break this kind of stuff all the time

2021-04-06T20:35:55.117800Z

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.

2021-04-06T20:36:22.118300Z

And it won't be that inconvenient for users anyway, since you can just separate out all your parking ops onto their own expressions.

nilern 2021-04-06T20:36:31.118600Z

I have seen those dotty aliases in heavy use

➕ 1
nilern 2021-04-06T20:38:16.119Z

Ah yes a more traditional thread pool usage could be fine

2021-04-06T20:38:21.119200Z

is foo.bar really valid syntax in cljs to lookup bar on foo?

nilern 2021-04-06T20:38:37.119300Z

But I think e.g. promesa will have similar issues

2021-04-06T20:39:01.119600Z

I hate cljs so much

thheller 2021-04-06T20:41:08.120300Z

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

nilern 2021-04-06T20:41:49.121700Z

I always thought it was an accidental feature and now it can't be prevented any more

2021-04-06T20:42:35.122600Z

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

nilern 2021-04-06T20:48:17.123400Z

Or manifold

2021-04-06T20:50:05.123800Z

Do those halt execution of threads before they complete execution?

2021-04-06T20:54:02.124Z

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.

p-himik 2021-04-06T21:11:42.124400Z

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.

borkdude 2021-04-06T21:12:53.125500Z

> 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

borkdude 2021-04-06T21:15:30.125700Z

@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

borkdude 2021-04-06T21:16:05.125900Z

Contrary a.b is documented behavior in CLJS: http://cljs.github.io/api/syntax/#dot

p-himik 2021-04-06T21:26:25.126700Z

Then I was incorrect, I didn't know it was documented.

borkdude 2021-04-06T21:27:44.126900Z

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

p-himik 2021-04-06T21:29:54.127300Z

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.

borkdude 2021-04-06T21:30:40.127500Z

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

cch1 2021-04-06T22:34:47.127900Z

“maybe it’s better to say that colls preserve metadata and seqs don;t” … (coll? (range 1)) =&gt; true but (meta (empty (with-meta (range 1) {:x true}))) =&gt; nil .

2021-04-06T23:54:06.128900Z

Is it possible to update ex-data while keeping everything else the same, including the stacktrace?

alexmiller 2021-04-06T23:58:04.129700Z

the ex-data is an immutable field of the exception, so literally, no

alexmiller 2021-04-06T23:59:09.131400Z

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

ghadi 2021-04-06T23:59:13.131500Z

@frozenlock there is a sad exception in pedestal that I have a utility function to rewrite

2021-04-06T23:59:44.132300Z

user=&gt; (def e ((fn mark-stack [] (ex-info "foo" {:a 1}))))
#'user/e
user=&gt; (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=&gt;

☝️ 1
🙌 1
alexmiller 2021-04-06T23:59:48.132700Z

is there anything in slingshot that does this?