malli

https://github.com/metosin/malli :malli:
ikitommi 2020-06-23T05:45:00.122400Z

mutual recursion, current status:

(mg/generate
  ::ping
  {:registry (mr/composite-registry
               (m/default-schemas)
               {::ping [:maybe [:tuple [:= "ping"] [:ref ::pong]]]
                ::pong [:maybe [:tuple [:= "pong"] [:ref ::ping]]]})
   :size 7, :seed 86})
; => ["ping" ["pong" ["ping" ["pong" ["ping" nil]]]]]
tested also a fail-fast on ambiguity with refs, so instead of:
(mg/generate
  ::ping
  {:registry (mr/composite-registry
               (m/default-schemas)
               {::ping [:maybe {:id ::pong} [:tuple [:= "ping"] [:ref ::pong]]]
                ::pong [:maybe [:tuple [:= "pong"] [:ref ::ping]]]})
   :size 7, :seed 86})
; => ["ping" ["ping" ["ping" ["ping" ["ping" nil]]]]]
the default code will throw instead:
Execution error (ExceptionInfo) at malli.core/fail! (core.cljc:80).
:malli.core/ambiguous-ref {:type :ref, :ref :user/pong}

ikitommi 2020-06-23T05:46:36.122900Z

the code is in a new PR: https://github.com/metosin/malli/pull/209

2020-06-23T06:48:10.127500Z

@ikitommi [suggestion] if you can embed registries locally, then maybe you don't need to have support for a custom global registry - just ask the users to include their registry as a part of their models.

ikitommi 2020-06-23T06:59:49.135400Z

The custom Schema elements can’t be embedded as data, if they are code. It’s good have a mechanism to add those to the schema registry. All user/project-defined that are just data COULD be used via embedded registries, a decision done in user space.

ikitommi 2020-06-23T07:02:25.137800Z

I woudn’t register any project-spesific (data) schemas into global registry, as they can be references mosty as Vars.

ikitommi 2020-06-23T07:05:15.140500Z

The recursive schemas could also be allowed to be introduces using Vars, but not sure if that’s a good idea.

ikitommi 2020-06-23T07:06:29.141900Z

Something like:

(declare Pong)

(def Ping [:maybe [:tuple [:= "ping"] [:ref #'Pong]]])

(def Pong [:maybe [:tuple [:= "pong"] [:ref #'Ping]]])

ikitommi 2020-06-23T07:09:44.144500Z

Imaginary example with not much boilerplate with schematized fns using custom schema element :db/ref registered into the global registry:

(m/defn my-fn [x :- int?, y :- [:maybe [:db/ref uuid?]]
  (println x y))

ikitommi 2020-06-23T07:14:33.146900Z

About the Var refs - how would that be serialized? It could be done by having an asymmetric m/form for that component, which might be a bad idea:

(m/form [:maybe [:tuple [:= "ping"] [:ref #'Pong]])
;[:registry
; {:registry {::ping [:maybe {:id ::pong} [:tuple [:= "ping"] [:ref ::pong]]]
;             ::pong [:maybe [:tuple [:= "pong"] [:ref ::ping]]]}}
; [:ref ::ping]]

ikitommi 2020-06-23T07:14:49.147100Z

or is it?

ikitommi 2020-06-23T07:47:47.147300Z

this works now too:

(mg/generate
  [:registry
   {:registry {::ping [:maybe [:tuple [:= "ping"] [:ref ::pong]]]
               ::pong [:maybe [:tuple [:= "pong"] [:ref ::ping]]]}}
   [:ref ::ping]]
  {:size 7, :seed 86})
; => ["ping" ["pong" ["ping" ["pong" ["ping" nil]]]]]