3

Trying to read a hash map from string but if keys are "keyword" type values I got an error from cljs.reader/read-string. What is the correct way to read a hash-map from string?

This version without keywords work:

(cljs.reader/read-string (pr-str {1 "a", 1481876814936 "sdafa", 1481876816039 "afdas", 1481876817344 "asdfa", 2 "b"}))
=> {1 "a", 1481876814936 "sdafa", 1481876816039 "afdas", 1481876817344 "asdfa", 2 "b"}

But this version with keywords throws an error:

(cljs.reader/read-string (pr-str {:1 "a", :1481876814936 "sdafa", :1481876816039 "afdas", :1481876817344 "asdfa", :2 "b"}))
 cljs.user=> #object[TypeError TypeError: Cannot read property '0' of null]
TypeError: Cannot read property '0' of null
    at cljs$reader$read_keyword (file:///test/resources/public/js/ui-out/cljs/reader.js:681:19)
    at cljs$reader$read_delimited_list (file:///test/resources/public/js/ui-out/cljs/reader.js:397:20)
    at cljs$reader$read_map (file:///test/resources/public/js/ui-out/cljs/reader.js:466:41)
    at cljs$reader$read (file:///test/resources/public/js/ui-out/cljs/reader.js:879:34)
    at cljs$reader$read_string (file:///test/resources/public/js/ui-out/cljs/reader.js:911:25)
    at eval (eval at figwheel$client$utils$eval_helper (file:///test/resources/public/js/ui-out/figwheel/client/utils.js:143:8), <anonymous>:1:114)
    at eval (eval at figwheel$client$utils$eval_helper (file:///test/resources/public/js/ui-out/figwheel/client/utils.js:143:8), <anonymous>:9:3)
    at eval (eval at figwheel$client$utils$eval_helper (file:///test/resources/public/js/ui-out/figwheel/client/utils.js:143:8), <anonymous>:14:4)
    at figwheel$client$utils$eval_helper (file:///test/resources/public/js/ui-out/figwheel/client/utils.js:143:8)
nil

Same code works on clojure:

user=> (read-string (pr-str {:1 "a", :1481876814936 "sdafa", :1481876816039 "afdas", :1481876817344 "asdfa", :2 "b"}))
{:1 "a", :1481876814936 "sdafa", :1481876816039 "afdas", :1481876817344 "asdfa", :2 "b"}
1
  • in case those keywords are generated inbetween some API calls, just disable the keyword conversion on your end. Numbers make fine keys. and if this is the first place with "odd" behaviour, others will follow. (keyword s) accepts anything and you may end up with broken things. try (pr-str {(keyword "a :b") :c}) Commented Dec 17, 2016 at 8:11

2 Answers 2

6

cljs.reader doesn't support keywords starting with a numeric character, probably because symbols, and by extension, keywords, must start with a non-numeric character, though the formulation in the official documentation is open to multiple interpretations. See http://clojure.org/reference/reader#_symbols and http://clojure.org/reference/reader#_literals

The clojure reader (the jvm implementation) always has supported keywords like :1234 and that's probably not going to change now.

Shorter example of failure:

(require 'cljs.reader)
(cljs.reader/read-string ":1")

Addendum: it's always been possible to construct other kinds of unreadable keywords in clojure using the keyword function and problems resulting from using those keywords fall under "undefined behaviour" - in other words: you're on your own if you do things like (keyword " ")

Addendum 1: JIRA ticket about the issue in cljs.reader is at http://dev.clojure.org/jira/browse/CLJS-677

Sign up to request clarification or add additional context in comments.

4 Comments

So from what I understand clojure reader accidentally supports keywords that start with numeric characters like :1234 right?
@slhsen I think so, but numeric keywords are a bit of a special case; numeric symbols would clash with numeric literals so they must be disallowed to prevent ambiguous syntax, but numeric keywords can work (and do work in clojure/jvm). You just cannot do things like convert a numeric keywords to a symbol (and then read it)
Seems like there is a bug report for that in Clojurescript and general consensus is it should be resolved on Clojure side first :) dev.clojure.org/jira/browse/CLJS-677 @Joost Diepenmaat can you add URL to your answer?
@slhsen I added the link
4

On the Clojure side, the original intention was that keyword identifiers followed essentially the same rule as symbol identifiers (where leading numbers are not allowed).

However, the regex used to accept these was buggy (due to how it was used in tandem with symbols, then keywords and the difference in leading digits). In particular, the leading : messes it up. The fix was implemented in CLJ-1252 and committed during the work for Clojure 1.6.

We immediately learned upon releasing an early version of 1.6 that many people were actually using keywords with leading digits. In particular, I recall that java.jdbc was naming columns that way.

As there didn't seem to be any good reason to break these programs when everything was working fine, we reverted the change and essentially grandfathered the ability of keywords to accept digits. I do not think it's likely that this is ever going to change in Clojure. At this point, I think ClojureScript should probably follow suit and match Clojure.

edn is kind of a different story - it's intentionally more restrictive than Clojure/ClojureScript in a number of ways. At this point it's TBD what will be done there.

2 Comments

I can understand why EDN is more restrictive than Clojure/ClojureScript but with current state of things I can output EDN from Clojurescript via pr-str, which is how you do it in Clojurescript afaik, but can't read it back with reader/read function which is strange. Source code of pr-str doesn't claim to output EDN though so that's that. In that case may be Clojurescript should have a dedicated function that outputs only valid EDN (I can't seem to find one if there is)
Agreed and it would be nice if there was a pr variant designed to emit only edn-safe data.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.