Morning
buenos dias, dzien dobry!
Buongiorno altogether!
Good morning folks! One step closer to Friday... it's one of those weeks.
Morning
dobrý den
dobrý den 🙂
Bono estente
the feeling when not sure if it’s russian or ukrainian
Czech my friend
oh right there are more slav languages
mmm... today I shall be using cond->
transient
and persistent!
oh, and assoc!
so... a bit of record-scrubbing
(defn scrub-record [{::keys [id birthday start end placement] :as rec}]
(let [t (transient rec)]
(cond-> t
id (assoc! ::wce/id id)
start (assoc! ::wce/report-date (t/date start))
end (assoc! ::wce/ceased (t/date end))
birthday (assoc! ::wce/birthday (t/date birthday))
placement (assoc! ::wce/placement placement)
true (persistent!))))
(def scrub-records-xf
(map scrub-record))
why not use defrecord
?
it would might be less surprising...
and transient
stops being an issue IIRC
I feel a bit dirty about the true at the end of the (cond->)
, but I like that more than wrapping the whole thing in a ->
around the cond->
I like true too
Why not just a persistent! around the cond->
You don’t have to thread everything.
I like doing either threading or inside out
tho I've not got a good reason
I wonder if the Clojure compiler folds this:
user=> (macroexpand '(cond :else 1))
(if :else 1 (clojure.core/cond))
into just 1
. Should look at the bytecode...
Apparently not:
user=> (clj-java-decompiler.core/decompile (fn [] (if :else 1)))
// Decompiling class: user$fn__207
import clojure.lang.*;
public final class user$fn__207 extends AFunction
{
public static final Keyword const__0;
public static final Object const__1;
public static Object invokeStatic() {
final Keyword const__0 = user$fn__207.const__0;
if (const__0 != null) {
if (const__0 != Boolean.FALSE) {
return user$fn__207.const__1;
}
}
return null;
}
@Override
public Object invoke() {
return invokeStatic();
}
static {
const__0 = RT.keyword(null, "else");
const__1 = 1L;
}
}
So everytime you write (cond ... :else :foo)
you actually contribute to global warming, go figure. ;P
Compare?
Two tests: that one with case and one without wherr you skipped that true clause check. Criterium should be able to warmup hotspot sufficiently.
That's only measuring. But how can we see that hotspot is really not hitting that path anymore
would be nice if all the stuff that @mfikes has done in CLJS to optimise and short-circuit these conditions were brought back into Clojure
@borkdude You should try the same for cljs.
cljs.user=> (str (fn [] (if :else 1)))
"function (){\nreturn (1);\n\n}"
🎉Guten Nachmittag.
A quite univeral expression in slavic languages, right? Spelling and details vary though?
I typically use :always
instead of true
for a catch-all in conds.
Naaa, pretty sure hotspot would catch this?!
Guten vor-Feierabend
How to check?
After work is before work.
Bon après-midi !
ooh. I like that
I like that this is getting out of hand
and good afternoon
I don't read threads so I'll never find out 🙂
@raymcdermott does this feel like a legible and reasonable use of transient? https://clojurians.slack.com/archives/CBJ5CGE0G/p1600947978004300
😉
damn you!
did you already explain why you need them?
If speed is your concern here. 🤷
since you use multiple assoc! why not combine them in a single one? assoc! already has support for that
oh I see, it's in cond->, never mind
^^^ didn't quite pass the totally legible test 🙂
I know @borkdude is not such an advanced developer but still
(deftype Foo [m]
clojure.lang.Associative
(assoc [this k v]
(if v
(Foo. (assoc m k v))
this))
clojure.lang.ILookup
(valAt [this k]
(get m k))
Object
(toString [this]
(str m)))
(let [v (assoc (Foo. {})
:a false
:b nil
:c 3
:d 4)]
(prn v)
(prn (get v :c)))
#object[user.Foo 0x695a69a1 "{:c 3, :d 4}"]
3
:thinking_face:we actually have a function for this in our codebase at work (uses transients):
(defn nn-hash-map
"Like c.c/hash-map but does not include false or nil values."
[& keyvals]
...
ret))
☝️
@borkdude I've written things like that in the past. This feels like a reasonably quick use of the std lib (tho I've only done it this way today so I'm not devoted to the idea)
yeah, seems fine to me
It's quite readable, I would say.
is this a reasonable use of a transient?
it gets called for a fair few records, so I'd like it to be quick. It currently takes around 41 seconds to process the data I'm working with
so there might be some other wins hidden around
use of transients is probably reasonable if you have measurable performance benefits
Cool. Looks like i should measure then 😉
I like that you challenged the meaning of legibility there @borkdude