clj-kondo 2026-09-02

is it possible to create a linting rule to prevent head retention in certain contexts?

✅ 1

can you give code examples?

(defn defaulting [fallback expr]
  (if (some? expr) expr fallback))

(let [v (apply f args)] ;; <-- very large sequence
  (defaulting v ;; <-- oops, head retention
    (cond
      (not (apply cacheable-args? args))
      (log/info "non-cacheable args; NOT writing" k)

      (not (cacheable? v))
      (log/info "non-cacheable result; NOT writing" k)

      :else
      (do (cache-write! k v) ;; <-- writes large sequence, causing OOM
          (log/info "wrote" k)
          (cache-read k)))))

just looking for strategies to avoid head retention of certain expressions, the fix here is to throw away the defaulting expression, and return v in each of the first two cond clausese

i guess i'm out of my element because i don't know how you'd possibly figure out that there's head retention in that

how would clj-kondo know that v is a seq of some large size? what if v was a map or a java date? i guess you'd have to rely on the inferred return type of f?

maybe a good rule of thumb is to not let laziness escape a function? (not sure)

i'll have to think about it more, wanted to check in case it's been solved before

generally, if a giant sequence can't fit in memory, i need to mark the source and the sink

so the v binding is the source, and the sink is cache-write!

like Noah hints, I think this is hard to do using static analysis

it does kinda remind of of lifetime annotations in Rust, where just one function can take ownership of something

👀 1

maybe the sink has to be in something like a tail position

It is enforcing linear usage, so linear types would be something like it, rust's lifetimes are a sort of superset of linear types

Linear types also warn if you don't use it at least once, so it would catch not realized laziness too

hey @ambrosebs can typed clojure do linear types?

I wrote a let-drain macro that statically checks for any symbolic references that might retain the head of a sequence at the expression tagged with ^:draining. Not perfect, but communicates intent.