Clojurescript how to read-string map with numeric keywords? - clojure

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"}

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

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.

Related

Error while defining map in clojure

I am new clojure and am not able to understand exact issue while running this code
(def my-map {name "Srini" empid "10000"})
Receving this error:
CompilerException java.lang.RuntimeException: Unable to resolve symbol: empid in this context, compiling:(/Users/srini/Downloads/clojureexample/my-stugg/src/my_stugg/corecomponents.clj:95:1)
(def my-map {name "Srini" :empid "10000"})
Running successfully.
What wrong I am doing in the first line?
(def my-map {name "Srini" empid "10000"})
Clojure is trying to resolve empid and can't, so it returns an error.
Clojure allows you to define maps with most anything as a key. This includes keywords and strings, which are very common, but also symbols, functions, numbers, data structures such as vectors and other maps, and more.
Your example contains one valid mapping, that is, the function bound to name which maps to the string "Srini". However, the other mapping is invalid because empid is not bound to anything.
The most common case is to use keywords for your keys, which have a particular advantage of allowing the values to be accessed via the keywords in a safe (non-NullPointerException-causing) way:
(:name {:name "Srini" :empid 10000})
=> "Srini"
So, while there may be a case where you'd want to map a function to something, this is clearly not the right way for you here. (as an aside, strings as keywords are particularly useful when reading from a file or database where the data is already a string and there's no advantage to converting to and from a keyword.
You probably want to change your keys to keywords. In Clojure keywords are written with a colon in front of a string, but you don't need the quotes (as long as your keyword has no spaces in it, but let's not get complicated).
I recommend starting simply:
(def my-map {:name "george"})
This will create a map with a single key and value in it.
If you want to use let, do it like so:
(let [my-map {:name "george"}] my-map)
Update: Hm you seemed to have changed your question a fair bit! I still really recommend starting simply.
name is already defined as a function in clojure (it gets the string version of a symbol or keyword), so you shoudln't use that. Use :name if you want to create a key, unless you want to shadow the name "name" in a let binding, but that's nothing to do with keys in maps.
I feel like perhaps you need to learn the basics of maps and let before you go crazy with examples. Stick to simple things, and make sure your keys are not symbols.

Namespace qualified record field accessors

I've made the same dumb mistake many many times:
(defrecord Record [field-name])
(let [field (:feld-name (->Record 1))] ; Whoops!
(+ 1 field))
Since I misspelled the field name keyword, this will cause a NPE.
The "obvious" solution to this would be to have defrecord emit namespaced keywords instead, since then, especially when working in a different file, the IDE will be able to immediately show what keywords are available as soon as I type ::n/.
I could probably with some creativity create a macro that wraps defrecord that creates the keywords for me, but this seems like overkill.
Is there a way to have defrecord emit namespaced field accessors, or is there any other good way to avoid this problem?
Because defrecords compile to java classes and fields on a java class don't have a concept of namespaces, I don't think there's a good way to have defrecord emit namespaced keywords.
One alternative, if the code is not performance sensitive and doesn't need to implement any protocols and similar, is to just use maps.
Another is, like Alan Thompson's solution, to make a safe-get funtion. The prismatic/plumbing util library also has an implementation of this.
(defn safe-get [m k]
(let [ret (get m k ::not-found)]
(if (= ::not-found ret)
(throw (ex-info "Key not found: " {:map m, :key k}))
ret)))
(defrecord x [foo])
(safe-get (->x 1) :foo) ;=> 1
(safe-get (->x 1) :fo) ;=>
;; 1. Unhandled clojure.lang.ExceptionInfo
;; Key not found:
;; {:map {:foo 1}, :key :fo}
I feel your pain. Thankfully I have a solution that saves me many times/week that I've been using a couple of years. It is the grab function from the Tupelo library. It does not provide the type of IDE integration you are hoping for, but it does provide fail-fast typo-detection, so you always be notified the very first time you try to use the non-existant key. Another benefit is that you'll get a stacktrace showing the line number with the misspelled keyword, not the line number (possibly far, far away) where the nil value causes a NPE.
It also works equally well for both records & plain-old maps (my usual use-case).
From the README:
Map Value Lookup
Maps are convenient, especially when keywords are used as functions to look up a value in a map. Unfortunately, attempting to look up a non-existent keyword in a map will return nil. While sometimes convenient, this means that a simple typo in the keyword name will silently return corrupted data (i.e. nil) instead of the desired value.
Instead, use the function grab for keyword/map lookup:
(grab k m)
"A fail-fast version of keyword/map lookup. When invoked as (grab :the-key the-map),
returns the value associated with :the-key as for (clojure.core/get the-map :the-key).
Throws an Exception if :the-key is not present in the-map."
(def sidekicks {:batman "robin" :clark "lois"})
(grab :batman sidekicks)
;=> "robin"
(grab :spiderman m)
;=> IllegalArgumentException Key not present in map:
map : {:batman "robin", :clark "lois"}
keys: [:spiderman]
The function grab should also be used in place of clojure.core/get. Simply reverse the order of arguments to match the "keyword-first, map-second" convention.
For looking up values in nested maps, the function fetch-in replaces clojure.core/get-in:
(fetch-in m ks)
"A fail-fast version of clojure.core/get-in. When invoked as (fetch-in the-map keys-vec),
returns the value associated with keys-vec as for (clojure.core/get-in the-map keys-vec).
Throws an Exception if the path keys-vec is not present in the-map."
(def my-map {:a 1
:b {:c 3}})
(fetch-in my-map [:b :c])
3
(fetch-in my-map [:b :z])
;=> IllegalArgumentException Key seq not present in map:
;=> map : {:b {:c 3}, :a 1}
;=> keys: [:b :z]
Your other option, using records, is to use the Java-interop style of accessor:
(.field-name myrec)
Since Clojure defrecord compiles into a simple Java class, your IDE may be able to recognize these names more easily. YMMV

