malli

https://github.com/metosin/malli :malli:
ikitommi 2020-11-15T09:30:52.061300Z

wrote an issue for discussing about auto-updating with container schemas: https://github.com/metosin/malli/issues/304. Comments most welcome.

ikitommi 2020-11-15T11:37:28.061800Z

function schemas, comments welcome: https://github.com/metosin/malli/issues/125#issuecomment-727555388

borkdude 2020-11-15T11:40:34.062400Z

I think you are ignoring the fact that different arities can have different return types/schemas

borkdude 2020-11-15T11:41:04.062900Z

I think clojure.spec is also making this too difficult. In clj-kondo I chose to define the spec per arity

borkdude 2020-11-15T11:41:17.063200Z

It is more verbose, but at least easy to verify

ikitommi 2020-11-15T11:42:23.063800Z

good point, the schema also forces the return to be the same for all arities.

borkdude 2020-11-15T11:42:42.064500Z

really?

ikitommi 2020-11-15T11:43:04.064900Z

I guess there are lot of examples in the core where the different arities return different things?

ikitommi 2020-11-15T11:43:14.065100Z

yes.

borkdude 2020-11-15T11:44:13.065800Z

confirmed: > - The output schema always goes on the fn name, not the arg vector. This means that all arities must share the same output schema.

borkdude 2020-11-15T11:45:02.066800Z

well, clojure.core/map, filter, etc, is an example that have different return types for different arities. I kinda wish that the transducer arity was just a different version like clojure.core/mapping but that ship has sailed. I often make mistakes with this

borkdude 2020-11-15T11:45:40.067100Z

look at the spec that will result from this: https://github.com/borkdude/speculative/blob/master/src/speculative/core.cljc#L297-L302

borkdude 2020-11-15T11:46:22.067900Z

I think this is not ergonomic at all. spec has to do backtracking etc, to match the right arity.

borkdude 2020-11-15T11:46:48.068200Z

imo the fn spec should match the structure of the defn args+bodies

ikitommi 2020-11-15T11:49:44.069800Z

good point. what would be a good malli definition for this:

(defn fun
  ([x] x)
  ([x y] [x (* x x)]))

ikitommi 2020-11-15T11:50:14.070300Z

clj-kondo has a good syntax for the different arities, use thatโ€™ish?

ikitommi 2020-11-15T11:50:53.070600Z

something like:

(m/=> fun {:arities {1 {:output int?
                        :input [:tuple int?]}
                     2 {:output [:tuple int? pos-int?]
                        :input [:tuple int? int?]}}})

borkdude 2020-11-15T11:51:30.071300Z

That's how I do it in clj-kondo yes.

borkdude 2020-11-15T11:52:11.071800Z

Not sure if that's the best, but I optimized for matching speed, so clj-kondo can find the right arg types fast

borkdude 2020-11-15T11:53:22.072200Z

as for defn syntax, maybe:

(defn fun
  ([x :- int?] :- int?
   x)
  ([x :- int? y :- string?] :- [:tuple string? int?]
   [y (* x x)]))

borkdude 2020-11-15T11:53:40.072500Z

so the return type directly after the arg vec

ikitommi 2020-11-15T11:54:49.072800Z

๐Ÿ‘

ikitommi 2020-11-15T11:57:58.074200Z

the extracted schemas could be in the compact/short :=> format, which is always 1-arity:

(defn fun1 [x] (* x x))

;; short
(m/=> fun1 [:=> int? [:tuple pos-int?]])
(defn fun
  ([x] (fun x x))
  ([x y] [x (* x x)]))

;; short
(m/=> fun [:or
           [:=> int? [:tuple int?]]
           [:=> [:tuple int? pos-int?] [:tuple int?]]])

ikitommi 2020-11-15T11:59:00.075Z

the long versions:

(defn fun1 [x] (* x x))

;; long
(m/=> fun1 {:arities {1 {:input int?
                         :output [:tuple pos-int?]}}})
(defn fun
  ([x] (fun x x))
  ([x y] [x (* x x)]))

;; long
(m/=> fun {:arities {1 {:output int?
                        :input [:tuple int?]}
                     2 {:output [:tuple int? pos-int?]
                        :input [:tuple int? int?]}}})

borkdude 2020-11-15T11:59:00.075100Z

could work yes

borkdude 2020-11-15T11:59:48.075400Z

doesn't fn spec hinge on sequence specs which are not exposed yet?

ikitommi 2020-11-15T12:01:14.076900Z

yes, those are needed for varags. we just had an internal tech-talk on friday, did a plan how to get the sequence schemas & schema parsing out. takes few days to make that good.

ikitommi 2020-11-15T12:01:57.077900Z

first demo of the function schemas will be with non-vargargs. enough to get feedback & start with the clj-kondo integration.

๐Ÿ‘ 1
ikitommi 2020-11-15T16:09:37.078600Z

(require '[malli.schema :as ms])

(ms/defn ^:always-validate fun :- [:tuple int? pos-int?]
  "returns a tuple of a number and it's value squared"
  ([x :- int?] :- any? ;; arity-level override
   (fun x x))
  ([x :- int?, y :- int?] ;; uses the default return
   [x (* x x)]))

(clojure.repl/doc fun)
; -------------------------
; demo/fun
; ([x] [x y])
;   
;   [:-> [:tuple int?] any?]
;   [:-> [:tuple int? int?] [:tuple int? pos-int?]]
; 
;   returns a tuple of a number and it's value squared

ikitommi 2020-11-15T16:16:52.078900Z

full meta:

(ms/defn square :- pos-int?
  [x :- int?]
  (* x x))

(meta #'square)
;{:schema [:or [:-> [:tuple int?] pos-int?]],
; :ns #object[clojure.lang.Namespace 0x3c5f3ba8 "demo"],
; :name square,
; :file "/Users/tommi/projects/metosin/malli/src/malli/schema.cljc",
; :column 1,
; :raw-arglists ([x :- int?]),
; :line 64,
; :arglists ([x]),
; :doc "\n[:or\n [:-> [:tuple int?] pos-int?]]"}

ikitommi 2020-11-15T20:28:40.079100Z

๐Ÿ‘ 1
2