I wish to throw an exception and have the following:
(throw "Some text")
However it seems to be ignored.
You need to wrap your string in a Throwable:
(throw (Throwable. "Some text"))
or
(throw (Exception. "Some text"))
You can set up a try/catch/finally block as well:
(defn myDivision [x y]
(try
(/ x y)
(catch ArithmeticException e
(println "Exception message: " (.getMessage e)))
(finally
(println "Done."))))
REPL session:
user=> (myDivision 4 2)
Done.
2
user=> (myDivision 4 0)
Exception message: Divide by zero
Done.
nil
clojure.contrib.condition provides a Clojure-friendly means of handling exceptions. You can raise conditons with causes. Each condition can have its own handler.
There are a number of examples in the source on github.
It's quite flexible, in that you can provide your own key, value pairs when raising and then decide what to do in your handler based on the keys/values.
E.g. (mangling the example code):
(if (something-wrong x)
(raise :type :something-is-wrong :arg 'x :value x))
You can then have a handler for :something-is-wrong:
(handler-case :type
(do-non-error-condition-stuff)
(handle :something-is-wrong
(print-stack-trace *condition*)))
If you want to throw an exception and include some debugging info in it (in addition to a message string), you can use the built-in ex-info function.
To extract the data from the previously-constructed ex-info object, use ex-data.
Example from clojuredocs:
(try
(throw
(ex-info "The ice cream has melted!"
{:causes #{:fridge-door-open :dangerously-high-temperature}
:current-temperature {:value 25 :unit :celcius}}))
(catch Exception e (ex-data e))
In a comment kolen mentioned slingshot, which provides advanced functionality that allows you not only to throw objects of arbitrary type (with throw+), but also use a more flexible catch syntax to inspect data inside thrown objects (with try+). Examples from the project repo:
tensor/parse.clj
(ns tensor.parse
(:use [slingshot.slingshot :only [throw+]]))
(defn parse-tree [tree hint]
(if (bad-tree? tree)
(throw+ {:type ::bad-tree :tree tree :hint hint})
(parse-good-tree tree hint)))
math/expression.clj
(ns math.expression
(:require [tensor.parse]
[clojure.tools.logging :as log])
(:use [slingshot.slingshot :only [throw+ try+]]))
(defn read-file [file]
(try+
[...]
(tensor.parse/parse-tree tree)
[...]
(catch [:type :tensor.parse/bad-tree] {:keys [tree hint]}
(log/error "failed to parse tensor" tree "with hint" hint)
(throw+))
(catch Object _
(log/error (:throwable &throw-context) "unexpected error")
(throw+))))
Related
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.
My re-frame views.cljs has:
(re-frame/dispatch [::re-graph/init
{:http-url "https://api.spacex.land/graphql"
:ws-url nil
:http-parameters {:with-credentials? false}}])
(re-frame/dispatch [::re-graph/query
"{ launches { id, mission_name } }" ;; your graphql query
[::update-data]])
My events.cljs has:
(re-frame/reg-event-db
::update-data
(fn [db [_ {:keys [data errors] :as payload}]]
(-> db
(assoc :errors errors)
(assoc :data data))))
But I keep getting this error:
core.cljs:3919 re-frame: no :event handler registered for: undefined
The solution is to include the nil for the query variables
(re-frame/dispatch
[::re-graph/query
"{launches {id, mission_name}}"
nil
[:add-launches]])
You should use :events/update-data in views.cljs. The :: refers to the current namespace (:views/update-data), and that event handler is not defined there, but in the events namespace.
Also note that you can use:
(-> db
(assoc :errors errors
:data data)))
saves you one assoc.
How do I make sure that clojure/cljs.spec is verifying function call arguments and return values?
Say I have this function:
(defn my-inc [x]
(inc x))
After which I have this:
(s/fdef my-inc
:args (s/cat :x number?)
:ret number?)
This code compiles because [cljs.spec.alpha :as s] has been required.
Now I call the function so as to hopefully generate an error:
(my-inc "Not a number")
I would like to see the fdef being used, and see the error message stating that my-inc cannot be called with a string. How do I make this happen in a very general way, for instance with a setting in project.clj or user.cljs?
With this code in user.cljs:
(:require
[cljs.spec.alpha :as s]
[cljs.spec.test.alpha :as ts])
(defn my-inc [x]
(inc x))
(s/fdef my-inc
:args (s/cat :x number?)
:ret number?)
(ts/instrument)
(defn x-1 []
(my-inc "Hi"))
I can call x-1 from the cljs/figwheel REPL, and get this failure message:
#error {:message "Call to #'cljs.user/my-inc did not conform to spec:\nIn: [0] val: \"Hi\" fails at: [:args :x] predicate: number?\n:cljs.spec.alpha/spec #object[cljs.spec.alpha.t_cljs$spec$alpha50572]\n:cljs.spec.alpha/value (\"Hi\")\n:cljs.spec.alpha/args (\"Hi\")\n:cljs.spec.alpha/failure :instrument\n", :data #:cljs.spec.alpha{:problems [{:path [:args :x], :pred cljs.core/number?, :val "Hi", :via [], :in [0]}], :spec #object[cljs.spec.alpha.t_cljs$spec$alpha50572], :value ("Hi"), :args ("Hi"), :failure :instrument}}
I can also get conformance errors when working with a real project running code in a browser. The errors show up in the developer's console on the browser.
Put (ts/instrument) at the bottom of user.cljs to turn instrumentation on for all namespaces for development.
Edit
Just in case you are hit by this problem: https://dev.clojure.org/jira/browse/CLJS-1792
- the fix is to include [org.clojure/test.check "0.10.0-alpha2"] (probably with a more recent version) in your project.clj dependencies.
You can use instrument to check args conformance for certain symbols or all vars.
When called without arguments, instrument wraps all instrumentable vars into a function that checks the args before delegating to the original function.
I would like to display the html output of the following object:
(defn search-input [_ owner]
(reify
om/IInitState
(init-state [_]
{:text nil})
om/IRenderState
(render-state [this state]
(dom/input
#js {:type "text"
:value (:text state)
:className "form-control"
:onChange (fn [event] (handle-change event owner state))}))))
There is a render-to-str method in om.dom. But if I type
om.dom/render-to-str
in the ClojureScript repl all I get is nil. And calling om.dom/render-to-str gives the correspondign error message.
TypeError: 'undefined' is not an object (evaluating 'om.dom.render_to_str.call')
The strange thing: Code completion in the repl gives me the render-to-str call.
Ok the problem with om.dom/render-to-str returning nil is solved. The problem was that I didn't connect to a real browser repl but a headless repl. Therefore no index.html was loaded and therefore not react.js was loaded.
But now calling
(dom/render-to-str (search-input nil {}))
returns
"Error evaluating:" (dom/render-to-str (search-input nil {})) :as "om.dom.render_to_str.call(null,om_oanda.core.search_input.call(null,null,cljs.core.PersistentArrayMap.EMPTY));\n"
#<Error: Invariant Violation: renderComponentToString(): You must pass a valid ReactComponent.>
Error: Invariant Violation: renderComponentToString(): You must pass a valid ReactComponent.
After some more tests I think I have to change the call like this:
(dom/render-to-str (om.core/build search-input a-cursor {}))
So the last question is: How do I create a cursor.
(defn render-to-str
"Equivalent to React.renderComponentToString"
[c]
(js/React.renderComponentToString c))
Try calling the function with the component as an argument.
I'm coding something like REPL Server. Request from users evaluates in such function:
(defn execute [request]
(str (try
(eval (read-string request))
(catch Exception e (.getLocalizedMessage e)))))
Each client in separate thread. But they have the same namespace. How can I run code in dynamic created namespace ? So when new client connected, I want to create new namespace and to run client handling loop code there. Or maybe it's possible to run (eval ..) in other namespace ?
Thanks.
upd.
Solved!
Execute function:
(defn execute
"evaluates s-forms"
([request] (execute request *ns*))
([request user-ns]
(str
(try
(binding [*ns* user-ns] (eval (read-string request)))
(catch Exception e (.getLocalizedMessage e))))))
Each client gets it's own namespace by:
(defn generate-ns
"generates ns for client connection"
[] (let [user-ns (create-ns (symbol (str "client-" (Math/abs (.nextInt random)))))]
(execute (str "(clojure.core/refer 'clojure.core)") user-ns)
user-ns))`
(defn delete-ns
"deletes ns after client disconnected"
[user-ns] (remove-ns (symbol (ns-name user-ns))))
offtop: How to make offsets in code snippets on begin of line ?
Solved:
(binding [*ns* user-ns] (eval (read-string request)))
(symbol (str "client-" (Math/abs (.nextInt random)))
I just wanted to add, that this could be achieved with
(gensym "client-")
(I wanted to comment, but it turns our that I can't :))
Changing namespace means that you will have to reinitialize all the aliases, or refer to even clojure.core stuff with a fully qualified name:
user=> (defn alien-eval [ns str]
(let [cur *ns*]
(try ; needed to prevent failures in the eval code from skipping ns rollback
(in-ns ns)
(eval (read-string str))
(finally
(in-ns (ns-name cur))
(remove-ns ns))))) ; cleanup, skip if you reuse the alien ns
#'user/alien-eval
user=> (alien-eval 'alien "(clojure.core/println clojure.core/*ns*)") ; note the FQN
#<Namespace alien> ; the effect of println
nil ; the return value of alien-eval
You can write a macro that mimics
(defmacro my-eval [s] `~(read-string s))
It works better that eval because the symbol resolution of s occurs in the context that calls my-eval. Thanks to #Matthias Benkard for the clarifications.