Fork me on GitHub
#clj-kondo
<
2024-06-07
>
István Karaszi11:06:57

Linting bang functions

1
István Karaszi11:06:42

I was thinking about a new linter that might be interesting for others as well. It would give a warning on a function that is using bang functions and if its name is not ending with an exclamation mark. This would trigger a warning:

(defn something
  (swap! atom inc))
This would not:
(defn something!
  (swap! atom inc))

delaguardo11:06:58

such linter might get very annoying in case you have a stack of multiple function, one calling another.

(defn baz []
  42)

(defn bar []
  (baz))

(defn foo []
  (bar))

(foo)
after changing baz to use swap! or other "bang" function you probably will have either an explosion of warnings everywhere or only one upon fixing which a new one pops up.
(def x (atom nil))

(defn baz [] ;; warning here
  (reset! x 42))

(defn bar []
  (baz))

(defn foo []
  (bar))

(foo)

;;; after fix

(def x (atom nil))

(defn baz! []
  (reset! x 42))

(defn bar [] ;; warning here
  (baz!))

(defn foo []
  (bar))

(foo)

István Karaszi11:06:45

yes, true, but I was thinking about enabling this for a certain namespace, like DB models

István Karaszi11:06:45

and not enable for the whole app

delaguardo11:06:25

yeah, I see how and where this might be useful. But even within a single namespace the problem with a sudden new warning after fixing another might happen.

István Karaszi11:06:38

yes, definitely there is an avalanche effect, but for a DB model namespace, I believe that would be even favorable

imre12:06:51

A bit like java's checked exceptions, but opt in eh?

István Karaszi12:06:08

haha, kind of, yeah 😅

escherize21:06:53

poor man's effect system

2
Mario Trost05:06:26

> such linter might get very annoying in case you have a stack of multiple function, one calling another. Sounds like a feature, not a bug when a function deep down in the call stack turns side-effecting 😄

1
escherize14:06:22

How does that behave with a higher order function? I’m guessing static analysis would miss that, right? (defn f [f-bang] (f-bang 7)) (defn f! [n] (println n)) (f f!)

István Karaszi14:06:44

Yeah, but in that case the mutating function is the argument, not the caller.

delaguardo14:06:02

(do (f f!) (f str))

István Karaszi14:06:19

You don’t want to rename the map function to map! just because it can receive a mutating fn

escherize13:06:30

Yeah, I was thinking that too. So calling non-bang functions can side-effect :thinking_face: