This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-09-22
Channels
- # babashka (106)
- # beginners (29)
- # biff (29)
- # calva (9)
- # cider (6)
- # clj-kondo (24)
- # clojure (40)
- # clojure-europe (94)
- # clojure-japan (1)
- # clojure-nl (1)
- # clojure-norway (45)
- # clojure-uk (13)
- # clojuredesign-podcast (5)
- # clojurescript (12)
- # clr (4)
- # community-development (2)
- # conjure (13)
- # cryogen (4)
- # cursive (4)
- # deps-new (1)
- # fulcro (18)
- # hugsql (2)
- # hyperfiddle (67)
- # jobs (1)
- # malli (47)
- # meander (2)
- # missionary (34)
- # off-topic (1)
- # podcasts-discuss (1)
- # polylith (24)
- # reagent (19)
- # reitit (9)
- # sci (7)
- # shadow-cljs (3)
- # testing (28)
- # tools-deps (1)
- # xtdb (9)
Morning
Friday thought: I want to do an episode on apropos about when can't we escape the imperative loop
and enter the functional safety of reduce
also: when you have multiple independently updating values rather than a simple aggregation
you might be able to do the multiple things using things like fuse or other similar things to https://github.com/henrygarner/redux
but I might use loop when firing off events where I really only care about the side effects
Here is an example from the core https://github.com/clojure/clojure/blob/6975553804b0f8da9e196e6fb97838ea4e153564/src/clj/clojure/core/server.clj#L109
I think there that it's a stylistic thing. You could imagine creating a lazy seq of connections and create a thread that used reduce to consume that sequence. There's nothing stopping loading the next element of a lazy seq from blocking, right? I think it comes down to either how easy it is to read and follow what's going on, or performance. If there's a processes that is sitting in a loop waiting to do something, it makes sense to use the word loop
and that makes loads of sense in something like core async. Or if you don't want to create loads of intermediate sequences, or you're porting an algorithm from other langs that uses loops then maybe it's about performance. But you can pretty much always find a way to use reduce if you really want to. These two things seem fairly equivalent:
;; Loop
(let [socket (create-socket)
args []
client-daemon false
bind-err true]
(thread
(try
(loop [client-counter 1]
(when (not (closed? socket))
(try
(let [conn (.accept socket)]
(start-connection conn client-counter client-daemon bind-err args))
(catch SocketException _disconnect))
(recur (inc client-counter))))
(finally :close))))
;; reduce
(let [socket (create-socket)
args []
client-daemon false
bind-err true
sockets (->> (repeatedly (fn []
(when (not (closed? socket))
(accept socket))))
(take-while some?))]
(thread
(try
(->> sockets
(reduce
(fn [{:keys [client-counter]} conn]
(try
(start-connection conn client-counter client-daemon bind-err args)
(catch SocketException _disconnect))
{:client-counter (inc client-counter)})
{:client-counter 1}))
(finally :close))))
the loop thing is an orchestration process ... it's there so you can build your own machinery on top of, right? Like the STM or atoms or whatever, or async
/`await` in js, they're there so you can build your own layer of side effecty thing, and it's considered good design to make those things as small as possible and get into the immutable world asap ... I'm currently working at a place that uses typescript a lot, and I keep having to convince js devs that every time you await
, you're dealing with side effects, and these stacks of promises should be as thin as possible ...
I was looking at a loop
that I had for reading / evalling forms and wondering if I should convert it to a reduce but I ended up in a similar state as your example, which didn't appeal cos it was more convoluted. And like you say, we like to get out of the imperative world ASAP but sometimes it doesn't make sense so just wanted to get input / ideas. Thanks for yours!!
I've not watched any apropos for a while ... I might stick that on while I "refactor" this typescript 😜
we have been on an extended break ... the crew are ready to restart in October so 🤞:skin-tone-3:
cool ... I think I've successfully sold 3 copies of Eric's book on this current contract 😉
it's really good to have a book to point at that explains some of the stuff I keep ranting about much better than I do 😉
The concrete answer is: use recur when you aren't iterating over a collection. (E.g. traversing a tree, doing some specific algorithm like A*, reading from a queue, operating on a timer, etc.)
When you have a collection and need to keep state between elements, reduce is preferred because it does the iterating over the coll for you.
Bonus material: prefer seq fns when you're going over a coll and don't need to keep state between elements.
Excellent summary @U07S8JGF7 !!
måning
So I have been doing quite a bit of Rust lately... but I seem to spend 95% of my time dealing with compile errors and trying to figure out how to keep the compiler happy and not give any errors. But sometimes that seems impossible, it gives you suggestions, you follow the suggestion and the error stays... :face_with_symbols_on_mouth:

