Error while defining map in clojure - 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.

Related

Is there a canonical way to name variables that would otherwise cause name collisions?

Say I'd like to name a variable seq, referring to some kind of sequence.
But seq is already a function in clojure.core, so if I try to name my variable seq, the existing meaning of seq will be overwritten.
Is there a canonical way in Clojure to name a variable that would otherwise have a name collision with a default variable?
(e.g., in this case, my-seq could be used, but I don't know whether that would be standard as far as style goes)
There is no "standard" way of naming things (see the quote and the related joke).
If it is a function of only one thing, I often just name it arg. Sometimes, people use abbreviations like x for a single thing and xs for a sequence, list, or vector of things.
For small code fragments, abbreviating to the first letter of the "long" name is often sufficient. For example, when looping over a map, each MapEntry is often accessed as:
(for [[k v] some-map] ; destructure key/val into k & v
...)
Other times, you may prefix it with a letter like aseq or the-seq.
Another trick I often use is to add a descriptive suffix like
name-in
name-full
name-first
(yes, there is a Clojure function name).
Note that if you did name it seq, you would create a local variable that shadowed the clojure.core/seq function (it would not be "overwritten"). I often just "let it slide" if the scope of the shadowing is limited and the name in question is clear & appropriate (key and val are often victims of this practice). For name, I would also probably just ignore the shadowing of clojure.core/name, since I rarely use that function.
Note that you can shadow your own local variables. This is often handy to coerce data in to a specific format:
(defn foo
[items]
; assume we need a sorted vector with no duplicates
(let [items (vec (sort (set (items))))]
...))
By shadowing the original items argument, we ensure the data is in the desired form without needing to come up with two good, descriptive names. When this technique doesn't quite fit, I often fall back to the suffix trick and just name them items-in and items or similar.
Sometimes a suffix indicating type is valuable, when multiple representations are required. For example:
items
items-set
items-vec
type-str
type-kw
type-sym
There are many other possibilities. The main point is to make it clear to the reader what is happening, and to avoid creating booby traps for the unaware.
When in doubt, add a few more letters so it is obvious to a new reader what is happening.
You won't override clojure.core/seq. You will be simply shadowing the var seq with your local bindings or vars. One can always use fully qualified name to use core seq.
Example:
;; shadow core seq
(def seq [1 2 3])
WARNING: seq already refers to: #'clojure.core/seq in namespace: user, being replaced by: #'user/seq
=> #'user/seq
;; local binding
(defn print-something [seq]
(prn seq)
(prn (var seq)))
=> #'user/print-something
;; use fully qualified name
(clojure.core/seq "abc")
=> (\a \b \c)
(print-something "a")
"a"
#'user/seq
=> nil
(prn seq)
[1 2 3]
=> nil
(var seq)
=> #'user/seq
But, its not a clean practice to shadow clojure.core vars as it might lead to buggy code. It does more harm than good if any. I usually name vars based on code context, like employee-id-seq, url-seq etc. Sometimes, it okay to use short names like x or s if usage scope is limited. You can also see clojure.core implementation to find more examples.
A good guide: https://github.com/bbatsov/clojure-style-guide#idiomatic-names
I also recommend clj-kondo plugin

keys*/keys with inlined value specs

I want to write a spec with keys/keys* but being able to inline the value specs, which is not supported by design, and I get the reasoning behind it. However sometimes you do want (or simply have, by legacy or 3rd-party) coupling between keys and values when there is specific context to the map.
I'm still new to spec and this is just my first time integrating it with an existing project and it constantly gives me issues because it is assuming way too much, especially because of the reason mentioned above. E.g. imagine a map that describes a time period and has an until key for a date, and in the same ns there's a map for list processing and there's an until that takes a predicate function. I now need to mess with manually writing fully namespaced keys for namespaces that don't even exist (aliasing is cute but it would have to be duplicated constantly across multiple namespaces/files). Aside from being irritating I feel like it's also error-prone.
And another place where keys/keys* assumes too much is if I even want keywords as my keys. I'm writing a DSL for non-programmers but technical users right now, and bottom line is that I want to spec a map with symbols as keys. That doesn't seem to be supported whatsoever.
Is there something I'm not getting? or is spec really missing essential functionality?
You can spec a map with symbols as keys either with map-of:
(s/def ::sm (s/map-of symbol? any?))
or by spec'ing the map as a collection of entries:
(s/def ::sm (s/every (s/tuple symbol? any?) :kind map? :into {}))
The latter is particularly interesting as instead of a single tuple you can s/or many different kinds of tuples to describe more interesting maps. You can even connect those symbols to other existing specs in this manner:
(s/def ::attr1 int?)
(s/def ::attr2 boolean?)
(s/def ::sm (s/every (s/or :foo (s/tuple #{'foo} ::attr1)
:bar (s/tuple #{'bar} ::attr2))
:kind map? :into {}))
(s/valid? ::sm {'foo 10 'bar true}) ;; => true
I now need to mess with manually writing fully namespaced keys for
namespaces that don't even exist
I've been using this approach as well, and I think I actually like it more than I like making sure that your keywords' namespaces always correspond to real Clojure NS forms. I use keywords like :business-domain-concept/a-name rather than :my-project.util.lists/a-name.
You can make keywords with arbitrary namespaces that don't map to any Clojure NS. For instance, in your until situation, you could define a :date/until spec that describes dates, and a (perhaps there's a better name for this one) :list/until spec that describes your list processing map's field.
It sounds like you're already aware of this arbitrary-keyword-namespace approach - in particular, I buy that it feels error-prone, since you're typing this stuff out by hand and spec doesn't appear to choke if you feed your s/keys a :fate/until by accident. FWIW, though, I think you're currently feeling the pain that namespaced keywords are specifically intended to solve: you're in one Clojure file, you've got two maps with keys named until, and they mean two completely different things.
I'm writing a DSL for non-programmers but technical users right now,
and bottom line is that I want to spec a map with symbols as keys.
I think that map-of is what you want here:
user=> (s/def ::valid-symbols #{'foo 'bar 'baz})
:user/valid-symbols
user=> (s/def ::symbol-map (s/map-of ::valid-symbols int?))
:user/symbol-map
user=> (s/valid? ::symbol-map {'foo 1 'bar 3})
true
user=> (s/valid? ::symbol-map {'foo 1 'quux 3})
false

This map destructuring isn't working quite how I'd expect. What am I doing wrong?

As an example that, hopefully, states things far better than I could in words:
(let [{:keys [a b c] :or {a 1 b 2 c 3} :as m} {}]
(println a b c) ; => works as expected, output is: 1 2 3
(println m) ; => this doesn't work, output is: {}
)
I expected the output of the second println to be the map containing the default values as though shoved in there by merge (that is {:a 1 :b 2 :c 3}).
Instead it looks like vars are being conjured and or'd after m is bound. Why does :as not get affected by :or like :keys does?
What's wrong with my mental model? How should I be looking at this?
EDIT:
I figured out how it works as I thought I'd shown above (although thanks for the links nonetheless). I've also since read through the source of clojure.core/destructure and now know exactly what it is doing. My question really is 'Why?'
In Clojure there always seems to be a reason things work the way they do. What are they here?
I apologize that the question came across as 'how does destructuring work with :as and :or'.
I'm not Rich, so obviously I didn't choose how this works, but I can think of a couple reasons the current behavior is better than the behavior you expected.
It's faster. A lot of Clojure's low-level core features get used all the time in your program, and they are optimized more for speed than elegance, in order to get acceptable performance. Of course if this were a matter of correctness it'd be a different story, but here there are two reasonable-sounding ways for :as to behave, so picking the faster one seems like a good plan. As for why it's faster, I presume this is obvious, but: we already have a pointer to the original map, which we can just reuse. To use the "modified" map, we have to build it with a bunch of assoc calls.
If :as doesn't give you back the original object, how can you possibly get the original object? You can't, really, right? Whereas if :as gives you back the original object, you can easily construct a modified version if you want. So one behavior leaves more options open to you.
According to Special Forms, :as and :or are both on their own in regards to the init-expr:
In addition, and optionally, an :as key in the binding form followed by a symbol will cause that symbol to be bound to the entire init-expr. Also optionally, an :or key in the binding form followed by another map may be used to supply default values for some or all of the keys if they are not found in the init-expr
As you have discovered, the :or key in the destructuring does not influence :as. :as will capture the original input, regardless of the application of defaults or encapsulation of remaining elements via & etc.
To quote the docs on clojure.org
Also optionally, an :or key in the binding form followed by another
map may be used to supply default values for some or all of the keys
if they are not found in the init-expr
...
Finally, also optional, :as followed by a symbol will cause that
symbol to be bound to the entire init-expr

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 do I get core clojure functions to work with my defrecords

I have a defrecord called a bag. It behaves like a list of item to count. This is sometimes called a frequency or a census. I want to be able to do the following
(def b (bag/create [:k 1 :k2 3])
(keys bag)
=> (:k :k1)
I tried the following:
(defrecord MapBag [state]
Bag
(put-n [self item n]
(let [new-n (+ n (count self item))]
(MapBag. (assoc state item new-n))))
;... some stuff
java.util.Map
(getKeys [self] (keys state)) ;TODO TEST
Object
(toString [self]
(str ("Bag: " (:state self)))))
When I try to require it in a repl I get:
java.lang.ClassFormatError: Duplicate interface name in class file compile__stub/techne/bag/MapBag (bag.clj:12)
What is going on? How do I get a keys function on my bag? Also am I going about this the correct way by assuming clojure's keys function eventually calls getKeys on the map that is its argument?
Defrecord automatically makes sure that any record it defines participates in the ipersistentmap interface. So you can call keys on it without doing anything.
So you can define a record, and instantiate and call keys like this:
user> (defrecord rec [k1 k2])
user.rec
user> (def a-rec (rec. 1 2))
#'user/a-rec
user> (keys a-rec)
(:k1 :k2)
Your error message indicates that one of your declarations is duplicating an interface that defrecord gives you for free. I think it might actually be both.
Is there some reason why you cant just use a plain vanilla map for your purposes? With clojure, you often want to use plain vanilla data structures when you can.
Edit: if for whatever reason you don't want the ipersistentmap included, look into deftype.
Rob's answer is of course correct; I'm posting this one in response to the OP's comment on it -- perhaps it might be helpful in implementing the required functionality with deftype.
I have once written an implementation of a "default map" for Clojure, which acts just like a regular map except it returns a fixed default value when asked about a key not present inside it. The code is in this Gist.
I'm not sure if it will suit your use case directly, although you can use it to do things like
user> (:earth (assoc (DefaultMap. 0 {}) :earth 8000000000))
8000000000
user> (:mars (assoc (DefaultMap. 0 {}) :earth 8000000000))
0
More importantly, it should give you an idea of what's involved in writing this sort of thing with deftype.
Then again, it's based on clojure.core/emit-defrecord, so you might look at that part of Clojure's sources instead... It's doing a lot of things which you won't have to (because it's a function for preparing macro expansions -- there's lots of syntax-quoting and the like inside it which you have to strip away from it to use the code directly), but it is certainly the highest quality source of information possible. Here's a direct link to that point in the source for the 1.2.0 release of Clojure.
Update:
One more thing I realised might be important. If you rely on a special map-like type for implementing this sort of thing, the client might merge it into a regular map and lose the "defaulting" functionality (and indeed any other special functionality) in the process. As long as the "map-likeness" illusion maintained by your type is complete enough for it to be used as a regular map, passed to Clojure's standard function etc., I think there might not be a way around that.
So, at some level the client will probably have to know that there's some "magic" involved; if they get correct answers to queries like (:mars {...}) (with no :mars in the {...}), they'll have to remember not to merge this into a regular map (merge-ing the other way around would work fine).