I have a problem when trying to access a static method of some class.
Here is the code and the error message:
(ns demo.app
(:import
[java.nio.file Paths]
[java.util List])
=> (Paths/get "a" "B" "c")
CompilerException java.lang.IllegalArgumentException: No matching method: get, compiling:(*cider-repl clj-demo*:68:16)
=> (java.nio.file.Paths/get ".")
ClassCastException java.lang.String cannot be cast to java.net.URI clj-demo.ch08/eval11327 (form-init3786136217280477578.clj:76)
=> (java.nio.file.Paths/get "." "finally.txt")
ClassCastException java.lang.String cannot be cast to [Ljava.lang.String; clj-demo.ch08/eval11329 (form-init3786136217280477578.clj:78)
The java doc follows:
https://docs.oracle.com/javase/10/docs/api/java/nio/file/Paths.html#get(java.lang.String,java.lang.String...)
I've found on https://clojure.org/reference/java_interop , it writes:
(Classname/staticMethod args*)
I also tried these:
=> (System/getProperty "java.vm.version")
"25.102-b14"
=> (clojure-version)
"1.8.0"
No problem. So:
Why does Paths/get not work in REPL?
Why does java.nio.file.Paths/get not work, either?
Why does Paths/get and java.nio.file.Paths/get get different error messages?
I've also tried
(List/of 0) ;; => [0]
(List/of 1 2 3) ;; => Method
java.util.List.of(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/List;
must be InterfaceMethodref constant
(List/of (Integer. 1)) ;; => Method java.util.List.of(Ljava/lang/Object;)Ljava/util/List; must
be InterfaceMethodref constant
(List/of "s") ;; => Method java.util.List.of(Ljava/lang/Object;)Ljava/util/List; must
be InterfaceMethodref constant
Paths.get(String, String...) takes two arguments - a string and an array of strings. Java creates the array automatically for you, but in clojure you need to construct the array yourself e.g.
(Paths/get "a" (into-array String ["b" "c"]))
Calling
(java.nio.file.Paths/get ".")
fails because the only overload of get which takes a single argument requires it to be a URI not a String.
Related
I can use memfn to create a clojure function that invokes a java function.
(macroexpand '(memfn startsWith prefix))
=> (fn* ([target2780 prefix] (. target2780 (startsWith prefix))))
((memfn startsWith prefix) "abc" "a")
=> true
memfn requires that the function name be a symbol. I'm wondering if I can write a macro to invoke an arbitrary method whose name is provided as a string. That is, I'd like to be able to invoke the following:
(def fn-name "startsWith")
=> #'user/fn-name
(macroexpand '(memfn' fn-name "prefix"))
=> (fn* ([target2780 prefix] (. target2780 (startsWith prefix))))
((memfn fn-name "prefix") "abc" "a")
=> true
The only way I can think to do this involves using read-string.
(defmacro memfn' [fn-name arg-name]
`(memfn ~(read-string fn-name) ~arg-name))
Edit: A version using read-string and eval that actually works the way I want it to.
(defn memfn' [fn-name arg-name]
(eval (read-string (str "(memfn " fn-name " " arg-name ")"))))
Am I missing a fundamental macro building tool to take the string that a symbol references and turn it into a literal without potentially executing code, as read-string might?
Thanks for any ideas!
There's no way to do this, with or without read-string. Your proposed solution doesn't work. The distinction you're really trying to make is not between string and symbol, but between runtime data and compile-time literals. Macros do not evaluate the arguments they receive, so even if fn-name is the name of a var whose value is "startsWith", memfn (or your memfn' macro) will only ever see fn-name.
If you are interested in calling java methods only then you can rely on java.lang.reflect.Method and its invoke method.
Something like this should work for parameterless methods and would not require a macro.
(defn memfn' [m]
(fn [o] (.invoke (.getMethod (-> o .getClass) m nil) o nil)))
((memfn' "length") "clojure")
;=>7
I'm trying to learn Clojure, and am blocked up around the literal function syntax. I can't figure out what the literal function equivalent of (defn fourteen [] 14) is.
(def fourteen (fn [] 14))
;; => #'user/fourteen
(fourteen)
;; => 14
(defn defn-fourteen [] 14)
;; => #'user/defn-fourteen
(defn-fourteen)
;; => 14
(def literal-14 #(14))
;; => #'user/literal-14
(literal-14)
;; ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn user/literal-14 (form-init2956929406616221071.clj:1)
I don't think this is a dup of How many arguments does an anonymous function expect in clojure?, but maybe it is and I just don't possess the experience to recognize that.
How, or can, I def literal-14 to allow the (literal-14) invocation to work?
As A. Webb pointed out, constantly is the most idiomatic way to write this:
(def fourteen (constantly 14))
The problem with the anonymous function literal is that it always expands to a function whose body is a list:
'#(stuff and things) ;=> (fn* [] (stuff and things))
So here's what's happening in your attempt:
'#(14) ;=> (fn* [] (14))
There is no way to get rid of those parentheses. You could hack your way around them using do or identity or something like that, but the bottom line is that if you need to write an anonymous function whose body isn't suited to being written as a list, you shouldn't use the literal syntax.
14 isn't a function, but do or -> will do in a pinch:
#(do 14)
#(-> 14)
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.
Is there any way to mock (not stub) a protocol function with Midje (clojure) using something like the "provided" syntax?
This is simial to the question in: Mocking Clojure protocols, but with mocking.
In more detail: I have a protocol and a function that returns something that implements it. I would like to stub the function to return a mock of the protocol and I would like to register an expectation on one of the functions of the mocked protocol "implementation".
edit - here is an example:
There is a protocol and it's implementation:
(defprotocol Thiny (go-bump [_ _]))
(deftype TheThing []
Thiny
(go-bump [_ _] 23))
There is a function that returns an implementation of a protocol:
(defn gimme [] (TheThing.))
TheThing might be a DB or network connection or some other nasty thing you want to get rid of in the test.
Then, there is the function I want to test:
(defn test-me [n]
(let [t (gimme)]
(-> t (go-bump n))))
I want to make sure it calls go-bump with n.
This is my first attempt to create a test. But it is only halfway done, I would like to setup expectations on the Thiny returned by gimme:
(fact
(test-me 42) => 42
(provided (gimme) => (reify Thiny (go-bump [_ n] n))))
Mocking protocols should be no different than mocking functions, you need to consider that the first dispaching parameter is this and so the mocking function should take that type in consideration.
For instance, given a protocol P:
(defprotocol P
(foo [this])
(bar-me [this] [this y]))
Extended for type Integer
(extend-protocol P
Integer
(foo [this]
(+ this 4))
(bar-me [this]
(* this 5))
(bar-me [this y]
(+ this y)))
You can check a couple things first, there's no implementation for Long:
(foo 3)
=> IllegalArgumentException No implementation of method: :foo of
protocol: #'P found for class: java.lang.Long
clojure.core/-cache-protocol-fn (core_deftype.clj:541)
Works as expected for ints:
(foo (int 3))
=> 7
Now define the fact and provide the protocol function as you need:
(fact
(foo (int 3)) => 7
(provided (foo 3) => 8))
In this case it properly fails since the mock is returning 8 instead of 7 for the specified input.
FAIL at (test.clj:20)
Expected: 7
Actual: 8
If value mocks are not enough and you need to provide an alternative implementation instead, take a look at with-redefs-fn, you can wrap tested functions with it.
=> (defn f [] false)
=> (println (f))
;; false
=> (with-redefs-fn {#'f (fn [] true)}
#(println (f)))
;; true
There's also a discussion in GitHub about his with a couple alternatives for runtime dispatching mocks.
For posterity. Here is a working test:
(fact
(test-me 42) => 42
(provided (gimme) => :the-thingy)
(provided (go-bump :the-thingy 42) => 42))
The trick was to use multiple provided statements that tie into each other.
Wierd observation. The same way of testing doesn't work for a function that uses the other syntax for calling functions on the protocol. No idea why.
(defn test-me2 [n]
(let [t (gimme)]
(.go-bump t n)))
I'd like to call java.nio.file.Files/readAttributes, but doing so always fails with No matching method. For example:
user=> (java.nio.file.Files/readAttributes (-> "/etc/passwd" clojure.java.io/file .toPath) "posix:group")
CompilerException java.lang.IllegalArgumentException: No matching method: readAttributes, compiling:(NO_SOURCE_PATH:1:1)
user=> (java.lang.System/getProperty "java.version")
"1.7.0_25"
For a sanity check, doing the equivalent works fine from the scala repl:
scala> java.nio.file.Files.readAttributes((new java.io.File("/etc/passwd")).toPath, "posix:group")
res11: java.util.Map[String,Object] = {group=root}
It turns out that to call java variadic methods, you have to explicitly pass the vararg part as an array, even if you don't care about it. (see the question Java Interop — Netty + Clojure)
The following works:
user=> (java.nio.file.Files/readAttributes (-> "/etc/passwd" clojure.java.io/file .toPath) "posix:group" (into-array java.nio.file.LinkOption []))
{"group" #<Group root>}