clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
danielgrosse 2021-07-05T05:54:18.358600Z

(deftest init-game-test
  (testing "Init the game state"
    (let [state {}
          stack [:foo :bar :baz]
          state' (sw/init-game! (atom state) stack)]
      (is (= true (contains? (:stack @state') :foo))))))
No assertions (or no tests) were run.Did you forget to use 'is' in your tests?

πŸ‘ 1
Adie 2021-07-05T12:53:27.360300Z

I want to convert a clojure service to Go. What is the most optimal way to do this?

πŸ™€ 10
Adie 2021-07-06T08:52:10.387100Z

Thanks a tonn @noisesmith for the detailed answer. I now have more clarity on what should be the steps to follow in the transition. :thanks3:

Ben Sless 2021-07-05T12:58:38.360700Z

Why? Performance?

p-himik 2021-07-05T12:59:51.361Z

"Optimal" in what way?

vemv 2021-07-05T13:01:40.361200Z

if the clojure service has integration tests, you might hack them so that they work over http so that you can exercise the Go version is in fact equivalent to the old one

Rupert (All Street) 2021-07-05T14:30:05.361900Z

If this is a big (e.g. multi-week migration) project, you could do it gradually by creating some clojure<->go interop/bridge code then slowly replacing the service piece by piece.

borkdude 2021-07-05T14:58:06.363100Z

It would be interesting to hear more about the why. If you want low latency (startup) and lower memory footprint, GraalVM native-image might offer you that with the code you have right now (or minor changes)

πŸ‘ 1
vemv 2021-07-05T15:04:55.363400Z

forthcoming GCs promise sub-ms latency as well, so if that's the reason you might do with GC tuning for the time being and no-setup GC with newer JDKs

borkdude 2021-07-05T15:08:27.363700Z

Golang also uses GC, and probably less advanced or less options compared to Java

borkdude 2021-07-05T15:09:05.363900Z

although typically for Clojure programs you would generate more garbage due to lazy seqs / FP style programming

πŸ‘ 2
vemv 2021-07-05T15:10:33.364300Z

GOGC=off that's Go's only option :)

Ben Sless 2021-07-05T15:33:57.364700Z

From my experience, Clojure applications with performance issues have a metric ton of performance lying on the floor. There are lots of easy improvements without sacrificing readability or idiomatic code

βž• 1
niwinz 2021-07-05T16:06:30.365800Z

ZGC and Shenandoah gc offers SUB ms pauses. Right now we have all of our services running with ZGC with JDK16, and the 99% of pauses (the percentile 0.99) is 0.03ms (analyzing the gc log of today).

πŸ’― 2
Ben Sless 2021-07-05T16:11:02.366100Z

@niwinz how big is your heap, if you don't mind sharing?

niwinz 2021-07-05T16:13:11.366400Z

the concrete log is for 2gb heap, but I have similar/identical situation for services that runs with 16gb, ZGC does not depends on heap size, in fact, it has better pauses and better behavior with bigger heaps.

Ben Sless 2021-07-05T16:15:18.366800Z

That's what my question was aiming at. Since Clojure is more GC intensive than other langs I was wondering what kind of heaps you need to keep with ZGC for it to not keel over under the GC pressure

Ben Sless 2021-07-05T16:15:41.367Z

Or is it Shenandoah which keels over when the allocation rate gets too high?

vemv 2021-07-05T16:20:54.367200Z

Probably relevant: > Pause times do not increase with the heap, live-set or root-set size https://wiki.openjdk.java.net/display/zgc/Main

niwinz 2021-07-05T16:25:57.367500Z

It depends on the application, In my opinion, shenandoah behavior under highΒ memory pressure is better than ZGC, but has a bit higher pauses (avg 1-2ms) (only tested under JDK15, so don't know if it is improved with jdk16 changes). With JDK16, ZGC improves behavior on small heaps and removes the scalability issue related to the root size (number of threads, etc...) so the GC pause was reduced from AVG 1ms to <1ms.

niwinz 2021-07-05T16:26:45.367700Z

I'm pretty sure that right now shenandoah and zgc behavior will be very similar

niwinz 2021-07-05T16:34:10.368Z

In our case, we process large json/transit objects and serialize/deserialize them constantly, generating pretty big ammount of garbage on each request

Ben Sless 2021-07-05T16:45:15.368200Z

Large maps or large sequences of maps?

2021-07-05T19:24:49.368600Z

@aditi.mishra clojure and go are different enough that I don't think the answer will be a code oriented one. If I were undertaking that translation I'd start with diagram (on paper or whiteboard) of the big pieces, and the relationships between them (what they share, how they transfer data to one another, which code crosses the boundaries between those pieces). often times with real world apps the answer is that the boundaries are crossed everywhere and you don't actually have separate pieces, but in that case the process of making the diagram helps you realize how you should have written it and can guide the new version. of course this is a clojure forum so most answers are going to be attempts to argue you don't need to switch

2021-07-05T19:26:07.368800Z

also, logging can help a lot - put the same log calls in the same places, feed the same input, and look at differences in the log output

2021-07-05T19:26:58.369Z

similarly, it can be useful to use the existing codebase to generate data for example based tests (look at what the existing code returns, make a test in the other impl that expects the equivalent result)

2021-07-05T19:27:46.369200Z

and once again, if your existing codebase doesn't play well with that kind of data-in / data-out testing, that's another indicator of something to fix in the reimplementation

2021-07-05T19:30:09.369400Z

another trick I've used is to do a "bad rewrite" - code that is not idiomatic / readable in the new language but translates 1->1 with the old one. verify that the result works, then do the "readability / not being a pile of crap" edits as a second iteration

2021-07-05T19:30:59.369600Z

because in particular here, a lot of clojure idioms are going to be terrible / unreadable / very poor in performance when directly copied to go

2021-07-05T19:31:44.369800Z

lazy-seqs will probably become a queue consumer(?) - I think this is enough brain dump on my part now, I just find this kind of question interesting in general

vemv 2021-07-05T19:42:19.370Z

> another trick I've used is to do a "bad rewrite" - code that is not idiomatic / readable in the new language but translates 1->1 with the old one. one would end up with "aphyr interview code" :)

2021-07-05T19:43:11.370200Z

right, but that's not code I'd ever publish (or at least I'd delete the branch as soon as possible) :D

2021-07-05T19:43:39.370400Z

it's an intermediate step, but breaking into steps with limited roles can be helpful with big complex tasks

2021-07-05T19:43:55.370600Z

"do one thing at a time" and all that

πŸ‘Œ 1
clj8394 2021-07-05T19:53:31.371400Z

general clojure question, what's the proper way to emulate a for loop with a conditional break statement in clojure?

2021-07-05T19:57:20.372Z

reduce to replicate the loop, returning reduced values when you want to early exit.

βž• 1
seancorfield 2021-07-05T19:57:30.372100Z

Without knowing a bit more context and specifics, it's hard to be precise, but generally a reduce that can return reduced for the conditional "break" should work.

πŸ‘ 1
seancorfield 2021-07-05T19:58:15.372400Z

And that sort of question is going to get you better answers in #beginners (because the folks there have opted in to helping folks learning Clojure in depth -- which is not the case in this channel).

phronmophobic 2021-07-05T20:02:59.372700Z

depending on if you want a for loop for iteration or for side effects, the for macro also accepts a :while to "break" early:

phronmophobic 2021-07-05T20:03:08.372900Z

&gt; (for [i (range)
        :while (&lt; i 5)]
   i)
(0 1 2 3 4)

phronmophobic 2021-07-05T20:03:56.373100Z

for side effects:

&gt; (doseq [i (range)
          :while (&lt; i 5)]
    (println i))
0
1
2
3
4
nil

seancorfield 2021-07-05T20:12:15.373300Z

(which is why we encourage this sort of question in #beginners because there's a lot of context needed to get the best answer for a given situation)