This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-04-03
Channels
- # announcements (4)
- # aws (13)
- # babashka (35)
- # beginners (162)
- # boot (8)
- # calva (5)
- # chlorine-clover (15)
- # cider (64)
- # clj-kondo (20)
- # cljs-dev (29)
- # clojars (6)
- # clojure (166)
- # clojure-europe (3)
- # clojure-finland (6)
- # clojure-france (8)
- # clojure-germany (3)
- # clojure-italy (3)
- # clojure-nl (7)
- # clojure-spec (49)
- # clojure-uk (83)
- # clojurescript (39)
- # clojurex (5)
- # core-typed (2)
- # cursive (3)
- # data-science (17)
- # datascript (3)
- # datomic (22)
- # exercism (5)
- # fulcro (3)
- # jobs-discuss (2)
- # joker (2)
- # kaocha (3)
- # malli (26)
- # off-topic (89)
- # pathom (10)
- # pedestal (14)
- # protorepl (14)
- # re-frame (23)
- # reitit (2)
- # shadow-cljs (27)
- # slack-help (10)
- # spacemacs (14)
- # tools-deps (10)
- # tree-sitter (3)
- # xtdb (19)
- # yada (2)
It's also probably a good time to remind people to use threads when it looks like a conversation is going to take a deep dive and go back and forth a lot between just a few people...
just tossing this out here: several months ago I watched a presentation where the presenter showcased chaining re-frame subscriptions to do some clever form validation. Does anybody know what talk I watched?
check this https://clojure.wladyka.eu/posts/form-validation/ let me know if it is useful
TL; DR; I did https://github.com/kwladyka/form-validator-cljs to solve this - this is very small library bo do the job
How does leiningen act when two of my project dependencies depends on the same library but in different versions?
If you've not tried it already, lein deps :tree
should help with working out what dependency is coming from where.
Hello everyone, how do I run an expression at the end of a ring handler once the response has been sent? The following is my use case:
(defn file-download-handler []
;; .... logic to generate a temp file
(resp/file-response tmp-file-path))
; subsequent to sending the response, I want to delete the file at tmp-file-path
The file-download-handler sends a file response. Subsequent to sending this (temporary) file, I want to delete the temp file. This would be fairly straightforward if I could add an expression after (resp/file-response tmp-file-path)
. But doing this means that the handler will not work as expected. How do I accomplish this?I believe file-response
just wraps the file in an inputstream. per the https://github.com/ring-clojure/ring/wiki/Concepts :
> The contents of the stream is sent to the client. When the stream is exhausted, the stream is closed.
you can make a proxy of an inputstream (using reify
) that deletes the temporary file when closed.
you may also want to have some process that cleans up temporary files that are older than a certain threshold in case an exception in the handler causes the input stream to not clean up after itself for some reason
depending on how quickly the temporary files fill up disk space, another quick and dirty solution is just to have a process that deletes temp files older than X minutes
Thanks @U7RJTCH6J, Iâll probably go with the approach of periodically deleting the tmp files. Although I was primarily wondering if there was a way of knowing when a respone has finished in compojure. In Express/Node.js there is an event that is fired on response end, where you could handle cases like this.
there maybe, but I think it might depend on which server implementation you use (ie. http-kit, jetty, etc)
not sure where to ask so I'll ask here. Anyone seen any implementations of QUIC made in clojure or a clojure wrapper for a QUIC java library?
(I went looking, just for fun. I havenât used any of the tools below) https://github.com/quicwg/base-drafts/wiki/Implementations https://github.com/ztellman/aleph wraps Netty, but Netty only has unofficial, partial support for QUIC: https://github.com/protocol7/quincy
Ah, those links are great. Thanks a lot! We're both in the same boat, just looking at QUIC for fun đ
Is there any reason as to why calling (hash-map :a 1 ,,,)
etc returns a clojure.lang.PersistentHashMap
but calling (into (hash-map) {:a 1,,,})
will return a clojure.lang.PersistentArrayMap
(up until the hash map threshold) ?
maybe looking at the implementation of into
will give you the answer: https://github.com/clojure/clojure/blob/30a36cbe0ef936e57ddba238b7fa6d58ee1cbdce/src/clj/clojure/core.clj#L3333-L3341
https://clojuredocs.org/clojure.core/array-map#example-57392e25e4b071da7d6cfd0c See this.
OK so I figured out that (type (hash-map))
will return clojure.lang.PersistentArrayMap
what gives?
I think the attitude you will find among most Clojure programmers is: why do you care?
Both map types behave the same way, and as long as you do not have an array map with hundreds of elements, performance is good.
It is perfectly right if you consider the concrete type to be an irrelevant detail, as long as behavior is correct, and performance is acceptable.
Also Clojurescript behaves the "correct" way:
cljs.user=> (type (hash-map))
cljs.core/PersistentHashMap
But practically I think "hash map" is what the language refers to them as, so most people would reach for that function when building small maps, eg.
(apply hash-map [1 2 3 4])
@andy.fingerhut I surely agree with the "why do you care?" thing, but I also agree with @qythium that because we have public hash-map
and array-map
functions one would expect to behave accordingly
Stop expecting that đ
So in that sense the language pushes the most common use-case to a more optimized performance
(is my recommendation, not a command -- care or expect all you want)
@andy.fingerhut are you aware of the technical reason as to why (hash-map)
has to return an array map?
has to? I doubt there is a reason it does that by conscious design choice.
It has to because the code is currently written that way.
I can explain why the current code does, but that is a different question.
In my understanding, an array map is more efficient when it has few elements, and Clojure tries to make it easy to do the efficient thing.
O(1) for operations on an empty hash map or empty array map -- I just cannot bring myself to worry about the class.
ok sure I can also understand why it returns an array map from the code I was asking if there is a more technical reason as to why it is designed this way
user=> (type (hash-map))
clojure.lang.PersistentArrayMap
user=> (type (hash-map :a 1))
clojure.lang.PersistentHashMap
Empty array map and empty hash map are both O(1) for all operations. So they are all fast.
Performance is very important to me.
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/PersistentHashMap.java#L39
If (hash-map) returned a hash map, I doubt that someone filing a bug saying it ought to return an array map would be responded to. Nor vice versa.
@gerritjvv yes I saw that
the main reason for the inconsistency may be that from a clojure perspective it doesn't matter.
@andy.fingerhut fair point
Hunch: we're digging into implementation details and we're not really supposed to care which IPersistentMap
we're using. Observed inconsistencies may be accidental. I found (source hash-map)
and (source map?)
to be helpful.
^^ this is a much nicer answer than just "why do you care?" đ. and hints at the reasoning behind why the inconsistencies on types.
Sorry if my answer comes across as rude. Try to think of "why do you care?" not as "you should never care about such question", because I think you should ask such questions. The question "why do you care?" is meant from the perspective of "I think that, I you reflect on it as long as you care to, you will probably arrive to the conclusion that it isn't all that important of a distinction"
I took @andy.fingerhutâs original comment as frank directness ("let's find the facts"), but I'm also inclined towards direct communication.
yup. @andy.fingerhut no worries, I dnd't perceive it as rude đ. but such answers (which sometimes I give myself) might be perceived differently especially by beginners to the community.
@U05509S91 agreed. I'm often trying to balance "encouragement" and "fact-finding" myself. I sometimes find that getting totally obsessed with facts can inhibit any action, in a totally unproductive way. But this is hard stuff. I've found differentiating between observations and judgements to be helpful.
Yeah that was my conclusion as well - the hash-map function simply returns the literal {}
which the clojure reader turns into an arraymap
@teodorlu I completely agree with this reasoning but I also (for the sake of argument) believe that one might have somewhere in their code something like (into (hash-map) ,,,)
and somewhere else could have dispatched a multimethod on the expected type (`clojure.lang.PersistentHashMap`) or could have an instance?
check somewhere**. Reasoning about your program has a lot to do with consistent behavior and (hash-map)
returning clojure.lang.PersistentArrayMap
is not consistent.
I am not arguing here that this is something that one should do, or is the clojure way of doing things. Let's also think about newcomers here.
Worth noting that I made this exact mistake before, not knowing that you could extend multimethods with interfaces (`IPersistentMap` in this case)
(defmulti mm class)
(defmethod mm clojure.lang.IPersistentMap
[x] "matched")
(mm :not-a-map)
;; Unhandled java.lang.IllegalArgumentException
;; No method in multimethod 'mm' for dispatch value: class
;; clojure.lang.Keyword
(mm (array-map :a 1)) ;; => "matched"
(mm (hash-map :a 1)) ;; => "matched"
Yes multimethods dispatch with isa?
so you can built whole hierarchies (with derive etc) but I bet beginners will have a hard time debugging issues like that coming from inconsistencies
Running into this issue actually led me to learn about hierarchies and derive
etc.
In general while learning Clojure I found that bugs stemming from my misunderstanding would show themselves pretty obviously, and the process of solving them brings about a better understanding of the language and its idioms
> Yes multimethods dispatch with `isa?` [...] I wasn't aware of that. Thanks! So for our case of hash maps, we could use the fact that
(isa? clojure.lang.PersistentHashMap clojure.lang.IPersistentMap)
... to do multimethod dispatch on the interface.@qythium I think sooner or later every clojure programmer will go through this process despite the fact that clojure is notorious of how easy it is to reason about, despite its dynamic nature
And, of course, the reference covers this: https://clojure.org/reference/multimethods#_isa_based_dispatch I'll add my often last step to @qythiumâs process: figuring out that the "weird" topic is actually explained well in the reference or a guide.
Yeah, it seems like it would be harder to define dispatch using predicate functions (`map?`) than dispatching directly on the type.
Because to me, I think I would reach for "I want to dispatch on some type" using Haskell, Elm or Rust, whereas in Clojure I'm more inclined to (a) just (cond ...)
a bunch of predicates, and possibly (b) to use "less dispatch things", rather work with a more general map where I know what type is "under" each keyword.
Well, I have a more open approach to things. I am sure that there is a time and place for everything. Multimethods and protocols are great tools for runtime polymorphism. Sometimes I use multimethods sometimes protocols and sometimes (cond ,,,)
it depends on the context
This is for my docker image. What I want to do it install all the dependencies and cache it. (Of course change it when the dependencies change).
Ah, I see. I just run something like clj -e '(println "Test run successful")'
on a clean ~/.m2
folder and then cache the whole dir.
Okay nice. Let me try that. Thanks @U2FRKM4TW
Even more simpler. Thanks @U064X3EF3
Regarding persistent queues, since the first part of a queue is implemented as a seq for easy popping, it means it would be trivial to prepend as well (which is what I need), wouldn't it? I cannot manage to find anything in the std lib or any method on the class to do so.
I guess it wouldnât be a queue anymore if you add to the front. Or did I misunderstand you?
Well, it would approach a double-ended queue I guess, unless I am missing something obvious
Thanks, that would indeed be a solution, but ideally I was hoping for something that would work out of the box with CLJS
Sorry I have no idea if there is any lib in CLJS for that. I don't know if you can get away with JavaScript arrays..
@U11BV7MTK That's why I am wondering if I am missing something obvious. The fact a persistent queue is implemented with a seq followed by a vector for easy popping from the start and appending to the end looks like it can just as easily be used for prepending and popping from the end.
i agree with you. looking at it in java the fields are private so its a bit annoying (`f` and r
are the seq and vector respectively). looking at cljs now
But then, in CLJS, can you easily rebuild a queue by providing a seq and a vec?
(defprotocol IDeque
(-rcons [q e])
(-lcons [q e])
(-rpop [q])
(-lpop [q])
(-rpeek [q])
(-lpeek [q]))
(extend-protocol IDeque
PersistentQueue
(-rcons [q e] (conj q e))
(-lcons [q e] (PersistentQueue. (meta q)
(inc (count q))
(conj (.-front q) e)
(.-rear q)
nil)))
deque.impl> (-rcons #queue [1 2 3] :new)
#queue [1 2 3 :new]
deque.impl> (-lcons #queue [1 2 3] :new)
#queue [:new 1 2 3]
Neat!
Too bad it's not that simple in clj. I don't see any way we can overcome the fact that the seq and vec are private.
Which is not what we want for a data structure. Should someone bother with opening a ticket about it? I mean, what are the chances of this grabbing some attention?
As to the original question, turning the queue implementation into a dequeue implementation adds some pathological performance corner cases. Consider:
(-> (dequeue (range 100))
(.lpop)
(.rpop)
(.lpop)
(.rpop))
A naive implementation using the same strategy as a FIFO queue would cause an O(n) shuffle of data between the front and rear of the dequeue for each of the above operations.I think this is one of the problems that the finger trees linked above are aimed at solving
Fair enough, yes. However, before diverting to deques, I was actually mostly interested by being able to prepend (a deque without rpop I guess). Then, I believe we wouldn't have that kind of problems (I might be mistaken).
If your set of operations are: rpush, lpush, lpop (and not rpop), yes you could easily make your own using a list for the head and a vector for the tail, following the same implementation as PersistentQueue. It's a somewhat uncommon set of operations for a data structure, so that's a good enough reason for it to not be supported in the official clojure.lang
bag of functions, but there's no reason you can't make and use it yourself!
anybody using jsonista? Is there a way to parse json only partially, similar to cheshire/parsed-seq? documentation looks a little thin
I have not used jsonista, but I have used regex to extract substructures from JSON text. Is that what you are looking for?
Maybe try this: https://github.com/dmillett/clash/blob/master/src/clash/core.clj#L168
It depends what you want out of the underlying JSON. A transducer can be setup with a parser --> extractor for you data to handle large amounts.
ok thanks, I'll have to keep that in mind. for now, I found it simple do it with a java lib com.jayway.jsonpath.JsonPath/parse (I'm not a purist đ ) but I'll have to benchmark memory consumption of it
I'm struggling with a clean, idiomatic way to achieve this: I've got a customized search tree sort of structure, and I want to lazily return a sequence of leaf values. it feels like (apply concat (for [c children] (blah blah recursion)))
is not the Right Way to achieve this.
then again, maybe it is - it's lazy and it doesn't blow the stack or anything.
apply doesn't realize lazy values
the function might do it, but apply itself never does
user=> (apply (fn [x & _] x) (range))
0
this would not have returned if apply was eagerwow, tree-seq
looks great.
itâs one of the things that makes it feel impossible to use another language đ
"There's a (clojure.core) function for this page of complicated Java."
Why do a lot of clojure/core.clj functions have redundant arity definitions? For instance:
(defn vector
"Creates a new vector containing the args."
{:added "1.0"
:static true}
([] [])
([a] [a])
([a b] [a b])
([a b c] [a b c])
([a b c d] [a b c d])
([a b c d e] [a b c d e])
([a b c d e f] [a b c d e f])
([a b c d e f & args]
(. clojure.lang.LazilyPersistentVector (create (cons a (cons b (cons c (cons d (cons e (cons f args))))))))))
There are 8 arities and several of them are redundant. Is this purely for performance reasons or is there something more to it?There is a small part of this talk that describe this problem And show that if clojure compiler where build in clojure, it could be solved way more elegantly https://www.youtube.com/watch?v=eDad1pvwX34
Is there a particular reason why the different types of sequences do not implement IPersistentStack? To me at least, this is not intuitive as sequences are "logical lists". Is it because a seq might then pop from a wrapped data structure which is not optimized to pop efficiently like a list? For instance :
(pop (list 1)) ; works as intented
(pop (cons 1 '())) ; throws
(pop (seq [1])) ; throws
cons
, seq
, map
, and filter
are sequence abstractions. Theyâre designed to be lazy. They can be backed by any number of concrete data types or, potentially, none at all (e.g. infinite sequences).
list
, vector
, and PersistentQueue
are all concrete data types. They are designed to hold all contents in memory and to be very efficient at various operations, depending on the type. For example, list
has a very efficient prepend operation, but poor indexed access. vector
has a very efficient append operation and very efficient indexed access.
In clojure, peek
, pop
. and conj
are queue functions. Theyâre only available on concrete data types which implement the IPersistentStack
interface. (That includes lists, vectors, and PersistentQueue.) This restriction is to quasi-guarantee that peek
and pop
will be extremely efficient (usually requiring no more than a single allocation). This also guarantees that each function keeps the same data type as the original collection (unlike sequence functions).
Yes, I understand that all of this is organized for the sake of efficiency, yet I sometimes find it a bit odd. For instance, regarding how conj
fits in all that :
(class (conj (range 1))) ; Cons, you can conj to a seq
(pop (range 1)) ; Throws
I have been using Clojure for years but once in a while I reconsider something that basic
I honestly didnât know conj
worked on sequences. I agree thatâs odd. Though, I would never use it for them intentionally. I would never even use cons
for sequences intentionally. Ordinarily, you shouldnât be dealing with individual items or indices when operating on sequences. You should always consider them as a sequence.
conj
is kind of an oddball operation. Itâs the default way to add to a collection (as opposed to cons
in other lisps). For stacks, it means âpush.â For sets, it means âadd.â For lists, it means âappend.â For maps, it means âadd this key-value pair.â
My rule of thumb that allows me to not think about it: map, filter, concat are for sequences. conj, peek, pop, assoc are for when you want to control the concrete data type.
@contact904 there's https://walkable.gitlab.io Clojure is data-oriented, so the preferred way is through data, not objects, therefore no ORMs
I have two vectors of maps such as Vector #1 [{:_id 'abc} {:id 'bcd} ... n]_
and vector #2 ['zxy 'xyz 'abc ... n]
. My goal is to filter out maps that ids' does not match the ids of vector #2. Are the functions filter
and .contains
a good approach? Or should I do a reducer?
@contact904 Yes, that's enough. Or you can convert vector 2 to set and use contains?
okay so I now have vector #1 filtered. I have another vector #3 [{:id 'abc :someList [a b c d] ... n]
What I did is
(defn some-reducer
[vectorOne {:keys [id someList] :as vectorThree}]
(cond (some? vectorThree)
(map #(cond (= (:id %) id)
(assoc % :someList someList)
:else %) vectorOne)
:else vectorOne))
(reduce some-reduce vectorOne vectorThree)
Is this a good way to do so? It works but I wonder if there is a more clojure idiomatic way to do it.
Edit: My goal is to assoc a keyword with the value to be someList
when the ids are successfully matched.My solution
(letfn [(index-by [f coll]
(into {}
(map (juxt f identity))
coll))]
(let [els [{:id 1} {:id 2}]
els-by-id (index-by :id els)]
(assoc-in els-by-id [1 :v] 42)))
=> {1 {:id 1, :v 42}, 2 {:id 2}}
> My goal is to assoc a keyword with the value to be `someList` when the ids are successfully matched
So you want your output to look like {'abc [a b c d]}
?
Oh I think I see what you want
Like this? -
(defn my-transform
[v1 v2 some-list]
(let [ids (set v2)]
(into []
(comp (filter (comp ids :id))
(map #(assoc % :someList some-list)))
v1)))
=> #'user/my-transform
(my-transform [{:id "a"} {:id "b"} {:id "c"}]
["a" "c"]
["a" "b" "c" "d"])
=> [{:id "a", :someList ["a" "b" "c" "d"]} {:id "c", :someList ["a" "b" "c" "d"]}]