How to make java API wrapper work with different function arities? - clojure

I'm writing a small wrapper for a Java API, and I create a listener like this
(defn conv-listener [f]
(proxy [com.tulskiy.keymaster.common.HotKeyListener] [] (onHotKey [hotKey] (f))))
Is there a way in which I can make this work whether the function f accepts 1 or zero arguments. (Ie. if f does not accept arguments, just call it with (f), if it accepts an argument - which will be the value of the hotkey in this case - call it with (f hotKey))?

No. Just call (f hotKey) all the time, and if someone wants to use a function that ignores hotKey then they can just pass something like (fn [_] (...do whatever...)).

This is how we ended up solving it (pull request from Nic Marsh):
(defn arg-count [function]
"Counts the number of arguments the given function accepts"
(let [method (first (.getDeclaredMethods (class function)))
parameters (.getParameterTypes method)]
(alength parameters)))
(defn call-with-correct-args [function & args]
"Call the given function on all given args that it can accept"
(let [amount-accepted (arg-count function)
accepted-args (take amount-accepted args)]
(apply function accepted-args)))
(defn- conv-listener [function]
"Takes a function with one argument, which will get passed the keycode, and creates a listener"
(proxy [com.tulskiy.keymaster.common.HotKeyListener] []
(onHotKey [hotKey] (call-with-correct-args function hotKey))))
http://github.com/houshuang/keymaster-clj

Related

Transducer's init not called

I am writing a custom transducer as an exercise, but I am surprised to see that its 0-arity init function is not called.
Why?
Is it related to which aggregation function I am using? If yes, which ones would call the init function and why others are not?
(defn inc-xf [xf]
"inc-xf should be equivalent to (map inc)"
(fn
;; init
([]
(println "init") ;; <- this is not called (???)
(xf))
;; step
([result input]
(println "step" result input)
(xf result (inc input)))
;; completion
([result]
(println "completion" result)
(xf result))))
(transduce inc-xf
+
100
[5 5 5])
If you look at the implementation of transduce you can see what happens.
(defn transduce
;; To get the init value, (f) is used instead of ((xform f))
([xform f coll] (transduce xform f (f) coll))
([xform f init coll]
,,,))
Why, however, is more difficult to answer.
Transducers implementing the zero arity is part of the requirements for a transducer, but it is never actually used in any transducing context in clojure.core. On the mailing list there's been a post asking the same question as you and proposal of an implementation of transduce that actually uses the init arity. The jira ticket was declined, however, with the explanation:
Rich asked me to decline the ticket because the init arity of the xform should not be involved in the reducing function accumulation.
- Alex Miller
Why, then, is the init arity part of the contract for a transducer if it's not used anywhere? ¯\_(ツ)_/¯

Clojure higher order functions

I was solving problems in 4clojure and got stuck at Problem 46 or example
(= true ((__ >) 7 8))
where we need to fill in the _
Basically I need to create a function that would take in another function as an argument but don't know where to go with that. Also, the evaluation kind of confuses me, if I create an anonymous function
(fn [f] ())
which takes in the function > I don't know how to pass in the other arguments for > to operate on.
I don't want the answer, just some direction.
Given what ever you put in the blank will take > and then be evaluated as a function what you need is a function that returns a function.
You have this:
(fn [f] ())
How do we get that to return a function?
(fn [f] (fn [something here] (something here)))
Now all you have to do is fill out the 'something here's.
The function you are looking for is the one that returns a function that reverses its arguments and applies to the original function:
(fn [f]
(fn [x y]
(f y x)))

Variadic function with keyword arguments

