This question answers how to read a public field from a Java object:
(let [p (java.awt.Point.)]
(.x p)) ; <- returns 0
I thought I could write the field in a similar way:
(let [p (java.awt.Point.)]
(.x p 42))
But I get the following error:
IllegalArgumentException No matching method found: x for class java.awt.Point
clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:53)
This is covered in the Clojure - Java Interop:
(set! (. instance-expr instanceFieldName-symbol) expr)
Assignment special form.
When the first operand is a field member access form, the assignment is to the corresponding field. If it is an instance field, the instance expr will be evaluated [and assigned to the corresponding instance field].
Also note the use of '-' in resolving a field:
If the second operand [of (. instance-expr member)] is a symbol starting with -, the member-symbol will resolve only as field access (never as a 0-arity method) and should be preferred when that is the intent..."
Thus:
(set! (. p -x) 42)
Alternatively, the "preferred idiomatic forms for accessing field or method members" is slightly different, and this equivalency is shown in the macro expansion at the top of the page.
(set! (.-x p) 42)
Related
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.
Given the following piece of code:
(map Integer/parseInt ["1" "2" "3" "4"])
Why do I get the following exception unless I wrap Integer/parseInt in an anonymous function and call it manually (#(Integer/parseInt %))?
clojure.lang.Compiler$CompilerException: java.lang.RuntimeException: Unable to find static field: parseInt in class java.lang.Integer
the documentation on java interop says the following:
The preferred idiomatic forms for accessing field or method members
are given above. The instance member form works for both fields and
methods. The instanceField form is preferred for fields and required
if both a field and a 0-argument method of the same name exist. They
all expand into calls to the dot operator (described below) at
macroexpansion time. The expansions are as follows:
...
(Classname/staticMethod
args*) ==> (. Classname staticMethod args*) Classname/staticField ==>
(. Classname staticField)
so you should remember, that Class/fieldName is just a sugar for getting a static field, neither static method call, nor reference to the static method (java method is not a clojure function really), so there is no static field parseInt in Integer class, while (Class/fieldName arg) calls a static method, they are two totally different operations, using the similar sugary syntax.
so when you do (map #(Integer/parseInt %) ["1" "2" "3" "4"]) it expands to
(map #(. Integer parseInt %) ["1" "2" "3" "4"])
(you can easily see it yourself with macroexpansion),
and (map Integer/parseInt ["1" "2" "3"]) expands to
(map (. Integer parseInt) ["1" "2" "3"])
It fails when it is trying to get a field (which you think is getting a reference to a method).
Integer/parseInt is a static method of Integer class, not a clojure function. Each clojure function is compiled to java class which implements clojure.lang.IFn interface. map expects clojure function (which implements IFn interface) as a first argument, however, Integer/parseInt is not.
You can check that in the clojure repl.
user=> (type map)
clojure.core$map
user=> (type Integer)
java.lang.Class
user=> (type Integer/parseInt)
CompilerException java.lang.RuntimeException: Unable to find static field: parseInt in class java.lang.Integer, compiling:(/private/var/folders/p_/psdvlp_12sdcxq07pp07p_ycs_v5qf/T/form-init4110003279275246905.clj:1:1)
user=> (defn f [] 1)
#'user/f
user=> (type f)
user$f
user=> (type #(1))
user$eval9947$fn__9948
Perhaps reading this stackoverflow question will help you understand what is going on.
You might prefer to do it without the Java interop:
(map read-string ["1" "2"])
or for a more safe variant:
(map clojure.edn/read-string ["1" "2"])
I personally prefer to minimize the use of Java as much as possible in Clojure code.
As for why you can't just pass the Java function, because map expects a function in Clojure, not a function in Java.
Why does this code work?
(defn g []
do (println 10) (println 20))
Note: There is no ( before the do.
In theory this shouldn't even compile. The compiler ought to complain that do cannot be resolved since it is a special symbol not in the first position of a form.
This is a (likely unintended) consequence of using the same BodyExpr parsing code for both the do special form and the body of the fn* special form. When compiling a do special form, the leading do is dropped and the remaining forms compiled. Using this same parser for a function body means a single naked do can also appear first.
public static class BodyExpr implements Expr, MaybePrimitiveExpr{
...
public Expr parse(C context, Object frms) {
ISeq forms = (ISeq) frms;
if(Util.equals(RT.first(forms), DO))
forms = RT.next(forms);
....
You'll notice that if the do is repeated, this
(defn g [] do do (println 10) (println 20))
;=> CompilerException java.lang.RuntimeException:
Unable to resolve symbol: do in this context ...
does not compile, as expected.
let's try some calls to the "type" function :
user=> (type 10)
java.lang.Integer
user=> (type 10.0)
java.lang.Double
user=> (type :keyword?)
clojure.lang.Keyword
and now with an anonymous function :
user=> (type #(str "wonder" "what" "this" "is"))
user$eval7$fn__8
A) what does this mean "user$eval7$fn__8" ?
B) and what type is a function ?
the source for "type" is :
user=> (source type)
(defn type
"Returns the :type metadata of x, or its Class if none"
{:added "1.0"}
[x]
(or (:type (meta x)) (class x)))
nil
so a function needs to have a specific part of meta data or be a class
checking the meta of an anonymous function yields nada :
user=> (meta #(str "wonder" "what" "this" "is"))
nil
trying a different approach :
user=> (defn woot [] (str "wonder" "what" "this" "is"))
#'user/woot
user=> (meta woot)
{:ns #<Namespace user>, :name woot}
C) seems there is some meta but i figured this is the meta of the symbol "woot", right ?
what about the second half of the "or" :
user=> (class #(str "wonder" "what" "this" "is"))
user$eval31$fn__32
user=> (class woot)
user$woot
what are these : "user$eval31$fn__32" and "user$woot" and where do they come from ?
checking out the "class" function yields:
user=> (source class)
(defn ^Class class
"Returns the Class of x"
{:added "1.0"}
[^Object x] (if (nil? x) x (. x (getClass))))
nil
and further investigating yields :
user=> (.getClass #(str "wonder" "what" "this" "is"))
user$eval38$fn__39
user=> (.getClass woot)
user$woot
i don't get it.
D) is this a hashcode : eval38$fn__39 ?
E) is this a symbol : woot ?
F) why doesn't a function have a type ? isn't it supposed to be an IFn or something ?
A function is of type clojure.lang.IFn, which is a Java interface.
Every Clojure function is compiled into a Java class which implements clojure.lang.IFn. The name user$eval7$fn__8 is the "binary class name" of that class, i.e., its internal name in the JVM.
Clojure is built on the JVM.
The JVM doesn't support first-class functions, or lambdas, out of the box. Every Clojure function, once it is compiled, becomes its own anonymous class from the perspective of the JVM. Each function is, technically, it's own type.
The class it becomes implements IFn, but when you retrieve it's type, it gives you the name of the anonymous class which is different every time.
Here's the description in the API docs
Returns the :type metadata of x, or its Class if none
What you are seeing ("user$eval7$fn__8") is the name of the internally generated inner class created by Clojure to implement the anonymous function you have defined.
As you may have noticed, Clojure doesn't follow standard Java class naming conventions :-)
Note that the class implements the interface clojure.lang.IFn - this applies to all Clojure functions.
I am a clojure novice but i am going to be bold. First we have two different meaning for "type" of a function, one, the java interfaces and classes of clojure internals and otoh the type of the function as a programming concept. Taking the second approach the type of a function would be the type of its return value (or its params types and return value type):
1) i guess all functions implements the IFn interface, whatever their current class
2) the class name automatic generated by clojure diffs if the function is anonymous or named, but it seems in both cases are inner classes (tipically theirs names are separated by $ and go from outer classes to inner ones)
3) the type of the returned value can be in the :tag key of function metadata if you annotate it in function definition. F.e. the function class you expose have Class as its returned type cause in its def there is a ^Class before the name.
I am assuming you are familiar with java (or a similar oop lang), sorry if not
I'm a few days into learning Clojure and are having some teething problems, so I'm asking for advice.
I'm trying to store a Java class in a Clojure var and call its static methods, but it doesn't work.
Example:
user=> (. java.lang.reflect.Modifier isPrivate 1)
false
user=> (def jmod java.lang.reflect.Modifier)
#'user/jmod
user=> (. jmod isPrivate 1)
java.lang.IllegalArgumentException: No matching method found: isPrivate for class java.lang.Class (NO_SOURCE_FILE:0)
at clojure.lang.Compiler.eval(Compiler.java:4543)
From the exception it looks like the runtime expects a var to hold an object, so it calls .getClass() to get the class and looks up the method using reflection. In this case the var already holds a class, so .getClass() returns java.lang.Class and the method lookup obviously fails.
Is there some way around this, other than writing my own macro?
In the general case I'd like to have either an object or a class in a varible and call the appropriate methods on it - duck typing for static methods as well as for instance methods.
In this specific case I'd just like a shorter name for java.lang.reflect.Modifier, an alias if you wish. I know about import, but looking for something more general, like the Clojure namespace alias but for Java classes. Are there other mechanisms for doing this?
Edit:
Maybe I'm just confused about the calling conventions here. I thought the Lisp (and by extension Clojure) model was to evaluate all arguments and call the first element in the list as a function.
In this case (= jmod java.lang.reflect.Modifier) returns true, and (.getName jmod) and (.getName java.lang.reflect.Modifier) both return the same string.
So the variable and the class name clearly evaluate to the same thing, but they still cannot be called in the same fashion. What's going on here?
Edit 2
Answering my second question (what is happening here), the Clojure doc says that
If the first operand is a symbol that
resolves to a class name, the access
is considered to be to a static member
of the named class... Otherwise it is
presumed to be an instance member
http://clojure.org/java_interop under "The Dot special form"
"Resolving to a class name" is apparently not the same as "evaluating to something that resolves to a class name", so what I am trying to do here is not supported by the dot special form.
(Update: I've prepared something which might be acceptable as a solution... The original answer remains below a horizontal rule towards the end of the post.)
I've just written a macro to enable this:
(adapter-ns java.lang.reflect.Modifier jmod)
; => nil
(jmod/isStatic 1)
; => false
(jmod/isStatic 8)
; => true
The idea is to create a single-purpose namespace, import the statics of a given class as Vars into that namespace, then alias the namespace to some useful name. Convoluted, but it works! :-)
The code is looks like this:
(defmacro import-all-statics
"code stolen from clojure.contrib.import-static/import-static"
[c]
(let [the-class (. Class forName (str c))
static? (fn [x]
(. java.lang.reflect.Modifier
(isStatic (. x (getModifiers)))))
statics (fn [array]
(set (map (memfn getName)
(filter static? array))))
all-fields (statics (. the-class (getFields)))
all-methods (statics (. the-class (getMethods)))
import-field (fn [name]
(list 'def (symbol name)
(list '. c (symbol name))))
import-method (fn [name]
(list 'defmacro (symbol name)
'[& args]
(list 'list ''. (list 'quote c)
(list 'apply 'list
(list 'quote (symbol name))
'args))))]
`(do ~#(map import-field all-fields)
~#(map import-method all-methods))))
(defmacro adapter-ns [c n]
(let [ias (symbol (-> (resolve 'import-all-statics) .ns .name name)
"import-all-statics")]
`(let [ns-sym# (gensym (str "adapter_" ~n))]
(create-ns 'ns-sym#)
(with-ns 'ns-sym#
(clojure.core/refer-clojure)
(~ias ~c))
(alias '~n 'ns-sym#))))
The above looks up the Var holding the import-all-statics macro in a somewhat convoluted way (which is, however, guaranteed to work if the macro is visible from the current namespace). If you know which namespace it's going to be found in, the original version I've written is a simpler replacement:
(defmacro adapter-ns [c n]
`(let [ns-sym# (gensym (str "adapter_" ~n))]
(create-ns 'ns-sym#)
(with-ns 'ns-sym#
(clojure.core/refer-clojure)
;; NB. the "user" namespace is mentioned below;
;; change as appropriate
(user/import-all-statics ~c))
(alias '~n 'ns-sym#)))
(Original answer below.)
I realise that this is not really what you're asking for, but perhaps clojure.contrib.import-static/import-static will be useful to you:
(use 'clojure.contrib.import-static)
(import-static clojure.lang.reflect.Modifier isPrivate)
(isPrivate 1)
; => false
(isPrivate 2)
; => true
Note that import-static imports static methods as macros.
You are successfully storing the class in jmod, but isPrivate is a static method of java.lang.reflect.Modifier, not of java.lang.Class.
You could do this with reflection:
(. (. jmod getMethod "isPrivate" (into-array [Integer/TYPE]))
invoke nil (into-array [1]))
Here is a macro inspired by the two previous answers that handles static methods on class names and variables with class names as well as instance methods on objects:
(defmacro jcall [obj & args]
(let [ref (if (and (symbol? obj)
(instance? Class (eval obj)))
(eval obj)
obj) ]
`(. ~ref ~#args)))
As a relative newbie on macros, the tricky part was getting the evaluation order right.
For other newbies: The obj parameter to the macro is passed in with no evaluation, and we need to force the evaluation of vars so the var name expands into the class name it holds. We need an explicit eval for that, outside the actual macro body.
The test for whether obj is a symbol is there to restrict the evaluation to variables. The test for whether the variable contains a class is there to skip evaluation of non-classes, then it works for objects and instance methods too.
Example use:
;; explicit class name, static method
user=> (jcall java.lang.reflect.Modifier isPrivate 1)
false
;; class name from var, static method
user=> (jcall jmod isPrivate 1)
false
;; works for objects and instance methods too
user=> (jcall (Object.) toString)
"java.lang.Object#3acca07b"
;; even with the object in a variable
user=> (def myobj (Object.))
#'user/myobj
user=> (jcall myobj toString)
"java.lang.Object#4ccbb612"
;; but not for instance methods on classes
user=> (jcall Object toString)
java.lang.NoSuchFieldException: toString (NO_SOURCE_FILE:747)