Fork me on GitHub
#clj-kondo
<
2021-10-05
>
wdullaer07:10:47

I've got a question about the new 'loop-without-recur' linter

wdullaer07:10:09

we're pretty heavy users of manifold, and use it's version of recur from time to time

wdullaer07:10:17

we have d/loop aliased to core/loop

wdullaer07:10:38

but then the new linter will complain because it doesn't recognise the d/recur statement as a valid recur

wdullaer07:10:48

adding an alias for d/recur to core/recur doesn't fix this

wdullaer07:10:09

I've taken a look at the code for the linter, but couldn't immediately see where it goes wrong

borkdude07:10:04

@wdullaer ah I see, I think that warrants an issue

wdullaer07:10:51

alright, I'll post one in a minute

wdullaer07:10:37

like I said: I took a look, but it's too subtle for my noob skills 😉

wdullaer09:10:59

on closer inspection (trying make a minimal reproduction case), it's probably more due to some other deferred control flow mechanisms

wdullaer09:10:41

kondo handles it fine with the added alias for recur in "normal" scenario's

wdullaer09:10:12

but it gets confused if it's part of a d/catch (which takes a function), or if the d/recur is wrapped in a function that schedules it after a timeout

wdullaer09:10:13

(d/loop [] (side-effect :here) (mt/in 1000 d/recur))

borkdude09:10:42

oh right :)

borkdude09:10:09

perhaps it's better to just write a hook for d/loop then. hooks have gotten a little easier with the new :macroexpand option

borkdude09:10:09

or you could just lint d/loop as clojure.core/let and be done with it

wdullaer09:10:55

yeah, I think I'd actually have to write hooks for the control flow mechanisms (kondo also doesn't recognize normal loop and recur here)

wdullaer09:10:59

so sorry for the noise

borkdude09:10:27

@wdullaer lint-as let - isn't that a much easier solution?

wdullaer09:10:42

oh yeah, it's what I'll be doing 🙂

wdullaer09:10:41

I was just thinking out loud: the root cause is that kondo doesn't really understand that the arguments to stuff like d/catch are called later

wdullaer09:10:51

but it's also a bit unreasonable to expect it to do so 🙂

borkdude10:10:02

just for my understand, could you provide an example of that catch code?

wdullaer10:10:20

the example I gave earlier with mt/in is one -> kondo doesn't realize d/recur is executed later, I'll prepare a gist with d/catch

wdullaer10:10:12

essentially this retries a request infinitely until it succeeds, throttled by a rate-limiter

borkdude10:10:39

could you add require etc so I can understand what clj-kondo does not lint correctly?

borkdude10:10:18

E.g. I don't see any warnings here:

(ns private.tmp.repro
  (:require d log http))

(defn run-with-rl [x y] x y)

(declare endpoint http-opts bucket)

(d/loop []
  (->
   (d/chain (run-with-rl (partial http/get endpoint http-opts) bucket)
            :body)
   (d/catch #(do (log/error "failed to execute request:" %)
                 (d/recur)))))

borkdude10:10:47

but you probably are referring to the loop without recur

borkdude10:10:07

yeah that's really just written for clojure's loop/recur syntax

wdullaer10:10:28

indeed, I don't think it's reasonable to expect kondo to figure this out (without some serious hints from hooks)

wdullaer10:10:51

I jumped to conclusions because it always failed on d/loop, trying to make a minimal repro case provided clarity 🙂

borkdude10:10:15

ok, all clear then :)

wdullaer10:10:32

thanks for your time!

👍 1