malli

Serge Lerner 2024-08-27T08:22:28.029439Z

Hi. Trying to work with declarative :merge along with mutual recursion with :ref but failing either with invalid-ref or invalid-schema

(def ping-pong-registry
  {::ping [:maybe [:merge
                   [:map
                    [:value [:= "ping"]]
                    [:person string?]]
                   [:map
                    [:child [:ref ::pong]]]]]
   ::pong [:maybe [:merge
                    [:map
                     [:value [:= "pong"]]
                     [:bot string?]]
                    [:map
                     [:child [:ref ::ping]]]]]})

(def ping-pong-schema
  (m/schema [:schema {:registry ping-pong-registry} ::ping])) ; :malli.core/invalid-schema

(m/validate ping-pong-schema
            {:value "ping"
             :person "Jane"
             :child {:value "pong"
                     :bot "c-3po"
                     :child {:value "ping"
                             :person "Jane"
                             :child {:value "pong" 
                                     :bot "c-3po"
                                     :child {:value "ping"
                                             :person "Jane"
                                             :child nil}}}}})

Serge Lerner 2024-08-28T09:30:00.416089Z

❤️

ikitommi 2024-08-27T16:43:02.395419Z

if you get the :malli.core/invalid-schema from repl you can print *e to see what is wrong there

ikitommi 2024-08-27T16:44:23.925599Z

here, its’:

{:cause ":malli.core/invalid-schema",
       :data {:type :malli.core/invalid-schema,
              :message :malli.core/invalid-schema,
              :data {:schema :merge, ...
so, you don’t have :merge available.

ikitommi 2024-08-27T16:47:31.518609Z

it’s not part of the default registry

ikitommi 2024-08-27T16:47:38.628219Z

I would:

(ns user
  (:require [malli.core :as m]
            [malli.generator :as mg]
            [malli.registry :as mr]
            [malli.util :as mu]))

;; change the default (global) registry
(mr/set-default-registry!
 (mr/composite-registry
  (m/default-schemas) ;; defaults
  (mu/schemas))) ;; utility schemas like :merge

(def ping-pong-registry
  {::ping [:maybe [:merge
                   [:map
                    [:value [:= "ping"]]
                    [:person string?]]
                   [:map
                    [:child [:ref ::pong]]]]]
   ::pong [:maybe [:merge
                   [:map
                    [:value [:= "pong"]]
                    [:bot string?]]
                   [:map
                    [:child [:ref ::ping]]]]]})

(def ping-pong-schema
  (m/schema [:schema {:registry ping-pong-registry} ::ping]))

(mg/sample ping-pong-schema)
;(nil
; nil
; nil
; nil
; {:value "ping", :person "", :child nil}
; {:value "ping", :person "4", :child nil}
; {:value "ping", :person "", :child nil}
; {:value "ping", :person "fwYAz", :child {:value "pong", :bot "", :child nil}}
; {:value "ping", :person "Yk", :child nil}
; {:value "ping", :person "ne3ICu", :child nil})

2024-08-27T18:28:21.152729Z

Worth noting that that these features have known issues in combination https://github.com/metosin/malli/issues/1088

☝️ 1
2024-08-27T18:48:30.252219Z

Clojurists Together is funding Malli this quarter. I will be enhancing the base schemas with constraints that can express fine-grained specs like "map where :user and :pass only occur together" or "non-blank strings", all the way to "a password must contain numbers, letters, and symbols". The system will be extensible, and be fully compatible with validation/explanation/generation/parsing etc. Humanization will be enhanced for automatically improved error messages:

(def Password
  [:string {:min 5
            :and [[:not [:non-alpha]]
                  [:not [:non-numeric]]
                  (into [:or] (map #(do [:includes (str %)]))
                        [\; \! \@ \# \$ \% \^ \& \*])]}])

(me/humanize (m/explain Password ""))
;=> [[:and
      "should contain an alphabetic character"
      "should contain a numeric character"
      [:or
       "should include substring \";\""
       "should include substring \"!\""
       "should include substring \"@\""
       "should include substring \"#\""
       "should include substring \"$\""
       "should include substring \"%\""
       "should include substring \"^\""
       "should include substring \"&\""
       "should include substring \"*\""]
      "should be at least 5 characters, given 0"]]
I will be giving a talk on Thursday to help plan my quarter and gather feedback. I tried to choose a time overlapping with many American and European timezones. A recording will be posted after. https://www.youtube.com/live/28S96Ms8WOc https://time.is/1200PM_29_Aug_2024_in_CThttps://time.is/1200PM_29_Aug_2024_in_CT

15
2
🫶 2
2024-08-28T16:28:07.389679Z

@ben.sless agreed it's quite powerful to make them properties

Ben Sless 2024-08-28T16:32:47.792749Z

I was dissuaded from naming that property :charset because it was too overloaded, but an alternative could be :format with the value a keyword or a coll of keywords / preds alternative is to just let every one of those preds be a property. why? you can specify :alpha true :excludes #{"v" "c"}

Ben Sless 2024-08-28T16:33:11.584659Z

In a sense, a map, being an aggregate, already has :and semantics

👍 1
2024-08-28T17:00:49.498409Z

yes, an existing example is {:min 1 :max 2}

Ben Sless 2024-08-28T17:03:27.841489Z

precisely

Ben Sless 2024-08-28T17:03:43.203679Z

however, translating the Password example, the result seems nonsensical

Ben Sless 2024-08-28T17:03:44.951839Z

(def Password
  [:string {:min 5
            :alpha true
            :numeric true
            :includes [\; \! \@ \# \$ \% \^ \& \*]}])

2024-08-28T17:05:56.117549Z

right that's why there's connectives, but the top level is always a conjunction

Ben Sless 2024-08-28T17:06:04.491379Z

This should probably be :printable

Ben Sless 2024-08-28T17:06:52.003239Z

looking at all the is* predicates for java.lang.Character

Ben Sless 2024-08-28T17:07:08.143429Z

Another to consider is simply format, e.g. JSON Schema email

Ben Sless 2024-08-28T06:02:59.114209Z

String contents? That takes me back https://github.com/metosin/malli/pull/587

Ben Sless 2024-08-28T06:04:51.012659Z

In case I don't make it to the meeting, I firmly believe these should all be string properties and not schemas