This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-09-15
Channels
- # announcements (15)
- # babashka (8)
- # beginners (23)
- # biff (20)
- # calva (6)
- # cider (9)
- # clerk (31)
- # clj-kondo (3)
- # clj-otel (2)
- # clojure (116)
- # clojure-argentina (1)
- # clojure-austin (5)
- # clojure-europe (64)
- # clojure-nl (3)
- # clojure-norway (23)
- # clojure-sweden (40)
- # clojure-uk (1)
- # cursive (16)
- # data-science (2)
- # datahike (8)
- # emacs (3)
- # events (1)
- # hyperfiddle (24)
- # malli (5)
- # off-topic (24)
- # re-frame (9)
- # releases (1)
- # solo-full-stack (25)
- # sql (18)
- # tree-sitter (19)
- # xtdb (10)
I wonder if there ever was a discussion about why Clojure doesn't support writing integers with underscores, like 1_000_000
, while Java does.
The rational seems to be making numbers easier to parse for humans, so to me this feel more like a feature in an editor and not the language
There are already editor features that display lambda functions and partials with fancy symbols.
Many editors also colourise Hex colour codes.
All of which does not require a change to the underlying langues
The benefit of having this as an editor feature is that it could be configurable as to what separator to use, e.g _ , .
With comma and dot useful for currency values.
> There are already editor features that display lambda functions and partials with fancy symbols. Ohhh, you have no idea of the degree to which I cannot stand it. :D Fonts with ligatures get as little usage in my setup as feasible.
As these features are built into the editor rather than the language they never have to be enabled 😃 Personally I use hex colouring, font ligature, but not the fancy symbols. When the code is checked into a shared Git repository no one has to tolerate my editor preferences (I can also switch them off when pairing) 😆
@U2FRKM4TW I'm with you on that one. Coloring text is one thing, but visually transforming it into something it's not? (like two characters into one) uggghhhh get me away from that
> As these features are built into the editor rather than the language they never have to be enabled
True!
But having the ability only on the editor side will make it the only option.
Having the ability to add _
right in the source code will make it an option for everyone.
But it's great to have options to view things as you want, @U05254DQM
Changing the language forces the change on everyone, whether they want it or not. So this would no longer be an option, but a feature of the Clojure language and something everyone would have to live with I would find _ in numbers as disturbing as others have said the optional editor features are to them. I would prefer to avoid making Clojure syntax more complex, as that reduces the desire to use the language
> All of which does not require a change to the underlying langues Good insight! A non-intrusive approach could be to alternate the syntax color in groups of 3 e.g. assuming a black blackground, a given number chunk could be rendered in either white or light grey
I'm with @U05254DQM on this and have nothing to add as he's said everything I would say
I am 100% for adding support for this. code is meant to be read by humans, and making number literals easier to read and understand is an unreserved good. Plus, matching Java's reader allows for easier transition between the two languages.
> A non-intrusive approach could be to alternate the syntax color in groups of 3 That's a neat solution, I'd probably use it.
Personally my main concern would be that libraries probably shouldn't be written with this feature if they were to be compatible with multiple clojure versions, but I guess that's just the same as depending on any feature of new clojure versions.
when you push the solution to editors, you multiply the work required and you leave plain text representations out.
Yeah, that's a good point imo that the editor should do that, not the language itself.
Emacs users can try this: https://emacs.stackexchange.com/a/59343
I really miss this feature! Worked in a huge JDK codebase (largest I've ever seen) in a vendor for banks before and this was a great Java feature! I don't agree with the "everyone has to live with this argument". Great, don't use it in your code base then, but let everyone who wants to use it. This is not a colouring of functions feature, it will only affect your own local code. We should all have the choice over how our local code looks. It means it looks consistent across Github reviews, Emacs, VsCode etc. There is no plugin that can solve Github for example, which is necessary part of many organisations and probably the majority of where code is viewed across teams.
Is this expected?
user=> #inst"0001-01-01"
#inst "0001-01-01T00:00:00.000-00:00"
user=> (.toInstant #inst"0001-01-01")
#object[java.time.Instant 0xe362c57 "0000-12-30T00:00:00Z"]
Then again, I'm not sure what guarantees java.util.Date (#inst) and java.time.Instant gives for dates pre-unix epoc.
Oh, and the conversion back to #inst seems to be the inverse, so that's good:
user=> (java.util.Date/from (.toInstant #inst"0001-01-01"))
#inst "0001-01-01T00:00:00.000-00:00"
Brought to you by devs who wanted an easy, but useless way to time travel
It's a question of how you want to represent 00:00:00-00:00 o'clock within each type, right? And the makers of each type didn't agree. (Maybe the same guy at different times. Opinions may mutate when you're traveling forwards in time a second per second.)
About LocalDate
but same explanation applies to Instant
: https://stackoverflow.com/a/23978037/564509
Just a note, I'd recommend against using #inst
for anything but the simplest use cases, because you get java.util.Date
out of it - using java.time.*
directly is not that bad, and there's a couple of wrappers that help.
I wish we'd realized that much earlier in our app development
Oh yeah, it's a serious foot gun. Same as using java.util.concurrent.ScheduledThreadPoolExecutor/scheduleWithFixedDelay
for precise scheduling for tasks, it drifts and doesn't sync with properly with system time.
Just to be clear, in which ways do you consider it a footgun?
There's a lot written about it, but depending on what you're doing - the fact that it's not exactly a UTC timestamp (even though it seems to act like it) because it ignores leap seconds - javadoc for util.Date goes into more depth. Second thing is that pretty much all methods are deprecated as for JDK 1.1 :-)
Thank you 😊
Hey all, just want to share some mildly-interesting discovery. Today, I stumble upon this interesting/surprising behaviour of clojure.core/seq?
, which checks whether a given object implements the clojure.lang.ISeq
interface:
user> (seq? ())
true
user> (seq? '(1 2 3))
true
user> (seq? [])
false ;; <-- !?
user> (seq? [1 2 3])
false ;; <-- !?
user> (seq? (seq [1 2 3]))
true
Vectors are not ISeqs
! This is a bit surprising because I always thought the clojure.lang.ISeq
is the shared interface that makes the sequence abstraction in Clojure possible.
As it turned out, vectors are clojure.lang.IPersistentCollection
s. ISeq
is actually a sub-interface to IPersistenCollection
, not the other way around. And the trick for the clojure runtime to convert from vector to an ISeq
is in the super-interface – the clojure.lang.Seqable
interface, which defines a single method that converts itself to an ISeq
:
public interface Seqable {
ISeq seq();
}
Thanks for reading!you may find http://insideclojure.org/2015/01/02/sequences/ interesting
additionally, the 1.9 function https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/seqable? which is referenced at the bottom of that article
Thanks @U064X3EF3! I need to update my terminology now - most of the time when I said seq
I actually meant Seqable. This also summarized very well in the article:
> The Clojure list implementation (`clojure.lang.PersistentList`) is a list data structure and thus is both a concrete data structure and also implements the ISeq abstraction directly. All of the other collections are Seqable, but not ISeq.
> […]
> Some other important sequence predicates:
> • seq?
- checks whether an instance implements ISeq
> • sequential?
- checks whether an instance implements Sequential
>
@U064X3EF3 should you update that post to note that seqable?
was added to core? (not to make more work for you...)
Was there ever a discussion about adding a transducer version of clojure.string/join
to the core library? I feel like it could be nice, you often transform some data and then you want to write it to some file with new lines etc.
transducers tend to be concerned with performance, building strings is usually bad for memory, so bad for performance
If you want to join a string and use transducers to produce the input, you can do something like:
(clojure.string/join ", " (eduction xforms coll))
If you're trying to write to an output stream, you could do something like:
(with-open [w (io/writer "myfile.txt" )]
(binding [*out* w]
(transduce
(interpose \newline)
(completing
(fn [_ s]
(print s)))
nil
[1 2 3 4])))
Seems like you’d really want that as the final f in a transduce call rather than a transducer
Yea, you might just be looking for interpose
.
seems complected to me
complected in a bad way? I have used the str/join + eduction idiom a few times.
(clojure.string/join ", " (eduction xforms coll))
Having a shorthand seems plausible.I'd rather have some way to do (transduce xf str-accumulator coll) and then use interpose as one of the xf if needed
"append to string" is an accumulator function like conj
you're making a conveyor of string parts via transducers, then accumulating into a final string
maybe similar to this? https://github.com/cgrand/xforms/blob/2079b74271b858b6a91dcb87bc58f3b93ea0b19c/src/net/cgrand/xforms/rfs.cljc#L145-L147 https://github.com/cgrand/xforms/blob/2079b74271b858b6a91dcb87bc58f3b93ea0b19c/src/net/cgrand/xforms.cljc#L306-L314
that would be a good thing to have
Not at my PC atm and I didn't have time to check it, but maybe something like that?
(defn join [xf sep coll]
(let [first (volatile! true)
rf (fn
([sb] (.toString sb))
([^StringBuilder sb input]
(if (identical? @first) true)
(do (.append sb (str input))
(vreset! first false))
(.append sb sep input)))]
(transduce xf rf (StringBuilder.) coll)))
you're putting all the parts in one box. I want the parts
or rather, we have all the parts except the string accumulator, I want that missing part
like conj, where the 0 arity would return a stringbuilder, the completing arity would toString it
then you can (transduce (interpose ",") str! coll)
there's all kinds of things you might want to string accumulate
I have done that in the past, transduce + a string-builder rf + xforms to build strings
you might also want the transducer arity of join, but it should be built on this
right, you can do both • have a convenience function for common tasks • build it out of simpler pieces and make those available
https://clojurians.slack.com/archives/C03S1KBA2/p1686622008346419 if you want to avoid concatenating intermediate strings
Granted I wrote that ages ago, it’s full of str building with transduce https://github.com/mpenet/hayt/blob/7bbc06fae481bda89295dcc39d06382c4ba64568/src/clj/qbits/hayt/cql.clj#L36
Haven't caught up on the whole thread but to me this just sounds like a combination of the interpose
transducer and a string-building transducing context, which sounds useful to me.
transduce
itself is sufficient, but yeah, I guess you could fold the builder together potentially (like into does with conj)
could potentially even fold into into
depending how weird we want into
to be :)
Yeah, I'd agree it's sufficient, but we do have shorthand for sequence
and collecting to a specified collection type with into
, so having a shorthand for "into string" could be nice. Just having a string-accumulating rf would definitely be good enough though. It'd do what the str
multi-arity does already.
I'm putting it in our queue for 1.13, we'll think about it then
str
could be thought of as similar to into
except it takes elements, not a coll
Btw, fixed:
(defn join [xf sep coll]
(let [first (volatile! true)
rf (fn
([^StringBuilder sb] (.toString sb))
([^StringBuilder sb input]
(if @first
(do (vreset! first false)
(.append sb (str input)))
(.. sb (append sep) (append (str input))))))]
(transduce xf rf (StringBuilder.) coll)))
cgrand/xforms is for me a necessary extension for effective use of transducers
Although as I see from the conversation, you want to do it in a different way... didn't have time to read it too much yet, sorry...
It seems like a pretty clean way to do that, but everyone could have a different opinion on that...
Ok, I agree that this job can be forwarded to the str!
transducer... adding it to str/join
would complect the functionality...
(defn str!
([] (StringBuilder.))
([result] (str result))
([^StringBuilder result input] (.append result (str input))))
Hi all, I'm having some trouble streaming a csv to s3 with clojure.data.csv and cognitect aws-api. This is my incorrect code:
(defn write-rows-to-s3 [rows bucket file-key]
(with-open [to-s3 (io/input-stream )
output (io/output-stream to-s3)
writer (io/writer output)]
(let [s3 (get-s3-client)
_ (csv/write-csv writer rows)
res (aws/invoke s3 {:op :PutObject
:request {:Body to-s3
:ContentEncoding "utf8"
:Bucket bucket
:Key file-key}})]
(if (:Error res)
(prn "ERROR:" res)
(prn "Successfully copied csv to s3.")))))
I am handling the input and output streams wrong. How can I write the csv to something I can pass along as the :Body of the request?Does this help? https://github.com/cognitect-labs/aws-api/blob/5900e359365041ed9380947d010b0f0a853e793c/examples/s3_examples.clj#L64-L66
I don't believe the cognitect aws API allows streaming, https://github.com/cognitect-labs/aws-api/issues/14
> We do accept and return InputStream
s, but the underlying http-client does not.
ah, that's right - I just remembered that I had to use AWS Java SDK to efficiently sync big files to S3 (gigs in size)
yea, I recently switched to https://github.com/mcohen01/amazonica for this exact reason.
Whether aws-api actually streams the request is less important to me here than whether I can avoid putting a file on the server that creates the CSV. I'm struggling more with the part where I get a csv into an input stream.
ah, yea, that should be possible.
What is the input you're starting with? A file? An in memory representation?
I def did the same thing few years ago, so:
• get your CSV data like this https://github.com/clojure/data.csv#example-usage
• then pass (slurp writer)
to the s3 put request
Thanks for the help. I ended up borrowing piped-input-stream from ring (https://github.com/ring-clojure/ring/blob/1.9.0/ring-core/src/ring/util/io.clj#L11) and then my code is just:
(defn write-rows-to-s3 [rows bucket file-key]
(with-open [to-s3 (piped-input-stream (fn [outstream]
(with-open [writer (io/writer outstream)]
(csv/write-csv writer rows))))]
(let [s3 (get-s3-client)
res (aws/invoke s3 {:op :PutObject
:request {:Body to-s3
:ContentEncoding "utf8"
:Bucket bucket
:Key file-key}})]
(if (:Error res)
(prn "ERROR:" res)
(prn "Successfully copied csv to s3.")))))
weird behavior after a typo
user=> (Thread//sleep 10)
nil
user=> (name (nth (macroexpand '(Thread//sleep 10)) 2))
"sleep"
user=> (namespace (nth (macroexpand '(Thread//sleep 10)) 2))
""
user=>
user=> (macroexpand '(Thread//sleep 10))
(. Thread /sleep 10)
That's funny...I suppose it sort of makes sense since clojure.core//
is a thing.
yeah, a readable symbol can have any number of '/' in it, but cannot have / as the first character, and everything before the first / is read as the namespace
And we have +'
, -'
, and *'
, but no /'
-- that's unreadable 🙂
what is happening is inside the transformation from (Thread//sleep 10)
to (. Thread /sleep 10)
the code taking apart the symbol Thread//sleep into the namespace Thread and the name /sleep
Definitely surprising until I macroexpanded it. Clojure can still surprise us after all these years :rolling_on_the_floor_laughing:
but /sleep is interpreted by the symbol making stuff as being a symbol with "" as the namespace name and "sleep" as the name
/'
wouldn't make sense though, there's nothing it could do that requires a promotion that would be detectable I think. Like I don't think you can tell if you're losing precision when doing a floating point division, can you?
Although I realize that it being unreadable would pose a problem for its use anyway
...because /
on its own has a special reader rule...
The promoting ops was just where my mind went when played with the original macroexpansion... 🙂
user=> (macroexpand '(Thread/bar/baz 10))
(. Thread bar/baz 10)
So of course, this actually works too:
user=> (Thread/ignore-me/sleep 10)
nil
@U5NCUG8NR There is one case with 64-bit wide long / that requires promotion to give the correct answer: (/ Long/MIN_INT -1)
is outside of the range of a 64-bit long
And yes, I have filed an issue or two years back in Clojure JIRA, at least one of which resulted in a change in Clojure code.