How to create placeholder variables in catch try block in Clojure

I have Java code that I would write as follows:

String var1;
String var2;
int var3;

try {
    String var1 = func1_that_might_throw();
    String var2 = func2_that_might_throw();
    int var3 = func3_that_might_throw();

    do_something_that_might_throw(var1, var2, var3);
} catch (Exception e) {
    cleanup_resources(var1, var2, var3);
}

Including this in Clojure is a nightmare.

(try+
  (let [var1 (func1_that_might_throw)
        var2 (func2_that_might_throw)
        var3 (func3_that_might_throw)]
    (do_something_that_might_throw var1 var2 var3))
  (catch Exception e
    (cleanup_resources var1 var2 var3)))

The problem is var1, var2, var3does not exist in the catch block. Moving letto the side tryis problematic, as functions 1 through 3 can be thrown and must be caught and resources cleared.

I think that I may need only three placeholder variables outside of try, but a) I don’t even know if Clojure allows this, and b) makes it all the more complicated. I do not think this is the way the veteran did it in clojure. What is the solution here?

+4
source share
4 answers

. . , , , :

  • ,
  • , , .

, . , , , .

, , clojure , , " ", clojure java. , , , , .

, , :

user> (def ^:dynamic var1)
#'user/var1
user> (def ^:dynamic var2)
#'user/var2
user> (def ^:dynamic var3)
#'user/var3

user> (defn do-stuff []
        (binding [var1 nil
                  var2 nil
                  var3 nil]
          (try
            (set! var1 42)
            (set! var2 43)
            (set! var3 44)
            (+ var1 var2 var3)
            (catch Exception e
              e))))
#'user/do-stuff
user> (do-stuff)
129

, ( clojure , -), . , , , , " " Clojure.

, concurrency, clojure . , var1, var2 .., .

- ( ), , try-catch,

catch try , .

user> (defn do-stuff []
        (let [var1 (delay (inc 41))
              var2 (delay (dec 43))
              var3 (delay (/ 42 0))]
          (try
            (+ @var1 @var2 @var3)
            (catch Exception e
              (println "cleaning up: " var1 var2 var3)))))
#'user/do-stuff
user> (do-stuff)
cleaning up:  #object[clojure.lang.Delay 0x3c55d284 {:status :ready, :val 42}]
              #object[clojure.lang.Delay 0x7bfa6dd1 {:status :ready, :val 42}]
              #object[clojure.lang.Delay     0x5e9e285b {:status :failed, :val #error {
              :cause Divide by zero

, , , , , . delay , , . :

user> (def x (delay (/ 2 0)))
#'user/x
user> @x
ArithmeticException Divide by zero  clojure.lang.Numbers.divide (Numbers.java:158)
user> @x
ArithmeticException Divide by zero  clojure.lang.Numbers.divide (Numbers.java:158)

, , set! dyanamic vars. .

+3

Java Clojure . , , . Java , , , getErrors(). Clojure - , , --2. , - : {:exception nil} 4 : do_something_that_might_throw .

Clojure / . , ( ( )), .

+2

Ref/Atom

:

(let [v1 (ref nil)
      v2 (ref nil)
      v3 (ref nil)]
    (try
      (dosync
       (ref-set v1 (f1))
       (ref-set v2 (f2))
       (ref-set v3 (f3))
       (do-something @v1 @v2 @v3))
      (catch Exception e
        (cleanup-resources @v1 @v2 @v3))))

, :

(let [v1 (func1)]
  (try
    (let [v2 (func2)]
      (try
        (let [v3 (func3)]
          (try
            (do
              (try
                (do-something v1 v2 v3)
                (catch Exception e <error-handling>)))
            (finally (cleanup-3 v3))))
        (finally (cleanup-2 v2))))
    (finally (cleanup-1 v1))))

, cleanup-resources , , , Java-, . :

(defmacro with-cleanups [bindings & body]
  (reduce
   (fn [form [v value cleanup]]
     `(let [~v ~value]
        (try ~form
             (finally ~cleanup))))
   `(do ~@body)
   (reverse (partition 3 bindings))))

, :

(with-cleanups [v1 (func1) (cleanup-1 v1)
                v2 (func2) (cleanup-2 v2)
                v3 (func3) (cleanup-3 v3)]
  (try
    (do-something v1 v2 v3)
    (catch Exception e <error-handling>)))

, cleanup :

(with-cleanups [v1 (func1) cleanup-1
                v2 (func2) cleanup-2
                v3 (func3) cleanup-3]
  (try
    (do-something v1 v2 v3)
    (catch Exception e <error-handling>)))
+1

update:

@coredump, , op ( ):

(defmacro with-resources [[var expr & other :as resources]
                          body cleanup-block
                          [error-name error-block :as error-handler]]
  (if (empty? resources)
    `(try ~body
          (catch Throwable e#
            (let [~error-name e#] ~error-block))
          (finally ~cleanup-block))
    `(try
       (let ~[var expr]
         (with-resources ~other ~body ~cleanup-block ~error-handler))
       (catch Throwable e#
         (let ~(vec (interleave (take-nth 2 resources)
                                (repeat nil)))
           ~cleanup-block
           (let [~error-name e#] ~error-block))))))

: , - , cleanup-block ( nil ) error-block error-name , , cleanup-block body.

:

user> (with-resources [a 100 b 200 c 300]
        ;;body
        (+ a b c)
        ;;cleanup
        (println "cleanup:" :a a :b b :c c)
        ;;error handler
        (error (do (println "Error caught:" (.getMessage error))
                   :error)))

cleanup: :a 100 :b 200 :c 300
600

user> (with-resources [a (throw (Exception. "A THROWS")) b 200 c 300]
        ;;body
        (+ a b c)
        ;;cleanup
        (println "cleanup:" :a a :b b :c c)
        ;;error handler
        (error (do (println "Error caught:" (.getMessage error))
               :error)))

cleanup: :a nil :b nil :c nil
Error caught: A THROWS
:error

user> (with-resources [a 100 b (throw (Exception. "B THROWS")) c 300]
        ;;body
        (+ a b c)
        ;;cleanup
        (println "cleanup:" :a a :b b :c c)
        ;;error handler
        (error (do (println "Error caught:" (.getMessage error))
                   :error)))

cleanup: :a 100 :b nil :c nil
Error caught: B THROWS
:error

user> (with-resources [a 100 b 200 c (throw (Exception. "C THROWS"))]
        ;;body
        (+ a b c)
        ;;cleanup
        (println "cleanup:" :a a :b b :c c)
        ;;error handler
        (error (do (println "Error caught:" (.getMessage error))
                   :error)))

cleanup: :a 100 :b 200 :c nil
Error caught: C THROWS
:error

user> (with-resources [a 100 b 200 c 300]
        ;;body
        (throw (Exception. "BODY THROWS"))
        ;;cleanup
        (println "cleanup:" :a a :b b :c c)
        ;;error handler
        (error (do (println "Error caught:" (.getMessage error))
                   :error)))

Error caught: BODY THROWS
cleanup: :a 100 :b 200 :c 300
:error
+1

Source: https://habr.com/ru/post/1627364/


All Articles