Clojure, why both key and values are using ":"?

(defn explain-defcon-level [exercise-term]
(case exercise-term
:fade-out :you-and-what-army
:double-take :call-me-when-its-important
:round-house :o-rly
:fast-pace :thats-pretty-bad
:cocked-pistol :sirens
:say-what?))
If I understand correctly, usually key has colon and value does not.
What is the purpose here?
Thanks.
Words starting with : are keywords. Keywords act as known values like enums in some languages. You could also use strings as you might do in Python or JavaScript, but keywords have some nice features.
In this case, if the function receives e.g. the known keyword :round-house, it will return the known value :o-rly. Some other code in turn knows what :o-rly means. You might as well use strings, if the code calling explain-defcon-level would expect it to return strings.
The keywords:
Can look themselves up in a map (def m {:abba 1 :beef 2}) .. (:abba m) => 1
Are easy to stringify (name :foo) => "foo"
Are easy to make from strings (keyword "bar") => :bar
Are performant: fast to compare and don't waste memory
: is just a reader form for creating keywords.
Keywords in clojure are data structures, just like symbols, strings or numbers.
Keywords are used as keys in hashmaps because they implement IFn for invoke(), allowing you to write things like (:mykey my-map) instead of (my-map :mykey) or (get my-map :mykey).
Generally, you could use any data structure as a hashmap key:
(def my-map
{ {:foo :bar} 1
{:bar :baz} 2 })
(my-map {:foo :bar}) ; 1
You can think of clojure code as consisting of a lot of symbols in s-expressions. When the reader parses these expressions, it has to resolve the symbol to a value. Some of these symbols are self evaluating i.e they will evaluate to themselves. for example, the symbol "Hello world" is a string symbol, which evaluates to itself. the number 123 is also self-evaluating and will evaluate to the number 123.
Other symbols need to be bound to a value. If you just had the symbol fred it needs to be bound to some value i.e. (def fred "Hello world"). If the symbol is the first symbol in a list (s-expression) it must evaluate to a function i.e. (def fred (fn ....) or the shorthand (defn fred [] ....). Note, this is simplifying things a little as you also have macros, but they can be ignored for now - they are special, in fact, often referred to as special forms.
Apart from strings and numbers, there is another very useful self-evaluating symbol, the keyword. It is distinguished by the leading ':'. Keywords evaluate to themselves i.e. :fred evaluates to :fred.
Keywords also have some very nice properties, such as fast comparison and efficient use of space. They are also useful when you want a symbol that represents something, but don't want to have to define (bind) it to something before you use it. In hash maps, keywords are also a function of the hash map, so you can do things like (:mykey maymap) instead of (get mymay :mykey).
In general, every symbol must evaluate to a value and the first symbol in a non-quoted list must evaluate to a function. You can quote lists and symbols, which essentially says "don't evaluate me at this point".
With that in mind, you can use any of these symbols as a value in a clojure data structure i.e. you can have vectors of functions, keywords, strings, numbers etc.
In the example you provide, you want your case statement to return some sort of symbol which you can then presumably use to make some decision later in your program. You could define the return value as a string, such as "You and whose army" and then later compare it to some other string to make a decision. However, You could even make things a bit more robust by defining a binding like
(def a "You and whose army")
and then do things like
(= a return-val)
but it isn't really buying you anything. It requires more typing, quoting, memory and is slower for comparison.
Keywords are often really useful when you are just playing around at the repl and you want to just test some ideas. Instead of having to write something like ["a" "b" "cc"], you can just write [:a :b :c].

How to rename an operation in Clojure?

In my list, addition, the operation + appears as #. How can I make this appear exactly as +? When I eval it, it should also work exactly the same as +.
I guess this would also apply in all kinds of functions in Clojure...
Thanks guys.
The # character is simply not a valid character in symbol names in Clojure (see this page for a list of valid characters) and while it might work sometimes (as it often will), it is not a good practice to use it. Also, it will definitely not work at the beginning of a symbol (actually a literal, you could still do (symbol "#"), though there's probably no point in that). As the Clojure reader currently stands, there's nothing to be done about it (except possibly hacking the reader open to have it treat # (that's '#' followed by a space) as the symbol # -- or simply + -- though that's something you really shouldn't do, so I almost feel guilty for providing a link to instructions on how to do it).
Should you want to alias a name to some other name which is legal in Clojure, you may find it convenient to use the clojure.contrib.def/defalias macro instead of plain def; this has the added benefit of setting metadata for you (and should handle macros, though it appears to have a bug which prevents that at this time, at least in 1.2 HEAD).
And in case you'd like to redefine some built-in names when creating your aliases... (If you don't, the rest of this may not be relevant to you.)
Firstly, if you work with Clojure 1.1 or earlier and you want to provide your own binding for a name from clojure.core, you'll need to use :refer-clojure when defining your namespace. E.g. if you want to provide your own +:
(ns foo.bar
(:refer-clojure :exclude [+]))
;; you can now define your own +
(defn + [x y]
(if (zero? y)
x
(recur (inc x) (dec y))))
;; examples
(+ 3 5)
; => 8
(+ 3 -1)
; => infinite loop
(clojure.core/+ 3 -1)
; => 2
The need for this results from Clojure 1.1 prohibiting rebinding of names which refer to Vars in other namespaces; ns-unmap provides a way around it appropriate for REPL use, whereas (:refer-clojure :exclude ...), (:use :exclude ...) and (:use :only ...) provide the means systematically to prevent unwanted names from being imported from other namespaces in the first place.
In current 1.2 snapshots there's a "last-var-in wins" policy, so you could do without the :refer-clojure; it still generates a compiler warning, though, and it's better style to use :refer-clojure, plus there's no guarantee that this policy will survive in the actual 1.2 release.
An operation is just a piece of code, assigned to a variable. If you want to rebind an operation, you just rebind that variable:
(def - +)
(- 1 2 3)
# => 6
The only problem here, is that the # character is special in Clojure. I'm not sure whether you can use # as a variable name at all, at the very least you will need to quote it when binding it and probably also when calling it:
(def # +)
# => java.lang.Exception: No dispatch macro for:
Unfortunately, I'm not familiar enough with Clojure to know how to quote it.

safely parsing maps in clojure

I'm looking for an easy and safe way to parse a map, and only a map, from a string supplied by an untrusted source. The map contains keywords and numbers. What are the security concerns of using read to do this?
read is by default totally unsafe, it allows arbitrary code execution. Try (read-string "#=(println \"hello\")") as an example.
You can make it safer by binding *read-eval* to false. This will cause an exception to be triggered if there #= notation is used. For example:
(binding [*read-eval* false] (read-string "#=(println \"hello\")"))
Finally, depending on how you are using it there is a potential denial of service attack by supplying a large number of keywords (:foo, :bar). Keywords are interned and never freed so if enough are used the process will run out of memory. There's some discussion about that on the clojure-dev list.
If you want to be safe I think you basically need to parse it by hand without doing an eval. Here is an example of one way to do it:
(apply hash-map
(map #(%1 %2)
(cycle [#(keyword (apply str (drop 1 %)))
#(Integer/parseInt %)])
(string/split ":a 23 :b 32 :c 32" #" ")))
Depending on the types of numbers you want to support and how much error checking you want to do you will want to modify the two functions that are being cycled over to process every map every other value to a keyword or number.