(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].
Related
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
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.
I've noticed that in Clojure it's common to use keywords as keys in a map, while in other languages that do not have such concept it is common to use strings.
What are benefits of using keywords instead of strings or other types?
Keywords are a symbol type, semantically distinguished from strings and with a smaller range of representable values. They can be namespaced, there are checks for legal use of namespaces and such, and hence users can be more confident in EDN keyword keys meaning what they expect than in e.g. JSON with the equivalent string keys.
Keywords are interned, so all instances of a keyword refer to identically the same object. This is useful for e.g. equality checks.
Keywords are functions, acting like get with themselves as the key argument. This is convenient for cases like "I want the phone number of all these people" - (map :phone-no people) vs. (map #(get % "phone-no") people)
Keywords are also convenient with let and other destructuring tools:
(let[{:keys [foo bar]} {:foo 1 :bar 30}]
(+ foo bar)) ;;=>31
but this does actually apply to string keys as well using the less common :strs destructuring.
When we talk about a clojure (or other lisp) prog, what should we say when refer an object declared in it? For example:
(let [a ...
Of course, if a is a function, we say function a, but what should we say when it's not a function? Form? Data? Symbol? Literal?
Generally, we say those things in other prog language variables or objects.
The let special form creates bindings between symbol(s) and a value.
The official documentation for let actually uses the vocabulary and wording that you are looking for and should use, specifically the Binding Forms section sheds more light to the subject:
The basic idea is that a binding-form can be a data structure literal containing symbols [or just a single symbol] that get bound to the respective parts of the init-expr.
I think the question mixed up two things, or at least didn't make it clear that they're different.
As the other answers so far indicated, "a" is a symbol, which evaluates to something else. So when we talk about a, we could mean the symbol, or the something else. And we could even mean the var that is an intermediary between the symbols and the something else. (See the page linked by Guillermo Winkler for more on the var/symbol relationship, which I'll leave in the background.)
A symbol is never a function, but it can have a function as its value. When you call a function, you are just using its value in a special way. You can even set the values of built-in functions to other values:
(def + -)
WARNING: + already refers to: #'clojure.core/+ in namespace: user, being replaced by: #'user/+
#'user/+
user=> (+ 5 2)
3
user=> (def - "This used to be the minus function")
WARNING: - already refers to: #'clojure.core/- in namespace: user, being replaced by: #'user/-
#'user/-
user=> -
"This used to be the minus function"
I gave + the value of -, and then made -'s value a string. (Yes, there were warnings, but it worked.) The fact that functions are just values of symbols is a way in which Clojure differs from many other languages. (Scheme is similar. Common Lisp is similar, but in a more complicated way.)
So the symbol is just the symbol. And it usually has a value, which may be a function, or a number, or a string, or a keyword, or anything that can be a value in Clojure--vectors, lazy sequences, lists (which may be Clojure code), or even another symbol (even the same one: (def a 'a).) You could call some of these things data if that's useful in a particular context. It's sometimes reasonable to describe functions as data in Clojure.
(def add-fns [#(+ 1 %) #(+ 2 %) #(+ 3 %)]) ;=> #'user/add-fns
add-fns ;=> [#object[user$fn__1178 0x71784911 "user$fn__1178#71784911"] #object[user$fn__1180 0x45ed957d "user$fn__1180#45ed957d"] #object[user$fn__1182 0x7838c8c5 "user$fn__1182#7838c8c5"]]
add-fns is a vector of functions. What should we call the functions that are elements of the vector? Aren't they data in some sense, if we use them like data? We can map a function over them, or reorder them, for example:
(map #(% 10) add-fns) ;=> (11 12 13)
(map #(% 10) (reverse add-fns)) ;=> (13 12 11)
Each of those expressions takes each function in add-fns and calls it with 10 as its argument, returning the results in a sequence.
Exception: macros don't follow the same rules:
user=> and
CompilerException java.lang.RuntimeException: Can't take value of a macro: ...
Some of the Java interop tricks don't follow the same rules, either.
a is always a symbol, independently of the value bound to it (whether it's a function or a number or anything else)
There's a previous answer on Clojure symbols that may clarify things a little bit more here: Symbols in Clojure
And remember symbols are not the same thing as vars
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).