malli

Mateusz Mazurczak 2024-09-06T12:53:08.536339Z

How to make function validation to return true only based on arity, ignoring the output of the function? So I have something like this:

(-> [:=> [:cat :any] :any]
    (malli/schema {::malli/function-checker malli-generator/function-checker})
    (malli/validate (fn [x] (nth [0 1] x)))
And this returns false. In humanize version :error is nil. I would expect it to return true, because there is one parameter and I don't care about output.
(-> [:=> [:cat :any] :any]
    (malli/schema {::malli/function-checker malli-generator/function-checker})
    (malli/validate (fn [x] x))
For example this works as expected and returns true. I assume the former version can't predict what an output is because I call a function inside of it. But I don't care about output, that's why I said :any. Is there a way to check only arity?

ikitommi 2024-09-09T08:22:17.364189Z

I don’t rememeber. Future-engineered option that I have never used myself.

opqdonut 2024-09-09T08:27:28.009909Z

I'm not sure what Mateusz was after, but it looks like :scope is only available when instrumenting, that is, when somebody is calling the function anyway. I think Mateusz was after validating functions before calling them?

opqdonut 2024-09-09T08:27:33.668019Z

as data

ikitommi 2024-09-09T08:28:02.318649Z

ok, so the :scope is not used in function-checker..

ikitommi 2024-09-09T08:28:31.490899Z

.. but in wrapping around it, e.g. instrument

opqdonut 2024-09-06T13:08:44.340129Z

I guess you could make your own function-checker?

πŸ‘ 1
opqdonut 2024-09-06T13:12:01.983369Z

the problem with your first example is that malli-generator/function-checker calls your function with inputs like nil, which causes an exception, which causes the function-checker to reject the function

πŸ‘ 1
opqdonut 2024-09-06T13:18:49.763189Z

the reason why it's not in malli by default is because detecting the arity of a function is a bit tricky in clojure and impossible in clojurescript

opqdonut 2024-09-06T13:30:04.214099Z

here's a version that works for simple cases:

user=> (defn my-function-checker [?schema _opts] (fn [f] (when-not (= (:arity (malli/-function-info ?schema)) (arg-count f)) {::malli/result :wrong-arity :result :wrong-arity})))
#'user/my-function-checker
user=> (malli/explain (malli/schema [:=> [:cat :any] :any] {::malli/function-checker my-function-checker}) (fn [x] (nth [0 1] x)))
nil
user=> (malli/explain (malli/schema [:=> [:cat :any] :any] {::malli/function-checker my-function-checker}) (fn [x y] (nth [0 1] x)))
{:schema [:=> [:cat :any] :any], :value #object[user$eval6147$fn__6148 0x1fd0ae78 "user$eval6147$fn__6148@1fd0ae78"], :errors ({:path [], :in [], :schema [:=> [:cat :any] :any], :value #object[user$eval6147$fn__6148 0x1fd0ae78 "user$eval6147$fn__6148@1fd0ae78"], :check {:malli.core/result :wrong-arity, :result :wrong-arity}})}

opqdonut 2024-09-06T13:31:12.952289Z

using this not-at-all robust arg-count from stackoverflow:

(defn arg-count [f]
  {:pre [(instance? clojure.lang.AFunction f)]}
  (-> f class .getDeclaredMethods first .getParameterTypes alength))

ikitommi 2024-09-08T09:24:43.895379Z

Great answer @joel.kaasinen! There is :scope option, which can be narrowed to check any of :input, :output or :guard. If that is only set to :input, it would only check arity and type of input I think. Might still fail on the function invocation with nil thou.

πŸ‘ 1
opqdonut 2024-09-09T06:16:22.667089Z

is :scope also available when checking a schema that includes function values? something like {:callback (fn [a b] ...)}? or only when instrumenting defns?