ClassCastException when calling Clojure function - clojure

I have written the following bit of code
(defn create [title url]
(when (not-any? clojure.string/blank? '(title url))
(println "doing stuff")))
However when I call the function
(create "title" "url")
I get the following error and cannot figure out what I am doing wrong
ClassCastException clojure.lang.Symbol cannot be cast to java.lang.CharSequence clojure.string/blank? (string.clj:279)

This is one of the things that also tripped me up when I first started learning clojure. Basically, you should use
(list title url)
The clojure compiler treats
'(title url)
as
(quote (title url))
'quote' does not evaluate anything inside of it, so 'title' and 'url' are just symbols (clojure.lang.Symbols to be precise).
Here's a better explanation than mine: http://blog.8thlight.com/colin-jones/2012/05/22/quoting-without-confusion.html

Related

Cannot Figure Out How to Remove String cannot be cast to clojure.lang.IFn From Small Clojure Function

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.

How can I iterate over a list with a macro?

I am trying to print the documentation for all functions in a given namespace by invoking the following expression in a REPL:
(doseq
[f (dir-fn 'clojure.repl)]
(doc f))
However the invocation of this expression returns nil without printing the documentation to the REPL. I know this might have to do with doc being a macro, but I'm a Clojure novice and am not entirely sure how to understand the problem.
Why does this expression return nil without printing the documentation?
How can this expression be modified so that it prints the documentation for each function in a given namespace?
Thanks!
Update: Combined both provided answers:
(defn ns-docs [ns']
(doseq [[symbol var] (ns-interns ns')]
(newline)
(println symbol)
(print " ")
(println (:doc (meta var)))))
(ns-docs 'clojure.repl)
I would, instead, start here:
The Clojure CheatSheet
ClojureDocs.org
Clojure-Doc.org (similar name, but different)
The API & Reference sections at Clojure.org
Note that doc is in the namespace clojure.repl, which reflects its intended usage (by a human in a repl). Here is some code that will also iterate on a namespace & print doc strings (using a different technique):
(doseq [[fn-symbol fn-var] (ns-interns 'demo.core)]
(newline)
(println fn-symbol)
(println (:doc (meta fn-var))))
where demo.core is the namespace of interest.
Note that ns-interns gives you both a symbol and var like:
fn-symbol => <#clojure.lang.Symbol -main>
fn-var => <#clojure.lang.Var #'demo.core/-main>
The meta function has lots of other info you may want to use someday:
(meta fn-var) =>
<#clojure.lang.PersistentArrayMap
{ :arglists ([& args]),
:doc "The Main Man!",
:line 9, :column 1,
:file "demo/core.clj",
:name -main,
:ns #object[clojure.lang.Namespace 0x14c35a06 "demo.core"]}>
While this probably won't help you with answering your question, the problem of evaluating macro's comes up a lot when you are learning Clojure.
Macros are responsible for the evaluation of their arguments. In this case clojure.repl/doc will ignore the current lexical context and assume that the symbol f that you're giving it is the name of a function you want to see the documentation for. It does this because it's intended to be used at the REPL, and is assuming you wouldn't want to type quotes all the time.
As f doesn't exist, it prints nothing. Then doseq returns nil, since it exists to do something for side effects only - hence starting in do. In order to pass an argument to a macro that refuses to respect the lexical context like this, you need to write the code for each element in the list.
You can do this by hand, or by constructing the code as data, and passing it to eval to execute. You can do this in an imperative style, using doseq:
(doseq [f (ns-interns 'clojure.repl)]
(eval `(doc ~(symbol "clojure.repl" (str (first f))))))
or in a slightly more Clojurey way (which will allow you to see the code that it would execute by removing eval from the end and running it at the REPL):
(->> (ns-interns 'clojure.repl)
(map #(list 'clojure.repl/doc (symbol "clojure.repl" (str (first %)))))
(cons `do)
eval)
In both of these we use quote and syntax-quote to construct some code from the list of symbols reflected from the namespace, and pass it to eval to actually execute it. This page on Clojure's weird characters should point you in the right direction for understanding what's going on here.
This an example of why you shouldn't write macro's, unless you've got no other options. Macro's do not compose, and are often difficult to work with. For a more in depth discussion, Fogus's talk and Christophe Grand's talk are both good talks.
Why does this expression return nil without printing the documentation?
Because the doc macro is receiving the symbol f from your loop, instead of a function symbol directly.
How can this expression be modified so that it prints the documentation for each function in a given namespace?
(defn ns-docs [ns']
(let [metas (->> (ns-interns ns') (vals) (map meta) (sort-by :name))]
(for [m metas :when (:doc m)] ;; you could filter here if you want fns only
(select-keys m [:name :doc]))))
(ns-docs 'clojure.repl)
=>
({:name apropos,
:doc "Given a regular expression or stringable thing, return a seq of all
public definitions in all currently-loaded namespaces that match the
str-or-pattern."}
...
)
Then you can print those maps/strings if you want.

Clojure core.typed annotation for apply inside a 3rd-party macro

I'm using slingshot's throw+ macro to raise an exception that looks like:
(throw+ {:type ::urlparse})
The type checker doesn't like it:
Type Error (stream2es/http.clj:79:17) Bad arguments to apply:
Target: [String t/Any * -> String]
Arguments: (PersistentList String)
in: (clojure.core/apply clojure.core/format (clojure.core/list "throw+: %s" (clojure.core/pr-str %)))
Type Checker: Found 1 error
The macro in slingshot looks like:
(defmacro throw+
([object]
`(throw+ ~object "throw+: %s" (pr-str ~'%)))
([object message]
`(throw+ ~object "%s" ~message))
([object fmt arg & args]
`(let [environment# (s/environment)
~'% ~object
message# (apply format (list ~fmt ~arg ~#args))
stack-trace# (s/stack-trace)]
(s/throw-context ~'% message# stack-trace# environment#)))
([]
`(s/rethrow)))
I've tried various ann ^:no-check forms on apply and format and none works. Since it's a macro, I'm assuming I can't annotate it since it replaces the code that's there. But I also can't rewrite the code in the macro like was suggested in this other answer, because it's in a library. How do I gradually type in this case?
If you’re not able to rewrite the implementation of throw+, I suggest a wrapper macro like this.
(defmacro typed-throw+ [object]
`(let [o# ~object]
(t/tc-ignore
(throw+ o#))
(throw (Exception.)))) ; unreachable
;; other arities are an exercise ..
This way, you still type check the argument, and core.typed still thinks throw+ always throws an exception — it doesn't really know that, but the final throw clause allows core.typed to give the entire expression type Nothing.
The real answer should be we can improve apply to know that applying a non-empty list will satisfy at least one argument, however this answer should work today.

Casting DynamicLabel to Label in Clojure for Neo4j Embedded

I am trying to rewrite the neo4j sample code located here in clojure. But when I try to create a node, I get the following error
ClassCastException Cannot cast org.neo4j.graphdb.DynamicLabel to [Lorg.neo4j.graphdb.Label; java.lang.Class.cast (Class.java:3094)
Here is my code:
(ns neotest.handler
(:import (org.neo4j.graphdb
DynamicLabel
GraphDatabaseService
Label
Node
ResourceIterator
Transaction
factory.GraphDatabaseFactory
schema.IndexDefinition
schema.Schema)))
(def db
(let [path "C:\\Users\\xxx\\code\\neotest\\resources\\db1"]
(. (new GraphDatabaseFactory) (newEmbeddedDatabase path))))
(defn create-node []
(try (let [tx (. db beginTx)
l (. DynamicLabel (label "User"))]
(. db (createNode l))
(. tx success))))
I have tried type-hinting of all kinds and in all places, and I still get the same error.
It's because of the varargs Label... parameter. This was a bit of Clojure/Java interop I didn't know about: you have to pass the parameter in as an array (even if there's only one), so you need to do something like:
(. db (createNode (into-array Label [l])))
to make it work. There's another afternoon I won't be getting back!
the calls to dynamicLabel in the example java code look like:
DynamicLabel.label( "User" )
which would translate to:
(DynamicLabel/label "user")
because label is a static method of the class org.neo4j.graphdb.DynamicLabel which has the signature:
static Label label(String labelName)

Unable to get random (doc) from a namespace

I want to display random (doc) page for some namespace.
The random function name I can get by:
user=> (rand-nth (keys (ns-publics 'clojure.core)))
unchecked-char
When I try to pass this to (doc) I get this:
user=> (doc (rand-nth (keys (ns-publics 'clojure.core))))
ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.Symbol clojure.core/ns-resolve (core.clj:3883)
I'm new to Clojure and I'm not sure how to deal with this... I tried to convert this into regexp and use (find-doc) but maybe there is a better way to do this...
Explanation
The problem here is that doc is a macro, not a function. You can verify this with the source macro in the repl.
(source doc)
; (defmacro doc
; "Prints documentation for a var or special form given its name"
; {:added "1.0"}
; [name]
; (if-let [special-name ('{& fn catch try finally try} name)]
; (#'print-doc (#'special-doc special-name))
; (cond
; (special-doc-map name) `(#'print-doc (#'special-doc '~name))
; (resolve name) `(#'print-doc (meta (var ~name)))
; (find-ns name) `(#'print-doc (namespace-doc (find-ns '~name))))))
If you're new to Clojure (and lisps), you might not have encountered macros yet. As a devastatingly brief explanation, where functions operate on evaluated code, macros operate on unevaluated code - that is, source code itself.
This means that when you type
(doc (rand-nth (keys (ns-publics 'clojure.core))))
doc attempts to operate on the actual line of code - (rand-nth (keys (ns-publics 'clojure.core))) - rather than the evaluated result (the symbol this returns). Code being nothing more than a list in Clojure, this is why the error is telling you that a list can't be cast to a symbol.
Solution
So, what you really want to do is evaluate the code, then call doc on the result. We can do this by writing another macro which first evaluates the code you give it, then passes that to doc.
(defmacro eval-doc
[form]
(let [resulting-symbol (eval form)]
`(doc ~resulting-symbol)))
You can pass eval-doc arbitrary forms and it will evaluate them before passing them to doc. Now we're good to go.
(eval-doc (rand-nth (keys (ns-publics 'clojure.core))))
Edit:
While the above works well enough in the repl, if you're using ahead ahead-of-time compilation, you'll find that it produces the same result every time. This is because the resulting-symbol in the let statement is produced during the compilation phase. Compiling once ahead of time means that this value is baked into the .jar. What we really want to do is push the evaluation of doc to runtime. So, let's rewrite eval-doc as a function.
(defn eval-doc
[sym]
(eval `(doc ~sym)))
Simple as that.