Fork me on GitHub
#malli
<
2024-01-03
>
mx200018:01:30

How do I create a schema for a vector of two integers >= 0, where [val max], the second integer 'max' should always be >= val. Example [0 0], [1 3], [5 10], [0 9]

Stig Brautaset18:01:51

I don't know how to do that without the dreaded :fn schema, unless you make them distinct ranges, e.g.:

[:tuple [:int {:min 0 :max 10}] [:int {:min 10 :max 20}]]

mx200018:01:54

I do not have any ranges, just val needs to be <=max in [val max]

mx200018:01:32

I am having this val-max vector in my videogame for damage [min-max] or hitpoints [current-max].

mx200018:01:45

Maybe writing a custom 'type' ?

Stig Brautaset18:01:34

Maybe...? The only other thing I can think of is:

[:and [:tuple [:int {:min 0}] [:int {:min 0}]] [:fn (fn [[a b]] (<= a b))]]

Stig Brautaset18:01:21

But I gather :fn schemas are discouraged, and best avoided if you can find a different solution.

mx200018:01:18

Why are they discouraged? I am new to malli haven't used it before.

mx200018:01:01

It would be nice to have a type :val-max because I am using the same schema at hp/mana/damage

Stig Brautaset18:01:21

I haven't used Malli that much either, and can't really answer why they're discouraged. But I've seen that mentioned a few times in this channel.

Stig Brautaset18:01:34

Here's another example that is an actual vector of two elements:

[:and [:vector {:min 2 :max 2} [:int {:min 0}]] [:fn (fn [[a b]] (<= a b))]]

👍 1
Noah Bogart18:01:18

[:and ... [:fn ...]] is "bad" because it's opaque. you get a message that it failed because of the function and nothing else

Noah Bogart18:01:55

but if you add in an :error/fn metadata, you can customize

Noah Bogart18:01:38

[:fn {:error/fn (fn [{[a b] :value} _]
                  (when (< b a)
                    (format "Expected a (%d) to be smaller than b (%d)" a b)))}
 (fn [[a b]] (<= a b))]

👍 2
1
mx200019:01:00

This is giving me a NullponterException when 'explain' ing. Is it not possible to give a nicer error message?

(def test-schema (m/schema pos?))
(m/explain test-schema nil)

Noah Bogart20:01:21

you could use fnil to avoid this issue

Noah Bogart20:01:46

i believe the issue comes from https://github.com/metosin/malli/blob/6f454709fd240913faa5eef9a56b6ef740547f77/src/malli/core.cljc#L679where there's no try/catch, just calling the given validator function (`pos?`) on the input (`nil`), which will result in a thrown exception in both clojure and malli

ikitommi20:01:20

many of the Clojure built-its fail with nil. We could wrap ’em all with m/-safe-pred

ikitommi20:01:38

Wrote a quick doc on different ways to manage & reuse schemas: https://github.com/metosin/malli/blob/master/docs/reusable-schemas.md. And yes, I prefer the Plumatic-style Var-definitions of Schema over using spec-style global registry. But, both are and will be supported. comments and other viewpoints to comparison matrix welcome!

ikitommi20:01:56

For Vars, could add mx/defschema that adds the Var symbol and namespace into the schema as schema properties. Either separate or combined into a new key. so:

(ns my-domain
  (:require [malli.experimental :as mx]))

(mx/defschemas Size [:enum "S" "M" "L"])
.. would create a Schema:
;; 1) separately, like Plumatic has (as metadata)
[:enum {:name 'Size
        :namespace 'my.domain} "S" "M" "L"]

;; 2) concatenated into name, property name??
[:enum {:id 'my.domain/Size} "S" "M" "L"]
[:enum {:ref 'my.domain/Size} "S" "M" "L"]
[:enum {:name 'my.domain/Size} "S" "M" "L"]

Ben Sless07:01:30

The problem with plumatic style is that things like json schema generation dereference it recursively instead of lifting it to definitions because schemas are passed by value

Ben Sless07:01:16

For var style, add an option to pass vars as schemas as reference?

ikitommi07:01:16

I think if we just add the full Var symbol into properties, we can see “this is a my-domain.User instead of an value.

ikitommi07:01:02

so, referencing to a schema would inline the value, but with data saying what was it all about.

ikitommi07:01:29

but, we need some way to reference to support recursion, maybe just use Vars like Schema does?

ikitommi07:01:42

would be natural way for “just a reference”

👍 1
Ben Sless07:01:23

Yes, and will also provide legible swagger API results for complex domain schemas

ikitommi07:01:39

(mx/defschema User
  [:map
   [:id :uuid]
   [:name :string]
   [:address #'Address] ;; a reference
   [:friends [:set [:ref #'User]]]) ;; recursion

🎉 1
ikitommi07:01:29

making a Var a valid reference type makes sense. Already added qualified symbols.