>>  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.

Reply via email to