metosin / malli

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

spec-like implicit, global key checking #806

Closed vemv closed 1 year ago

vemv commented 1 year ago

https://clojure.org/guides/spec#_entity_maps says:

Also note that ALL attributes are checked via keys, not just those listed in the :req and :opt keys.

Probably you know this behavior, but as a recap, it means that all specs are subject to be checked in any map. Which can be nice as it maximizes the use of specs, and encourages using namespaced keys.

I wonder if Malli can replicate this behavior?

Here is a minimal scratchpad:

(ns foo
  (:require [malli.core :as m]
            [malli.error :as me]
            [malli.registry :as mr]))

(def global-registry (atom {}))

(defn def! [type schema]
  (swap! global-registry assoc type schema))

(def default-registry
  "Copied/adapted from `malli.core/default-registry`"
  (let [strict (identical? mr/mode "strict")
        registry (mr/fast-registry (if (identical? mr/type "custom") {} (m/default-schemas)))]
    (when-not strict (mr/set-default-registry! registry))
    registry))

(mr/set-default-registry!
 (mr/composite-registry
  default-registry
  (mr/mutable-registry global-registry)))

(def! ::string :string)

(comment
  (m/validate ::string "hello") ;; `true` - as expected
  (m/validate ::string 52) ;; `false` - as expected
  (m/validate [:map] {::string 1}) ;; `true` - contrary to clojure.spec's behavior
  )

...it shows that the implicit key checking isn't there. Which fully makes sense for Malli's philosophy, tbh. Is this a problem someone has tackled before?

Probably overriding the definition of :map isn't too far-fetched - I might try that. Still, I'd be interested in alternative approaches if any are possible.

Cheers - V

vemv commented 1 year ago

Mmm, https://github.com/metosin/malli/tree/4b4fe875203317eba1057bdffba6d7d8c9e78469#qualified-keys-in-a-map seemed promising, but it needs one to explicitly enumerate the keys.

IOW, reusing the scratchpad above, (m/validate [:map ::string] {::string 1}) has the behavior I want, but (m/validate [:map] {::string 1}) not.

opqdonut commented 1 year ago

We think that this is a design problem in spec, see e.g.

https://quanttype.net/posts/2021-03-06-clojure-spec-and-untrusted-input.html

We don't want this in malli currently, but could be convinced otherwise if there are good arguments!