Fork me on GitHub
#malli
<
2019-11-22
>
roklenarcic11:11:07

Not having a global registry has a downside though. Let’s say some library introduces a new Schema protocol implementation. Then someone uses that in schemas in namespace X. With Spec when you load the namespace you automatically gain all specs in there with all the required bits and pieces to make it all work. But with the way Malli is set up, if something is using a custom :datetime schema, then all the users of that thing must invoke all the Malli functions with an opts map where :registry is set to the default one with all the extensions added. If the calls to m/validate and such are burried inside a third party library, then that library must provide a way for you to pass the registry to be used otherwise, you cannot use any new schema types except the ones defined in malli.core

roklenarcic11:11:39

Also I’d like to ask, what is the policy regarding non-serializable content in schemas. I see that some examples include opts map where you have keys like {:decode/string some-fn} which isn’t really serializable

roklenarcic11:11:24

This is meant vis-a-vis developing own schema implementations… should I disallow non-serializable options?

ikitommi12:11:35

@roklenarcic you can always define the library schemas as Vars, the first value in the Schema vector syntax can be an instance of IntoSchema.

ikitommi12:11:43

(def date-time (reify IntoSchema ...))

(m/validate date-time (java.time.DateTime.))
; => true

ikitommi12:11:19

the work like components on Reagent.

roklenarcic12:11:04

so I can also do `[datetime “format”]?

ikitommi12:11:06

policy on non-serializable: up to the user. There is an issue about making an utility that checks if a schemas can be fully persisted or not. One can put that into project tests.

roklenarcic12:11:23

thanks that is very helpful

roklenarcic12:11:52

I mean any schema that specifies decode/encode implementation cannot be persisted?

ikitommi12:11:54

(defprotocol IntoSchema
  (-into-schema [this properties children opts] "creates a new schema instance"))

ikitommi12:11:12

quote the functions and they can be serialized.

ikitommi12:11:55

(require '[malli.core :as m])
(require '[malli.edn :as edn])

(-> [:and
     [:map
      [:x int?]
      [:y int?]]
     [:fn '(fn [{:keys [x y]}] (> x y))]]
    (edn/write-string)
    (doto prn) ; => "[:and [:map [:x int?] [:y int?]] [:fn (fn [{:keys [x y]}] (> x y))]]"
    (edn/read-string)
    (doto (-> (m/validate {:x 0, :y 1}) prn)) ; => false
    (doto (-> (m/validate {:x 2, :y 1}) prn))) ; => true
;[:and 
; [:map 
;  [:x int?] 
;  [:y int?]] 
; [:fn (fn [{:keys [x y]}] (> x y))]]

roklenarcic12:11:56

ah.. but then the code processing the value has to call eval or something on it?

ikitommi12:11:36

it uses sci , a small interpreter. So it works on JVM, ClojureScript & GraalVM, no eval needed

ikitommi12:11:59

it’s <10kb on cljs.

ikitommi12:11:56

that’s all normal code, no clojurescript compiler used in the demo.

roklenarcic12:11:01

sorry, I should be clearer. If my custom datetime schema has an option :formatter which someone puts the value of DateTimeFormatter/ISO_INSTANT

ikitommi12:11:04

e.g. “production code”

ikitommi12:11:27

oh, that. can’t serialize that.

roklenarcic12:11:43

if I quote this, I get a symbol

ikitommi12:11:53

it could have :format option, which could be string?

borkdude12:11:04

the latest version of sci supports Java reflection, if you bring this class in with the :classes option. but that won't work in CLJS of course

roklenarcic12:11:15

sure, but you cannot specify instant formatter by giving a string

borkdude12:11:46

but you can use reader conditionals for that maybe

roklenarcic12:11:49

specifying something like “yyyy-MM-dd” then telling it to format an Instant object will fail

ikitommi12:11:18

{:format :iso_instant} maybe?

ikitommi12:11:38

time is hard to get do in a portable way anyway.

roklenarcic12:11:42

My current idea is to support everything

roklenarcic12:11:57

so if someone puts a formatter instance there, then I will use it

ikitommi12:11:00

string or a (predefined) keyword?

ikitommi12:11:11

.. or a class, which is used.

roklenarcic12:11:23

but then they lose serializability, but that’s on them

roklenarcic12:11:33

ok… this was very helpful

ikitommi12:11:41

one thing there could be is to have a startup-time altering of a registry. via JVM options for example. It would be explicit, but still global.

ikitommi12:11:17

plan is to pull schema defn and fn syntax helpers, can’t pass easily any custom options/registry there.

ikitommi12:11:15

(m/defn plus :- int? 
  [x :- int?, y :- int?] 
  (+ x y))

ikitommi12:11:32

to support something like [:json-schema {:type "string"}] schema would require some way to change the one registry. could just be the var reference (`[json/json-schema {:type "string"}]`), but could be a JVM property to bootstrap some extra schemas into the registry… not sure if thi is a good idea

ikitommi12:11:02

… or just malli.evil ns, which has all the public functions of other namespaces, but with the default-registry as an atom.

(require '[malli.evil :as evil])

(evil/register! ::int int?)

(evil/validate ::int 1)
; => true

ikitommi12:11:57

… or a JVM option to define the default-registry implementation. by default, it points to the immutable map, but could be swapped to atom impl. Bad Idea 🙂