I've used tagged unions to represent lookup identifiers for variant types 
and found them to be pleasant to work with. I think part of the reason they 
don't seem useful in the context of this discussion is that many of the 
examples given have not actually been variants. For example, in [:image/file 
"/images/image" :jpeg], every value will have those three data elements, 
and for {:status :response :result val}, every value will have both of 
those fields, and their values will be the same type. The identifiers I was 
working with were in heterogenous collections, so I might have [:image/ref 
45] or [:user/name "bob"] or [:project/element "project-slug" 435].

It would certainly be just as easy to use {:collection/type ::person 
:user/name "bob"} and {:collection/type ::project :project/slug 
"project-slug" :project/element 435}, but I like the concision of the 
tagged unions.

On Friday, August 25, 2017 at 4:30:37 PM UTC-4, tbc++ wrote:
>
> >> 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 <did...@gmail.com <javascript:>> 
> 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.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 
>> implement some I guess, like an update-variant. So given this fact,  using 
>> maps have an advantage that they're more homoiconic, you don't need to 
>> parse them, when you construct them the result is not a type constructor, 
>> but the actual datastructure you'd want to work with.
>>
>> 2) What can you use them for?
>>
>> 2.1) As pseudo type constructor they work well. For cases where the type 
>> is constructed by hand, they're a nice DSL. I find they make sense then for 
>> hiccup for example. When your types are constrcuted by the computer, I 
>> think maps are better. No need to parse them. It would be cool maybe to 
>> deftype an actual variant type. In a way, defrecords are almost that.
>>
>> 2.2) As open sum types. When something expects a variant, it means that 
>> thing can be one of any variant type. With namespaced keys, they can be 
>> restricted to a smaller open set. So :image/... variants are the set of 
>> open image variants. Something can spec that it takes a variant whose 
>> namespace is image. Then you're free to extend image variants with more of 
>> them, like image/web, image/file, etc.
>>
>> 2.3) As closed sum types. I guess you could also spec something to accept 
>> a specific set of specific variants, like either a :success or a :failure 
>> variant.
>>
>> 2.4) They could be used as product types too. Just allow the type 
>> argument to be a vector.
>>
>> [[:image/file :image/web] "/images/image" :jpeg "www.myimage.com/image"]
>>
>> This gets harder to oberload arguments though. Unless you use the named 
>> pair version.
>>
>> [[:image/file :image/web] :image.file/path "/images/image" :
>> image.file/format :jpeg :image.web/url "www.myimage.com/image"]
>>
>> 2.conclusion)
>>
>> I can see now how Jeanine was saying you can use them as the foundation 
>> for types of a programming language. Personally, I'll explore they're use 
>> when I'm coming up with DSLs, or any time I need to manually create types, 
>> I might use variants to construct them, even if what I'm constructing is a 
>> map, they're a little nicer to type and read.
>>
>> --
>> You received this message because you are subscribed to the Google
>> Groups "Clojure" group.
>> To post to this group, send email to clo...@googlegroups.com 
>> <javascript:>
>> Note that posts from new members are moderated - please be patient with 
>> your first post.
>> To unsubscribe from this group, send email to
>> clojure+u...@googlegroups.com <javascript:>
>> 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+u...@googlegroups.com <javascript:>.
>> 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.

Reply via email to