Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spec allows consecutive colons in symbols and keywords #68

Open
favila opened this issue Sep 22, 2014 · 3 comments
Open

Spec allows consecutive colons in symbols and keywords #68

favila opened this issue Sep 22, 2014 · 3 comments

Comments

@favila
Copy link

favila commented Sep 22, 2014

EDN spec does not say that consecutive colons are illegal (e.g. a::b) even though clojure.reader and clojure.edn won't parse these.

(What is the reason for this restriction?)

@favila favila changed the title Spec does not disallow consecutive colons Spec allows consecutive colons in symbols and keywords Sep 22, 2014
@raszi
Copy link

raszi commented Jun 27, 2016

A sample code to reproduce the issue:

(clojure.edn/read-string (pr-str (into {} [[(keyword "a::b") "a"]])))
java.lang.RuntimeException: Invalid token: :a::b

@raszi
Copy link

raszi commented Jun 27, 2016

An even crazier example which is also somehow related:

(clojure.edn/read-string (pr-str (into {} [[(keyword "a b c") "a"]])))
{:a b, c "a"}

@xpe
Copy link

xpe commented Jul 11, 2021

In reply to the comment by @raszi ...

For those that are surprised or perhaps confused by the example from @raszi that shows how (clojure.edn/read-string (pr-str (into {} [[(keyword "a b c") "a"]]))) evaluates to {:a b, c "a"}.

Clarifying the keyword function

Let's start with some basics. First, let's review the keyword function. (doc keyword) gives:

clojure.core/keyword
([name] [ns name])
  Returns a Keyword with the given namespace and name.  Do not use :
  in the keyword strings, it will be added automatically.

I want to call attention to the part that says "a" keyword. To emphasize, only * one * keyword is returned.

Here are some correct and idiomatic uses of keyword:

  • (keyword "foo") => :foo
  • (keyword "bar" "foo") => :bar/foo

Here is an obviously incorrect use: (keyword "bar" "foo" "extra") gives

Execution error (ArityException) at user/eval163 (REPL:1).
Wrong number of args (3) passed to: clojure.core/keyword

Using keyword in a perhaps confusing way

Now, let's talk about a valid but perhaps confusing use of keyword. (I also consider the following example to be non-idiomatic or at least uncommon.) Consider (keyword "x y") which prints :x y at the REPL.

Aside: I did not say that (keyword "x y") evaluates to :x y -- that would be impossible, since y is syntactically a symbol and in this context, undefined. You can verify this by trying (resolve 'y).

To return to the previous thread, (keyword "x y") prints :x y at the REPL. To be clear, :x y is the printed representation of one keyword with the name "x y". You can easily verify this:

  1. (type (keyword "x y")) evaluates to clojure.lang.Keyword.
  2. (name (keyword "x y")) evaluates to "x y".

If you haven't seen name before, here is the documentation, available by evaluating (doc name) at the REPL:

clojure.core/name
([x])
  Returns the name String of a string, symbol or keyword.

Similarly, (keyword "x y z") evaluates to one keyword: :x y z. This keyword has the name "x y z".

Revisiting the first example

Let's take this from smaller to larger:

  1. Let's look at (a smaller piece of) the example that @raszi mentioned, [(keyword "a b c") "a"]. Based on what I showed above, we can reason through this and see that this will return a vector with two elements: (i) a keyword with the name ":a b c" and (ii) the string "a". Here is one way to demonstrate this: (clojure.string/join "<$>" [(keyword "a b c") "a"]) evaluates to ":a b c<$>a"

  2. Next, let's look at (into {} [[(keyword "a b c") "a"]]) which prints as {:a b c "a"}. This is a map with one key, :a b c and one value, "a".

  3. What happens if you type {:a b c "a"} at the REPL? You'll get (my markdown markup added): "Syntax error compiling at (REPL:0:0). / Unable to resolve symbol: b in this context". Conclusion: don't type this into the REPL! :)

  4. What should you type at the REPL instead? This: {(keyword "a b c") "a"}. You might say, "That's much less ambiguous" and you would be right! :)

  5. Next, let's look at (pr-str (into {} [[(keyword "a b c") "a"]])) which evaluates to "{:a b c \"a\"}". Based on number 3, above, if you pass this to clojure.edn/read-string, you should not expect it to evaluate to the input value!

Conclusion

Using (clojure.edn/read-string (pr-str thing)) is not guaranteed to recover the thing!

To state it in a different way, #(clojure.edn/read-string (pr-str %1)) is not the identity function. It appears to work that way in a vast majority of common cases, but it breaks down in some 'weird' ways. Caveat evaluator!

The Fun Doesn't Have to Stop here...

Read much more about this issue (and more) on this Clojure Google Group thread: How to escape a space in a keyword?.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants