Why is this exception not being caught? - clojure

If I define the following function
(defn catcher [x] (try
(load-string x)
(catch Exception e
(prn "caught"))) )
(catcher "(+ 2 \"2\")") => "caught"
but (catcher "(keys [1 2])")=> ClassCastException java.lang.Long cannot be cast to java.util.Map$Entry
Normally both of these inputs throw ClassCastException, so why is only the first one being caught?

It seems that the exception is happening when trying to print the result of (keys [1 2]).
(type (catcher "(keys [1 2])"))
=> clojure.lang.APersistentMap$KeySeq
Here you can see the expression is actually creating/returning a KeySeq without throwing/catching an exception. It's only when that KeySeq is printed that the exception is thrown:
java.lang.ClassCastException: java.lang.Long cannot be cast to java.util.Map$Entry
at clojure.lang.APersistentMap$KeySeq.first(APersistentMap.java:168)
at clojure.lang.RT.first(RT.java:685)
at clojure.core$first__5107.invokeStatic(core.clj:55)
at clojure.core$print_sequential.invokeStatic(core_print.clj:64)
at clojure.core$fn__7021.invokeStatic(core_print.clj:174)
at clojure.core$fn__7021.invoke(core_print.clj:174)
at clojure.lang.MultiFn.invoke(MultiFn.java:233)
at clojure.tools.nrepl.middleware.pr_values$pr_values$fn$reify__784.send(pr_values.clj:35)
Notice in the stack trace this exception is originating from KeySeq.first method (which isn't being called until after your function has created and returned its value) when the REPL tries to realize the sequence for printing.

Please note that [1 2], when you type it, is a clojure vector literal, not a Clojure MapEntry. See these results:
(ns tst.demo.core
(:use tupelo.test)
(:require
[tupelo.core :as t] ) )
(t/refer-tupelo)
(dotest
(newline)
(let [my-map {:a 1 :b 2}
map-entries (vec my-map)
map-entry-1 (first map-entries)
map-keys (keys my-map)
entry-1-key (key map-entry-1)
]
(is= map-entries [[:a 1] [:b 2]] )
(is= map-entry-1 [:a 1] )
(is= map-keys [:a :b])
(is= entry-1-key :a)
(spyxx my-map)
(spyxx map-entries)
(spyxx map-entry-1)
(spyxx map-keys)
(spyxx entry-1-key)
))
with results:
Testing tst.demo.core
my-map => <#clojure.lang.PersistentArrayMap {:a 1, :b 2}>
map-entries => <#clojure.lang.PersistentVector [[:a 1] [:b 2]]>
map-entry-1 => <#clojure.lang.MapEntry [:a 1]>
map-keys => <#clojure.lang.APersistentMap$KeySeq (:a :b)>
entry-1-key => <#clojure.lang.Keyword :a>
Ran 2 tests containing 4 assertions.
0 failures, 0 errors.
The problem is that a MapEntry prints the same as a vector:
`[1 2]`
However, they are different types. When you do (load-string "[1 2]") it is returning the vector, not a map, so you cannot call the keys function.
Part #2
In your original question, you ask about
(catcher "(keys [1 2])") => Exception
I suspect that load-string is returning a lazy result, and this is not realized until after your code exits the try-catch block, and this is why the exception is not caught.

Related

Unquoting without namespace

I need to quote without namespace and combine it with unquoting. Something like:
'[a b ~c]
Unfortunately, unquoting works only with syntactic quoting:
`[a b ~c]
But then it expands to
[user/a user/b 7]
I would like to expand without namespaces.
What was suggested on clojurians slack channel is as follows:
Use a combination of "quote unquote" for symbols to get rid of namespaces:
`[~'a ~'b ~c]
and this works perfectly.
As a reference, I have been working on a similar capability that doesn't require defensive treatment like ~'a for each symbol you wish to remain unchanged. It isn't published yet, but here is the technique:
;-----------------------------------------------------------------------------
(defn unquote-form?
[arg]
(and (list? arg)
(= (quote unquote) (first arg))))
(defn unquote-splicing-form?
[arg]
(and (list? arg)
(= (quote unquote-splicing) (first arg))))
(defn quote-template-impl
[form]
(walk/prewalk
(fn [item]
(cond
(unquote-form? item) (eval (xsecond item))
(sequential? item) (let [unquoted-vec (apply glue
(forv [it item]
(if (unquote-splicing-form? it)
(eval (xsecond it))
[it])))
final-result (if (list? item)
(t/->list unquoted-vec)
unquoted-vec)]
final-result)
:else item))
form))
(defmacro quote-template
[form]
(quote-template-impl form))
and unit tests to show it in action:
;-----------------------------------------------------------------------------
(def vec234 [2 3 4])
(dotest
(is (td/unquote-form? (quote (unquote (+ 2 3)))))
(is (td/unquote-splicing-form? (quote (unquote-splicing (+ 2 3)))))
(is= (td/quote-template {:a 1 :b (unquote (+ 2 3))})
{:a 1, :b 5})
(is= (td/quote-template {:a 1 :b (unquote (vec (range 3)))})
{:a 1, :b [0 1 2]})
(is= (td/quote-template {:a 1 :b (unquote vec234)})
{:a 1, :b [2 3 4]})
(let [result (td/quote-template (list 1 2 (unquote (inc 2)) 4 5))]
(is (list? result))
(is= result (quote (1 2 3 4 5))))
(is= (td/quote-template [1 (unquote-splicing vec234) 5]) ; unqualified name OK here
[1 2 3 4 5])
(is= (td/quote-template [1 (unquote-splicing (t/thru 2 4)) 5])
[1 2 3 4 5])
(is= (td/quote-template [1 (unquote (t/thru 2 4)) 5])
[1 [2 3 4] 5])
)
So, instead of Clojure's syntax-quote (i.e. backquote), you can use quote-template. Then, use either unquote or unquote-splicing to insert values into the quoted template without having the namespace prepended to other symbols.
I know it is probably not the answer you are looking for by why not use code to construct that thing you want? Why force it to be all done in one expression? You could for example use
(conj '[a b] c)

Why doesn't map convey dynamic bindings? [duplicate]

From what I understand, setting a new binding on a dynamic var affects all functions called within that binding, and all functions called from those functions.
Why does the binding appear to be lost in the first example below?
(def ^:dynamic *out-dir* "/home/user")
(binding [*out-dir* "/home/dave"] (map #(str *out-dir* %) [1 2 3]))
; gives: ("/home/user1" "/home/user2" "/home/user3")
; expected: ("/home/dave1" "/home/dave2" "/home/dave3")
(binding [*out-dir* "/home/dave"] (conj (map #(str *out-dir* %) [1 2 3]) *out-dir*))
; gives: ("/home/dave" "/home/dave1" "/home/dave2" "/home/dave3")
This is caused by lazyness - map returns a lazy sequence which is defined inside the binding but is evaluated outside. You need to force the evaluation from inside:
(binding [*out-dir* "/home/dave"]
(doall (map #(str *out-dir* %) [1 2 3])))
It's true that laziness and dynamic bindings can cause problems; however, abandoning laziness is not the only solution. If you wish to preserve laziness (or to use dynamic bindings with pmap), use bound-fn or bound-fn*.
(def ^:dynamic x 0)
=> (binding [x 3] (map #(+ x %) (range 10)))
;; (0 1 2 3 4 5 6 7 8 9)
=> (binding [x 3] (map (bound-fn [y] (+ x y)) (range 10)))
;; (3 4 5 6 7 8 9 10 11 12)
=> (binding [x 3] (map (bound-fn* #(+ % x)) (range 10)))
;; (3 4 5 6 7 8 9 10 11 12)
Another solution is to use Python-style generator functions available via lazy-gen and yield from the Tupelo library:
(ns tst.demo.core
(:use demo.core tupelo.test)
(:require
[tupelo.core :as t] ))
(t/refer-tupelo)
(def ^:dynamic foo 1)
(dotest
(let [result (binding [foo 3]
(lazy-gen
(doseq [x (range 3)]
(yield {:foo foo :x x})))) ]
(println result)))
result => ({:foo 3, :x 0}
{:foo 3, :x 1}
{:foo 3, :x 2})

Get partial results of map after exception

How can you get the partial results from a map when an exception happens during execution?
i.e
(let [numbers [3 3 0 3]
results (map #(/ 3 %) numbers)]
results)
and return:
[3 3]
For example:
(let [numbers [3 3 0 3]
results (map
#(try (/ 3 %)
(catch Exception e e))
numbers)]
(take-while #(not (instance? Exception %)) results))
since thrown exception unwinds the stack up to the place where exception is caught (in your case it goes up to the top level), you can't operate on result after if. What you can do, is to manually put some value where the error was caught:
(let [numbers [3 3 0 3]
results (take-while number?
(map #(try
(/ 3 %)
(catch Throwable ex :error))
numbers))]
results)
Here's an approach using the cats library's exception monad:
(ns your-project.core
(:require [cats.monad.exception :as e]))
(let [numbers [3 3 0 3]
results (->> numbers
(map #(e/try-on (/ 3 %))) ; wrap division result in a try
(take-while e/success?) ; take values while successful
(mapv e/extract))] ; unwrap the values from the trys
results)
;; => [1 1]
And if you want to keep the values that come after the exception:
(let [numbers [3 3 0 3]
results (->> numbers
(map #(e/try-on (/ 3 %)))
(filter e/success?) ; filter all successful values
(mapv e/extract))]
results)
;; => [1 1 1]
Use keep:
(let [numbers [3 3 0 3]]
(keep #(try
(/ 3 %)
(catch RuntimeException _ nil)) numbers))

Clojure binding of dynamic var not working as expected

From what I understand, setting a new binding on a dynamic var affects all functions called within that binding, and all functions called from those functions.
Why does the binding appear to be lost in the first example below?
(def ^:dynamic *out-dir* "/home/user")
(binding [*out-dir* "/home/dave"] (map #(str *out-dir* %) [1 2 3]))
; gives: ("/home/user1" "/home/user2" "/home/user3")
; expected: ("/home/dave1" "/home/dave2" "/home/dave3")
(binding [*out-dir* "/home/dave"] (conj (map #(str *out-dir* %) [1 2 3]) *out-dir*))
; gives: ("/home/dave" "/home/dave1" "/home/dave2" "/home/dave3")
This is caused by lazyness - map returns a lazy sequence which is defined inside the binding but is evaluated outside. You need to force the evaluation from inside:
(binding [*out-dir* "/home/dave"]
(doall (map #(str *out-dir* %) [1 2 3])))
It's true that laziness and dynamic bindings can cause problems; however, abandoning laziness is not the only solution. If you wish to preserve laziness (or to use dynamic bindings with pmap), use bound-fn or bound-fn*.
(def ^:dynamic x 0)
=> (binding [x 3] (map #(+ x %) (range 10)))
;; (0 1 2 3 4 5 6 7 8 9)
=> (binding [x 3] (map (bound-fn [y] (+ x y)) (range 10)))
;; (3 4 5 6 7 8 9 10 11 12)
=> (binding [x 3] (map (bound-fn* #(+ % x)) (range 10)))
;; (3 4 5 6 7 8 9 10 11 12)
Another solution is to use Python-style generator functions available via lazy-gen and yield from the Tupelo library:
(ns tst.demo.core
(:use demo.core tupelo.test)
(:require
[tupelo.core :as t] ))
(t/refer-tupelo)
(def ^:dynamic foo 1)
(dotest
(let [result (binding [foo 3]
(lazy-gen
(doseq [x (range 3)]
(yield {:foo foo :x x})))) ]
(println result)))
result => ({:foo 3, :x 0}
{:foo 3, :x 1}
{:foo 3, :x 2})

How can I pretty print a PersistentHashMap in Clojure to a string?

How can I pretty print a PersistentHashMap in Clojure to a string? I am looking for something like:
(str (pprint {... hash map here...})
which I can pass around as a String
(let [s (java.io.StringWriter.)]
(binding [*out* s]
(clojure.pprint/pprint {:a 10 :b 20}))
(.toString s))
Edit: Equivalent succinct version:
(with-out-str (clojure.pprint/pprint {:a 10 :b 20}))
This should help:
(clojure.pprint/write {:a 1 :b 2} :stream nil)
according to clojure.pprint/write documentation
Returns the string result if :stream is nil or nil otherwise.
user=> (import java.io.StringWriter)
java.io.StringWriter
user=> (use '[clojure.pprint :only (pprint)])
nil
user=> (defn hashmap-to-string [m]
(let [w (StringWriter.)] (pprint m w)(.toString w)))
#'user/hashmap-to-string
user=> (hashmap-to-string {:a 1 :b 2})
"{:a 1, :b 2}\n"
(pr-str {:a 1 :b 2}) ;; => "{:a 1, :b 2}"