Re: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
Thanks for everyone's great input. Currently, I see the big distinction being concise vs extension. Maybe for streaming variants would be better as the format would be smaller to transfer over a wire. And for small short lived programs, or things you know won't need extension, variants offer a slightly more convenient structure to work with. I think both can be specced easily. S/or a few s/tuple to spec a variant. And multi-spec over a set for the map version. I'd like to explore then the issue of extensibility with variants. How would you extend them, is there really no way? This is some of my brainstorming thoughts. 1) How to design a variant of more then one value? 1.1) I know this is a standard variant of one value: [:image/web "www.myimage.com/image.jpg"] I believe to extend it to more values, you would do: [:image/file "/images/image" :jpeg] That is, you'd threat it as a constructor function, which creates an :image/file type and takes an ordered list of arguments. This way, each variant type can even be overloaded on their arguments the way you'd overload a function. [:image/file "/images/image"] Can default to format :png for example, when using the one arg constructor. 1.2) An alternative is to keep variants as vector pairs, and nest them. [:image/file [:image/file-path "/images/image"] [:image/file-format:jpeg]] In this form, variants are more like their map counterpart. Each element is named and itself a variant. 1.3) You could also decide to limit variants to strict pairs, so the second element of any variant is either a variant or a vector of variants. [:image/file [[:image/file-path "/images/image"] [:image/file-format:jpeg]]] Now with both these forms, 1.2 and 1.3, if you threat them again as constructor functions, you now have a form of named parameter on your constructor, allowing mixed order. 1.4) At this point, the variant has become pretty similar to a map, losing in verbosity over it even. There's just one advantage, the type is not a key/value pair, which I find is more intuitive to use, no need to know the special name of key that holds the type. 1.5) Let's try to make it concise again. [:image/file {:image/file-path "/images/image" :image/format :jpeg}] This hybrid avoids needing a type key, while having named parameters, its the best of both worlds, but it mixes vectors and maps. 1.6) Here it is with the lispcast suggestion: {:image/file {:image/file-path "/images/image" :image/format :jpeg}} What I don't like about this, is how do you group variants together? Do you add more to this map? Do you keep each variant a map of one key and group them on a vector? It does solve the problem of wanting to pass a vector to your variant though, as the lispcast blog talks about. 1.7) So I'm left with this form, which Clojure macros and options often use: [:image/file :image/file-path "/images/image" :image/format :jpeg] This is harder to spec I think, but you could write a predicate that did, there's just not an existing one that can do it I think. Now a variant is a tuple with first element being the type, and an alternating pair of key/values. This is extensible like map, yet more concise. It isn't ambiguous to pass in a vector either, and lets you have names while not enforcing order. Now what if I'd want the option to create my variant with named parameters or not? Some languages allow for powerful constructors like that. 1.8) To do that, you need a way to distinguish if the rest of the vector is an alternating of named pairs, or an ordered list of arguments. I'm stuck here, I'm not sure its possible without restricting the typed a variant can take. If you group the rest in a vector or a map to indicate named pairs, then you can no longer pass a vector or map argument to a variant, since they'll be interpreted as a named pair list. You could use meta maybe, or a reader tag? Not sure I like those ideas though. 1.conclusion) I like 1.1 and 1.7 the best. I find 1.7 might actually be a better alternative to using maps. Its more intuitive, looks like a type constructor, but just like maps it allows arbitrary order and has names for readability while being more concise. Best of both worlds. Its not ambiguous either, you can easily pass in vector arguments. 1.1 is also great, if you don't mind losing named parameters and having implicit ordering. Its also non ambiguous, very concise and allows overloading. Now, that's when you use them as type constructors. But the "type" you construct from them, after parsing the variant might be best represented as a Clojure map or record. It would be annoying to use a variant like that as an actual datastructure to perform logic on. If you need to get the :image/format value in a lot of places, you probably don't want to be passing around the variant and perform linear search lookup for it, and you can't use any of Clojure's function to modify the variant. You could i
Re: Sum types in Clojure? Better to represent as tagged records or as variant vectors?
>> they're a little nicer to type and read And that's where I have to disagree. The problem with most of these options is they complect ordering with meaning. [:image/file "/images/image" :jpeg] Here I have no idea what these values mean. I have to have out-of-band information about what offset in the vector corresponds to what value. Functions have this same problem, look no further than `cons` vs `conj` to see potential confusion on argument ordering. So why don't we only use maps for function arguments? Well mostly to make the functions easier to manipulate by humans. But some of the best libraries for web APIs (AWS) that I've used have used almost an exclusively map based interface. Once again I have to repeat my mantra about DSLs. Don't start with DSLs. Start with maps. Build everything off of maps. They're extensible, easy to introspect, and can drive a host of metaprogramming algorithms. If maps are to hard to understand, build constructor functions on top of them. Then finally build a DSL on top, if you need it. Frankly, I have so many things I have to remember during programming. I'd much rather see a very uniform map-based interface. Than any sort of nested vectors, tagged values, or anything else. Surely we can't say that this: >> [[:image/file :image/web] :image.file/path "/images/image" :image.file/format :jpeg :image.web/url "www.myimage.com/image"] Is a better interface than: {:image.file/path "/images/image" :image.file/format :jpeg :image.web/url "www.myimage.com/image"} And as I said before, spec is designed from the start to support data in this format. Stating "this is a file", "this is a web image". Is just book keeping that doesn't need to be done. Is a map a :image/web? Well check its members and see if they match the spec. If they match, then you have a :image/web. No need for sum types, tags, wrapping values in vectors. Simply state what a thing should have for it to conform to an interface, and forget whatever else might be in there. It couldn't be simpler. On Fri, Aug 25, 2017 at 11:59 AM, Didier wrote: > Thanks for everyone's great input. > > Currently, I see the big distinction being concise vs extension. Maybe for > streaming variants would be better as the format would be smaller to > transfer over a wire. And for small short lived programs, or things you > know won't need extension, variants offer a slightly more convenient > structure to work with. > > I think both can be specced easily. S/or a few s/tuple to spec a variant. > And multi-spec over a set for the map version. > > I'd like to explore then the issue of extensibility with variants. How > would you extend them, is there really no way? This is some of my > brainstorming thoughts. > > 1) How to design a variant of more then one value? > > > 1.1) > > I know this is a standard variant of one value: > > [:image/web "www.myimage.com/image.jpg"] > > I believe to extend it to more values, you would do: > > [:image/file "/images/image" :jpeg] > > That is, you'd threat it as a constructor function, which creates an > :image/file type and takes an ordered list of arguments. This way, each > variant type can even be overloaded on their arguments the way you'd > overload a function. > > [:image/file "/images/image"] > > Can default to format :png for example, when using the one arg constructor. > > 1.2) > > An alternative is to keep variants as vector pairs, and nest them. > > [:image/file [:image/file-path "/images/image"] [:image/file-format:jpeg]] > > In this form, variants are more like their map counterpart. Each element > is named and itself a variant. > > 1.3) > > You could also decide to limit variants to strict pairs, so the second > element of any variant is either a variant or a vector of variants. > > [:image/file [[:image/file-path "/images/image"] > [:image/file-format:jpeg]]] > > Now with both these forms, 1.2 and 1.3, if you threat them again as > constructor functions, you now have a form of named parameter on your > constructor, allowing mixed order. > > 1.4) > > At this point, the variant has become pretty similar to a map, losing in > verbosity over it even. There's just one advantage, the type is not a > key/value pair, which I find is more intuitive to use, no need to know the > special name of key that holds the type. > > 1.5) > > Let's try to make it concise again. > > [:image/file {:image/file-path "/images/image" :image/format :jpeg}] > > This hybrid avoids needing a type key, while having named parameters, its > the best of both worlds, but it mixes vectors and maps. > > 1.6) > > Here it is with the lispcast suggestion: > > {:image/file {:image/file-path "/images/image" :image/format :jpeg}} > > What I don't like about this, is how do you group variants together? Do > you add more to this map? Do you keep each variant a map of one key and > group them on a vector? > > It does solve the problem of wanting to pass a vector to your variant > though, as the lispcast blog talks about. > > 1
Clojure(script) Contractors in Vancouver?
Hello all, I'm wondering how many members of this group are interested in doing Clojure(script) based contract work? We're in Vancouver, BC, and would love to meet with anyone in the area looking for gigs and/or fulltime work. Please email me or phone me if interested. Apologies if this violates the group etiquette, just figured it would be a good place to get the word out. -- Regards, Scott Klarenbach Invisible Robot Technologies invisiblerobot.io 604-537-1856 scott@i nvisiblerobot.io Suite R - 2404 Guelph Street Vancouver, BC, V5T 3P3 ___ To iterate is human; to recur, divine *The information contained in this message is intended only for the use of the individual or entity to which it is addressed. It may contain information that is privileged, confidential and protected from disclosure. If you have received this communication in error, please notify us immediately by replying to this message and deleting it from your computer. * -- 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.