Fork me on GitHub
#malli
<
2019-12-11
>
ikitommi06:12:38

Checked the code., the build-transformer doesn’t work correctly. I think the same problem is with all sequential schemas: the chain in :leave shoud be in reverse order, so first apply the (child) value-transformers, then keys and last the map. This way, when changing the keys in :leave , it’s the last thing that happens.

ikitommi06:12:20

Also, all the three type of transformers should be created outside of the build-transformer fn. Current impl creates all three two times (both in enter & leave) causing potential state-bugs with -value-transformerimpls as the enter & leave have different instances attached to them.

ikitommi06:12:32

I’ll write an issue, PR welcome.

ikitommi06:12:22

After that, this is the way to rename keys locally on encode:

(m/encode
  [:map
   [:foo [:map
          [:foo int?]
          [:bar int?]]]
   [:bar int?]]
  {:foo {:foo 2 :bar 1} :bar 3}
  (transformer
    {:encoders {'int? (constantly inc)
                :map (constantly {:leave #(set/rename-keys % {:foo :oof :bar :rab})})}}))
; => {:oof {:oof 3, :rab 2}, :rab 4}

ikitommi06:12:09

after the upcoming interceptor :compile, will reduce to:

(m/encode
  [:map
   [:foo [:map
          [:foo int?]
          [:bar int?]]]
   [:bar int?]]
  {:foo {:foo 2 :bar 1} :bar 3}
  (transformer
    {:encoders {'int? inc
                :map {:leave #(set/rename-keys % {:foo :oof :bar :rab})}}}))
; => {:oof {:oof 3, :rab 2}, :rab 4}

ikitommi06:12:51

which will, I hope, solve your case @rschmukler elegantly enough?

ikitommi15:12:26

would be interesting to see how malli would work on the dev-tooling space. Related to https://www.reddit.com/r/Clojure/comments/e95pr5/data_structure_shape_hints_and_intellisense/

rschmukler15:12:23

@ikitommi nice! Good catch on build-transformer being called twice and its impact on local state! Changing the order of the chains is also perfect - that'll make it much more like a postwalk on leave

rschmukler18:12:27

@ikitommi I've got the first prototype of my defn-spec / ghostwheel clone. For now I'm doing it as a separate project (called aave). Will be releasing a first version shortly

rschmukler18:12:21

The syntax is abstractable though, so you can use schema style too if you want. Things that it currently supports are purity detection, configurable hooks for input and output validation at both compile time and run time. Next up is benchmarking.

rschmukler18:12:36

I also thought it'd be cool to be able to do something like

rschmukler18:12:09

(defn add-name-key
  [a]
  [:map => (+ a [:name string?])]
  (assoc a :name "Bob")
Where + symbol basically expands out to "merge schema from parameter a "

rschmukler18:12:54

Might be overkill haha - but it could enable deep compile-time tracking of types

ikitommi18:12:45

@rschmukler sounds awesome, and what a name! 👻

rschmukler18:12:59

😄 I'm glad you like it

beders18:12:59

ok, let's try this again. How would you write a function that returns a malli spec to test if a string is of length n?

beders18:12:03

I'm getting this far:

beders18:12:06

(defn fixed-length? [n]
  [:and 'string? [:fn `(~'fn [~'val] (~'= ~n (~'count ~'val)))]]
  )

beders18:12:52

that gives me:

(preds/fixed-length? 10)
=> [:and string? [:fn (fn [val] (= 10 (count val)))]]

rschmukler18:12:14

@beders Do you need it to all be quoted? Malli should take care of that for you. if not, you can just do:

(defn fixed-length? [n]
  [:and string? [:fn #(= n (count %)]])

beders18:12:20

but my code doesn't look very nice. Lots of quoting to prevent backtick from full name resolution

beders18:12:19

your version captures n

beders18:12:00

(defn fixed-length? [n]
  [:and string? [:fn #(= n (count %))]])
=> #'sql-to-malli.integration/fixed-length?
(edn/write-string (fixed-length? 10))
=>
"[:and string? [:fn #object[sql_to_malli.integration$fixed_length_QMARK_$fn__10416 0x576cee60 \"[email protected]\"]]]"

rschmukler18:12:53

Ah I suppose that's true!

beders18:12:57

if I don't use the extension unquote-quoting, I get the fully resolved names

beders18:12:34

(defn fixed-length? [n]
  [:and 'string? [:fn `(fn [val] (= ~n (~'count val)))]]
  
  )
(fixed-length? 10)
=> [:and string? [:fn (clojure.core/fn [clojure.core/val] (clojure.core/= 10 (count clojure.core/val)))]]

beders18:12:49

which kinda works, just not very readable

beders18:12:31

trying to stick with the data-only mantra here

beders18:12:43

so no capturing

rschmukler18:12:45

But you need your schema to be serializable?

beders18:12:29

I'm working on a library that takes a SQL database schema and spits out malli

rschmukler18:12:39

I'd consider adding it to your registry using fn-schema

beders18:12:39

I can probably do without serialization

ikitommi18:12:42

you can single-quote the fn

beders18:12:57

if I single-quote the fn, I'm capturing n

rschmukler18:12:59

@ikitommi he needs to get the 10 in there though

rschmukler18:12:44

but with a custom registry, you could just do [:and 'string? [:fixed-length 10]]

beders18:12:46

yes, adding it to the registry might be the only sane choice for now

beders18:12:47

that would make my library a runtime dependency

beders18:12:11

I was hoping I could run this on a database schema, copy the resulting malli schemas and then run with them

beders18:12:40

looks like I need to decide on various trade-offs

ikitommi18:12:55

a :fn variant that takes extra args from properties?

beders18:12:58

just wanted to get some ideas on code generation

beders18:12:11

that make for readable schemas

beders18:12:28

I like [:fixed-length 10] though. Very readable

beders18:12:32

thanks for the ideas!

rschmukler18:12:03

you might be able to do

rschmukler18:12:31

Nope, never mind 😄

ikitommi18:12:16

JSON Schema defines extra attributes for numbers, strings and arrays. Malli could have those too? [string? {:min 10, :max 10}]

👍 1
ikitommi18:12:22

Also, malli schema properties should be defined with malli schemas to get nice docs & validation of those too.

ikitommi18:12:31

@beders if the runtime dependwncy would be ok, you could also just use the Schema Var instead of registry: [my-lib.schemas/fixed-lenght 10]

👍 1
beders18:12:10

Good point. I mean, technically, this works too:

(m/validate [:re #"^.{4}$"] "bubu")

beders18:12:16

but probably a bit overkill 😉

ikitommi18:12:38

the fn can also be a string btw, so this should work (injection warning!!): [:and string? [:fn (str "#(= " n " (count %))")]]

beders19:12:38

ah, didn't know that. Thx!

ikitommi19:12:07

looking forward to seeing the lib.