This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-10-27
Channels
- # announcements (10)
- # beginners (95)
- # biff (2)
- # calva (33)
- # cherry (1)
- # clj-kondo (16)
- # clojure (96)
- # clojure-australia (1)
- # clojure-china (1)
- # clojure-europe (42)
- # clojure-filipino (1)
- # clojure-france (2)
- # clojure-hk (1)
- # clojure-indonesia (1)
- # clojure-japan (1)
- # clojure-korea (1)
- # clojure-my (1)
- # clojure-nl (1)
- # clojure-norway (24)
- # clojure-sg (11)
- # clojure-taiwan (1)
- # clojure-uk (1)
- # clojurescript (21)
- # cursive (22)
- # data-science (3)
- # events (7)
- # fulcro (3)
- # graalvm (4)
- # gratitude (6)
- # helix (11)
- # honeysql (7)
- # hoplon (1)
- # introduce-yourself (1)
- # jobs (2)
- # jobs-discuss (16)
- # lsp (15)
- # malli (14)
- # nbb (73)
- # practicalli (3)
- # reagent (8)
- # reitit (5)
- # releases (1)
- # ring (5)
- # rum (3)
- # sci (17)
- # scittle (7)
- # shadow-cljs (22)
- # tools-deps (26)
- # xtdb (9)
What would be the unintended consequences of using ALL_CAPS var names in Clojure and/or ClojureScript? eg.:
(def ASD 123)
=> #'user/ASD
ASD
=> 123
seems to work in JVM Clojurenot even in clojurescript? my only worry is that there is a convention of Java requiring that only class names can start with capital letters...
Java is fine with upper case letters to start class member names, and classes can be all lower case as well
From the design perspective and without any regard to implementation concerns, would it make sense to make keys
, when it receives a map, return something that behaves in the exact same way it does right now with an addition of it being a set?
Not really, most of the time you don’t need the result to be a set, so as a core feature I would not except to assume it’s the case. If you want it as a set you could just call set over it and that’s it. Composability.
Interesting question, and got me investigating a bit. I agree with Alexis. KeySeqs are more streamlined for its purpose. If you need set semantics, you can add it when necessary.
Even though much of the semantics overlap, there are some important differences. For one thing KeySeq
s are seqs, while sets aren't:
(clojure.set/difference (supers clojure.lang.APersistentMap$KeySeq) (supers clojure.lang.PersistentHashSet))
; #{java.util.List
; clojure.lang.Obj
; clojure.lang.Sequential
; clojure.lang.ISeq
; clojure.lang.ASeq}
Sets have a bunch of extra bells and whistles that KeySeq
s don't have, though:
(clojure.set/difference (supers clojure.lang.PersistentHashSet) (supers clojure.lang.APersistentMap$KeySeq))
; #{clojure.lang.AFn
; java.lang.Runnable
; java.util.Set
; clojure.lang.APersistentSet
; clojure.lang.IPersistentSet
; java.util.concurrent.Callable
; clojure.lang.Counted
; clojure.lang.IFn
; clojure.lang.IEditableCollection}
Interestingly, calling seq
on a set returns a KeySeq
: (type (seq #{1})) ; clojure.lang.APersistentMap$KeySeq
I wish the result of keys
was a set if we could disregard the implementation concerns... based on little more than the observation that I typically always end up writing (set (keys m))
to make use of them, and conversely them being a seq offers no surface benefit AFAIK (of course if we care about implementation, perhaps it would be desirable to have both keyseq
and keyset
/shrug
Being able to get a set of keys fits into the basic properties of data structures that is desirable, and slightly opens the door to implementing a way to have maps deliver sets of keys efficiently (rather than building a completely new set every time).
To quantify 'I typically always' it seems from searching my code it's more like 50% 😆 sorry for exaggerating.
Hello! Is there some good place where I can learn how the clojure Compiler.java works and why it does what it does?
Have a look at this old presentation of mine, it gives an initial overview of the main parts and how they interact https://github.com/reborg/euroclojure-2014/blob/master/clojure-beasts-euroclj-2014.pdf
thank you!
Oh that's tight, thanks for the presentation!
Looks like your talk was also uploaded to Vimeo here: https://vimeo.com/100518965 🙂
Right, I didn't post it because it forces you to open the slides regardless (no idea why it wasn't merged at the time). But if you feel you want the live talk feel, then by all mean open both and scroll along!
BTW I love the first slide. Sufficiently scary 🙂
Hey all, I'm struggling to write a macro that I feel should be pretty simple but I feel like I'm missing something obvious and can't think what the terms to search for would be. More details in thread so I don't clog up the main channel
I have a working macro that looks like this:
(defmacro merge-alt!!
[& channels]
`(loop [i# 0
out# {}]
(if (= i# (count (vector ~@channels)))
out#
(recur
(inc i#)
(async/alt!!
[~@channels] ([response#] (merge out# response#))
[(async/timeout 5000)] (println "timed out"))))))
Which when called like this, works
(merge-alt!! channel-1 channel-2)
But I need to be able to call it like this:
(def channels [channel-1 channel-2])
(merge-alt!! channels)
And I'm not sure how to alter the original macro to be able to do this (apart from changing the args), does anyone have any advice or tips for what to search for? Thanks in advance!Silly question, is there a reason for this not to be plain function that you could then (apply merge-alt!! channels)
?
not a silly question at all, and I did think of that first of all, but the problem is the bit where it calls async/alt!!
which needs to expand out to be [channel-1 channel-2]
etc
that's kind of the reason I went down the macro route, that alt!!
call functions a lot like cond
I don't know if I'm explaining this very well so please let me know if I'm not
if I could do
(def channels [channel-1 channel-2])
(alt!! channels [:foo "bar"]
[c3] [:c3 "baz"])
with https://clojuredocs.org/clojure.core.async/alt!! then I basically wouldn't need the macro, but I couldn't find a way of doing that eitherThe spicing unquote ~@
requires that channels
is know at macro expansion time, and you're passing in a symbol and not trying to resolve it into an actual value. What you've written will work for hard coded channels, but not for a variable number of channels. Do you need the list of channels to be variable, or are you trying to reference a constant value? If (def channels ...)
doesn't change then you can dereference that symbol outside the macro expansion and use the list of channels found there?
that makes a lot of sense now you've said it, sadly it's a variable number
I guess if it needs it at compile time that means I kind of can't do it though right? 😅
once you're in macro land you often get stuck in macro land ... what's the actual problem that you're trying to solve? why do you need to pass in a variable number of channels?
I think I can probably get round the initial problem by just duplicating a good chunk of code, but essentially we have a function that does fan-in style async network requests, similar to the macro above
but this varies on what those network requests are depending on earlier logic
the structure of the map etc always ends up roughly the same, but at the minute we basically have the same 40-50 line function copied and pasted 3-4 times that does very slightly different network requests
so this was my, perhaps foolish, attempt at genericizing it a bit to reduce the duplication
I guess you could write a macro that does the copy-pasta bit? ... but I think it's difficult to know what to advise without seeing the code 😉
yeah that's totally fair, I think with the above macro I can get the duplication down from 40-50 lines to about 10 at least
sorry I can't share more but it's super domain specific 😅
I really appreciate the help btw
to be clear, you can write something like this:
(def things [:a :b :c])
(defmacro some-things [t]
(let [ts @(resolve t)]
`(for [x ~ts]
x)))
(comment
(macroexpand-1 '(some-things things)) ; => (clojure.core/for [macro/x [:a :b :c]] macro/x)
)
but if you change things
by re def
ing it you'll also have to re-expand the macro> sorry I can't share more but it's super domain specific oh yeah ... totally understand 😉
yeah, that makes perfect sense - I can probably get away with e.g.
(if
(= 3 (count foo))
(merge-alt!! channel-1 channel-2 channel-3)
(merge-alt!! channel-1 channel-2))
which is still a good hundred lines less than what was there before
thanks again for all your help, I feel like I understand macros a lot better now 🙂
the takeaway lesson is that macros are responsible for evaluating their arguments, which is great because you get to control how those args get evaluated, but also means that you have to evaluate the arguments yourself. With great power ..... 😉
(let [[the-val the-chan] (alts!! (list* (timeout ...) the-chans))] (if (some #{the-chan} the-chans) (merge out the-val) (println "timed out")))
it's my understanding that alts!!
is different to alt!!
though right?
as in different behaviour in an asynchronous context
yeah that's what I thought, with alt!!/alts!! using <!! and >!!, and alt/alts! using <! and >!
so I might be a little confused but is the difference just that alts
supports a variable number of channels in this way, but I'd have to have some custom logic on the timeout instead?
I hadn't really read up on that one till now so forgive my ignorance
alts!!(and alts!) takes the set of operations as a collection and returns the channel and value that where chosen
it is more primitive than alt!! and alt!, you can build alt!! on alts!!, but not the other way around
that's super interesting, sorry for the late reply, timezones; in any case that's probably what I need, I'll do a bit of investigation but I didn't even think of changing the async function until now, thanks a bunch 👍
Selmer: Anyway to render arbitrary strings of html in the templating engine?
where header = <h1>This is a header</h1>
, render with
<div>
{{ header }}
</div>
Do those filters work with render-file?
Looks like yes! Thank you so much
{{header|safe}}
or set header
to [:safe "<h1>This is a header</h1>"]
(we use this a lot at work -- we generate hundreds of thousands of HTML emails every day with Selmer 🙂 )
is there a reason that apply
doesn't work with nullary functions? Like is there a reason (apply f)
shouldn't work? It seems really useful to me if I want to do like (run! apply some-fns)
I am well aware that it doesn't work and that it's not likely to change, I was just curious what the thinking was here.
I'm not 100% sure how the Clojure compiler handles it but in general this is common to other lists. At least in Racket, apply
is used internally as well, it is the actual function that applies functions to arguments and so it must get some kind of argument list as there's no definition of it that makes sense otherwise.
but, an empty list is still a list.
so you can do (apply f [])
and it will work.
the actual usecase here is as a higher order function to run!
or similar, so passing it extra args is useless here as I'd just pass #(%)
instead
I don't understand what are you applying? Oh, I guess you're thinking you want a function that invokes a function?
I don’t understand why it doesn’t work, so I can’t answer your question, but what about using
(run! (partial apply) some-fns)
?Maybe I’m misunderstanding.
The problem is that apply
requires at least one argument be passed to the function.
Yes. Apply has minimum arity of 2 args. The function, and a list to apply it to (or a first argument)
And because the function is in the first argument position, you can't do something like (partial apply [])
This should work though (but will make everyone cry):
(run! (comp eval list) fns)
the fns are function objects, not symbols, so #(%)
is the way to go
I see what you're aiming at.
yeah. I'm surprised there isn't some form of identity function on functions. I think Haskell has one.
(def idfn [f] (f))
is there a trick to make this return :found
, or are classes not available to case
at compile time?
(case (class 4)
java.lang.Long :found
:not-found)
;; => :not-found
Ah I see. So this hack works:
(case (str (class 4))
"class java.lang.Long" :found
:not-found)
;; => :found
but maybe once you're constructing strings at runtime you're not getting much benefit over cond
?if you write your own macro you can have it embed literal classes, but I would just use cond
you could use condp
with isa?
or instance?
as well
the last major way to consider here as well would be to just have a map
(def classes #{java.lang.Long})
(def classes-map (zipmap classes (repeat :found)))
(classes-map (class 4) :not-found)
;; => :found
if you have a lot of condp clauses that are equally likely the map will be faster
