This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-04-03
Channels
- # announcements (4)
- # aws (13)
- # babashka (35)
- # beginners (162)
- # boot (8)
- # calva (5)
- # chlorine-clover (15)
- # cider (64)
- # clj-kondo (20)
- # cljs-dev (29)
- # clojars (6)
- # clojure (166)
- # clojure-europe (3)
- # clojure-finland (6)
- # clojure-france (8)
- # clojure-germany (3)
- # clojure-italy (3)
- # clojure-nl (7)
- # clojure-spec (49)
- # clojure-uk (83)
- # clojurescript (39)
- # clojurex (5)
- # core-typed (2)
- # cursive (3)
- # data-science (17)
- # datascript (3)
- # datomic (22)
- # exercism (5)
- # fulcro (3)
- # jobs-discuss (2)
- # joker (2)
- # kaocha (3)
- # malli (26)
- # off-topic (89)
- # pathom (10)
- # pedestal (14)
- # protorepl (14)
- # re-frame (23)
- # reitit (2)
- # shadow-cljs (27)
- # slack-help (10)
- # spacemacs (14)
- # tools-deps (10)
- # tree-sitter (3)
- # xtdb (19)
- # yada (2)
OK, I have been yak shaving experimenting with Malli most of the day, having lots of fun!
I have a bunch of questions:
• After creating a new predicate, I want to assoc it into the existing predicate-registry
map (creating my own). -register-var
and -register
are private. Obviously I can copy/paste these functions...suggestions?
• Given my metadata-schema
above, I want to transform a map via this schema. I get how m/decode
is helpful, and I can specify key-transformer
but I'm scratching my head about why/how I need to specify specific value transformers eg, string-transformer
. Given that my schema has specified the type of each value (eg. int?
), then what I want to say is "coerce whatever you see into the specified type" I get that not all coercions can be pre-defined, and maybe there needs to be a way to add coercions....advice?
• Again, given my metadata-schema
above, let's say I want to obtain all the keys of that map schema. The top level form is a schema, and m/children
gives me the list of children, but I can't figure out how to obtain the "name" of each child, without resorting to digging into the current implementation of that vector. In other words, sure I can first
each child, but is there some better way to get that info/field?
• Similar to the above, say I want to get each key in my map schema transformed in some way. When I want to transform a map, I do this: (m/decode my-schema my-map (mt/key-transformer {:decode csk/->snake_case_keyword})
because SQL column names are snake_case, but wouldn't it be better to include that transformer into my schema itself? Maybe I should define a new property at the map level :postgres/key-transformer
and I (-> my-schema m/properties :postgres/key-transformer)
? Thoughts?
• Similar to the schema navigation/accessor questions above. when I first attempted to write the function to walk the schema and generate the DDL based on the :postgres/
properties, I tried to use m/accept
with an function that would look at each [schema properties _]
it encountered, but this didn't work because AFAICT the children are not schemas themselves. Finally I gave up and walked the schema using the obvious vector destructuring, but I don't feel this the right way to do it...
• properties are open, there is a bunch of reserved keys (`:title` , :description
, :default
, :min
, :max
etc.) and some namespaces that are reserved for extensions (`json-schema`, gen
, swagger
, encode
, decode
etc.). Should be documented and I guess a good convention could be “new unqualified might be used by malli in the future, to be safe, use qualified keys to avoid future clashes”. Goal is to describe the properties with malli schemas to get good error reporting on those too
• -register
and -register-var
could be made non-private, just undocumented. But in the end, it’s just assoc
into the map. There is https://github.com/metosin/malli/pull/188 for discussion whether to support mutable registries oob
• all :map
schemas implement MapSchema
, which allows you to say (m/map-entries schema)
to get the entries out
• if you want to transform just the entities, you could create a new Transformer
, that only mount to :map
s which have the :postgres/schema
defined, this way, it doesn’t effect all maps. You can add a custom encoder & decoder just like with key-transformer
.
• the m/accept
… each IntoSchema
is responsible for it’s own syntax so walking over schemas requires some knowledge of the schema in question. With maps, children
is the sequence of entries, which are tuple3 of key-props-child.
You can run (m/accept Schema m/map-syntax-visitor)
to see the structure to be walked. With Java, the Visitor impl would have dispatch methods with different class signatures, with clojure, one can use multimethods for doing schema-based dispatch, see malli.generator
or malli.json-schema
for examples.
Currently, only :map
and :multi
use custom syntax for it’s contents, later some variants of :or
, :cat
and :alt
too (to support named branches). Could be others.
the long?
predicate… could be, or :long
type schema to companion :date-time
, :date
etc
@ikitommi any plans to release the malli? malli is awesome! without release I clone malli to my projects every month manually.
@ikitommi Thank you for all the answers/advice/tips, I will try those tomorrow! I will also try to better explain my coercion question, but too tired to do it justice ATM...
@mike1452 not going to do that yet, but soon. with deps, you can depend on th the latest commit directly. With Leiningen, there is [metosin/malli "0.0.1-20200305.102752-13"]
, could start pushing new SNAPSHOTS after each merge.
as soon as malli works fully with reitit, could push first alpha. some small hiccups still.
edit: all of this is garbage 🙂 see below
`long?` predicate might be missing from Clojure due to the original design decision of having transparently coercing number types based on number size (eg. short -> int -> long -> BigInteger) which was then removed in was it 1.5 or smth? Or maybe it was even before that. Oh well. It might also be that it’s relatively hard (or so I’ve been told) to detect anything else than doubles on CLJS side.
int?
is true for Bytes, Shorts, Integers, Longs, while integer?
is true for Integers, Longs, Clojure BigInts, BigIntegers, Shorts and Bytes. I don’t think there’s specifically benefit for checking if value is strictly Long, but YMMV.
Differentiating between Integer and Long would allow generating precise Java source code from a schema
@ikitommi m/map-entries
and map-schema-entry structure as 3tuple of [key props child]
is very helpful.
For some reason, it makes me uneasy to just destructure the map-schema-entry 3tuple (instead of having accessor functions),
but the good news is that you enforce that format, so even if the entry was specified without props, you provide the correct 3tuple, so destructuring works well!
How would I test for a map schema? Is there a better way than: (= :map (m/name my-schema))
?
@ikitommi (m/accept Schema m/map-syntax-visitor)
is an awesome way to understand how the walking works, thanks!
ATM, for my current "database table schema" task, I am pretty happy with m/map-entries
and processing the returned map-schema-entries....
But I'm sure I'll want to walk schemas at some point and good to know this.
@ikitommi I don't want to mutate the default registry, I just want to add a few more things to the default registry.
My initial feeling is that I am happy to provide my registry as options map.
Maybe there should be public functions to support users adding to the registry, to make their own variants
I'll just copy/paste from -register
and -register-var
for now....
@ikitommi WRT my earlier coercion question(s), I studied the README and transform.cljc
further,
looks like this machinery will do everything I want, I'll just need to provide/add my own encoding and decoding functions...I'll get to that soon enough
Regarding long?
and/or :long
This requires further thought. If I am specifying a key :epoch-millisconds
then it feels like that should specified as a long, not an int, so transformers can provide the correct type.
Currently:
(def +string-decoders+
(merge
+json-decoders+
{'integer? string->long
'int? string->long
So, ATM those transforms do produce longs, but this seems somewhat arbitrary and not as precise as it might be.
If the schema doesn't care about the the specific numeric type, then specifying something that includes a variety of types is fine.More progress this afternoon:
• Was able to add a new predicate to the default-registry, creating my-registry, and used it to define and validate a schema
• Created a value transformer, and was able to get it invoked to transform a value via decode.
Questions:
• What is the meaning/use of the :name
key in the mt/transformer
input map? Can I put anything I want here? Should I use a namespaced keyword?
• What other uses are there for the (optional) options map argument? (other than :registry
)?