In Clojure creating a function that creates 'n' JButtons each with different 'actionPerformed' method in other namespace - function

I have adapted this method from p.216 Joy of Clojure.
(defn makeButton [txt func panel]
(let [btn (JButton. txt) ] ;bfun (symbol func) ]
(doto btn
(.addActionListener
(proxy [ActionListener] [] (actionPerformed [_]
(func))) ;(bfun)))
))
(.add panel btn)
))
Plus this test code:
(defn fba [] (prn "fba"))
(defn fbb [] (prn "fbb"))
(def funs [fba fbb])
(def btnames ["B1" "B2"])
Buttons & actionPerformed are created thusly in a gui setup function:
(doseq [i (range (count btnames)) ]
(makeButton (get btnames i) (nth funs i) aJpanel))
This works as I want; clicking "B1" prints string "fba", etc. My difficulty arises when I want to attach to 'actionPerformed' something such as: ns1.ns2/myFunction instead of these simple cases (i.e. use a list/vector of several namespaced qualified functions). The exception reported is
wrong number of args (0) passed to Symbol
From the 'makeButton' function above my attempt to solve this is commented out. What is an idiomatic clojure solution to this?

This code works for me in the REPL. I'm using clojure.pprint/pprint as an example of a function that is in a different namespace:
(import '[javax.swing JFrame JPanel JButton]
'[java.awt.event ActionListener])
(def panel (JPanel.))
(def frame (doto (JFrame. "Hello Frame")
(.setContentPane panel)
(.setSize 200 200)
(.setVisible true)))
(defn make-button [label f panel]
(let [button (JButton. label)]
(doto button
(.addActionListener
(proxy [ActionListener] []
(actionPerformed [evt]
(f evt)))))
(.add panel button)))
(make-button "One" clojure.pprint/pprint panel)
(.revalidate frame)

Related

How to pass a symbol to a function to create a function in clojure

As a minimal example of what I want to do:
(defn mkfn [func]
(fn func [a] (print "I am a function")))
(mkfn 'x) ; => #function[user/mkfn/func--10871]
(type x)
(x)
The last two both result in:
Syntax error compiling at (conjure-log-12628.cljc:1:1).
Unable to resolve symbol: x in this context
I'm not sure why this doesn't work since fn takes symbols as input and 'x is a symbol. I'm also not sure how to accomplish this task.
For that matter:
user=> (def (eval 'y) 3)
Syntax error compiling def at (conjure-log-12628.cljc:1:1).
user=> (def 'y 3)
Syntax error compiling def at (conjure-log-12628.cljc:1:1).
First argument to def must be a Symbol
First argument to def must be a Symbol
user=> (type 'y)
clojure.lang.Symbol
Other things that don't work:
(defn mkfn [func]
(fn (sympol func) [a] (print "i am a function")))
(symbol "y") ; => y ; a symbol
(def (symbol "y") 3) ; => an err
You will probably need a macro. It seems that you want to call that function by the provided name, so you also have to replace fn with defn.
And you have to be careful about a number of arguments, because function x with argument vector [a] must be called with one argument, and not like (x).
(defmacro mkfn [func]
`(defn ~func [~'a]
(print "I am a function")))
(mkfn x)
=> #'user/x
(x 1)
I am a function=> nil
There is also other way, using intern, so you can completely avoid writing macros:
(intern *ns* 'x (fn [a] (print "I am a function")))
=> #object...
(x 1)
I am a function=> nil
Example with intern:
(defn mkfn [func]
(intern *ns* func (fn [a] (print "I am a function"))))
=> #'user/mkfn
(mkfn 'y)
=> #'user/y
(y 1)
I am a function=> nil
As for your errors, def is a special form, so it has different evaluation rules. It doesn't evaluate the first argument, which has to be a symbol- and (unevaluated) (eval 'y), 'y or (symbol "y") aren't symbols, while y is.
You gonna need a macro for that since you need code writing code.
(defmacro mkfn [func]
`(fn ~func [~'a] ...))
There 2 ways of doing it, either function plus eval or macro. If you really want to programatically create a new function with your chosen name, the macro solution is the way to go.
The function + eval solution is instructive, but you'll have to either quote the function name when you call it (via a 2nd eval) or save the created function in another variable when you create it.
If you are interested in writing macros, please see this other question first: How do I write a Clojure threading macro?
For the function + eval, we can start with my favorite template project and add the following:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defn maker-eval
[fn-sym]
(let [ll (list 'fn 'anon-fn [] (str "I am " fn-sym))]
(spyx :eval ll)
(eval ll)))
(verify
(let [anon-fn-1 (maker-eval 'aaa)]
(is= "I am aaa" (anon-fn-1))) ; need the temp local variable
(let [anon-fn-2 (maker-eval 'bbb)]
(is= "I am bbb" (anon-fn-2))) ; need the temp local variable
)
and we can see the creation and use of the function, along with printed output:
:eval ll => (fn anon-fn [] "I am aaa")
:eval ll => (fn anon-fn [] "I am bbb")
For the macro version, we type
(defn maker-macro-impl
[fn-sym]
(let [ll `(defn ~fn-sym [] (str "I am " (str (quote ~fn-sym))))]
(spyx :macro ll)
ll))
(defmacro maker-macro
[fn-sym] (maker-macro-impl fn-sym))
(verify
(let [anon-fn-3 (maker-macro-impl 'ccc)]
(is= anon-fn-3 (quote
(clojure.core/defn ccc [] (clojure.core/str "I am " (clojure.core/str (quote ccc)))))))
(maker-macro ddd)
(is= (ddd) "I am ddd"))
and see printed:
:macro ll => (clojure.core/defn ccc [] (clojure.core/str "I am " (clojure.core/str (quote ccc))))
Note that the local variable anon-fn-3 was only used to test the maker-macro-impl function, but was not needed to call the newly-created function ddd
at the end of the unit test.

How to return a promise from a go block?

The question is how to send to a nodejs app the result of a go block
i found a solution with callback
but i need a solution with promise
Promise solution?
Clojurescript app
(defn foo []
(go 1))
;;how to change foo,wrap to promise?, so node app can await to get the 1
;;i used 1 for simplicity in my code i have something like
;;(go (let [x (<! ...)] x))
Node app
async function nodefoo() {
var x = await foo();
console.log(x); // i want to see 1
}
Callback solution (the one that works now)
So far i only found a way to pass a cb function, so this 1 goes back to node.js app
Clojurescript app
(defn foo1 [cb]
(take! (go 1)
(fn [r] (cb r))))
Node app
var cb=function () {....};
foo1(cb); //this way node defined cb function will be called with argument 1
But i dont want to pass a callback function, i want node.js to wait and get the value.
I want to return a promise.
This function takes a channel and returns a Javascript Promise that resolves with the first value the channel emits:
(defn wrap-as-promise
[chanl]
(new js/Promise (fn [resolve _]
(go (resolve (<! chanl))))))
Then to show usage:
(def chanl (chan 1))
(def p (wrap-as-promise chanl))
(go
(>! chanl "hello")
(.then p (fn [val] (println val))))
If you compile that and run it in your browser (assuming you called enable-console-print!) you'll see "hello".
It is also possible to extend the ManyToManyChannel type with extend-type.
Here's a naif implementation using a similar wrap-as-promise function
(require '[clojure.core.async.impl.channels :refer [ManyToManyChannel]])
(defn is-error? [val] (instance? js/Error val))
(defn wrap-as-promise
[channel callback]
(new js/Promise
(fn [resolve reject]
(go
(let [v (<! channel)]
(if (is-error? v)
(reject v)
(resolve (callback v))))))))
(extend-type ManyToManyChannel
Object
(then
[this f]
(wrap-as-promise this f)))
(def test-chan (chan 1))
(put! test-chan (new js/Error "ihihi"))
(put! test-chan :A)
(defn put-and-close! [port val]
(put! port val)
(async/close! port))
(-> test-chan
(.then (fn [value] (js/console.log "value:" value)))
(.catch (fn [e] (js/console.log "error" e)))
(.finally #(js/console.log "finally clause.")))

clojurescript iterate over the keys of an object

I am using clojurescript 0.0-2371 and I am trying to write some code that will clone an object. I have this code where I want to clone a node and calls a clone-object function:
(def animate
(js/React.createClass
#js
{:getInitialState
(fn []
(this-as this
{:children
(->
(.. this -props -children)
(js/React.Children.map (fn [child] child))
(js->clj :keywordize-keys false))}))
:render
(fn []
(this-as this
(let [children (:children (.. this -state))]
(doseq [[k v] children]
(clone-object (aget children k))))))}))
clone-object looks like this:
(defn clone-object [obj]
(log/debug obj)
(doseq [[k v] obj]
(log/debug k)))
And if I call clone-object like this:
(doseq [[k v] children]
(clone-object v))
I get this error:
Uncaught Error: [object Object] is not ISeqable
The answer was to use goog.object.forEach:
(defn clone-object [key obj]
(goog.object/forEach obj
(fn [val key obj]
(log/debug key))))
You don't strictly need Google Closure for looping through keys:
(defn obj->vec [obj]
"Put object properties into a vector"
(vec (map (fn [k] {:key k :val (aget obj k)}) (.keys js/Object obj)))
)
; a random object
(def test-obj (js-obj "foo" 1 "bar" 2))
; let's make a vector out of it
(def vec-obj (obj->vec test-obj))
; get the first element of the vector and make a key value string
(str (:key (first vec-obj)) ":" (:val (first vec-obj)))
About obj->vec:
vec convert a sequence to a vector (optional in case you are ok with a sequence)
map execute (fn [k] ...) for each element of the list.
(fn [k] ...) Take the k element of the list and put it in a key/value map data structure, aget takes the obj property referred by the key k.
(.keys js/Object obj) Get all the keys from a js object.

Retrieve Clojure function metadata dynamically

Environment: Clojure 1.4
I'm trying to pull function metadata dynamically from a vector of functions.
(defn #^{:tau-or-pi: :pi} funca "doc for func a" {:ans 42} [x] (* x x))
(defn #^{:tau-or-pi: :tau} funcb "doc for func b" {:ans 43} [x] (* x x x))
(def funcs [funca funcb])
Now, retrieving the metadata in the REPL is (somewhat) straight-forward:
user=>(:tau-or-pi (meta #'funca))
:pi
user=>(:ans (meta #'funca))
42
user=>(:tau-or-pi (meta #'funcb))
:tau
user=>(:ans (meta #'funcb))
43
However, when I try to do a map to get the :ans, :tau-or-pi, or basic :name from the metadata, I get the exception:
user=>(map #(meta #'%) funcs)
CompilerException java.lang.RuntimeException: Unable to resolve var: p1__1637# in this context, compiling:(NO_SOURCE_PATH:1)
After doing some more searching, I got the following idea from a posting in 2009 (https://groups.google.com/forum/?fromgroups=#!topic/clojure/VyDM0YAzF4o):
user=>(map #(meta (resolve %)) funcs)
ClassCastException user$funca cannot be cast to clojure.lang.Symbol clojure.core/ns-resolve (core.clj:3883)
I know that the defn macro (in Clojure 1.4) is putting the metadata on the Var in the def portion of the defn macro so that's why the simple (meta #'funca) is working, but is there a way to get the function metadata dynamically (like in the map example above)?
Maybe I'm missing something syntactically but if anyone could point me in the right direction or the right approach, that'd would be great.
Thanks.
the expression #(meta #'%) is a macro that expands to a call to defn (actually def) which has a parameter named p1__1637# which was produced with gensym and the call to meta on that is attempting to use this local parameter as a var, since no var exists with that name you get this error.
If you start with a vector of vars instead of a vector of functions then you can just map meta onto them. You can use a var (very nearly) anywhere you would use a function with a very very minor runtime cost of looking up the contents of the var each time it is called.
user> (def vector-of-functions [+ - *])
#'user/vector-of-functions
user> (def vector-of-symbols [#'+ #'- #'*])
#'user/vector-of-symbols
user> (map #(% 1 2) vector-of-functions)
(3 -1 2)
user> (map #(% 1 2) vector-of-symbols)
(3 -1 2)
user> (map #(:name (meta %)) vector-of-symbols)
(+ - *)
user>
so adding a couple #'s to your original code and removing an extra trailing : should do the trick:
user> (defn #^{:tau-or-pi :pi} funca "doc for func a" {:ans 42} [x] (* x x))
#'user/funca
user> (defn #^{:tau-or-pi :tau} funcb "doc for func b" {:ans 43} [x] (* x x x))
#'user/funcb
user> (def funcs [#'funca #'funcb])
#'user/funcs
user> (map #(meta %) funcs)
({:arglists ([x]), :ns #<Namespace user>, :name funca, :ans 42, :tau-or-pi :pi, :doc "doc for func a", :line 1, :file "NO_SOURCE_PATH"} {:arglists ([x]), :ns #<Namespace user>, :name funcb, :ans 43, :tau-or-pi :tau, :doc "doc for func b", :line 1, :file "NO_SOURCE_PATH"})
user> (map #(:tau-or-pi (meta %)) funcs)
(:pi :tau)
user>
Recently, I found it useful to attach metadata to the functions themselves rather than the vars as defn does.
You can do this with good ol' def:
(def funca ^{:tau-or-pi :pi} (fn [x] (* x x)))
(def funcb ^{:tau-or-pi :tau} (fn [x] (* x x x)))
Here, the metadata has been attached to the functions and then those metadata-laden functions are bound to the vars.
The nice thing about this is that you no longer need to worry about vars when considering the metadata. Since the functions contain metadata instead, you can pull it from them directly.
(def funcs [funca funcb])
(map (comp :tau-or-pi meta) funcs) ; [:pi :tau]
Obviously the syntax of def isn't quite as refined as defn for functions, so depending on your usage, you might be interested in re-implementing defn to attach metadata to the functions.
I'd like to elaborate on Beyamor's answer. For some code I'm writing, I am using this:
(def ^{:doc "put the-func docstring here" :arglists '([x])}
the-func
^{:some-key :some-value}
(fn [x] (* x x)))
Yes, it is a bit unwieldy to have two metadata maps. Here is why I do it:
The first metadata attaches to the the-func var. So you can use (doc the-func) which returns:
my-ns.core/the-func
([x])
put the-func docstring here
The second metadata attaches to the function itself. This lets you use (meta the-func) to return:
{:some-key :some-value}
In summary, this approach comes in handy when you want both docstrings in the REPL as well as dynamic access to the function's metadata.

Clojure: exit program when Window Frame is Closed

I'd like my Clojure program to exit when a JFrame is closed.
I'm attempting to trap and handle the close event as such:
(def exit-action (proxy [WindowAdapter] []
(windowClosing [event] (fn [] (System/exit 0)))
)
)
(.addWindowListener frame exit-action)
This doesn't throw any obvious errors but it also doesn't appear to do what I want.
Assistance is appreciated.
Answer:
Adapting Rekin's answer did the trick:
(.setDefaultCloseOperation frame JFrame/EXIT_ON_CLOSE)
Note that that is:
setDefaultCloseOperation
not:
setDefaultOperationOnClose
In Java it's:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
More elaborate examples can be found in official Java Swing tutorial about Frames
I would use EXIT_ON_CLOSE, but the reason your first attempt didn't work is that the body of proxy should contain (System/exit 0), not (fn [] (System/exit 0)). Rather than exiting, you were returning (and then throwing away) a function that, when called, would exit.
Here's a short demonstration program I showed on my blog a while ago
(ns net.dneclark.JFrameAndTimerDemo
(:import (javax.swing JLabel JButton JPanel JFrame Timer))
(:gen-class))
(defn timer-action [label counter]
(proxy 1 []
(actionPerformed
[e]
(.setText label (str "Counter: " (swap! counter inc))))))
(defn timer-fn []
(let [counter (atom 0)
label (JLabel. "Counter: 0")
timer (Timer. 1000 (timer-action label counter))
panel (doto (JPanel.)
(.add label))]
(.start timer)
(doto (JFrame. "Timer App")
(.setContentPane panel)
(.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
(.setLocation 300 300)
(.setSize 200 200)
(.setVisible true)))
(println "exit timer-fn"))
(defn -main []
(timer-fn))
Note the line in timer-fn[] that sets the default close operation. Just about like Java but with a little syntax fiddling.
The purpose of the blog entry was to show an example of a closure in Clojure.