>> https://github.com/arrdem/guten-tag
The name alone deserves a +1. :D On Mon, Aug 28, 2017 at 2:53 PM, Reid McKenzie <m...@arrdem.com> wrote: > FWIW I wrote a library around defining tagged map types > https://github.com/arrdem/guten-tag and used it heavily in the Grimoire > implementation almost entirely to my satisfaction. No idea if anyone else > ever picked it up, and due to implementation details of Clojure's let map > destructuring you can't just treat one of my map wrapper types as a normal > Clojure map and :keys it apart but it works great with core.match. > > Reid > > On Friday, August 25, 2017 at 1:30:37 PM UTC-7, 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> 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 >>> 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 >>> 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. >>> 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. > -- “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.