Good replacement for s/either?

83 views
Skip to first unread message

Ian Davis

unread,
May 18, 2016, 10:16:54 PM5/18/16
to Plumbing and Graph: the Clojure utility belt
Hiya Jason et al!

I'm upgrading us from 0.4.0 to 1.1.1 in order to leverage the fancy Schema generators in our tests. Unfortunately, this means that our coercion breaks every time we use `s/either`. A couple questions:

1. What was the rationale behind deprecating `s/either`? It has always seemed pretty useful to me, and is especially good for schematizing legacy code / data.
2. What are other folks replacing it with? `conditional` seems to be the preferred method from the docs, but in my case the schema basically boils down to `(s/conditional first-schema? FirstSchema ...)`, which seems a little silly.

Ian

Jason Wolfe

unread,
May 18, 2016, 11:44:18 PM5/18/16
to Ian Davis, Plumbing and Graph: the Clojure utility belt


On Thu, May 19, 2016 at 9:16 AM, Ian Davis <jung...@gmail.com> wrote:
Hiya Jason et al!

Hey Ian! 
 

I'm upgrading us from 0.4.0 to 1.1.1 in order to leverage the fancy Schema generators in our tests. Unfortunately, this means that our coercion breaks every time we use `s/either`. A couple questions:

1. What was the rationale behind deprecating `s/either`? It has always seemed pretty useful to me, and is especially good for schematizing legacy code / data.

While simple on the surface, `s/either` and `s/both` broke the model that the rest of Schema adhered to -- that each datum (or part of datum) is matched against exactly one schema.  With `s/either` there is backtracking, and `s/both` two schemas are matched against the same datum.  Both of these cause new interactions with each additional feature we added (coercion, recursion, etc), provided lousy error messages, and were in general a significant source of bugs and added complexity in the Schema codebase.  

It's still possible to add them (correctly) as a third-party schema  -- it was just a judgement call to not have them in core anymore, based on the existence of reasonable alternatives which greatly simplified the implementation, with better error messages and performance.     
 
2. What are other folks replacing it with? `conditional` seems to be the preferred method from the docs, but in my case the schema basically boils down to `(s/conditional first-schema? FirstSchema ...)`, which seems a little silly.

`conditional` in general, `if` if there are only two options, and `cond-pre` is a drop-in replacement for `either` when the alternatives are superficially different.  (It's basically just `s/conditional`, but where the precondition for each subschema is used as the conditional condition).  So `(s/cond-pre s/Str {:x s/Int})` will work fine, because this becomes `(s/conditional string? s/Str map? {:x s/Int})`.  But, `(s/cond-pre {:x s/Int} {y :s/Str})` won't, and you'll have to use `s/if` in this case.  

Hope this helps, happy to elaborate on any of this if you have follow-up questions. 

-Jason
 

Ian

--
You received this message because you are subscribed to the Google Groups "Plumbing and Graph: the Clojure utility belt" group.
To unsubscribe from this group and stop receiving emails from it, send an email to prismatic-plumb...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Andrea Richiardi

unread,
May 22, 2016, 4:30:19 PM5/22/16
to Plumbing and Graph: the Clojure utility belt, jung...@gmail.com


On Wednesday, May 18, 2016 at 8:44:18 PM UTC-7, Jason Wolfe wrote:

Hope this helps, happy to elaborate on any of this if you have follow-up questions. 


I played with s/either vs s/cond-pre and I tried this:

(def User {(s/required-key :id) s/Int
           :first-name s/Str
           :last-name s/Str
           :password s/Str
           :superuser? s/Bool
           :admin-of #{(s/cond-pre Branch Region)}})

against this:

(def User {(s/required-key :id) s/Int
           :first-name s/Str
           :last-name s/Str
           :password s/Str
           :superuser? s/Bool
           :admin-of #{(s/either Branch Region)}})

But the (generate User) raises an exception, the generator thinking that it needs to generates both Branch and Region probably.
I don't know if this is a generator bug (in which case I can open an issue) but it looks like the behavior is different... 

Jason Wolfe

unread,
May 22, 2016, 10:19:21 PM5/22/16
to Andrea Richiardi, Plumbing and Graph: the Clojure utility belt, Ian Davis
What are Branch and Region?  As I mentioned above, `cond-pre` only works for superficially different types.  If they are, e.g., both maps, then you can't use it.  Instead, I would recommend one of; 
 - s/cond or s/if (the smallest change)
 - abstract-map-schema
 - change your data model so as to not require any such constructs (e.g., separate :admin-branches and :admin-regions keys).  

--
Reply all
Reply to author
Forward
0 new messages