Some further thoughts
I think most if not all of the cases you described can be reduced to simple schemas.
Let’s look at 3, we can translate it to a syllogism:
The delivery method is a letter if and only if the order includes only a SIM card
i.e.
method = letter → order = [SIM]
Well then
Let
(def Item [:enum "SIM" "SAM"])
(def DeliveryMethods [:enum "SHIPMENT"])
(def RegularOrder
[:map
[:items [:vector Item]]
[:delivery DeliveryMethods]])
Let’s define (worth adding it as a shorthand, Tommi?)
(defn only
[v]
[:sequential {:min 1 :max 1} [:= v]])
We can write case 3 as:
(def SimOrder
(m/schema
[:map
[:items (only "SIM")]
[:delivery [:or DeliveryMethods [:= "letter"]]]]))
Then any order is just:
[:or SimOrder RegularOrder]
Now, that was quite manual. What’s interesting is, can we take it a step further?
Previously, I formalized the requirement as a logical relation, i.e.
;;; generally
[:rel schema1 schema2]
;;; Specifically
[:rel
[:map [:delivery [:= "letter"]]]
[:map [:items (only "SIM")]]]
I believe the other constraints you have specified can be laid out this way as well. Do they all reduce to logical conjunction? All the clauses you specified have an or
between them
Now here’s the kicker - we already have a language and model for relations - logical programming and datalog!
Could it perhaps be possible to write a datalog → schema compiler, where you specify the desired relations between entities and a schema is derived?
As an initial step, you can just formalize each of these requirements about fields as particular schemas, start from a base schema S0
, define Si
where you just mu/assoc
all the constraints regarding specific fields and close(?) the schema, then S* = [:or S1 ... Si ... Sn]
Thoughts?