metosin / malli

High-performance data-driven data specification library for Clojure/Script.
Eclipse Public License 2.0
1.46k stars 208 forks source link

mx/defn broken with destructuring #839

Open opqdonut opened 1 year ago

opqdonut commented 1 year ago

Two related issues:

  1. Sequence destructuring generates invalid types. The type for a [i :- int, s :- string] destructuring should be [:cat :int :string [:* :any]], not [:cat [:? :int] [:? :string] [:* :any]] which admits any sequence

  2. Using a :cat schema for an argument doesn't work. The :cat schema for the argument gets combined with the top-level :cat. This doesn't work even with a [:schema [:cat ..]] workaround for the argument. However [:tuple ...] luckily works.

Repl session below.

;; 1. Sequence destructuring
user=> (require '[malli.experimental :as mx])
nil
user=> (require 'malli.dev)
nil
user=> (mx/defn foo [[i :- :int, s :- :string]] {:int i :string s})
#'user/foo
user=> (malli.dev/start!)
..instrumented #'user/foo
started instrumentation
nil
user=> (foo ["x"])
{:int "x", :string nil}
user=> (foo ["x" 1])
{:int "x", :string 1}
user=> (:schema (meta #'foo))
[:=> [:cat [:maybe [:cat [:? :int] [:? :string] [:* :any]]]] :any]

;; 2. :cat arguments
user=> (mx/defn bar [things :- [:cat :int :string]] things)
..instrumented #'user/bar
#'user/bar
user=> (bar [1 "x"])
-- Schema Error ---------------------------------------------------------------- NO_SOURCE_FILE:1 --

Invalid function arity (1):

  [[1 "x"]]

Function Schema:

  [:=> [:cat [:cat :int :string]] :any]

More information:

  https://cljdoc.org/d/metosin/malli/CURRENT/doc/function-schemas

----------------------------------------------------------------------------------------------------
-- Schema Error ---------------------------------------------------------------- NO_SOURCE_FILE:1 --

Invalid function arguments:

  [[1 "x"]]

Function Var:

  user/bar

Input Schema:

  [:cat [:cat :int :string]]

Errors:

  {:in [0], :message "should be an integer", :path [0 0], :schema :int, :value [1 "x"]}

More information:

  https://cljdoc.org/d/metosin/malli/CURRENT/doc/function-schemas

----------------------------------------------------------------------------------------------------
[1 "x"]
user=> (mx/defn bar2 [things :- [:schema [:cat :int :string]]] things)
..instrumented #'user/bar2
#'user/bar2
user=> (bar2 [1 "x"])
-- Schema Error ---------------------------------------------------------------- NO_SOURCE_FILE:1 --

Invalid function arity (1):

  [[1 "x"]]

Function Schema:

  [:=> [:cat [:schema [:cat :int :string]]] :any]

More information:

  https://cljdoc.org/d/metosin/malli/CURRENT/doc/function-schemas

----------------------------------------------------------------------------------------------------
[1 "x"]
;; workaround:
user=> (mx/defn bar3 [things :- [:tuple :int :string]] things)
..instrumented #'user/bar3
#'user/bar3
user=> (bar3 [1 "foo"])
[1 "foo"]
opqdonut commented 1 year ago

For case 2, the root cause seems to be a bug in m/-regex-min-max, which m/-function-info uses

(m/-function-info (m/schema [:=> [:cat [:schema [:cat :int :string]]] :any]))
==> {:min 2, :arity 2, :input [:cat [:schema [:cat :int :string]]], :output :any, :max 2}
(m/-regex-min-max (m/schema [:cat [:schema [:cat :int :string]]]))
==> {:min 2, :max 2}
opqdonut commented 1 year ago

A fix for case 2 in #851