malli

2024-08-07T17:24:03.298979Z

👍 4
👀 4
1
2
ikitommi 2024-08-08T09:30:25.960379Z

red onions, will watch in full later today 🙂

😄 1
ikitommi 2024-08-13T13:22:45.829859Z

I would be great if the schemas would always know their local registry. If we fully deref the smallest example, you get a Schema form that can’t be serialized due to hidden registry:

(m/deref-all
 [:schema {:registry {::a [:seqable [:ref ::a]]}}
  ::a])
; => [:seqable [:ref :user/a]]

ikitommi 2024-08-13T13:25:26.843949Z

is the :schema needed in your example?

; => [:seqable {:registry {:user/a [:seqable [:ref :user/a]]}} :user/a]

👍 1
ikitommi 2024-08-13T13:32:45.131039Z

oh, missed this: https://github.com/metosin/malli/issues/1088, good explanation of this 👍

2024-08-13T15:45:06.042809Z

I've never thought about how to achieve this with dynamic scope, it might be difficult and/or ugly to get completely right. I don't have a good feeling about it. Say we had another property :let for lexical scoping that worked in the way we're talking about:

(m/deref [:schema {:let {::a [:seqable ::a]}}
          ::a])
=> [:seqable [:schema {:let {::a [:seqable ::a]}}
              ::a]]
I know how to implement that. You don't need :ref because you eliminate the recursive positions whenever you "unfold" the type (they are substituted to the original type).

2024-08-13T15:53:29.929459Z

I don't think we should change how local registries work, I'm curious what the mood might be for deprecating :registry though? In preference to something like :let above.

2024-08-13T16:01:13.837139Z

I can see a world where they coexist if we're careful enough though. But the shortcomings of :registry might not be fully surmountable and might always come with these caveats.

2024-08-13T00:32:42.430829Z

Read what is in the gh repo docs so far. Looking forward to reading more of the walkthrough when complete!

2024-08-13T00:35:22.623499Z

thanks Mike! it raised a lot of questions, not sure what I'm going to tackle next.

👍 1
2024-08-13T00:35:33.108779Z

I think nailing down the representation of lexical scope is a good start.

2024-08-13T00:36:23.656909Z

I finally found a real-world example that cannot be solved by dynamic scope https://github.com/metosin/malli/issues/1088

👍 1
2024-08-13T00:37:56.725519Z

So I have renewed interest after wondering if this was all theoretical. e.g., This otherwise-useful transformation would cause utter chaos with dynamically scoped registries. https://github.com/metosin/malli/pull/1086

2024-08-13T00:41:16.577829Z

this is the smallest example of the root cause of the problem.

(m/deref [:schema {:registry {::a [:seqable [:ref ::a]]}}
          ::a])
;;actual
=> ::a
;;preferred
=> [:seqable [:schema {:registry {::a [:seqable [:ref ::a]]}}
              ::a]]

Ryan 2024-08-07T21:11:32.990669Z

Is there a good pattern for making this schema less verbose? specifically relating to the shared elements of the multi-schema:

(def test-schema [:multi {:dispatch :type} [:ns/type-a [:map [:id 'string?] [:type ['qualified-keyword? {:namespace :ns}]] [:details 'nil?]]] [:ns/type-b [:map [:id 'string?] [:type ['qualified-keyword? {:namespace :ns}]] [:details 'int?]]] [:ns/type-c [:map [:id 'string?] [:type ['qualified-keyword? {:namespace :ns}]] [:details 'string?]]] [:ns/type-d [:map [:id 'string?] [:type ['qualified-keyword? {:namespace :ns}]] [:details 'any?]]] ])```

Ryan 2024-08-07T21:11:59.196729Z

the only part that differs is the schema of the :details key, the rest is identical for every type

ikitommi 2024-08-08T11:02:41.439089Z

if you have ideas on how to make a good data syntax to simplify this, I’m all ears. Have a old draft of optionally supporting :extends property for maps. Not sure if that is a good idea…

Ryan 2024-08-08T14:13:55.804339Z

I’m not sure I have any concept of how feasible/reasonable something like this would be, but it would be great if I could just use the multi-schema dispatch on the [:details…] .. there’s probably complexity in having the dispatch value be a sibling property and not a child of the [:details…], but the pattern shows up often enough in the work I do that i’d love a cleaner way to achieve it.

2024-08-08T16:36:26.638019Z

a direction to look into might be to enhance :merge with the ability to handle :multi . Maybe when you compile a validator for such a schema, the dispatch/s of the child multi's are pushed up to the top level.

2024-08-08T16:38:38.562909Z

[:merge
 [:map [:a :int]]
 [:multi {:dispatch :bar}
  [:baz [:map [:bar :baz]]
  [:flub [:map [:bar :flub]]
this would compile a validator similar to
[:multi {:dispatch :bar}
 [:baz [:map [:a :int] [:bar :baz]]
 [:flub [:map [:a :int] [:bar :flub]]

2024-08-08T16:39:37.314589Z

you'd push the merge into the multi as the intermediate step:

[:multi {:dispatch :bar}
 [:baz [:merge [:map [:a :int]] [:map [:bar :baz]]]
 [:flub [:merge [:map [:a :int]] [:map [:bar :flub]]]

2024-08-08T16:41:16.086419Z

this might event work for parsing, I don't think :merge or :multi are path elements in the parsed data structure output (therefore it's fine to manipulate their positions). :multi clauses are surfaced in the result of m/parse so we'll need to figure out a stable way to do that. e.g., :multi 's are nested left-to-right, [:merge :dispatch1 :dispatch2 :dispatch3] => [:dispatch1 [:dispatch2 [:dispatch3 :merge]]] I'm guessing we'd build a protocol for a "mergable" type that multi and others would extend (maybe :or?).

2024-08-08T17:41:34.957339Z

feels a bit more general than mergable, maybe distributive.

🙏 1
2024-08-08T18:30:01.930549Z

prototype https://github.com/metosin/malli/pull/1086

2024-08-08T21:08:24.003129Z

@rdonahue could you try my branch and tell me if it's what you're after?

ikitommi 2024-08-09T12:50:47.626779Z

Love the approach, commented the issue.

Ryan 2024-08-09T13:12:19.780389Z

@ambrosebs I will try that now, might take me a minute to figure out how to build it 🙂 relatively new to things off the beaten path with dependencies!

2024-08-09T16:34:14.800099Z

@rdonahue yeah wow it's not easy, let me push an installer script to my branch.

Ryan 2024-08-09T16:36:22.127829Z

No worries, I got a test project up and running.. I was going to need to figure out how to add local deps to a cljs project at some point, and between theller’s smart error messages and @p-himik’s help, I have gotten as far as validating :int 🙂

2024-08-09T16:38:46.018109Z

nice. well I added a ./bin/install script to my branch to install a local snapshot.

👍 1
exitsandman 2024-08-08T03:11:19.001289Z

I don't use Malli, but that schema looks like Just Data™ to me. You can probably use a for with a case for the different bit - if you really want to avoid ~15 lines of repeated code, that is

☝️ 1
Ryan 2024-08-12T12:48:47.736399Z

@ambrosebs back at it, sent you an invite to my testing repo, still working on it, but I think it’ll do exactly what I need it to 🙂