Fork me on GitHub
#clojure
<
2021-04-06
>
wombawomba01:04:01

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

andy.fingerhut01:04:42

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

andy.fingerhut01:04:15

Your version looks pretty simple to me.

wombawomba01:04:48

alright, thanks

noisesmith02:04:08

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

hiredman02:04:47

(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
hiredman03:04:15

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

hiredman03:04:59

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

1
martinklepsch07:04:46

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

nilern08:04:52

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

borkdude08:04:16

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

martinklepsch09:04:42

@U04V15CAJ wow, that looks pretty cool!

martinklepsch10:04:40

@U04V15CAJ 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
Jakob Durstberger09:04:04

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 🙂

jkxyz09:04:51

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 Durstberger10:04:59

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

raspasov12:04:17

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.

raspasov12:04:22

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

flowthing12:04:31

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

👌 3
raspasov13:04:23

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:

raspasov13:04:15

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

Elso13:04:33

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?

Azzurite13:04:45

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

raspasov14:04:39

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

raspasov14:04:20

Other stuff sounds pretty good to me.

Ben Sless15:04:27

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 Sless15:04:55

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

☝️ 1
stopa17:04:53

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?

lukasz17:04:40

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

lukasz17:04:18

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

stopa17:04:00

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

lukasz18:04:52

Yes, JSoup or hickory are super helpful here

stopa18:04:51

Nice, thanks team!

stopa19:04:26

flexmark worked like a charm. Thanks team!

🎉 1
cch119:04:51

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

thheller19:04:02

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

nilern19:04:42

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

nilern19:04:47

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

alexmiller20:04:47

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

alexmiller20:04:45

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

Joshua Suskalo20:04:52

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

nilern20:04:05

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

alexmiller20:04:27

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.

alexmiller20:04:12

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

nilern20:04:48

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

alexmiller20:04:37

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

alexmiller20:04:56

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

alexmiller20:04:18

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

nilern20:04:21

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

nilern20:04:56

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.

cch122:04:47

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

Joshua Suskalo20:04:11

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

ghadi20:04:03

what are you trying to solve?

Joshua Suskalo20:04:28

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.

Joshua Suskalo20:04:00

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.

Joshua Suskalo20:04:03

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

Joshua Suskalo20:04:43

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.

nilern20:04:48

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

Joshua Suskalo20:04:03

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

nilern20:04:55

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

Joshua Suskalo20:04:43

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

Joshua Suskalo20:04:02

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.

Joshua Suskalo20:04:19

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

nilern20:04:23

Thread pools break this kind of stuff all the time

Joshua Suskalo20:04:55

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.

Joshua Suskalo20:04:22

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

nilern20:04:16

Ah yes a more traditional thread pool usage could be fine

nilern20:04:37

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

nilern20:04:17

Or manifold

Joshua Suskalo20:04:05

Do those halt execution of threads before they complete execution?

Joshua Suskalo20:04:02

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.

borkdude20:04:45

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

Derek Passen20:04:42

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 Passen20:04:10

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

nilern20:04:31

I have seen those dotty aliases in heavy use

1
hiredman20:04:21

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

hiredman20:04:01

I hate cljs so much

thheller20:04:08

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

nilern20:04:49

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

hiredman20:04:35

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

p-himik21:04:42

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.

borkdude21:04:30

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

borkdude21:04:05

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

p-himik21:04:25

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

borkdude21:04:44

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 @U0CMVHBL2 can also tell you about this, as he made a project that has checked set functions

p-himik21:04:54

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.

borkdude21:04:40

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

borkdude21:04:53

> 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

frozenlock23:04:06

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

hiredman23:04:44

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

☝️ 1
🙌 1
alexmiller23:04:04

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

alexmiller23:04:09

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

ghadi23:04:13

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

alexmiller23:04:48

is there anything in slingshot that does this?