Fork me on GitHub
#clojure-europe
<
2023-09-22
>
simongray07:09:49

good morning

maleghast07:09:28

maidin mhaith!

mdiin07:09:27

Godmorgen

schmalz07:09:43

Morning all.

dharrigan07:09:24

Good Morning!

ray07:09:46

Good morning

ray07:09:23

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

❤️ 1
ray07:09:00

the most common case I have is when re-reading forms to eval ...

ray07:09:34

but would welcome other cases - or suggestions to fix that one from the big brains!

simongray08:09:54

when you need a side-effect?

simongray08:09:32

also: when you have multiple independently updating values rather than a simple aggregation

otfrom08:09:16

you might be able to do the multiple things using things like fuse or other similar things to https://github.com/henrygarner/redux

1
otfrom08:09:28

depending on what you mean by "independent"

otfrom08:09:51

but I might use loop when firing off events where I really only care about the side effects

ray09:09:20

ok ... separate question but why use loop rather than doseq

ray09:09:55

there is a whole DSL around that :man-shrugging::skin-tone-3:

otfrom09:09:48

I think I've only really used loop when doing stuff with core.async

otfrom09:09:00

and that is probably b/c I was cargo culting code that was using loop

otfrom09:09:38

so when there is lots of interop?

ray10:09:37

I thought it was because it blocks on conn (.accept socket)

ray10:09:56

ie there is no way to feed a next value

Ed13:09:18

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))))

Ed13:09:57

But I think the loop requires less mental gymnastics to see what it's doing.

ray13:09:36

ok - there is a way 🙂 ... but yeah I agree, it's less obvious

Ed13:09:24

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 ...

Ed13:09:14

what's the driver for the interest in loop?

ray14:09:37

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!!

ray14:09:13

And I was thinking that it might be interesting for https://vimeo.com/user141124172

👍 1
Ed14:09:42

I've not watched any apropos for a while ... I might stick that on while I "refactor" this typescript 😜

😆 1
ray14:09:34

we have been on an extended break ... the crew are ready to restart in October so 🤞:skin-tone-3:

Ed14:09:29

cool ... I think I've successfully sold 3 copies of Eric's book on this current contract 😉

Ed14:09:53

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 😉

Ed14:09:47

Say thanks to him for me 😉

ray14:09:18

haha great - will do

👍 1
potetm22:09:39

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.)

potetm22:09:16

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.

potetm22:09:48

Bonus material: prefer seq fns when you're going over a coll and don't need to keep state between elements.

ray07:09:01

Excellent summary @U07S8JGF7 !!

ray07:09:15

Thanks

metal 1
thomas10:09:21

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:

creeper 2
maleghast11:09:29

Sorry to hear it's been so frustrating... I am or have been considering trying to upskill into Rust - not so sure now... 😱

thomas11:09:34

you have to consider that I am not clever enough to understand the borrow checked... so YMMV of course.

maleghast12:09:55

"borrow checked"? I know that I could just Google it, but...

thomas12:09:32

checkeR sorry

mccraigmccraig12:09:39

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...

mccraigmccraig12:09:18

i suspect there are some new ways of thinking required to work with statically checked languages, and it takes some time to grok them

thomas12:09:28

I have an existential crisis most of the time

😂 2
thomas12:09:19

but I got my problem resolved now... but took quite a bit of faffing around.

mccraigmccraig12:09:39

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

thomas12:09:30

yup, and I have implemented my own little LRU cache... and it works!

mccraigmccraig12:09:28

and no runtime and no gc!

thomas12:09:57

yup.... and it is even a Postgres extension... so all running as part of the DB.

mccraigmccraig12:09:42

rust is on my shortlist of languages i would like to work with

thomas12:09:19

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.

mccraigmccraig12:09:02

can you run arbitrary PG extensions in PGAAS like RDS ?

thomas12:09:22

No, you can't. They don't allow it.

thomas12:09:44

so you have to self hosted. and except for 1 of our customers they all are.

mccraigmccraig12:09:48

does the rust static checking give you more confidence that you aren't going to crash customer's databases ?

thomas12:09:33

I hope so... and the other option would be C... and that would be a bad idea IMHO.

thomas12:09:32

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.

mccraigmccraig12:09:59

do PG extensions get run in a separate process ?

mccraigmccraig12:09:19

so if they bork it's just a query failure rather than a PG crash ?

thomas12:09:46

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.

mccraigmccraig12:09:49

is that because rust can guarantee to clean up after itself even in a panic ?

thomas12:09:18

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.

thomas12:09:53

I think so, haven't looked into the details

thomas13:09:01

it is PoC at the moment. but overall it looks very promising for the customer IMHO

vijaykiran13:09:33

I feel like compiler errors are waaay more helpful than clojure’s in general.

otfrom13:09:07

I think instead of rust I might go after this: https://github.com/pfeodrippe/recife

otfrom13:09:16

but I partly don't believe the type checking hype

vijaykiran13:09:16

I’m into Rust for crazy fast binaries though 😅

👍 2
vijaykiran13:09:54

And for smaller scripts/programs - I’m digging Janet

mccraigmccraig13:09:41

@U0525KG62 i find clojure difficult without schema-checking at least at program boundaries... i'm definitely drawn by the promise of type-checking

thomas14:09:43

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.

seancorfield16:09:34

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.

otfrom16:09:56

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

ray18:09:03

Zig is a nicer C and can compile C but without the Borrow Checker. @U080181PF has posted some videos about it.

mccraigmccraig18:09:25

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?

ray19:09:06

Fewer 🤕 ?

ray19:09:20

When writing 😂

shiyi.gu10:09:34

Late morning.

lread16:09:35

MORNING! (oops, sorry, morning!)

2
Ⓜ️ 2
2
2
nextjournal 2
ℹ️ 2
🆖 2
pez16:09:10

CAPS LOCK MORNING!

😄 1