Keys in EDN maps: keywords or symbols

357 views
Skip to first unread message

Anton Vodonosov

unread,
Oct 24, 2019, 12:32:22 AM10/24/19
to Clojure
I'm working on a config file format in EDN, and in doubt: should I use keywords or symbols as map keys.

After all, the colon is an extra keyboard press - an extra effort for users.

As EDN parser doesn't evaluate expressions, there is no need to quote symbols when using them as EDN map keys (unlike clojure map literals)

I mean, 

(edn/read-string "{a 1 b 2}")

works, while using a clojure map literal would require quoting A and B:

{'a 1 'b 2}

So, is it a good idea to use symbols instead of keywords as map keys in a config file?

Probably, an advantage of keywords that they look different visually, so a reader easily distinguishes keys from values; But proper indentation, optional commas between the key-value pairs in map may be enough for a reader to understand everything immediately.
 

Jason Felice

unread,
Oct 24, 2019, 9:36:48 AM10/24/19
to clo...@googlegroups.com
My personal opinion is to prefer keywords, and prefer less preprocessing of the configuration before the program uses it.

If it gets to a place where the configuration changes a lot, and a "natural" (read: clojure-like) expression of the configuration in EDN has either a lot of redundancy, or bits that must agree with each other, then I'd look to create a sublanguage for the config that makes it harder to specify incorrect things, and that's when I might use symbols.  That depends on how the language looks, mostly.

That said, I have a place in my config where I use symbols as map keys because it is specifying GraphQL attribute names, and other non-Clojure developers edit it.


James Reeves

unread,
Oct 24, 2019, 9:46:34 AM10/24/19
to clo...@googlegroups.com
In edn, a keyword is an identifier that designates itself, while a symbol is a keyword that designates something else.

This means keys in a map should generally be keywords, unless they identify something beyond themselves. For example:

    {:profiling/function-count {clojure.core/vec 10}}

--
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/clojure/b4c1d021-a624-482b-9e75-fa5c08057a42%40googlegroups.com.


--
James Reeves

Anton Vodonosov

unread,
Oct 24, 2019, 11:48:03 AM10/24/19
to Clojure
Regarding the idea that a keyword is an identifier that designates itself, while a symbol is a keyword that designates something else.

Keys in config file map do not designate themselves, they designate the corresponding map values.
   
    {username "vasya" email "a@b.c"} 

Here the EMAIL symbol designagtes "a@b.c". Moreover, one of the config file sections allows the user to give arbitrary names to regula expressions. In this config section the user chooses the symbols, the symbols are not predefined in the config file format.

    {3digits "\\d{3}" 2digits "\\d{2}"}

in another config section user can refer the regex by the symbols he defined that way. So the map keys in user's definitions allow hig to give names to values; here the names do not designate themselves, they are names for the regular expressions.

Thinking that way, a keywords can be used as vues. For example, some DIRECTION attribute can have one of 4 values: :up, :down, :left, :right. So map key will be a symbol and value will be a keyword:  

    {speed 100 direction :up}

Note, this config file is unlikely to be used with Clojure. For users, that's just a text document they author in an editor. The system which parses and is configured by this file is written in Java.


David Chelimsky

unread,
Oct 24, 2019, 5:26:04 PM10/24/19
to Clojure
I think James talking about resolution of values when he said "designates", not "the value bound to a key in the map." In other words, the keyword :email resolves to the keyword :email, whereas the symbol clojure.core/vec resolves to a function.

Keywords-as-keys give you some benefits when you're in clojure (you can use them as functions of maps e.g. (:email {:email "a...@b.com"}) returns "a...@b.com"), which is part of why they are somewhat ubiquitous in Clojure code. In your case, since the system is not written in Clojure, perhaps it doesn't matter.

As for your example, I see what you mean re:  {3digits "\\d{3}" 2digits "\\d{2}"}, where 3digits is intended to be a symbol that always means #"\\d{3}". In a Clojure namespace we'd just (def 3digits #"\\d{3}") and use 3digits everywhere else. I think keywords, however, would make more sense for {:username "vasya" :email "a@b.c"}, because the values bound to username and email in that map limited to that map.

Hope that all helps.

James Reeves

unread,
Oct 24, 2019, 5:28:18 PM10/24/19
to clo...@googlegroups.com
On Thu, 24 Oct 2019 at 16:48, Anton Vodonosov <avodo...@gmail.com> wrote:
Regarding the idea that a keyword is an identifier that designates itself, while a symbol is a keyword that designates something else.

Keys in config file map do not designate themselves, they designate the corresponding map values.
   
    {username "vasya" email "a@b.c"} 

Sure, but if I'm reading the edn spec correctly, the intention is that symbols in isolation should identify something. So if I just wrote:

    username

What does that identity? If it has no identity beyond itself, then it should be a keyword. Contrast that to a symbol like:

    clojure.core/conj

We know that symbol identifies a clojure function. Even outside of a map, it has an external identity.

--
James Reeves

Anton Vodonosov

unread,
Oct 25, 2019, 10:29:51 AM10/25/19
to Clojure
Thank you for responses.

The last two arguments do not resolve the doubt, however:

1. symbols also implement IFn:

    (require 'clojure.edn)
    ('a (clojure.edn/read-string "{a 1 b 2}"))
    => 1
 
2. EDN does not specify any evaluation model, so how can a symbol designate something "in isolation"?
    For example, clojure.core/conj in the context of EDN does not designate a function - EDN is not Clojure.

    One way I can imagine symbols to designate something is by agreement of particular EDN config file format.
    Maybe a user can specify {:angle PI}, and this specific config file format declares that the PI symbol
    can be used to refer the 3.14... constant.

    Still, I'm not sure such interpretation implies symbols in map keys should be resolved by the config file parser
    to some values....    




James Reeves

unread,
Oct 25, 2019, 1:34:37 PM10/25/19
to clo...@googlegroups.com
As you point out, symbols in edn have no inherent meaning; it's entirely up to how you interpret them. However, if you saw the symbol "clojure.core/conj" in an edn file, then the chances are that it's intended to refer to a Clojure function. Similarly, the symbol "PI" likely refers to the mathematical constant.

A good litmus test is to replace the symbol with the thing it identifies, and then see if the data still makes sense. So if we take your example:

    {:angle PI}

We can substitute the symbol for the data it identifies:

    {:angle 3.141592653589793}

And the edn file still makes sense. It's lost no significant semantic meaning. However, if we return to your earlier example:

    {username "vasya"}

What external identity does the symbol "username" have? If we say that it's "vasya", then the substitution test fails:

    {"vasya" "vasya"}

Since "username" has no meaning beyond itself, it should ideally be a keyword:

    {:username "vasya"}


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


--
James Reeves
Reply all
Reply to author
Forward
0 new messages