代码之家  ›  专栏  ›  技术社区  ›  Sam

写入try块的Clojure宏

  •  3
  • Sam  · 技术社区  · 7 年前

    问题

    我试图使用Clojure定义一个宏,其工作原理与java中的类似 它应该能够有一个绑定表单,例如[变量值],可以绑定到closable的实例。

    Java中的示例

    try (Socket s = new Socket()) {
    s.accept();
    } catch(Exception e) {}
    

    在这段代码中,套接字“s”会自动关闭,就像有一个明确的finally子句一样

    finally {
    if (s != null) s.close();
    }
    

    尝试的解决方案

    ;I am defining my macro. It can either take one argument(expression) or can take two arguments(expression and a vector with two elements(a variable and value)
    ;expression is a form expression and should be able to be evaluated
    (defmacro safe 
        ; if a vector and also an expression is passed into the macro
        [s NewS expression]
        ;I am defining my try block, and unquoting it with a ' so that the try block is not executed within the macro
        `(try   
            ;I am letting the variable(NewS) be equal to the value(s)
            (let [s NewS]
            ;I am trying the expression to see if it is valid and printing the exception if there is one
            (try 
                ~expression (catch Exception e (str "caught exception: " (.getMessage e))))
            )
            ;I am checking if my value is an instance of a java closeable
            (instance? java.util.Closeable s)
            ;I am catching the exception from the let statement if there is one
            (catch Exception e (str "caught exception: " (.getMessage e)))
        )
        ;if only an expression is passed into the macro
        [expression]
        `(try
            ~expression (catch Exception e (str "caught exception: " (.getMessage e)))
        )
    )
    

    输入和输出示例

    user> (def v (safe (/ 1 0)))
    user> v
    #<ArithmeticException java.lang.ArithmeticException: Divide by zero>
    user> (def v (safe (/ 10 2)))
    user> v
    5
    user> (def v (safe [s (FileReader. (File. "file.txt"))] (.read s)))
    user> v
    105 ; first byte of file file.txt
    user> (def v (safe [s (FileReader. (File. "missing-file"))] (. s read)))
    user> v
    #<FileNotFoundException java.io.FileNotFoundException:
    missing-file (No such file or directory)>
    

    错误消息

    当我将这些示例输入输入到我的主函数中时,我得到了一个我不理解的编译器异常。

    CompilerException clojure.lang.ArityException: Wrong number of args (1) passed to: core/safe, compiling:(/private/var/folders/6f/q7lhngtn45q_xpzd_24gjp2h0000gn/T/form-init2350735096437822603.clj:1:8) 
    

    我不知道我可以在这个宏中调整什么,但我不能让它不返回错误。

    使现代化

    这个解决方案几乎可以工作

    (defmacro safe
        ([[s NewS] expression]
        `(try  
            (let [~s ~NewS] ~expression) (catch Exception e# (str "caught exception: " (.getMessage e#)))
        ))
        ([expression]
        `(try
            ~expression (catch Exception e# (str "caught exception: " (.getMessage e#)))
        ))
    )
    

    但以下测试失败

    (defn-主 “我还没有做太多……还没有。” [&参数] (导入java.io.FileReader java.io.File) (def v(safe[s(FileReader.(File.txt”))](.read s))) (println v) )

    user$ lein run
    caught exception: file.txt (No such file or directory)
    user$ cat file.txt 
    teast
    
    1 回复  |  直到 7 年前
        1
  •  2
  •   Taylor Wood    7 年前

    自动关闭行为已由解决 with-open 宏。参见实施 here . 这里有一个公式(我认为)可以满足你的要求。

    (defmacro safe
      ([body]
       `(try ~body
          (catch Exception e#
            (str "caught exception: " e#))))
      ([bindings & body]
       `(try
          (with-open ~bindings
            ~@body)
          (catch Exception e#
            (str "caught exception: " e#)))))
    

    示例用法:

    (safe (/ 1 nil))
    ;;=> "caught exception: java.lang.NullPointerException"
    
    (safe [s (clojure.java.io/reader "file.txt")]
      (prn (.read s)))
    ;;=> "caught exception: java.io.FileNotFoundException: file.txt (No such file or directory)"
    
    (spit "file.txt" "contents here")
    (safe [s (clojure.java.io/reader "file.txt")]
      (.read s))
    ;;=> 99
    

    然而

    1. 失败时返回字符串令人困惑,因为如果 预期 值也是字符串?那么,您如何知道您的评估是成功还是失败?也许您只想打印/记录异常并返回nil?(在这种情况下,考虑更换 str 具有 println 在您的 catch 块。)
    2. 有两个数的 safe (一种仅采用一种身体形态,另一种则采用装订 close d 身体形态)纠缠着可以说应该分开的担忧。 打开时 已经存在了,当我们可以重用它时,我们不应该再进行改造。

    此版本的 安全 可以采用任意数量的形式,而不是仅采用一种形式,因此现在更加灵活。

    (defmacro safe
      [& body]
      `(try ~@body
         (catch Exception e#
           (println "caught exception:" (.getMessage e#)))))
    

    我们可以很容易地使用 打开时 在内部 安全 要获得您想要的行为:

    (safe
     (prn (/ 1 2)) ;; prints 1/2
     (with-open [s (clojure.java.io/reader "not_a_file.txt")]
       (.read s))) ;; fails, prints exception
    
    (safe
     (with-open [s (clojure.java.io/reader "file.txt")]
       (char (.read s)))) ;; returns the first char from file we `spit` above
    ;;=> \c