>> 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 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.