Sorry to hear it's been so frustrating... I am or have been considering trying to upskill into Rust - not so sure now... 😱
you have to consider that I am not clever enough to understand the borrow checked... so YMMV of course.
i'm working on some typescript atm, hit a problem i couldn't see how to express and have eventually figured out that it's because i need existential types, which typescript doesn't have...
i suspect there are some new ways of thinking required to work with statically checked languages, and it takes some time to grok them
otoh i think that learning these new ways of thinking will make us better programmers... and getting closer to "if it compiles then it runs" seems like a worthwhile goal
and no runtime and no gc!
oh, nice
rust is on my shortlist of languages i would like to work with
if you had asked me if I thought it is a good idea to write a PG extension 3 weeks ago I would have said: NO, don't do it. but having done it now for 2.5 weeks it is quite a nice solution.
can you run arbitrary PG extensions in PGAAS like RDS ?
does the rust static checking give you more confidence that you aren't going to crash customer's databases ?
and I think you can use other languages (python etc) but we are dealing with multiple Gigs of data and want to be really fast. hence a compiled language makes more sense in our case I think.
do PG extensions get run in a separate process ?
so if they bork it's just a query failure rather than a PG crash ?
extension run as the same process. and the pgrx plugin/framework I use turns a Rust panic
just into a failed query, no DB crash.
is that because rust can guarantee to clean up after itself even in a panic
?
you can run Java (JVM) as an extension as well... but that seems a lot more complicated. So I haven't even tried to use Clojure here.
I feel like compiler errors are waaay more helpful than clojure’s in general.
I think instead of rust I might go after this: https://github.com/pfeodrippe/recife
And for smaller scripts/programs - I’m digging Janet
@U0525KG62 i find clojure difficult without schema-checking at least at program boundaries... i'm definitely drawn by the promise of type-checking
I like it when it helps me... but when it feels like I'm fighting it most of time I'm not convinced. Yes, I hope it is trying to do the right thing, but make it easy and simple to get to the solution. Mind you, I wouldn't know how to do that or if it is even possible at all.
Per Pragmatic Programmer, I try to learn a new language every few years "for fun" and I was impressed by Rust but found wrestling with the borrow checker to be somewhat frustrating. In a way, Rust was what I had hoped Go would be when I learned that a year or two earlier (I was very disappointed in Go). I think if I had to go back to systems programming (my C and C++ / embedded telecoms days), I would want to use Rust for the extra safety... but I don't think I'd really enjoy it. If I couldn't do Clojure any more, I think I'd be happy doing Kotlin. It seems to strike the right balance for me. Overall, I'm not really a fan of static typing. I prefer strong dynamic typing. Haskell drives me up the wall, and Scala wasn't much better (it's a real split-brain language). Luckily, I didn't have to use Haskell in production (but I spent a couple of years in Scala before coming to Clojure). I did Groovy for a while and much preferred that over Java.
given guix, I think I might be doing a bit more guile/scheme in the future. And someone just released a transducers library for emacs lisp https://lists.sr.ht/~fosskers/transducers/%3Cfae7dd04-c990-4eb2-bc06-700d3c45356c%40fosskers.ca%3E
Zig is a nicer C and can compile C but without the Borrow Checker. @U080181PF has posted some videos about it.
but why would you not want a borrow checker, given that they now exist and bad ownership assumptions are such a massive source of production bugs in non gc'd langs?