This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-07-18
Channels
- # announcements (35)
- # babashka (14)
- # beginners (23)
- # calva (5)
- # cljsrn (3)
- # clojure (154)
- # clojure-europe (12)
- # clojure-losangeles (2)
- # clojure-uk (5)
- # clojurescript (42)
- # conjure (3)
- # cursive (10)
- # datomic (3)
- # emacs (6)
- # events (1)
- # graalvm (1)
- # helix (1)
- # honeysql (1)
- # hyperfiddle (1)
- # jobs-discuss (1)
- # lsp (8)
- # malli (54)
- # meander (1)
- # membrane (1)
- # off-topic (246)
- # polylith (4)
- # practicalli (1)
- # re-frame (14)
- # releases (1)
- # shadow-cljs (21)
- # sql (58)
- # vim (1)
- # vrac (2)
I have a schema that parses a value correctly but then the unparse operation returns :malli.core/invalid
. Is it a known problem for some kind of schema or should I post a bug report?
To reproduce:
(let [my-schema (m/schema
[:and
list?
[:catn
[:wrapper [:= 'value]]
[:wrapped int?]]])]
(->> (m/parse my-schema '(value 5))
(m/unparse my-schema)))
m/unparse
works if I remove list?
from the schema, but then it's not the schema I need.
Is there an option in catn
to specify the list of container of the sequence?
yes, but the name "type" is not descriptive enough as it relates to the container's type and not the sequence itself.
In minimallist, I named it "coll-type" https://github.com/green-coder/minimallist/blob/all-work-and-no-play/src/minimallist/helper.cljc#L72-L76
Right now, I am trying to port my model from minimallist to malli in the Vrac project, so I have less things to maintain and it's better for the users if they face a mainstream library like Malli.
have you checked the bundle size? malli starts from ~2kb (just the minimal working set of schemas), but is quite big if all schemas are used (40kb?). with all the bells & whistles, it can be >100kb.
I’m not sure how :type
and :coll-type
are that different, but something like that would be good.
:type
will be fine
the bundle size won't be a problem, I can parse things offline.
:tuple
would also need to have the {:type :list}
option.
While chaining parse
and unparse
, I realized that the API would be more friendly to ->
if the parameter order of ?schema
and value
were exchanged.
did a quick run on improving performance of collection transformations, got easy x5 for CLJ in simple test:
(let [schema [:map
[:id :string]
[:type :keyword]
[:address
[:map
[:street :string]
[:lonlat [:tuple :double :double]]]]]
decode (m/decoder schema (mt/json-transformer))
json {:id "pulla"
:type "herkku"
:address {:street "hämeenkatu 14"
:lonlat [61 23.7644223]}}]
;; 920ns => 160ns
(cc/quick-bench
(decode json)))
also, coercion (transform + validate) is 5x faster with that data compared to Plumatic Schema. Which is kinda nice as have considered (the awesome) Plumatic as a performance reference.
@U055NJ5CC you can gain at least 10% and probably gain some mechanical sympathy by closing over the transforms:
(map
(fn [[k v]]
(fn [^Associative x]
(if-let [xe (.entryAt x k)]
(.assoc x k (v (.val xe)))
x)))
ts)
then reduce over them with -comp
looks good. But, I thing the varargs version of -comp
is actually slow as it uses the sequence abstraction, could be rewritten to use iterator? PR (and perf test before & after) most welcome.
already this is much faster that the current:
(defn -comp
([] identity)
([f] f)
([f g] (fn [x] (f (g x))))
([f g h] (fn [x] (f (g (h x)))))
([f1 f2 f3 & fs]
(let [fs (into [f1 f2 f3] fs)]
(fn [x] (let [i (.iterator ^Iterable fs)]
(loop [x x] (if (.hasNext i) (recur ((.next i) x)) x)))))))
My initial thought was that having transform and validation as separate steps can actually make it faster - as the created transformation chain is usually small enough to fit into the JVM inlining budget - while validation is always “complete” and generated more code. Having those in one sweep means more code. Haven’t looked at the perf profile, so just quessing.
I was just thinking about this recently - would interleaving transform and validation be faster? Since it involves lots of iteration and allocation, we can't necessarily assume JIT friendly code would be faster than a single pass over two passes
but thanks @ben.sless for the validation perf, mostly same optimizations for transformers 🙇
reduce-kv
should be fast, but for some reason, it’s not always used instead of reduce
. Recall there was a related bug in clojure for this. writing by hand is always fast 🙂
Have you had a chance to look at my proposal on https://github.com/metosin/malli/issues/474?
I have no idea how to compile my proposal to something efficient, though I assume it is possible and someone clever like nilern could do it
It is possible in Malli to write a schema which represent Clojure's destructurations of a map? For example, something which would parse this kind of data:
{a :a
b :b
:keys [c d]
:e/keys [f g]
:as h}
I don't know if spec can do something like that. In minimallist, I approach this problem by having map-of
taking a schema of a pair [key value], so I am able to use :alt on the pair to valid multiple different cases where the key and the value are correlated.
maybe m/parse
could have a custom user-defined property to allow users to give the parse & unparse functions for the given schema.
An escape hatch, yes it would work, but the hardship would be on the user.
Here it is: https://github.com/green-coder/vrac/blob/diy-furry/src/vrac/model.cljc#L25-L30
The escape hatch may not work well for the user in the case the model is using recursion, like in this example. That would become Malli -> fn -> Malli -> fn ...
Hi all! I've been working for a while on fabricate
, a static website generator that takes advantage of malli
in order to both provide a model for semantically valid HTML/Hiccup forms and to define its own order of operations. I wrote a post about my experience using malli
schemas to define a finite state machine that dispatches functions on the basis of the schemas matched by the data passed in to them, which I have taken to calling "finite schema machines."
https://fabricate-site.github.io/fabricate/finite-schema-machines.html
I'd be really interested in what other users of malli
think about this concept. It's highly experimental, but I've already found it quite interesting and useful.
This looks very interesting and I've had FSMs on my mind recently. Will need to give a deeper look
I've never worked with dependent types, but my impression of malli
is that you get a lot of the benefits of dependent types without a static type system. that said, when I think about how to validate/check these FSM definitions for cycles or non-termination at definition/compile time, I wonder how close I'm actually getting to reinventing a type system.