Fork me on GitHub
#malli
<
2021-11-01
>
Felipe Cortez16:11:02

hi! the doc says m/=> can be placed both before or after a defn. when putting it before, clj-kondo complains about the symbol being unresolved. :unresolved-symbol {:exclude [plus]} in clj-kondo's config.edn silences the error. should that be the default behavior?

borkdude16:11:13

that's usually not an optimal solution to unresolved symbols in clj-kondo

borkdude16:11:45

does malli export hooks and/or config as part of the library?

Felipe Cortez17:11:48

it seems to export the stuff that goes to .clj-kondo/configs/malli/config.edn automatically

borkdude17:11:26

I don't think malli exports any macro config. Does it have macros that clj-kondo doesn't understand?

Felipe Cortez17:11:37

the only thing it seems to export for m/=> is

{:linters
 {:type-mismatch {:namespaces {brincando {plus {:arities {2 {:args [:int :int], :ret :int}}}}}}}}
but since the macro would still be out of order even if defined for clj-kondo, is there any other way to solve this?

borkdude17:11:28

Please give an example, I find it hard to imagine what you're talking about

Felipe Cortez17:11:33

sorry! this is the working code

(m/=> plus [:=> [:cat [:int {:max 10}] :int] :int])
(defn plus [x y] (+ x y))
but I just noticed that if you "expand" that macro in clj-kondo to declare plus, it solves the undeclared variable warning, right?

borkdude17:11:17

I think you can just put the m/=> expression after the defn and then clj-kondo will understand that plus is a var

Felipe Cortez17:11:45

yep, but you can also put m/=> before the defn and it is supposed to work, and I'd like to have the option to put the function schema close to the top of the defn

borkdude17:11:58

I understand

borkdude17:11:17

In this case it's better to write a hook. Optimally malli itself would bundle this in the library and export it

borkdude17:11:55

alternatively you can use {:linters {:unresolved-symbol {:exclude [(malli.core/=>)]}}}

borkdude17:11:06

this will suppress all unresolved symbols in =>

borkdude17:11:27

but that config could also be exported by malli so it would work for everyone. cc @ikitommi

ikitommi19:11:31

so, definetely. so, whoever knows better what to put into malli repo so it would work, please do.

ikitommi19:11:41

oh, that, I can do it

ikitommi20:11:51

@UKW2FUL4D https://github.com/metosin/malli/pull/559. thing is, that if that is merged, clj-kondo will not warn on cases where you define the m/=>, but not the actual function that it points to. Not good either.

🎉 1
ikitommi20:11:17

but, that’s stops the warnings.

borkdude20:11:25

@ikitommi That's a step in the right direction, but you can make it better using an exported macro hook

borkdude20:11:40

but this is more time consuming and I understand the trade-offs

borkdude20:11:07

it would be better to export it as part of the malli library though, then people don't have to run malli first to get the better linting

ikitommi21:11:05

a better solution / clj-kondo macro hook would be great, anyone? Just merged the initial fix, thanks @borkdude for the code to paste in 🙂

Felipe Cortez13:11:29

nice! what about a hook that interprets m/=> as (fn [sym _schema] (clojure.core/declare %1))? kondo doesn't seem to complain about

(def thing)
(declare thing)
so it works the other way around too

borkdude17:11:16

I would say:

(do (declare thing) schema)
so everything you use in the schema is still seen by clj-kondo.

Felipe12:11:51

right! I'll try this 🙂

Felipe23:11:23

@ikitommi hmmm, apparently doesn't seem to catch errors for instrumented functions doing m/=> before the defn:

(require '[malli.core :as m])
  (require '[malli.dev :as dev])
  (dev/start!)

  (defn plus1 [x] (inc x))
  (m/=> plus1 [:=> [:cat :int] [:int {:max 6}]])
  (plus1 6)
  ;; 1. Unhandled clojure.lang.ExceptionInfo
  ;;  :malli.core/invalid-output {:output [:int {:max 6}], :value 7,
  ;;  :args [6], :schema [:=> [:cat :int] [:int {:max 6}]]}

  (m/=> plus1' [:=> [:cat :int] [:int {:max 6}]])
  (defn plus1' [x] (inc x))
  (plus1' 6)
  ;; => 7

ikitommi10:11:40

@UA2U3KW0L that’s not good. I guess (re-)calling (dev/start!) after definitions would work.

ikitommi10:11:23

so, that’s just dev-time annoyance, but then again, it’s supposed to be dev-time tooling. Ideas welcome

ikitommi10:11:09

I would assume m/=> with dev running, should put a var-watcher that takes care of that, but guess not :thinking_face:

Felipe12:11:43

it seems to put a watch on the function schema, so what seems to be happening is the watch fn is called, the function gets instrumented properly, but when you eval the defn again you lose the instrumentation

Felipe12:11:18

yep! as a quick test, I did

(defn start!
   ,,,
   (let [watch (fn [_ _ old new]
                 (println "watched")
                 (future ;; <-
                   (Thread/sleep 500) ;; <-
                   (mi/instrument! ,,,))]
     (add-watch @#'m/-function-schemas* ::watch watch))
   (mi/instrument! ,,,)
   ,,,))

ikitommi13:11:48

so, we should get an event when the var is redefined, e.g. via defn. I guess that's doable?

Felipe Cortez14:11:18

add-watch supports this, apparently:

(def changes* (atom []))  
  (def a 1)
  (add-watch #'a nil (fn [_ _ & old+new] (swap! changes* conj old+new)))
  (def a 2)
  (def a 3)
  @changes* ;; => [(1 2) (2 3)]

Felipe Cortez14:11:44

just noticed I use clojurians through 2 different slack accounts

borkdude17:11:45

or you can use {:lint-as {malli.core/=> clojure.core/def}}

borkdude17:11:55

which is not entirely semantically correct, but I think it works for linting

borkdude17:11:06

then you will get a redefined warning :)

Felipe Cortez17:11:18

clojure.core/declare, maybe?

ikitommi21:11:05

a better solution / clj-kondo macro hook would be great, anyone? Just merged the initial fix, thanks @borkdude for the code to paste in 🙂

Felipe23:11:23

@ikitommi hmmm, apparently doesn't seem to catch errors for instrumented functions doing m/=> before the defn:

(require '[malli.core :as m])
  (require '[malli.dev :as dev])
  (dev/start!)

  (defn plus1 [x] (inc x))
  (m/=> plus1 [:=> [:cat :int] [:int {:max 6}]])
  (plus1 6)
  ;; 1. Unhandled clojure.lang.ExceptionInfo
  ;;  :malli.core/invalid-output {:output [:int {:max 6}], :value 7,
  ;;  :args [6], :schema [:=> [:cat :int] [:int {:max 6}]]}

  (m/=> plus1' [:=> [:cat :int] [:int {:max 6}]])
  (defn plus1' [x] (inc x))
  (plus1' 6)
  ;; => 7