This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-08-29
Channels
- # admin-announcements (1)
- # announcements (3)
- # babashka (18)
- # beginners (35)
- # cider (4)
- # clj-kondo (52)
- # cljs-dev (2)
- # clojure (92)
- # clojure-spec (18)
- # clojurescript (17)
- # conjure (11)
- # core-async (1)
- # datomic (11)
- # emacs (5)
- # fulcro (11)
- # graalvm (10)
- # helix (21)
- # kaocha (6)
- # malli (1)
- # membrane (37)
- # off-topic (110)
- # re-frame (1)
- # reagent (12)
- # reitit (5)
- # rewrite-clj (1)
- # sci (1)
- # shadow-cljs (40)
- # vim (21)
- # vrac (17)
How would you go about spec’ing a (http) patch like api using nil
as a sentinel for retraction. Let’s say you have an entity that has an optional attribute, I’d hate to say that this attribute is s/nilable
in its global definition just because there is a patch endpoint using nil
to signal retraction
Like — if I send you this attribute, it will either not be there, or it will have a non-nil value.
I don’t know, it feels asymmetrical: I’ll always promise you either no value at all, or some value satisfying a spec, but you can provide me no value, a value satisfying a spec or nil, but only in this specific case. I guess it doesn’t help me that I can have only one definition for a namespaced keyword (here).
Isn't this just a non-required key in a spec?
It's either present (and conforms) or it is not present.
Yes, outgoing I would agree: I’ll (= server) never send you a nil
for a key I have no value for and instead omit it. The problem is in how you (= client) tell me that you don’t want this value anymore: If you omit it in your (partial) update request, do you mean to retract it, or to retain it?
So some rest endpoints allow you to patch
an entity, but with nil
as value, meaning ‘let’s get rid of this value for that attribute’. So now we have a bit of asymmetry: I’ll promise you to never send a nil
value, but you can send me a nil
to indicate retraction on this patch
endpoint.
I don’t know how to express, in spec, that I as a server make a stronger guarantee than you as a client have to when patch
’ing entities. If that makes any sense 🙂.
(Or more specifically, I don’t know how to do that when my keys are namespace qualified. If I were to use s/keys
with :req-un
I could maintain two specs.)
That's just two different specs (perhaps with reuse on the common stuff). A spec for the result of a call (where the key is optional but spec'd to be non-nil). A spec for the input value (where the key is nilable).
If it's an API spec, it's going to be for unqualified keys, surely? Since it will be a wire spec, e.g., JSON.
Right, we may have extended this nilability (or the spec, for that matter) too far into our system. We have a JSON handler that accepts this nil and is spec checked, that is unqualified, but then we have an update method shared between this JSON endpoint and other places that is also checked, but has qualified keys. Thats where friction occurs: the keys are qualified, but their semantics differ between what you supply and what you get.
Well, if you have an internal (qualified) name for that attribute, either it should be optional and non-`nil`, or it should be nilable -- in all cases. And if that's not possible, then the two semantics should have different names.
Sorry for the kinda fuzzy description… Right so in the latter of your options you say to extend the notion of valid values for this attribute to be a superset of all values it can take in all contexts. That means that consumers I could have promised no nil
s (because they consume the ‘outgoing’ format), have to consider nil
s because thats what I could promises them in my spec, right? I find that kinda sad.
I'm saying use different specs as needed and transform the data to match as you cross boundaries.
Another option is to chose a specific, unique, representation for a retraction (and, again, map from the inbound retraction to that representation).
(we had exactly this situation and we tried to blur the lines with nilable and optionality and it was a mess so we mapped it to a different representation altogether)
Right that makes sense
Yeah, I think we are going wrong in a similar way: have a single namespace qualified keyword for an attribute in all its contexts, but using it in different ways. I’ll put this in the hammock, thank you for the input, as always 🙂!