Fork me on GitHub
#beginners
<
2020-07-09
>
kmyokoyama01:07:38

Hello, people. I have a thread running a loop/recur (it's a Kafka consumer). That thread is launched from a call to component/start, and I want to stop that thread (break the loop) whenener component/stop is called. What would be the best way to do this? A hack is to create an atom that is a boolean, and check it before calling recur . It works but I want to avoid global state. If the question is not clear, I can create a gist and share it. Thank you.

seancorfield02:07:43

@yokoyama.km You can try calling future-cancel on it but the loop would need to be "well-behaved" and be able to be cancelled.

hiredman02:07:12

Using an atom like that is not a hack it is good practice

hiredman02:07:50

Be sure to wait on the future in your stop, so he future has actually exited by the time it returns

seancorfield02:07:48

Yeah, that would be my recommendation too: provide some mutable state that can be leveraged to cause the thread to stop. At work we do something similar (but the other way around) -- we wait on a promise in our main thread and the background thread running the server delivers the promise when it shuts down.

kmyokoyama02:07:59

Thank you, guys. I was thinking that could exist some way involving channels, but I didn't come up with a solution.

noisesmith14:07:00

an alternative to an atom (designed for arbitrary and async update from N threads) is a promise or delay (designed to start "uninitialized" and then to be initialized exactly once)

noisesmith14:07:19

this can give you the same behavior (call realized? to find out if its been initialized, if so you know its time to exit) while clarifying intent (this isn't a mutable box that gets switched around multiple times, it's a one time one way switch)

noisesmith14:07:01

you can use a promise-chan for the same purpose, but if you aren't already using core.async this isn't worth bringing it in

kmyokoyama15:07:26

Thank you too, @U051SS2EU. Could you extend the explanation on using promise-chan? My issue with using channel is that it blocks on receiving from an empty channel, right?

noisesmith15:07:32

poll! returns immediately in all cases, nil if there was no value, the value on the chan otherwise

noisesmith15:07:25

a promise chan will start blocked (returning nil on every poll! call) then switch permanently into unblocked (returning the same value for every poll! call)

noisesmith15:07:48

but really it isn't doing anything a bare promise wouldn't do, and if you aren't using eg. alts! already it's added complexity with no benefit

kmyokoyama15:07:39

Oh, I see. So I'll try first make promise and realized? work. Thank you again.

noisesmith15:07:27

the third function there is deliver (that turns the one way switch to realize the promise)

kmyokoyama15:07:06

Yes, sure. Thank you

nick13:07:18

number_to_human(123)                                          # => "123"
number_to_human(1234)                                         # => "1.23 Thousand"
number_to_human(12345)                                        # => "12.3 Thousand"
number_to_human(1234567)                                      # => "1.23 Million"
number_to_human(1234567890)                                   # => "1.23 Billion"
Is there something similar in Clojure or Java? It seems like a pretty common task but I couldn't find anything similar yet

nick13:07:40

"intword" Perfect. Thank you very much @jmayaalv

👍 3
noisesmith14:07:34

you can also do this with clojure.pprint/cl-format without any external library

(ins)user=> (clojure.pprint/cl-format nil "~r" 12)
"twelve"

noisesmith14:07:26

(ins)user=> (clojure.pprint/cl-format nil "~r" 1234567890)
"one billion, two hundred thirty-four million, five hundred sixty-seven thousand, eight hundred ninety"
not quite what you want I guess

nick14:07:03

cl-format looks pretty interesting. I need to check it out Thanks!

noisesmith14:07:47

it implements the format from common lisp, which is extensively documented online (I think someone wrote a whole book on that one function)

nick14:07:51

A bit surprising to know that cl- stands for Common Lisp.. I have never seen this before. Referencing to another lang pattern/convention from the core functions

noisesmith14:07:26

haha, we could rename clojure.set/join to clojure.set/sql-inner-join

😀 3
noisesmith14:07:17

rename for to for-but-not-like-c-for-loop

😂 6
fmn13:07:50

Hi, are there any clj source code formatter that format something like

(let [foo "foo"
      barqux "barqux"]
  ....)
into
(let [foo    "foo"
      barqux "barqux"]
  ....)

vncz13:07:09

Is this the correct way to define a spec to validate this?

papachan14:07:20

user> (number? ##NaN)
;; => true

manutter5114:07:10

That's actually correct, do a google search for "Is NaN a number"

👍 3
andy.fingerhut15:07:03

NaN is weird in so many ways, in multiple programming languages. This Github repo is not actually recommended, unless you are somewhat obsessive-compulsive about such things: https://github.com/jafingerhut/batman

😆 3
David Pham17:07:54

Is there an opposite to destructuring? For example I often have to write {:a a :b b} and I guess a macro could save me from the repetition. Wondered if someone already wrote it?

manutter5117:07:54

I defined a keyboard macro in my IDE. I type ==TAB and then whatever I type appears twice, with a colon in front of the first one

manutter5117:07:21

==TABfoo => :foo foo

David Pham03:07:58

Nice trick :)

seancorfield17:07:26

Several libraries have something similar @neo2551 including one of ours...

David Pham17:07:57

Thanks I will look for it then.

noisesmith17:07:57

there are a few ways to do it - I have this for debugging:

(ins)user=> (defmacro locals [] (into {} (map (juxt keyword identity) (keys &env))))
#'user/locals
(ins)user=> (let [a 0] ((fn [x] (loop [q :a] (locals)))  42))
{:a 0, :x 42, :q :a}

seancorfield17:07:32

Per the docstring of local-map in our library: Inspired by an example from @U051SS2EU on Slack. 🙂

6
noisesmith17:07:19

(demonstrating it captures all varieties of local binding)

noisesmith17:07:48

there's a version in the useful lib too

David Pham17:07:29

Thanks a lot!

telekid17:07:35

Why can I use clojure.string/capitalize without requireing it?

noisesmith17:07:49

because somebody else required clojure.string first

telekid17:07:02

okay cool, I thought as much

telekid17:07:11

:thumbsup:

noisesmith17:07:30

common gotcha - pprint will get loaded by default repls, won't load when running your app

noisesmith17:07:35

string may or not preload

telekid17:07:03

> common gotcha - pprint will get loaded by default repls, won’t load when running your app ooh, that’s sneaky

noisesmith17:07:04

best practice (outside a repl at least) is to always require and alias every ns you actually access

👍 3
stopa17:07:59

Hey team, is there a "best practices for setting up clojure with postgres" tutorial you'd recommend? (am looking for recommended libraries, potential ~orm-like stuff, discussion on tradeoffs between diff choices) If something like this exists would love to know!

seancorfield17:07:10

@stopachka We don't use "ORM" stuff because we don't have objects. next.jdbc is going to be your first stop -- that's a general JDBC wrapper and lets you read/write hash maps from/to rows in the database.

❤️ 3
seancorfield17:07:05

Start here: https://cljdoc.org/d/seancorfield/next.jdbc/1.1.547/doc/getting-started -- read all the docs (seriously!)... there's some PostgreSQL-specific stuff in the Tips &amp; Tricks page but the rest should all be generally applicable.

❤️ 3
stopa17:07:18

oo this looks great, on it -- thanks Sean!

seancorfield17:07:23

I test against Embedded PostgreSQL 12.2.0 but you'll want the regular JDBC driver org.postgresql/postgresql {:mvn/version "42.2.14.jre7"}

👍 3
seancorfield17:07:32

There's a #sql channel if you have questions.

stopa17:07:55

nice! one quick q, am guessing there may also be libraries that do stuff like: 1. something that handles migrations, etc 2. maybe a macro library, that given some sql structure, gives me a bunch of helper functions for querying 3. some higher-level sql library, so we don't need to write with strings perhaps 2 & 3 are a bit superfluous -- wonder how clj community handles these

jaihindhreddy18:07:27

Because you mentioned postgres, also check out https://github.com/nilenso/honeysql-postgres for an example of extending honeysql.

dharrigan18:07:19

I use flywaydb for migrations. Works great.

stopa18:07:11

nice, thanks for the recs team!

seancorfield18:07:21

I'd recommend Migratus for migrations and I'll recommend HoneySQL too (but, caveat, I maintain that as well as next.jdbc). And, yes, the PG extensions to HoneySQL are worth looking at.

❤️ 3
seancorfield18:07:06

There's a #honeysql channel if you need it. Also, if you want to keep your SQL out of your code (some folks prefer that approach), there's #hugsql -- which has a built-in adapter for next.jdbc (and there's a quick start guide for HugSQL in the next.jdbc docs).

👍 3
stopa18:07:18

ya'll rock team! : }

Eddie19:07:18

I just noticed that you can't call pop on a LazySeq, even after calling doall. I have some code that operates on a list as if it were a stack, but there are times when I also must map down the "stack" (I know that is technically a violation of the stack API) and still be able to call pop on the resulting seq. Any suggestions? The only solution I have come up with is (pop (reverse (into '() (map identity '(1 2 3))))) but I don't like needing the reverse.

Eddie19:07:11

I suppose I could switch to using rest instead of pop and get the same result except it remains as a LazySeq. Is that good/bad? I would rather have an efficient way to create a list from a LazySeq that doesn't reverse the order.

seancorfield19:07:23

@erp12 Use a vector instead? And mapv instead of map.

alexmiller19:07:37

or vector and transduce

noisesmith19:07:53

why do you need a list?

noisesmith19:07:03

why is pop better than rest for your use case?

noisesmith19:07:25

(agreed, for a stack, use a vector, if that's what you need)

alexmiller19:07:00

lists are faster than vectors for this

alexmiller19:07:10

but not if you turn them into seqs

noisesmith19:07:04

oh, so this might actually be the rarely seen use for list*

dpsutton19:07:37

i checked that. it still returns a seq

dpsutton19:07:49

user> (pop (list* (map inc (range 5))))
Execution error (ClassCastException) at user/eval140786 (form-init377868074523440121.clj:18).
clojure.lang.ChunkedCons cannot be cast to clojure.lang.IPersistentStack

dpsutton19:07:59

whoops. i see in main. sorry

noisesmith19:07:00

np - I should have mentioned that here as well

alexmiller19:07:15

as the doc string says "Creates a new seq ..."

alexmiller19:07:44

if you're going to use stack verbs then I'd stick with stack api

Eddie19:07:31

I went with lists because of the note here: https://clojure.org/reference/data_structures#Lists

alexmiller19:07:33

well that's fine until you do non-stack things

noisesmith19:07:09

@erp12 cool - sounds like an actual list is what you want, see the list* function linked on that same node

noisesmith19:07:20

it gives you a real list without reverse etc.

alexmiller19:07:58

that sounds worse than using a vector to me

noisesmith19:07:08

n/m it doesn't return a list

Eddie19:07:09

That makes sense. Stacks are stacks. No getting around it.

Eddie19:07:28

Thanks all! The only time I needed to map down the stack is to coerce the elements in the event stack is being passed from the outside. That might happen rare enough to accept the cost of a round trip through a seq, or maybe I will just check the stack with a spec and then throw if the caller screwed it up.

Dimitar Uzunov19:07:46

Hello clojurians, I was wondering if this is a correct way to use a repl a running ring webserver:

(defn -main
  "Blogo blog backend"
  []
  (future ;; Pretty cool, it unblocks me from using the repl!!
   (jetty/run-jetty app {:port 9091})))
It fixed my problem - when I run (-main) in the REPL I can still evaluate expressions in it, otherwise it somehow stops working correctly. I think this leaves me two threads - one for ring and one for me.

seancorfield20:07:49

Passing :join? false in that options hash map will do what you want for the REPL.

seancorfield20:07:24

But if you do it in your -main, when you run the app outside the REPL, it will just exit immediately.

seancorfield20:07:12

So I'd recommend writing a function to call specifically from the REPL, and then calling it from -main with a different argument:

(defn start [& [join?]] (jetty/run-jetty-app {:port 9091 :join? join?}))
;; call from REPL as (start)
(defn -main [& args ]
  (start true))
@dimitar.ouzounoff

Dimitar Uzunov20:07:16

Thanks! I just tried it out, works for me :)

Dimitar Uzunov20:07:48

but what do you think of using future like that if ring didn’t had :join? ?

seancorfield20:07:37

It will mean your -main will just exit when you run it from the command line -- which is not what you want.

seancorfield20:07:57

It also means you won't be able to stop the server within your REPL either.

seancorfield20:07:05

run-jetty should return a handle for the server, that you can call .stop on.

seancorfield20:07:49

See this code https://github.com/seancorfield/usermanager-example/blob/develop/src/usermanager/main.clj#L140-L175 which has logic to start/stop either Jetty or http-kit (so it's a bit more complex that what you're doing).

👍 3
thanks 3
seancorfield20:07:32

It captures the result of calling (jetty/run-jetty app {:port ... :join? false}) so that it can .stop it later.

seancorfield20:07:13

http-kit works differently -- it starts in the background anyway, and returns a handle to the server that you invoke with no args to stop it.

seancorfield20:07:03

Another reason not to use future like that is that if the server throws an exception, you won't see it -- the process inside the future will just silently die.

seancorfield20:07:32

You only see the failure if you deref the future -- when it will throw the exception that the process failed with.

Dimitar Uzunov20:07:31

thanks! Usermanager is just I was looking for - I wanted to read some clojure example code like this.

vasergen21:07:04

How can I repeat the last command in REPL (in shell it is arrow up or !!)?

noisesmith21:07:08

you can refer to the last value with *1 , you can use arrow keys if you use a repl with readline capability (like clj from the clojure team, or lein repl from the lein project)

noisesmith21:07:27

there's no symbol to repeat the last execution

☝️ 3
vasergen21:07:02

I am using REPL in CIDER

noisesmith21:07:48

IIRC you can use M-p in emacs to bring up the previous input

noisesmith21:07:16

but this is going to be different for every UI and isn't built into clojure itself

vasergen21:07:17

thank you! that works

Frosku23:07:25

Can someone please explain how I'd use spec in production code?

Frosku23:07:30

I understand it principally

Frosku23:07:37

And I've played about with it on the repl

Frosku23:07:49

But how is it actually used in the wild?

seancorfield23:07:24

https://corfield.org/blog/2019/09/13/using-spec/ talks about how we use Spec at World Singles Networks. We've been heavy users since the 1.9 alpha days.

metal 3
Ilari Tuominen05:07:43

Thanks for writing this, I really needed to read it.