plumatic / schema

Clojure(Script) library for declarative data description and validation
Other
2.4k stars 256 forks source link

`s/fn` validation has extra overhead #450

Open frenchy64 opened 1 year ago

frenchy64 commented 1 year ago

The (@~input-checker-sym args#) in the input checker code could be faster in the most common case without a rest argument and be more like (@~input-checker-sym ~@bind-syms).

(when-let [error# (@~input-checker-sym args#)]
  (error! (utils/format* "Input to %s does not match schema: \n\n\t \033[0;33m  %s \033[0m \n\n"
                         '~fn-name (pr-str error#))
          {:schema ~input-schema-sym :value args# :error error#})))))
frenchy64 commented 1 year ago

Perhaps a new macro called something like schema.core/arguments-checker could expand like this:

(arguments-checker 'my-fn-name ([a :- s/Int] [b :- s/Int, c :- s/Num] ))
;=>
(let [a_chk (checker s/Int)
      b_chk (checker s/Int)
      c_chk (checker s/Num)
      input_schema1 [(s/one a_chk 'a)]
      input_schema2 [(s/one a_chk 'b)
                     (s/one a_chk 'c)]]
  (fn
    ([a]
     (let [a_err (a_chk a)]
       (when a_err
         (let [error [(list 'named a_err a)]]
           (error! (utils/format* "Input to %s does not match schema: \n\n\t \033[0;33m  %s \033[0m \n\n"
                                  'my-fn-name (pr-str error))
                   {:schema input-schema1 :value [a] :error error})))))
    ([b c]
     (let [b_err (b_chk b)
           c_err (c_chk c)]
       (when (or b_err c_err)
         (let [error (mapv (fn [[n err]]
                             (when err
                               (list 'named err n)))
                           [[b b_err] [c c_err]])]
           (error! (utils/format* "Input to %s does not match schema: \n\n\t \033[0;33m  %s \033[0m \n\n"
                                  'my-fn-name (pr-str error))
                   {:schema input-schema2 :value [b c] :error error})))))))
w01fe commented 1 year ago

Interesting! A few questions:

frenchy64 commented 1 year ago

How significant is the overhead of the list compared to the typical overhead of checking itself?

Haven't done any perf tests, just a hunch for now. I happened to be reading the schema.macros implementation with my performance hat on. I'll try some tests with the prototype.

Would this require a lot of code duplication, or do you think we can just refactor the existing checks?

I think it could replace the existing checks if it works out in practice. It may be a challenge to manage method size, I tried to factor out as much code as I could into defn's in my prototype.

How will this tile with fn-validator?

Nothing needs to change there I think.