I'm trying to learn Langohr and I came across this piece of code:
(def ^{:const true} default-exchange-name "")
What does it do?
I'm not sure if its a duplicate. My question is regarding a map while ^:const is just a keyword.
There is no difference in the two notations according to the docs.
^:dynamic obj - Sets the given keyword to true in the object’s
metadata. Equivalent to ^{:dynamic true} obj
Related
When I try this:
(defrecord Attr [has-default default])
(def attr (->Attr true 1))
(get attr :default) ;;=> 1
(:default attr) ;;=> ClojureScript returns nil, Clojure returns 1
Is the difference in behavior when using keyword access expected? I couldn't find anything about it in the [docs][1] on the differences between Clojure and ClojureScript.
Update 2020-08-04
Deleted. See next.
Update 2020-08-06
It has been pointed out that my previous update contained a spelling error that caused the problem. I have removed the update rather than leave it as a trap for future readers.
It does not change the original observations or the solution.
[1]: https://www.clojurescript.org/about/differences#_data_structures
I have a small clojure function:
(defn split-legal-ref
"Highly specific function that expects one AssessPro map and a map key,
from which book and page will be extracted."
[assess-pro-acct extract-key]
(let [[book page] (cstr/split (extract-key assess-pro-acct) #"-")]
(list (cstr/trim book) (cstr/trim page))))
Given this: (extract-key assess-pro-acct) #"-"), the extract-key's value is :legal_ref. So, it is fetching a single value like 927-48 out of a map and splitting the value using '-'. I just need to catch when there isn't one of those nice values. That is where the split returns nil.
So, I am stuck having tried to replace the original function with the following.
(def missing-book 888)
(def missing-page 999)
.
.
.
(defn split-legal-ref
"Highly specific function that expects one AssessPro map and a map key,
from which book and page will be extracted."
[assess-pro-acct extract-key]
(let [[book page] (cstr/split (extract-key assess-pro-acct) #"-")]
(let [[trimBook trimPage] ((if book (cstr/trim book) (missing-book))
(if page (cstr/trim page) (missing-page)))]
(list (trimBook) (trimPage)))))
The problem is I keep getting the dreaded
String cannot be cast to clojure.lang.IFn From Small Clojure Function
error. How can I restructure this function to avoid the error?
Post Answers Edit:
Thank you for the answers:
I reworked the function to test for a "-" in a string. If it's not there, I use a dummy "888-99" as a value when none is there.
(def missing-book-page "888-99")
.
.
.
(defn split-legal-ref
"Highly specific function that expects one AssessPro map and a map key,
from which book and page will be extracted."
[assess-pro-acct extract-key]
(let [[book page]
(if (.contains "-" (extract-key assess-pro-acct))
(cstr/split (extract-key assess-pro-acct) #"-")
(cstr/split missing-book-page #"-"))]
(list (cstr/trim book) (cstr/trim page))))
You have an extra set of parentheses around the expression beginning with ((if book .... The if expression returns a string, and then since that string is in the first position of a list with the outer of those 2 parentheses, Clojure tries to invoke the string as a function.
Parentheses are very, very significant in Clojure. Unlike arithmetic expressions in languages like Fortran, C, C++, Java, Python, etc., where adding an extra set of parentheses around a subexpression is redundant, and maybe bad style, but harmless, it changes the meaning of Clojure expressions.
Can you add more information, like the function names and sample data? Also include more of the error message.
Somewhere in your code you are attempting to use a string as if it were a function. For example:
("hello" 3) ; should be (inc 3) or something. This is line #6
This generates the following error
ERROR in (dotest-line-5) (core.clj:6)
Uncaught exception, not in assertion.
expected: nil
actual: java.lang.ClassCastException: class java.lang.String cannot be cast to class clojure.lang.IFn (java.lang.String is in module java.base of loader 'bootstrap'; clojure.lang.IFn is in unnamed module of loader 'app')
at tst.demo.core$fn__18295.invokeStatic (core.clj:6)
<snip>
Note the last line of the error above refers to core.clj:6 which matches the namespace tst.demo.core and line number 6 where (hello 3) is found in the source code.
I'm new to Clojure and I've been messed up with ^ in Clojure
I'm currently reading clojure code of Jepsen which is used to test the consistency of distributed database.
You can find the code here.
In row 50 there is a ^MongoDatabase. Or:
(defn ^MongoCollection collection
"Gets a Mongo collection from a DB."
[^MongoDatabase db collection-name]
(.getCollection db collection-name))
I have no idea what it is because ^MongoDatabase or MongoCollection is never used in this function.
Can anyone give me some help. Thanks a lot.
In this context, that's a type hint saying collection should return a MongoCollection instance and db arg should be a MongoDatabase instance. This is useful for performance reasons, to avoid unnecessary reflection.
See this guide for more.
Another use of ^ is for type hints. These are used to tell the compiler what type the value will be and allow it to perform type specific optimizations thus potentially making resultant code faster:
The cap symbol ^ is used in Clojure for two purposes.
The first one is for type hints. When declaring a function, you may mark arguments' types or the result value as follows:
(defn ^String concat-strings
[^String a ^String b]
(str a b))
Type hints help the compiler to perform some optimizations.
The second option of using cap is when declaring metadata. The metadata might be either a boolean flag or a map. For example:
(def ^:private secret "test")
Now the variable above is marked as private so it won't be available from other namespaces.
Here is a meta-map usage example:
(def ^{:private true
:doc "My super secret password"
:added "product-version"}
secret
"test")
Let's try to read the metadata for that variable:
(meta #'secret)
returns
{:private true,
:doc "My super secret password",
:added "product-version",
:line 70,
:column 7,
:file "*cider-repl localhost*",
:name secret,
:ns #namespace[user]}
Another point to beware of is that type hints can be deceptive (i.e. they have no "enforcement" or "warning" utility):
(defn foo [x]
(type x))
(defn bar [^String x]
(type x))
(foo "abc") => java.lang.String
(foo 123) => java.lang.Long
(bar "abc") => java.lang.String
(bar 123) => java.lang.Long
In general, I would avoid type hints as they are rarely necessary (unless dealing with low-level Java code).
As part of improving Cider's debugger, I need to implement special handling for all possible special-forms. In order words, I need to know all symbols which satisfy special-symbol?.
The doc page on Special Forms, while helpful, doesn't offer all of them.
For instance, after some experimentation, I've learned that
Most of the forms listed there have a * counterpart (let* and loop*, for instance).
There is a clojure.core/import* special-symbol (which I wouldn't have found if not for sheer luck).
Is there a complete list of all special symbols?
Alternatively, is there a way to list all interned symbols? If so, then I could filter over special-symbol?.
Looking at the definition of special-symbol? provides a big clue:
(defn special-symbol?
"Returns true if s names a special form"
{:added "1.0"
:static true}
[s]
(contains? (. clojure.lang.Compiler specials) s))
Thus:
user=> (pprint (keys (. clojure.lang.Compiler specials)))
(&
monitor-exit
case*
try
reify*
finally
loop*
do
letfn*
if
clojure.core/import*
new
deftype*
let*
fn*
recur
set!
.
var
quote
catch
throw
monitor-enter
def)
I'd thought I'd post this as I got it to work through guesswork without a real understanding of what's going on and I thought it might be helpful if someone explained it.
I understand how to get at an element of the :params map in a Compojure handler:
(GET "/something" [some_arg] "this is the response body")
or
(GET "/something" {{some_arg "some_arg"} :params} "this is the response body")
although I don't completely understand what the {some_arg "some_arg"} part is doing :(
I also wanted to access the :remote-addr part of the request as well as some_arg. And I ended up with
(GET "/something" {{some_arg "some_arg"} :params ip :remote-addr}
(do-something-with some_arg ip))
So, I get that the unquoted strings some_arg and ip are the names of variables to which I want the values bound but the map above isn't a valid Clojure map. How does it work?
I also get that this is evaluated against the Ring request map (which is somehow supplied by the defroutes macro) but the expression above isn't a function or macro definition so how can it 'exist' as a valid expression in my code? Is there some sort of suspension of the normal rules for macro arguments? I've been unable to find a definition of the syntax of destructuring forms comprehensible to this non-Lisp'er.
The map is a valid destructuring map. In any place where you bind names, you can use destructuring. You could do the same thing in a let, like this:
user=> (let [{{some-arg "some_arg"} :params ip :remote-addr} {:remote-addr "127.0.0.1" :params {"some_arg" "some_value"}}] [ip some-arg])
["127.0.0.1" "some_value"]
I wrote a post about map destructuring in the context of named arguments, but it applies here. You might find this useful: Clojure - named arguments
There are a lot of blog posts demonstrating destructuring, including this one. I'm not sure which one would be a canonical place to learn from.
I don't pretend to know what exactly compojure does with that map under the hood, but I presume it throws it in a let or something similar as I demonstrated above. GET is a macro, so it doesn't have to evaluate the map you pass it, which is why you wouldn't get an error unless it evaluated it.
user=> (defmacro blah [m])
#'user/blah
user=> (blah {a "b" c "d"})
nil
user=> (defn blah [m])
#'user/blah
user=> (blah {a "b" c "d"})
java.lang.Exception: Unable to resolve symbol: a in this context (NO_SOURCE_FILE:9)
Under the hood, magic happens to that map and it gets passed to a function called destructuring that does the destructuring magic.
There isn't really anything special going on here other than normal macro/special form foo and delayed evaluation.
Destructing takes place within a binding form, and for map destructuring the var to be bound is on the left, and the key is on the right:
user=> (let [{a :foo} {:foo :bar}]
user=* a)
:bar
Compojure is doing a binding form behind the scenes, so that map destructuring form you were using above is effectively turned into something like:
(let [{{some_arg "some_arg"} :params} request]
...)
Where request is an implicitly provided map.
The vector version (e.g., [some_arg]), is an alternative that just binds against the :params map contained in the request.