Re: Just found out about Elixirs function argument pattern matching...
FWIW before I came to Clojure I did a lot of Erlang and in the beginning I was at the exact same spot wanting to use pattern matching everywhere because it is so damn cool. Same goes for tagged literals. After a little while I realized that it is just not the way to do it in Clojure and forcing the Erlang(or Elixir)-Way onto Clojure is not ideal and probably overthinking it. Protocols provide a good/better solution to some problems and using Clojure's excellent handling&support of "data" solves the rest. While it might feel weird in the beginning, wait a while for Clojure to click and you probably won't even miss pattern matching. Looking at the "(defn register [...])" example. Where is the problem with the first solution? It doesn't have the bugs the other implementations have and is extremely simple to reason about? The other two solutions do the exact same thing just slower with absolutely no gain. If you need the "status" abstraction use a real state machine. Don't write a recursive function when you don't have to. The code should never be at a point where it can be called with forged data and directly skip over the :create and :check-unqiue states which is possible in 2 of the 3 solutions. Embrace the data and data all the things is all I have to say. Also if you like types (using prismatic/schema [1], core.typed looks pretty similar): (def ContactId s/Int) (s/defrecord Placeholder [text :- s/Str]) (s/defrecord Existing [contact-id :- ContactId]) (s/defrecord OneOff [name :- s/Str email :- s/Str]) (def Recipient (s/either PlaceHolder Existing OneOff)) Just my 2 cents, /thomas [1] https://github.com/Prismatic/schema > > Possible F# (might not compipe!!) > > Recipient = | Placeholder of string >| Existing of ContactId >| OneoffRecipient of string * string > > > Possible options in Clojure > > {:type :placeholder ; or :existing :one-off > :placeholder-text :all-my-team-mates > :name nil > :email nil > :contact-id nil > } > > ;; or > > (defrecord PlaceholdRecipient [placeholder-text]) > (defrecord ExistingReceipient [contact-id]) > (defrecord OneoffReceipient [name email]) > > ;; or > > [:one-off name email] > [:existing contact-id] > [:placeholder text] > ;; [:self] > > The variant needed not be a two element vector. It is a 1 or more element > vector. We could just as easily have a fourth variant [:self] with no data > to indicate send message to self. > > To actually use the above data structures and get the actual email > addresses from them, we need atleast three methods - 1) fetch the emails of > all the users team mates, 2) fetch the email for a contact in the > datastore, 3) return the email as is. We could implement these using a > multi method or implement some protocol or use a pattern matched method. > Each choice has a different extensibility story and are all equally valid. > We don't always replace every hashmap with a record. Variants are a viable > alternative over using a map like the one shown in the first Clojure > choice. > > >> As much as possible I try to build my apps in such a way that the > program can self-explore the data you give it to self-optimize, > self-extend, or otherwise provide flexibility to the programmer. You can't > do that with a variant as a vector. Hand a vector to a function and the > logic has to be something like "If this is a two element vector and the > first thing is a keyword, then this might be a variant, but I'm not sure > because it could also just be a vector". While if you pass in a record, > type, or even a variant type, it's trivial to have a program recognize that > value and act upon it. > > I have no experience writing apps that had to operate on any arbitrary > data with any arbitrary structure. Pretty much all the functions in my apps > could rely on data arriving in a particular format in a particular order > (positional destructuring). > > More importantly we are pretty much agreeing that [:tagged vectors with > some data] are only a representation of a variant. One chosen out of > convenience and existing feature set. We could have a defrecord Variant > with two fields, a type keyword and value hashmap. If core.match was able > to easily pattern match over a record as well as easily destructure the > values in the value hashmap, we would use it. From what I have seen of > core.match, it is nowhere near as easy/clean as pattern matching a vector. > > >> As far as performance goes, this is normally the sort of thing that > gets baked into an app at a pretty low level, that's why I suggest it > should be as fast as possible. If you're going to be processing millions of > these things during the life of an app, and transforming them from one > format to another, a little tweaking here and there may save you some pain > in the end. > > Pattern matching itself is too slow for it to be viable in
usage of "and"
Typo? (and cnt (pos? cnt)) -- 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.
usage of "and"
Looks like a typo - these things happen. :) If you file it here we will get updated in the ebook (not sure yet how often that will update): https://pragprog.com/titles/vmclojeco/errata -- 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.
Re: Just found out about Elixirs function argument pattern matching...
On 6 September 2015 at 15:38, Timothy Baldridge wrote: > >> I'm not sure why you think that it "complicates the code, and makes it > harder to understand". > > Alright, I'll use the phrase: using vectors as variants complects order > with data. If you hand me a vector that says [:name "tim"] I have to know > that "the first thing in this vector is the name of the thing, the second > thing is the value of the thing". In addition, I have to know that you > passed me a variant. But if you simply passed me either #my.app.Name["tim"] > or #my.app.Variant{:key :name :value "tim"} I can programmatically > deconstruct that value into something usable by my system. > How? What can your program reasonably infer from that? If you remove the semantic information from your examples (which is only meaningful to humans), you get: #xxx.yyy["zzz"] #xxx.yyy{:aaa :www, :bbb "zzz"} What does that allow us to programmatically infer? That the type is "xxx.yyy" and that it may contain strings and keywords, but that's about it. What good is that? If the type satisfies known protocols, then you have more information, but if you don't use polymorphism the only benefit is that you know the type, which is almost meaningless by itself. In fact, one could argue that it hampers programmatic analysis if you're using maps. For instance, if you have a variant to set an expiry date: [:cache/expires #inst "2016-01-01T00:00:00.000Z"] Then you can fold this into a map: {:cache/resource #url "http://example.com/foo"; :cache/expires #inst "2016-01-01T00:00:00.000Z"} If you use a custom type instead of a variant: #cache/Expires [#inst "2016-01-01T00:00:00.000Z"] Then the software can no longer infer that this is related to the key on the map. I also disagree that variants "complect order with data". By that logic, a coordinate of: [4 3] Should be better expressed as: #coord2d {:x 4, :y 3} And a function like: (swap! a inc) Should be expressed as: (swap! :atom a, :function inc) Keywords have their place, but I don't think using positional indexes to look up data is necessarily bad, assuming that the vector or list is very small. It's worth noting that Datomic uses vectors to represent transactions, rather than maps or records, so presumably Rich and the other folks at Cognitect are not above using positional indexing in certain cases. > As far as performance goes, this is normally the sort of thing that gets > baked into an app at a pretty low level, that's why I suggest it should be > as fast as possible. If you're going to be processing millions of these > things during the life of an app, and transforming them from one format to > another, a little tweaking here and there may save you some pain in the > end. > I was curious as to whether records really were faster than a vector lookup. It turns out that vectors are faster: => (defrecord Foo [x y]) user.Foo => (let [f (->Foo 1 2)] (quick-bench (:x f))) Execution time mean : 6.592069 ns => (let [f [1 2]] (quick-bench (f 0))) Execution time mean : 4.758705 ns => (let [f [1 2]] (quick-bench (let [[x y] f] (+ x y Execution time mean : 19.388727 ns => (let [f (->Foo 1 2)] (quick-bench (let [{:keys [x y]} f] (+ x y Execution time mean : 68.845332 ns That said, a core.match is still going to be slower than a protocol lookup, so depending on your use-case using a record might still be quicker, but in terms of actually pulling information out, vectors are quicker. - James -- 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.
Re: Just found out about Elixirs function argument pattern matching...
On 6 September 2015 at 09:57, Amith George wrote: > > Could you elaborate on what you mean by variants being like a key value > pair? > I mean that tags are usually used to describe what the data *is*, whereas keys are usually used to describe what the data is for. For instance, one might have a map like: {:created-date #inst "2015-09-07T12:30:00.000Z" :modified-date #inst "2015-09-07T13:00:00.000Z"} The tags describe what the values are: instances in time. The keys describe what the data is for: to record when the entity was created and modified. In the same way, I'd consider variants another way of connecting a data value with an indicator of its purpose. - James -- 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.
Re: Just found out about Elixirs function argument pattern matching...
On 7 September 2015 at 13:59, James Reeves wrote: > On 6 September 2015 at 15:38, Timothy Baldridge > wrote: >> >> As far as performance goes, this is normally the sort of thing that gets >> baked into an app at a pretty low level, that's why I suggest it should be >> as fast as possible. If you're going to be processing millions of these >> things during the life of an app, and transforming them from one format to >> another, a little tweaking here and there may save you some pain in the >> end. >> > > I was curious as to whether records really were faster than a vector > lookup. It turns out that vectors are faster: > I realised after I sent my email that you were earlier referring to creation rather than lookup. In such a case, it *is* slightly quicker to use a record: => (let [x 1 y 2] (quick-bench (->Foo x y))) Execution time mean : 9.578002 ns => (let [x 1 y 2] (quick-bench [x y])) Execution time mean : 12.213409 ns So in terms of performance, it matters whether you're primarily reading or primarily writing. Vectors are faster for lookups, records are faster for creation. That said, if performance is important enough that even a couple of nanoseconds per iteration matter, then perhaps an array should be used instead. - James -- 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.
Re: Uberjar woes
I've had this problem too. I solved in a slightly different way. I had my main function in a clojure file that also defined a component. I moved my main function into a different file and that did the trick. I didn't need a :gen-class directive in each file that had a component. I didn't need to bring my user.clj into my main source tree, I didn't need the :clean-non-project-files false directive either...just pulled the main function out of the file that also defined a component. Each time I did a lein clean; lein deps; lein compile; lein repl -or- lein clean; lein deps; lein compile; lein uberjar These are the directives I did use in my project.clj: :main example.core :uberjar {:aot :all} :aot [example.core] :repl-options {:init-ns user} -- 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.
Re: Just found out about Elixirs function argument pattern matching...
> > Looking at the "(defn register [...])" example. Where is the problem with > the first solution? It doesn't have the bugs the other implementations have > and is extremely simple to reason about? The other two solutions do the > exact same thing just slower with absolutely no gain. If you need the > "status" abstraction use a real state machine. Don't write a recursive > function when you don't have to. The code should never be at a point where > it can be called with forged data and directly skip over the :create and > :check-unqiue states which is possible in 2 of the 3 solutions. > It bothered me as well that register could skip states. I couldn't figure out how to deal with that and resorted to making the function private :( . I was originally thinking - the sum of the all steps being the variant/sum-type and each step being one of the cases of the variant. However as you said, a state machine might be a better representation. Maybe this was a bad use case for variants. The output of the state machine steps could be a variant over :ok, :err. Either way lets ignore this usage. (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 am not proposing the variant vector to be used in place of records. I am looking at it as an improvement over using a hashmap containing a :type key. For some reason I am wary of records. The variant vector seemed like a good intermediate solution. On Monday, 7 September 2015 17:13:53 UTC+5:30, Thomas Heller wrote: > > > >> -- 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.
Re: Just found out about Elixirs function argument pattern matching...
>> Should be expressed as: >> (swap! :atom a, :function inc) One of Rich's talks on simplicity actually addresses that. He states that the above method (with keyword arguments) is actually simpler, but that this isn't exactly easy to program. And yes, I would take this same position about positional vectors being a type instead of a list or persistent vector. Same thing with Ratios we could represent them as [numerator denominator] but we don't as its simpler to give them their own type, and then I can easily determine if a [11 22] is a ratio, a 2D point or even a variant. Types are good even in dynamic languages, we should use the more. As mentioned by Thomas, records are the idiomatic way to store typed information in Clojure. They're simple, elegant, and interop very cleanly with the rest of the system. So that's why I react when someone takes a conference talk as an example of "the way it should be done". The above video is a exploratory explanation of a different way of thinking of data, but I have never seen code like that in production, and it's so different from idiomatic Clojure, that I would be hesitant to adopt it wholesale. Timothy -- 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.
Re: Just found out about Elixirs function argument pattern matching...
> > > (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.
add concurrency feature as language spec.
I have seen clojure now has different platform implementation.It would be good if its concurrency spec. must be a language style spec. like "go" ing Golang. Clojure-CLR,Clojure-Java,Clojurescript need a common concurrency spec. -- 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.
Re: Just found out about Elixirs function argument pattern matching...
On 7 September 2015 at 15:49, Timothy Baldridge wrote: > > Types are good even in dynamic languages, we should use the more. As > mentioned by Thomas, records are the idiomatic way to store typed > information in Clojure. > I don't think that's true. Or rather, I think it depends on what you mean by "type". In core.typed parlance, a "type" is a means of validating the shape of data at compile time. We can use a vector to represent a variant, and still have it typed: (defalias FooResult (U '[(Value :ok) Int] '[(Value :err) Str])) Dynamic typing libraries like Schema or Annotate have a similar notion of types, but perform their checks at runtime. In both cases, the type is independent of the data; we can determine whether a data structure matches a specific type, but the type isn't tied to the data. In contrast, we have a notion of what core.typed and edn calls a "tag", which is attached directly to the data structure. A record is essentially a tagged map, and if we didn't have to worry about performance, tags would probably just be implemented as metadata. If we're talking about records, then we're talking about tags more than types, and the question becomes: what data should we tag? If we want runtime polymorphism, then it makes sense to use a tag so we have something to dispatch off. But is there any other practical reason we'd want to use a tagged structure in Clojure? I'm not convinced there is. Records are useful for polymorphism, and perhaps performance if that level of optimisation is necessary, but I don't see any other benefit. > The above video is a exploratory explanation of a different way of > thinking of data, but I have never seen code like that in production, and > it's so different from idiomatic Clojure, that I would be hesitant to adopt > it wholesale. > A lot of people use Instaparse and Hiccup in production, which I believe the video explicitly mentions as being examples of variants. - James -- 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.
Re: add concurrency feature as language spec.
You mean like core.async? - James On 7 September 2015 at 16:49, Renjith Thankachan wrote: > I have seen clojure now has different platform implementation.It would be > good if its concurrency spec. must be a language style spec. like "go" ing > Golang. > Clojure-CLR,Clojure-Java,Clojurescript need a common concurrency spec. > > -- > 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. > -- 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.
Re: usage of "and"
> On Sep 7, 2015, at 7:58 AM, jongwon.choi wrote: > > Typo? > > (and cnt (pos? cnt)) Sometimes this kind of thing is reasonable if cnt might be nil, which would break pos?. So the expression says "cnt isn't nil (or false) and it's positive." -Lee -- 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.
Re: Just found out about Elixirs function argument pattern matching...
>> 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
Re: Just found out about Elixirs function argument pattern matching...
Thanks for your benchmark. I will upgrade all the dependencies and release 0.2.0 We are using defun with instparse in a DSL implementation, the performance is acceptable, but the code is much more readable. 2015-09-06 4:33 GMT+08:00 Rob Lally : > Out of interest, I ran the benchmarks as is, and got more or less the same > results - 15x. Then I tried upgrading the defun dependencies - clojure, > core.match and tools.macro - all of which have newer versions, and then > running the benchmarks without leiningen’s jvm-opts and in a trampolined > repl. The results are better (see below). Still not great - but down from > 15x to 10x. > > That said: > > * I’m not sure I’d care: for most applications the overhead of function > dispatch is probably not the bottleneck. > * Elixir and the BEAM VM are awesome at many things, but I suspect (from > experience not evidence) that the defun version is still faster than the > elixir version. > > > Rob > > --- > > user=> (bench (accum-defn 1)) > WARNING: Final GC required 2.590098761776679 % of runtime > Evaluation count : 429360 in 60 samples of 7156 calls. > Execution time mean : 139.664539 µs > Execution time std-deviation : 4.701755 µs >Execution time lower quantile : 134.451108 µs ( 2.5%) >Execution time upper quantile : 150.214646 µs (97.5%) >Overhead used : 1.565276 ns > > Found 5 outliers in 60 samples (8. %) > low-severe 5 (8. %) > Variance from outliers : 20.5880 % Variance is moderately inflated by > outliers > > user=> (bench (accum-defun 1)) > Evaluation count : 44940 in 60 samples of 749 calls. > Execution time mean : 1.361631 ms > Execution time std-deviation : 40.489537 µs >Execution time lower quantile : 1.333474 ms ( 2.5%) >Execution time upper quantile : 1.465123 ms (97.5%) >Overhead used : 1.565276 ns > > Found 9 outliers in 60 samples (15. %) > low-severe 1 (1.6667 %) > low-mild 8 (13. %) > Variance from outliers : 17.3434 % Variance is moderately inflated by > outliers > > --- > > > On 5 Sep 2015, at 05:16, Amith George wrote: > > Nice. Hadn't heard of it before. It looks interesting. The criterium > benchmark is kinda disappointing though. The pattern matched function took > nearly 15x the time of the normal function. > > > -- > 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. > -- 庄晓丹 Email:killme2...@gmail.com xzhu...@avos.com Site: http://fnil.net Twitter: @killme2008 -- 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.