I'm a newbie to Clojure and I was wondering if there is a way to define a function that can be called like this:
(strange-adder 1 2 3 :strange true)
That is, a function that can receive a variable number of ints and a keyword argument.
I know that I can define a function with keyword arguments this way:
(defn strange-adder
[a b c & {:keys [strange]}]
(println strange)
(+ a b c))
But now my function can only receive a fixed number of ints.
Is there a way to use both styles at the same time?
unfortunately, no.
The & destructuring operator uses everything after it on the argument list so it does not have the ability to handle two diferent sets of variable arity destructuring groups in one form.
one option is to break the function up into several arities. Though this only works if you can arrange it so only one of them is variadic (uses &). A more universal and less convenient solution is to treat the entire argument list as one variadic form, and pick the numbers off the start of it manually.
user> (defn strange-adder
[& args]
(let [nums (take-while number? args)
opts (apply hash-map (drop-while number? args))
strange (:strange opts)]
(println strange)
(apply + nums)))
#'user/strange-adder
user> (strange-adder 1 2 3 4 :strange 4)
4
10
Move the variadic portion to the the tail of the argument list and pass the options as a map:
(defn strange-adder [{:keys [strange]} & nums]
(println strange)
(apply + nums))
(strange-adder {:strange true} 1 2 3 4 5)
There is no formal support that I know of, but something like this should be doable:
(defn strange-adder
[& args]
(if (#{:strange} (-> args butlast last))
(do (println (last args))
(apply + (drop-last 2 args)))
(apply + args)))
I don't know if this can be generalized (check for keywords? how to expand to an arbitrary number of final arguments?). One option may be putting all options in a hashmap as the final argument, and checking if the last argument is a hashmap (but this would not work for some functions that expect arbitrary arguments that could be hashmaps).

How to get the metadata of clojure function arguments?

Is there a way to generically get metadata for arguments to a function in clojure? The answer posted in this question does not, actually, work in general:
user> (defn foo "informative dox!" [] 1)
#'user/foo
user> (defmacro get-docs [func] `(:doc (meta (var ~func))))
#'user/get-docs
user> (get-docs foo)
"informative dox!"
user> (get-docs (identity foo))
; Evaluation aborted.
user> (defn process-docs [f] (let [docs (get-docs f)] (reverse docs)))
; Evaluation aborted.
The second-to-last line doesn't work because you can't call var on the list (identity foo), and the last line doesn't even compile because the compiler complains about being unable to resolve f.
Most of the solutions for this problem I've found rely on the idea that you have access to the symbol in the function's definition, or something like that, so that you can do something like (resolve 'f) or (var f). But I want something that I can use on the argument to a function, where you don't know that information.
Essentially, I'd like an expression I can put in place of the question marks below to get the metadata of #'map:
(let [x map] (??? x))
its a mouthful though possible:
(let [x map]
(:doc (meta (second (first (filter #(and (var? (second %))
(= x (var-get (second %))))
(ns-map *ns*)))))))
produces the desired result:
"Returns a lazy sequence consisting of the result of applying f to the
set of first items of each coll, followed by applying f to the set
of second items in each coll, until any one of the colls is\n exhausted. Any remaining items in other colls are ignored. Function
f should accept number-of-colls arguments."
under the hood Namespaces are essentially maps of names to vars and the vars contain functions. you can search the contents of these vars for the one that matches the function you are seeking and then look at it's associated var and get the metadata from that var.

Why does dotrace throw a StackOverflowError here?

(use '[clojure.contrib.trace])
(dotrace [str] (reduce str [\a \b]))
In a nutshell:
That's because trace-fn-call, which is the thing dotrace uses to wrap the functions to be traced, uses str to produce the nice TRACE foo => val output.
Extended explanation:
The dotrace macro does its magic by installing a thread binding for each Var holding a function to be traced; in this case, there is one such Var, clojure.core/str. The replacement looks roughly like so:
(let [f ##'str]
(fn [& args]
(trace-fn-call 'str f args)))
The trace-fn-call, to quote its docstring, "Traces a single call to a function f with args.". In doing so, it calls the traced function, takes note of the return value, prints out a nice informative message of the form TRACE foo => val and returns the value obtained from the traced function so that regular execution may continue.
As mentioned above, this TRACE foo => val message is produced used str; however, in the case at hand, this is actually the function being traced, so a call to it leads to another call to trace-fn-call, which makes its own attempt to produce the tracing output string using str, which leads to another call to trace-fn-call... ultimately leading to the stack blowing up.
A workaround:
The following modified versions of dotrace and trace-fn-call should work fine even in the presence of weird bindings for core Vars (note that futures may not be scheduled promptly; if that's a problem, see below):
(defn my-trace-fn-call
"Traces a single call to a function f with args. 'name' is the
symbol name of the function."
[name f args]
(let [id (gensym "t")]
#(future (tracer id (str (trace-indent) (pr-str (cons name args)))))
(let [value (binding [*trace-depth* (inc *trace-depth*)]
(apply f args))]
#(future (tracer id (str (trace-indent) "=> " (pr-str value))))
value)))
(defmacro my-dotrace
"Given a sequence of function identifiers, evaluate the body
expressions in an environment in which the identifiers are bound to
the traced functions. Does not work on inlined functions,
such as clojure.core/+"
[fnames & exprs]
`(binding [~#(interleave fnames
(for [fname fnames]
`(let [f# #(var ~fname)]
(fn [& args#]
(my-trace-fn-call '~fname f# args#)))))]
~#exprs))
(Rebinding trace-fn-call around a regular dotrace apparently doesn't work; my guess is that's because of clojure.* Var calls still being hard-wired by the compiler, but that's a separate matter. The above will work, anyway.)
An alternative would be to use the above my-dotrace macro together with a my-trace-fn-call function not using futures, but modified to call custom replacements for the clojure.contrib.trace functions using the following in place of str:
(defn my-str [& args] (apply (.getRoot #'clojure.core/str) args))
The replacements are straightforward and tedious and I omit them from the answer.