I don't use schema/core.typed much in my actual projects, while I have done many attempts it just never worked out. I like the idea and should definitely use them more but it is just too much of a moving system and not stable enough yet (eg. didn't even know s/either is deprecated).
If you look at these implementations (def OneOff [(s/one s/Str 'name) (s/one s/Str 'email)]) (s/defrecord OneOff [name :- s/Str email :- s/Str]) (defrecord OneOff [name email]) All of them do more or less the same thing, just different. Clojure has really good support for records and they feel natural to me. I don't need to remember that :email is the second field. So I can do (:email one-off) instead of (get one-off 1). Remembering the positions can get tedious and very error prone over time. Always remember that software evolves over time. (:email one-off) still works if my data changes to (defrecord OneOff [name note email]), the vector version doesn't. Referring to things by name is a good thing. I do not know Elixir but in Erlang you very rarely use tuples for actual data, usually just messages and return values. Data is all Records, maybe maps these days but I left before R15B so can't say. I usually only do data validation on the system boundary and trust in it after. Otherwise you might end up validating the same data over and over again. So if I get something from a user (eg. HTTP) I validate that it is what I expect and transform if needed. I have an explicit (if (is-this-what-i-expect? data) ...) and not something hidden in a {:pre ...} or some macro magic. I expect the data to be wrong (never trust the user) and want to test that assumption ASAP. I don't like to use exception for validation errors. Writing validation functions is not fun but it is very simple. YMMV, do what feels right. Keep it simple. /thomas On Tue, Sep 8, 2015 at 4:42 AM, Amith George <strider...@gmail.com> wrote: > >> I probably wouldn't use protocols since I doubt there is a function > signature that is exactly identical for all branches. Each branch probably > needs access to different parts of your system (eg. database) and always > passing everything to everything is not ideal. > > >> Multi-Method is great if you want something openly extensible but that > again seems unlikely here and also assumes that everything requires the > same arguments. > > I had the same concerns and wanted to use a simple function with > pattern/condition matching to dispatch to the appropriate function. I had > considered using Records as synonymous with using polymorphic calls (multi > methods, protocols). Its good to know the alarm bells ringing in my head > had merit :). Thanks for that. > > Thinking out loud, if we are not using Records for polymorphism, are we > using it to guarantee structure? If you could indulge me a little more, > consider the following two implementations. > > > (def OneOff [(s/one s/Str 'name) (s/one s/Str 'email)]) > > (defn send-one-off > [something thing [name email :as data]] > {:pre [(s/validate OneOff data)]} > ,,,) > > (defn send > [app thing recipient] > (match [recipient] > [[:one-off & data]] (send-one-off (:something app) thing data))) > > > > > Individual schema are created for each variant case and are checked > against by the respective functions. The dispatch function only needs to > know how to check for individual cases of the variant. > > > (def OneOffMap {:type (s/eq :one-off) :name s/Str :email s/Str}) > (def ExistingContactMap {:type (s/eq :existing) :contact-id s/Int}) > > (def Recipient (s/either ExistingContactMap OneOffMap)) > ;; s/either is deprecated and s/cond-pre is recommended > ;; however, validating valid OneOffMap data using the following > ;; still throws an error. > ;; (def Recipient (s/cond-pre ExistingContactMap OneOffMap)) > > (defn send-one-off > [something thing {:keys [name email] :as data}] > ,,,) > > (defn send > [app thing recipient] > {:pre [(s/validate Recipient recipient)]} > (match [(:type recipient)] > [:one-off] (send-one-off (:something app) thing recipient))) > > This reverts to using a map with a :type key. Individuals schema for each > :type value. A combined schema to represent a recipient. The dispatch > function only needs to know about the existence of the :type key and the > values it can handle. > > > Whether we use records or maps or variants, the dispatch function needs to > know what contract is implemented by recipient. Whether it will be an > instance of something, or a variant or a map with type keys. Neither > versions care for any other data present in recipient or its structure. > > At this point I am confused, what makes one version better than the other. > Creating schema definitions for the records seemed a lot easier than for > the other two. Also I am assuming that if records are created using > s/defrecord, the factory functions (->, map->) will automatically validate > them? > > I really appreciate you taking the time to clarify things for me. > > -- > Amith > > > > On Monday, 7 September 2015 20:57:46 UTC+5:30, Thomas Heller wrote: >> >> >>> (def Recipient >>>> (s/either PlaceHolder >>>> Existing >>>> OneOff)) >>>> >>> >>> This looks interesting. Where would I actually use this? I mean, if I >>> have created three records, I may as well implement multi methods or >>> protocols, right? Even if I don't do those, I will still need to use >>> `(condp instance? obj ...)` or equivalent to select the appropriate branch >>> for processing. Is there a way I can use Recipient to select a branch? >>> >> >> I probably wouldn't use protocols since I doubt there is a function >> signature that is exactly identical for all branches. Each branch probably >> needs access to different parts of your system (eg. database) and always >> passing everything to everything is not ideal. >> >> Multi-Method is great if you want something openly extensible but that >> again seems unlikely here and also assumes that everything requires the >> same arguments. >> >> cond(p) sounds perfect for this case. I tend to write each branch as a >> single function and keep the dispatch as compact as possible. >> >> (defn send-placeholder [thing {:keys [text]}] >> ...) >> >> (defn send-existing [db thing {:keys [contact-id]}] >> ...) >> >> (defn send-one-off [something thing {:keys [name email]}] >> ...) >> >> (defn send [app thing recipient] >> (condp instance? recipient >> PlaceHolder >> (send-placeholder thing recipient) >> Existing >> (send-existing (:db app) thing recipient) >> OneOff >> (send-one-off (:something app) thing recipient))) >> >> >> That greatly reduces the cognitive load when looking at each separate >> implementation and also keeps the actual internal structure of the >> Recipient out of the dispatch. The conpd does not need to know how many >> fields are in OneOff, the tuple/vector/variant match versions must know >> that. >> >> /thomas >> >> >> >> >> >> >> -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient with > your first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > --- > You received this message because you are subscribed to a topic in the > Google Groups "Clojure" group. > To unsubscribe from this topic, visit > https://groups.google.com/d/topic/clojure/cuHfhVVE2zg/unsubscribe. > To unsubscribe from this group and all its topics, send an email to > clojure+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.