2017-01-07 6 views
4

Ich fand es überraschend schwierig, ein Makro zu definieren, um die Fehlerbehandlung in beiden clj und cljs zu definieren. Ich nahm an, dass es eine einfache Sache war, Exception mit js/Error auszutauschen, aber es stellte sich als komplizierter heraus.Fehlerbehandlung in cljc Makro

Zuerst habe ich versucht, dies:

(defmacro my-macro 
    [& forms] 
`(try 
    [email protected] 
    (catch #?(:clj Exception :cljs js/Error) e# 
     ,,,))) 

Aber das erzeugt Exception jedes Mal. Ich erkannte bald, dass das Problem war, dass das Makro während der Kompilierung meiner cljs Dateien aufgerufen wurde, die in einer clj Umgebung geschieht. Daher müsste das Makro ein Formular zurückgeben, das zur Laufzeit in die korrekte Ausnahmeklasse aufgelöst wird. Ich versuchte dies:

(def exception-class 
    #?(:clj Exception :cljs js/Error)) 

(defmacro my-macro 
    [& forms] 
`(try 
    [email protected] 
    (catch exception-class e# 
     ,,,))) 

Jetzt ist es in cljs arbeitete, aber nicht in CLJ !!! Nach einigen Experimenten entdeckte ich, dass JVM Clojure (anscheinend) nicht erlaubt, indirekt auf die Ausnahmeklasse zu verweisen. Sie müssen sich direkt auf den Namen Exception beziehen.

So schließlich, ließ ich mich auf diese:

(def fake-java 
    #?(:cljs (clj->js {:lang {:Exception js/Error}}))) 

(defmacro my-macro 
    [& forms] 
`(let [~'java fake-java] 
    (try 
     [email protected] 
     (catch Exception e# 
     ,,,)))) 

Exception zu java.lang.Exception erweitert, die jetzt auf die richtige Ausnahmeklasse löst sowohl in CLJ zur Laufzeit und cljs.

Meine Frage ist, gibt es einen besseren Weg dazu? Und warum erlaubt es JVM Clojure nicht, indirekt auf die Ausnahmeklasse zu verweisen, aber ClojureScript?

aktualisiert

Mit ClojureMostly Hilfe habe ich das Makro wie dieser Refactoring, und es funktioniert:

(defmacro my-macro 
    [& forms] 
`(try 
    [email protected] 
    (catch ~(if (:ns &env) 'js/Error 'Exception) e# 
     ,,,))) 

Antwort

2

Der gemeinsame Weg, dies zu tun ist, um check for the :ns key in den speziellen &env Bindung, die Sie in einem defmacro haben. Copied from plumatic/schema:

(defn cljs-env? 
    "Take the &env from a macro, and tell whether we are expanding into cljs." 
    [env] 
    (boolean (:ns env))) 

(defmacro try-catchall 
    "A cross-platform variant of try-catch that catches all exceptions. 
    Does not (yet) support finally, and does not need or want an exception class." 
    [& body] 
    (let [try-body (butlast body) 
     [catch sym & catch-body :as catch-form] (last body)] 
    (assert (= catch 'catch)) 
    (assert (symbol? sym)) 
    (if (cljs-env? &env) 
     `(try [email protected] (~'catch js/Object ~sym [email protected])) 
     `(try [email protected] (~'catch Throwable ~sym [email protected]))))) 

Verbrauch:

(macros/try-catchall (f) (catch e# ::exception)) 
+1

Sie haben die Definition von 'if-cljs' weggelassen, was der wichtigste Teil ist! Was sie hier machen, ist, dass sie je nach Umgebung ein komplett anderes "try/catch" Formular zurückgeben, was ich nicht machen wollte. Mit einigen Optimierungen macht '(: ns & env)' genau das, was ich brauche. Ich werde meine Frage mit dem neuen Code aktualisieren. – grandinero

+0

Sorry, ich habe meine Antwort bearbeitet. – ClojureMostly

+1

Ich nehme an, dass (nach ': ns' key in' & env') funktioniert heute, und könnte sogar morgen arbeiten. Aber es gibt absolut keine Garantie, dass es funktioniert. Wenn Sie diese Lösung implementieren, ist es wichtig zu wissen, dass Sie sich auf "undokumentiertes Verhalten" verlassen. –

1

Sie könnten Ihren Makro Refactoring in Bezug auf einen Funktionsaufruf ausgedrückt werden. Diese Funktion würde einen "Thunk" der Formulare akzeptieren und ihn in try umhüllen (anstatt dies direkt im Makro zu tun).

Zum Beispiel:

(defmacro my-macro [& forms] 
    `(my-macro* (fn [] 
       [email protected]))) 

Dann können Sie definieren my-macro* als:

(defn my-macro* [f] 
    (try 
    (f) 
    (catch #?(:clj Exception :cljs js/Error) e 
     ...))) 
+0

ich bei der Behebung des Problems so einließ, denke ich an diese offensichtliche Lösung nicht einmal!Die Antwort von ClojureMostly geht jedoch direkt auf meine Frage ein, also akzeptierte ich sie. – grandinero