Sum types in Clojure? Better to represent as tagged records or as variant vectors?
I'm reading on http://www.lispcast.com/reduce-complexity-with-variants and have seen Jeanine's talk on her encoding scheme for sum types, but for some reason, I'm not convinced of the benefit over a tagged record encoding. I see that the positional vector or hash-map suggested in the lispcast article has a theoretically smaller number of states, but because it lacks names, it seems at a higher risk of human error. Anyone has thoughts on this? And now that we have specs, I feel like a multi-spec would address concerns of the tagged record encoding by being able to validate that the correct set of keys exist for the given type, while also having the benefit of named fields. What do others think? -- 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: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
I find the arguments for variants very unconvincing. As you stated with specs and s/keys you can spec the same sort of data, and in a way that's much more open-ended. For example: [:person {:name "Tim" :grade "B"}] What is the type of this data? We would probably guess a :person. But what if we wanted to "cast" it to a :student? Well then we'd have to change the variant tag to :student, but then it would no longer be a person, unless we introduced some sort of inheritance that said all students are people. I much prefer spec: (def ::person (s/keys :req [::name])) (def ::student (s/keys :req [::grade ::name])) I'm very much in the camp that longs for a super fast s/valid? implementation so I could go and re-implement multi-methods and protocols on top of spec. Much more flexible, and honestly it more correctly models the real world. On Tue, Aug 22, 2017 at 3:52 PM, Didier wrote: > I'm reading on http://www.lispcast.com/reduce-complexity-with-variants > and have seen Jeanine's talk on her encoding scheme for sum types, but for > some reason, I'm not convinced of the benefit over a tagged record > encoding. I see that the positional vector or hash-map suggested in the > lispcast article has a theoretically smaller number of states, but because > it lacks names, it seems at a higher risk of human error. Anyone has > thoughts on this? And now that we have specs, I feel like a multi-spec > would address concerns of the tagged record encoding by being able to > validate that the correct set of keys exist for the given type, while also > having the benefit of named fields. What do others think? > > -- > 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. > -- “One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.” (Robert Firth) -- 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: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
On 22 August 2017 at 23:04, Timothy Baldridge wrote: > I find the arguments for variants very unconvincing. As you stated with > specs and s/keys you can spec the same sort of data, and in a way that's > much more open-ended. > > For example: > > [:person {:name "Tim" :grade "B"}] > > What is the type of this data? We would probably guess a :person. But what > if we wanted to "cast" it to a :student? Well then we'd have to change the > variant tag to :student, but then it would no longer be a person, unless we > introduced some sort of inheritance that said all students are people. > I don't think that example is a representative use of variants in Clojure. More typically, variants are used like key/value pairings that exist outside a map. For example: [:person/name "Tim"] If you actually have a map, the key/value pairs speak for themselves: #:person{:name "Tim", :grade "B"} We can tell this is a "person" because it adheres to a particular shape, which can be specced out: (s/def :person/student (s/keys :req [:person/name :person/grade])) But if you just have a key/value pairing, the value alone doesn't tell you much: "Tim" Using a vector to represent a key/value seems to fit in well with how Clojure currently works: (find m :person/name) => [:person/name "Tim"] But there's no "s/variant" or "s/entry" spec that's as convenient to use as s/keys. -- James Reeves booleanknot.com -- 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: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
Great, so these approaches are suggesting we wrap every value in a vector or a hash map (as the lisp cast article does). That doesn't really help much at all. Why is: [[:image/name "img.png"] [:encode/width 42] [:encode/height 33]] Any better than {:image/name "img.png" :encode/width 42 :encode/height 33} Or perhaps people aren't suggesting vectors of vectors, I don't know. All of it makes no sense to me when I look at in-production Clojure code. I don't pass image names around as bare strings. I pass them around as part of a hashmap where the keys give context to the strings, and also allow me to pass in other data about the parameter. Variants and sum types seem to come from OCaml and the like where strict typing makes them much more needed. But in Clojure we prefer heterogeneous collections, so I just don't understand why I would ever want variants. They complicate the code for really no benefit. The complexity they are attempting to solve can be solved by simply using maps and records instead of bare values. On Tue, Aug 22, 2017 at 5:43 PM, James Reeves wrote: > On 22 August 2017 at 23:04, Timothy Baldridge > wrote: > >> I find the arguments for variants very unconvincing. As you stated with >> specs and s/keys you can spec the same sort of data, and in a way that's >> much more open-ended. >> >> For example: >> >> [:person {:name "Tim" :grade "B"}] >> >> What is the type of this data? We would probably guess a :person. But >> what if we wanted to "cast" it to a :student? Well then we'd have to change >> the variant tag to :student, but then it would no longer be a person, >> unless we introduced some sort of inheritance that said all students are >> people. >> > > I don't think that example is a representative use of variants in Clojure. > More typically, variants are used like key/value pairings that exist > outside a map. For example: > > [:person/name "Tim"] > > If you actually have a map, the key/value pairs speak for themselves: > > #:person{:name "Tim", :grade "B"} > > We can tell this is a "person" because it adheres to a particular shape, > which can be specced out: > > (s/def :person/student (s/keys :req [:person/name :person/grade])) > > But if you just have a key/value pairing, the value alone doesn't tell you > much: > > "Tim" > > Using a vector to represent a key/value seems to fit in well with how > Clojure currently works: > > (find m :person/name) > => [:person/name "Tim"] > > But there's no "s/variant" or "s/entry" spec that's as convenient to use > as s/keys. > > -- > James Reeves > booleanknot.com > > -- > 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. > -- “One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.” (Robert Firth) -- 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: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
Right, I see things the way tbc++ does, but I'm wondering if I'm missing some key insights that make variants better. I similarly struggle with the difference between (s/or) and (s/multi-spec). If I were to implement the lispcast example today with spec I'd either use (s/or): (s/def ::image-type #{:image/in-memory :image/on-disk :image/web}) (s/def ::pixels vector?) (s/def ::filename string?) (s/def ::url string?) (s/def ::image-source (s/or :in-memory (s/keys :req-un [::image-type ::pixels]) :on-disk (s/keys :req-un [::image-type ::filename]) :web (s/keys :req-un [::image-type ::url]))) I could pair that with the case-of macro from https://github.com/lambdaisland/uniontypes to get some guarantees that I always handle all cases. Or I would use (s/multi-spec): (s/def ::image-type #{:image/in-memory :image/on-disk :image/web}) (defmulti image-type ::image-type) (s/def ::pixels vector?) (defmethod image-type :image/in-memory [_] (s/keys :req-un [::image-type ::pixels])) (s/def ::filename string?) (defmethod image-type :image/on-disk [_] (s/keys :req-un [::image-type ::filename])) (s/def ::url string?) (defmethod image-type :image/web [_] (s/keys :req-un [::image-type ::url])) (s/def ::image-source (s/multi-spec image-type ::image-type)) I'm not sure the practical difference between one or the other, but they both seem like they would cover the lispcast use case perfectly, where I now have a map like so: {:type :image/in-memory :pixels [...]} ;; OR {:type :image/on-disk :filename "/cats.jpg"} ;; OR {:type :image/web :url "http://cats.com/cats.jpg"} As all valid ::image-source. The spec restricts the possible state to only 3 and I get a nicer map representation with named values. So maybe variants were useful pre-spec? But even before spec, I still don't really get why they were any better? They just feel prone to the same human errors that ordered constructors are prone too. And ya, where my record has more then one value, what are you supposed to do? This: [[:image/name "img.png"] [:encode/width 42] [:encode/height 33]] Or this: [:image/name "img.png" 42 33] Or this: [:image/name ["img.png" 42 33]] Or this: [:image/name {:name "img.png" :width 42 :height 33}] ?? On Tuesday, 22 August 2017 17:18:31 UTC-7, tbc++ wrote: > > Great, so these approaches are suggesting we wrap every value in a vector > or a hash map (as the lisp cast article does). That doesn't really help > much at all. Why is: > > [[:image/name "img.png"] > [:encode/width 42] > [:encode/height 33]] > > Any better than > > {:image/name "img.png" > :encode/width 42 > :encode/height 33} > > Or perhaps people aren't suggesting vectors of vectors, I don't know. All > of it makes no sense to me when I look at in-production Clojure code. I > don't pass image names around as bare strings. I pass them around as part > of a hashmap where the keys give context to the strings, and also allow me > to pass in other data about the parameter. > > Variants and sum types seem to come from OCaml and the like where strict > typing makes them much more needed. But in Clojure we prefer heterogeneous > collections, so I just don't understand why I would ever want variants. > They complicate the code for really no benefit. The complexity they are > attempting to solve can be solved by simply using maps and records instead > of bare values. > > > > On Tue, Aug 22, 2017 at 5:43 PM, James Reeves > wrote: > >> On 22 August 2017 at 23:04, Timothy Baldridge > > wrote: >> >>> I find the arguments for variants very unconvincing. As you stated with >>> specs and s/keys you can spec the same sort of data, and in a way that's >>> much more open-ended. >>> >>> For example: >>> >>> [:person {:name "Tim" :grade "B"}] >>> >>> What is the type of this data? We would probably guess a :person. But >>> what if we wanted to "cast" it to a :student? Well then we'd have to change >>> the variant tag to :student, but then it would no longer be a person, >>> unless we introduced some sort of inheritance that said all students are >>> people. >>> >> >> I don't think that example is a representative use of variants in >> Clojure. More typically, variants are used like key/value pairings that >> exist outside a map. For example: >> >> [:person/name "Tim"] >> >> If you actually have a map, the key/value pairs speak for themselves: >> >> #:person{:name "Tim", :grade "B"} >> >> We can tell this is a "person" because it adheres to a particular shape, >> which can be specced out: >> >> (s/def :person/student (s/keys :req [:person/name :person/grade])) >> >> But if you just have a key/value pairing, the value alone doesn't tell >> you much: >> >> "Tim" >> >> Using a vector to represent a key/value seems to fit in well with how >> Clojure currently works: >> >> (find m :person/name) >> => [:person/nam
Re: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
I think the article is a bit misleading. Variants were never that popular in Clojure. Infact I've never seen them used in production code or the most common Clojure libraries. So I'm a bit perplexed as to why the article recommends them so strongly. So I think the answer is, they are a fun thought experiment in Clojure, but are of limited usefulness due to the tools we have available that make them unneeded. It's a bit like recommending that new users use actors in Clojure. Sure, you can shoehorn them in, but there's a reason why they aren't the default. -- “One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.” (Robert Firth) -- 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: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
Ya, I guess the article says that some DSL uses them, like hiccup, so maybe its only in the perspective of a DSL you'd need to parse, and not really for modeling business entities. Like this: (s/def ::temp (s/or :one string? :two int?)) Will conform to a variant: => (s/conform ::temp "one-two") [:one "one-two"] => (s/conform ::temp 12) [:two 12] And not to a tagged record like: {:tag :one :val "one-two"} But, I don't know, that seems like a different and uncommon use case, I don't really see this as using a sum type, just describing data to be parsed, which I guess the vector form can be easier to work with. I think the clojurescript AST is actually using tagged records, and most people find it too verbose because of it. Anyways, I'd still like to hear if there's a major benefit I'm overseeing, or a detail I'm not understanding, if any. P.S.: I figured the difference between (s/or) and (s/multi-spec) I think. S/or is like structural-types, my example actually doesn't work, because :filename and :url have the same structure, both string? So conform will not be able to tell them apart. So if you need to know any kind of semantic difference which the shape can not infer, (s/or) won't work, you'll need (s/multi-spec) instead, which are more like nominal-types. Multi-spec I guess can also use any function to select the appropriate spec, so they're more powerful, but often I think the way I'm using it will be most common. On Tuesday, 22 August 2017 18:00:21 UTC-7, tbc++ wrote: > > I think the article is a bit misleading. Variants were never that popular > in Clojure. Infact I've never seen them used in production code or the most > common Clojure libraries. So I'm a bit perplexed as to why the article > recommends them so strongly. > > So I think the answer is, they are a fun thought experiment in Clojure, > but are of limited usefulness due to the tools we have available that make > them unneeded. > > It's a bit like recommending that new users use actors in Clojure. Sure, > you can shoehorn them in, but there's a reason why they aren't the default. > -- > “One of the main causes of the fall of the Roman Empire was that–lacking > zero–they had no way to indicate successful termination of their C > programs.” > (Robert Firth) > -- 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: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
Nope, ClojureScript uses nested hash maps (try it for yourself here: http://vps124502.vps.ovh.ca/rage/resources/public/). As does tools.analyer. Instaparse and Hiccup use a variant of variants, but funny enough the first thing I ever do when writing code with instaparse is write a converter from it's vector-based parse trees into a nested hashmap AST. On Tue, Aug 22, 2017 at 7:13 PM, Didier wrote: > Ya, I guess the article says that some DSL uses them, like hiccup, so > maybe its only in the perspective of a DSL you'd need to parse, and not > really for modeling business entities. > > Like this: > (s/def ::temp (s/or :one string? > :two int?)) > > Will conform to a variant: > > => (s/conform ::temp "one-two") > [:one "one-two"] > => (s/conform ::temp 12) > [:two 12] > > And not to a tagged record like: > {:tag :one > :val "one-two"} > > But, I don't know, that seems like a different and uncommon use case, I > don't really see this as using a sum type, just describing data to be > parsed, which I guess the vector form can be easier to work with. I think > the clojurescript AST is actually using tagged records, and most people > find it too verbose because of it. > > Anyways, I'd still like to hear if there's a major benefit I'm overseeing, > or a detail I'm not understanding, if any. > > > > P.S.: I figured the difference between (s/or) and (s/multi-spec) I think. > S/or is like structural-types, my example actually doesn't work, because > :filename and :url have the same structure, both string? So conform will > not be able to tell them apart. So if you need to know any kind of semantic > difference which the shape can not infer, (s/or) won't work, you'll need > (s/multi-spec) instead, which are more like nominal-types. Multi-spec I > guess can also use any function to select the appropriate spec, so they're > more powerful, but often I think the way I'm using it will be most common. > > > On Tuesday, 22 August 2017 18:00:21 UTC-7, tbc++ wrote: >> >> I think the article is a bit misleading. Variants were never that popular >> in Clojure. Infact I've never seen them used in production code or the most >> common Clojure libraries. So I'm a bit perplexed as to why the article >> recommends them so strongly. >> >> So I think the answer is, they are a fun thought experiment in Clojure, >> but are of limited usefulness due to the tools we have available that make >> them unneeded. >> >> It's a bit like recommending that new users use actors in Clojure. Sure, >> you can shoehorn them in, but there's a reason why they aren't the default. >> -- >> “One of the main causes of the fall of the Roman Empire was that–lacking >> zero–they had no way to indicate successful termination of their C >> programs.” >> (Robert Firth) >> > -- > 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. > -- “One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.” (Robert Firth) -- 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: Sum types in Clojure? Better to represent as tagged records or asvariant vectors?
I think part of the issue is that the article dates back to mid-2015 and `clojure.spec` wasn’t a thing back then, was it? Variants feel like a solution to a problem for which we have a much better solution _today_ than we did two years ago. The article talks about using core.typed and core.match with variants – because that’s what we had then. I’m fairly sure that if Eric (and Jeanine) were writing their material today, we’d see `clojure.spec` front and center and regular hash maps being used. Sean Corfield -- (970) FOR-SEAN -- (904) 302-SEAN An Architect's View -- http://corfield.org/ "If you're not annoying somebody, you're not really alive." -- Margaret Atwood From: Timothy Baldridge Sent: Tuesday, August 22, 2017 6:00 PM To: clojure@googlegroups.com Subject: Re: Sum types in Clojure? Better to represent as tagged records or asvariant vectors? I think the article is a bit misleading. Variants were never that popular in Clojure. Infact I've never seen them used in production code or the most common Clojure libraries. So I'm a bit perplexed as to why the article recommends them so strongly. So I think the answer is, they are a fun thought experiment in Clojure, but are of limited usefulness due to the tools we have available that make them unneeded. It's a bit like recommending that new users use actors in Clojure. Sure, you can shoehorn them in, but there's a reason why they aren't the default. -- “One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.” (Robert Firth) -- 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: Sum types in Clojure? Better to represent as tagged records or asvariant vectors?
I think that's true, but even back in 2015, these were the only two sources calling for this approach, and it never really caught on. I even did some experiments with it, but my conclusion was that it was more trouble than it was worth. On Tue, Aug 22, 2017 at 7:46 PM, Sean Corfield wrote: > I think part of the issue is that the article dates back to mid-2015 and > `clojure.spec` wasn’t a thing back then, was it? > > > > Variants feel like a solution to a problem for which we have a much better > solution _*today*_ than we did two years ago. The article talks about > using core.typed and core.match with variants – because that’s what we had > then. I’m fairly sure that if Eric (and Jeanine) were writing their > material today, we’d see `clojure.spec` front and center and regular hash > maps being used. > > > > Sean Corfield -- (970) FOR-SEAN -- (904) 302-SEAN > An Architect's View -- http://corfield.org/ > > "If you're not annoying somebody, you're not really alive." > -- Margaret Atwood > > > > *From: *Timothy Baldridge > *Sent: *Tuesday, August 22, 2017 6:00 PM > *To: *clojure@googlegroups.com > *Subject: *Re: Sum types in Clojure? Better to represent as tagged > records or asvariant vectors? > > > > I think the article is a bit misleading. Variants were never that popular > in Clojure. Infact I've never seen them used in production code or the most > common Clojure libraries. So I'm a bit perplexed as to why the article > recommends them so strongly. > > > > So I think the answer is, they are a fun thought experiment in Clojure, > but are of limited usefulness due to the tools we have available that make > them unneeded. > > > > It's a bit like recommending that new users use actors in Clojure. Sure, > you can shoehorn them in, but there's a reason why they aren't the default. > > -- > > “One of the main causes of the fall of the Roman Empire was that–lacking > zero–they had no way to indicate successful termination of their C > programs.” > (Robert Firth) > > -- > 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. > -- “One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.” (Robert Firth) -- 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: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
On 23 August 2017 at 01:18, Timothy Baldridge wrote: > Great, so these approaches are suggesting we wrap every value in a vector > or a hash map (as the lisp cast article does). > What? No, that's not what I'm saying at all. If you have an unordered collection of key/value pairs where every key is unique, then *of course* you use a map. But if you have only *one* key/value pair, how do you represent that? Or if you want an arbitrarily ordered collection of key/value pairs? Or a collection with repetition? For example, a series of events that represent a player's moves in a card game: [:card/draw :9h] [:card/draw :qh] [:card/draw :4s] [:card/discard :4s] [:card/draw :7d] I've also found it a useful pattern for data access: [:data/found "Bob"] [:data/not-found] [:data/unauthorized] [:data/retry-in 600] A [k v] vector isn't the only way of representing data like this, but it is probably the most concise. -- James Reeves booleanknot.com -- 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: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
I can see it be quick and concise for representing events, but that's also exactly the use case example for multi-spec: https://clojure.org/guides/spec#_multi_spec What happens if your event needs more data? Maybe draw needs 2 attributes, the card and the deck? Now you have implicit encoding, where what the attributes are for the event are defined by its position in the vector. Where as you could have just as easily had: {:event/type :draw :card :4s :deck :player1} You can make a vector of those for the order of the events. Yes, you can also use variants, and have: [:event/draw :4s :player1] My question is, what's the pros/cons? Its more concise, but barely. Its harder for it to represent optional values, you have to allow nil: [:event/draw :4s nil]. You can mistake the order: [:event/draw :player1 :4s]. I think to represent the type of a single element, I agree with you, but if you've got a compound type, at first glance, they'd appear less practical and more error prone to me. So your second example makes more sense. On Tuesday, 22 August 2017 19:07:56 UTC-7, James Reeves wrote: > > On 23 August 2017 at 01:18, Timothy Baldridge > wrote: > >> Great, so these approaches are suggesting we wrap every value in a vector >> or a hash map (as the lisp cast article does). >> > > What? No, that's not what I'm saying at all. > > If you have an unordered collection of key/value pairs where every key is > unique, then *of course* you use a map. > > But if you have only *one* key/value pair, how do you represent that? Or > if you want an arbitrarily ordered collection of key/value pairs? Or a > collection with repetition? > > For example, a series of events that represent a player's moves in a card > game: > > [:card/draw :9h] > [:card/draw :qh] > [:card/draw :4s] > [:card/discard :4s] > [:card/draw :7d] > > I've also found it a useful pattern for data access: > > [:data/found "Bob"] > [:data/not-found] > [:data/unauthorized] > [:data/retry-in 600] > > A [k v] vector isn't the only way of representing data like this, but it > is probably the most concise. > > -- > James Reeves > booleanknot.com > -- 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: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
Put let's take that and look at it under a engineering lens a bit: >> For example, a series of events that represent a player's moves in a card game: >> >> [:card/draw :9h] You have an ad-hoc encoding of data here now. Expand it out a bit, and make it machine readable instead of preferring ease of writing and you get: {:player/action :card/draw :card/number :9 :card/suit :hearts} Much easier to introspect, and extensible as future data just flows through without existing code breaking because you add extra data. Likewise: >> I've also found it a useful pattern for data access: >> >> [:data/found "Bob"] I've worked with systems that have done this, and I really dislike it. Because now I have to zip the inputs of a function with the outputs if I want a composable system usable in pipelines and the like. What's much better: {:op/status :success :data/found "Bob" :data/key "444-434-3323" :server/ip } Now not only do I know what data I got, but where it came from, the key I originally used, etc. >> [:data/retry-in 600] If this is a web endpoint, I'd really like a URL with it: {:op/status :busy :retry/delay 600 :retry/url #url "http://"} In short variants almost never give me enough information, and when they do, it's encoded in some sort of DSL that I have to parse or enrich if I want nice machine level introspection. On Tue, Aug 22, 2017 at 8:48 PM, Didier wrote: > I can see it be quick and concise for representing events, but that's also > exactly the use case example for multi-spec: https://clojure. > org/guides/spec#_multi_spec > > What happens if your event needs more data? Maybe draw needs 2 attributes, > the card and the deck? Now you have implicit encoding, where what the > attributes are for the event are defined by its position in the vector. > > Where as you could have just as easily had: > > {:event/type :draw :card :4s :deck :player1} > > You can make a vector of those for the order of the events. > > Yes, you can also use variants, and have: > > [:event/draw :4s :player1] > > My question is, what's the pros/cons? Its more concise, but barely. Its > harder for it to represent optional values, you have to allow nil: > [:event/draw :4s nil]. You can mistake the order: [:event/draw :player1 > :4s]. > > I think to represent the type of a single element, I agree with you, but > if you've got a compound type, at first glance, they'd appear less > practical and more error prone to me. So your second example makes more > sense. > > > On Tuesday, 22 August 2017 19:07:56 UTC-7, James Reeves wrote: > >> On 23 August 2017 at 01:18, Timothy Baldridge wrote: >> >>> Great, so these approaches are suggesting we wrap every value in a >>> vector or a hash map (as the lisp cast article does). >>> >> >> What? No, that's not what I'm saying at all. >> >> If you have an unordered collection of key/value pairs where every key is >> unique, then *of course* you use a map. >> >> But if you have only *one* key/value pair, how do you represent that? Or >> if you want an arbitrarily ordered collection of key/value pairs? Or a >> collection with repetition? >> >> For example, a series of events that represent a player's moves in a card >> game: >> >> [:card/draw :9h] >> [:card/draw :qh] >> [:card/draw :4s] >> [:card/discard :4s] >> [:card/draw :7d] >> >> I've also found it a useful pattern for data access: >> >> [:data/found "Bob"] >> [:data/not-found] >> [:data/unauthorized] >> [:data/retry-in 600] >> >> A [k v] vector isn't the only way of representing data like this, but it >> is probably the most concise. >> >> -- >> James Reeves >> booleanknot.com >> > -- > 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. > -- “One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.” (Robert Firth) -- 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 beca
Re: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
On 23 August 2017 at 03:48, Didier wrote: > I can see it be quick and concise for representing events, but that's also > exactly the use case example for multi-spec: https://clojure. > org/guides/spec#_multi_spec > > What happens if your event needs more data? Maybe draw needs 2 attributes, > the card and the deck? Now you have implicit encoding, where what the > attributes are for the event are defined by its position in the vector. > Sometimes it's an advantage to have a deliberately constrained format, particularly when it comes to storage or indexing. Datomic, for instance, is effectively an indexed event log of [e a v t] tuples. If you're in a situation where the event data can and may expand, then use a map. But I'd argue that there are situations where the scope is limited, or you want a deliberate and hard constraint on what data is in an event. Another possible situation is if you're parsing map data incrementally. For example, a Ring request map could be read in as a series of key/value pairs: GET /example HTTP/1.1 Host: www.example.com [:request/method :get] [:request/uri "/example"] [:request/protocol "HTTP/1.1"] [:request/header ["host" "www.example.com"]] I'm not saying that variants should be favoured over maps in all situations; just that there are situations where you're certain that you need key/value pairings. -- James Reeves booleanknot.com -- 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: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
But Datomic has E in [e a v] which links multiple [a v] pairs into an entity...which is basically a map. So I don't think that applies here. GET /example HTTP/1.1 Host: www.example.com [:request/method :get] [:request/uri "/example"] [:request/protocol "HTTP/1.1"] [:request/header ["host" "www.example.com"]] Once again, a ad-hoc encoding. What is "GET", what is "/example". I see that datastructure and all I see are hashmaps. Do it the way ring does ;-) {:method :get :uri "..." :headers [...]} On Tue, Aug 22, 2017 at 9:14 PM, James Reeves wrote: > On 23 August 2017 at 03:48, Didier wrote: > >> I can see it be quick and concise for representing events, but that's >> also exactly the use case example for multi-spec: https://clojure.or >> g/guides/spec#_multi_spec >> >> What happens if your event needs more data? Maybe draw needs 2 >> attributes, the card and the deck? Now you have implicit encoding, where >> what the attributes are for the event are defined by its position in the >> vector. >> > > Sometimes it's an advantage to have a deliberately constrained format, > particularly when it comes to storage or indexing. Datomic, for instance, > is effectively an indexed event log of [e a v t] tuples. > > If you're in a situation where the event data can and may expand, then use > a map. But I'd argue that there are situations where the scope is limited, > or you want a deliberate and hard constraint on what data is in an event. > > Another possible situation is if you're parsing map data incrementally. > For example, a Ring request map could be read in as a series of key/value > pairs: > > GET /example HTTP/1.1 > Host: www.example.com > > [:request/method :get] > [:request/uri "/example"] > [:request/protocol "HTTP/1.1"] > [:request/header ["host" "www.example.com"]] > > I'm not saying that variants should be favoured over maps in all > situations; just that there are situations where you're certain that you > need key/value pairings. > > -- > James Reeves > booleanknot.com > > -- > 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. > -- “One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.” (Robert Firth) -- 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: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
On 23 August 2017 at 03:58, Timothy Baldridge wrote: > Put let's take that and look at it under a engineering lens a bit: > > >> For example, a series of events that represent a player's moves in a > card game: > >> > >> [:card/draw :9h] > > You have an ad-hoc encoding of data here now. Expand it out a bit, and > make it machine readable instead of preferring ease of writing and you get: > > {:player/action :card/draw > :card/number :9 > :card/suit :hearts} > > Much easier to introspect, and extensible as future data just flows > through without existing code breaking because you add extra data. > But possibly over-engineered. Suppose that the only function we want to produce is: (cards-in-hand events) => #{:9h :qh :7d} If we have a function that returns [k], [v] or {k v}, then I have a hard time buying the idea that the input to the function should be more complex than [[k v]]. If the scope of the data is not so constrained, then sure, use a map. >> I've also found it a useful pattern for data access: > >> > >> [:data/found "Bob"] > > I've worked with systems that have done this, and I really dislike it. > Because now I have to zip the inputs of a function with the outputs if I > want a composable system usable in pipelines and the like. > > What's much better: > > {:op/status :success > :data/found "Bob" > :data/key "444-434-3323" > :server/ip } > > Now not only do I know what data I got, but where it came from, the key I > originally used, etc. > Which is useful only if you plan on using that additional data. If you are only ever interested in the key and value, and the function is internal, why dress it up? -- James Reeves booleanknot.com -- 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: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
Simple: because failing to put it in a map constrains future growth. Putting things into a set restricts you to unique values. Putting values into a vector constrains you to a specific ordering. And quite frankly one little mistake like this can cause tons of refactoring down the road. Better to write it once, simply, and extensible. I can't tell you how many codebases I've worked on where they failed to allow for "extra data" in either the arguments or the return of a function or endpoint and we've had to do major refactoring once a single part of the system needs a configuration flag. But I guess I'd flip it around. Why would I ever want: [:response val] when I could have {:status :response :result val} The latter is more readable, self-documenting, and extensible. I can't think of a single design benefit the variant variant has over the map. On Tue, Aug 22, 2017 at 9:43 PM, James Reeves wrote: > On 23 August 2017 at 03:58, Timothy Baldridge > wrote: > >> Put let's take that and look at it under a engineering lens a bit: >> >> >> For example, a series of events that represent a player's moves in a >> card game: >> >> >> >> [:card/draw :9h] >> >> You have an ad-hoc encoding of data here now. Expand it out a bit, and >> make it machine readable instead of preferring ease of writing and you get: >> >> {:player/action :card/draw >> :card/number :9 >> :card/suit :hearts} >> >> Much easier to introspect, and extensible as future data just flows >> through without existing code breaking because you add extra data. >> > > But possibly over-engineered. Suppose that the only function we want to > produce is: > > (cards-in-hand events) > => #{:9h :qh :7d} > > If we have a function that returns [k], [v] or {k v}, then I have a hard > time buying the idea that the input to the function should be more complex > than [[k v]]. > > If the scope of the data is not so constrained, then sure, use a map. > > >> I've also found it a useful pattern for data access: >> >> >> >> [:data/found "Bob"] >> >> I've worked with systems that have done this, and I really dislike it. >> Because now I have to zip the inputs of a function with the outputs if I >> want a composable system usable in pipelines and the like. >> >> What's much better: >> >> {:op/status :success >> :data/found "Bob" >> :data/key "444-434-3323" >> :server/ip } >> >> Now not only do I know what data I got, but where it came from, the key I >> originally used, etc. >> > > Which is useful only if you plan on using that additional data. If you are > only ever interested in the key and value, and the function is internal, > why dress it up? > > -- > James Reeves > booleanknot.com > > -- > 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. > -- “One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.” (Robert Firth) -- 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: spec'ing relationship of values in a sequence?
Thanks. I hadn't thought of (apply > ...) - nice! On Sunday, August 20, 2017 at 5:55:01 PM UTC-7, Ghadi Shayban wrote: > > You have the right idea with s/and. I would name & defn the predicate to > something meaningful like increasing?. Defining and thus naming it is > essential so that you can get reuse, and also so that consumers of your > spec, whether it's you or someone else, don't have to interpret the code in > the brain. Btw, the impl can be simplified, but I still wouldn't inline it. > > (defn increasing? [coll] > (apply < coll)) > #'user/increasing? > user=> (increasing? [1 2 3]) > true > user=> (increasing? [1 2 2]) > false > (s/and (s/coll-of number?) increasing?) > > > On Sunday, August 20, 2017 at 7:53:48 PM UTC-4, Mark wrote: >> >> What's the 'right' way to spec relationship between values in a sequence? >> For example, the following spec defines a sequence of number in increasing >> order: >>> >>> >>> >>> (s/conform (s/and (s/coll-of number?) >>> #(->> % >>> (partition 2 1) >>> (map (fn [[a b]] (< a b))) >>> (every? true?))) >>>[1 2 3]) >>> >>> It feels like there ought to be a more elegant solution - especially as >> the relationships become more complex. Is there another approach? >> > -- 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: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
On 23 August 2017 at 04:27, Timothy Baldridge wrote: > But Datomic has E in [e a v] which links multiple [a v] pairs into an > entity...which is basically a map. So I don't think that applies here. > Except that [e a v t] facts in Datomic are ordered and not necessarily unique, and that's my part of my point. A collection/stream of variants (or tuples that contain some manner of [k v] part) are map-like but often have additional properties, such as ordering. > GET /example HTTP/1.1 > Host: www.example.com > > [:request/method :get] > [:request/uri "/example"] > [:request/protocol "HTTP/1.1"] > [:request/header ["host" "www.example.com"]] > > Once again, a ad-hoc encoding. What is "GET", what is "/example". I see > that datastructure and all I see are hashmaps. > > Do it the way ring does ;-) > > {:method :get > :uri "..." > :headers [...]} > And what happens if I want to stop processing the request the moment I hit the request method? For example: (fn [req] (go (match (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: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
On 23 August 2017 at 05:15, Timothy Baldridge wrote: > Simple: because failing to put it in a map constrains future growth. > Sometimes that's what you want. A constrained function is a simple function. Should (find {:a 1} :a) produce {:key :a, :val 1} instead of [:a 1]? No, because it doesn't need to be extensible. And for that matter, where do we stop? Should: {:person/score 89} Be: {:person/score {:val 89}} Just in case we want to extend it in future? {:person/score {:val 89, :max 100}} Any argument about extensibility around [[k v]] also applies to {k v}. But I guess I'd flip it around. Why would I ever want: > > [:response val] > > when I could have > > {:status :response > :result val} > Well, going purely by syntax, it's more concise, (IMO) more readable, easier to match and destruct, and intrinsically compatible with "into" like functions: (def latest-values (async/into {} ch)) I don't see how you can say {k v} is somehow fine, but a stream of [k v] pairs over time is somehow bad. -- James Reeves booleanknot.com -- 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.