質問には2つの方法があります。しかし、あなたがやりたいことを完全に自動化するには、独自のカスタムdefn
置き換え/ラッパーを定義する必要があると私は思っています。
最初に気付くべきことは、すべての機能が匿名であることです。我々は、入力するとき:
(defn hello [] (println "hi"))
を私たちは本当に入力している:私たちはそのターンポイントで無名関数に匿名var
を指すシンボルhello
を作成している
(def hello (fn [] (println "hi"))
。しかし、我々はそうのような機能に「内部名」を与えることができます。
(def hello (fn fn-hello [] (println "hi")))
だから今我々はhello
fn-hello
のシンボルのいずれかを使用してhello
を介して、または内部から外部からの機能にアクセスすることができます(ではない、これまで行ってください両方の場所でhello
を使用するか、または合法であるにもかかわらず多くの混乱を招くことになります)。
スローされた例外は(エラーの行番号は、多くの場合、スタックトレースから欠落しているより簡単に、問題の原因を突き止めるになり
fn-hello
シンボルが含まれますので、私は頻繁に(それ以外)無名関数で
fn-hello
メソッドを使用
)。 Instaparseを使用している場合たとえば、私たちは、匿名のマップを必要とするような関数変換:
{
:identifier fn-identifier
:string fn-string
:integer (fn fn-integer [arg] [:integer (java.lang.Integer. arg)])
:boolean (fn fn-boolean [arg] [:boolean (java.lang.Boolean. arg)])
:namespace (fn fn-namespace [arg] [:namespace arg])
:prefix (fn fn-prefix [arg] [:prefix arg])
:organization (fn fn-organization [arg] [:organization arg])
:contact (fn fn-contact [arg] [:contact arg])
:description (fn fn-description [arg] [:description arg])
:presence (fn fn-presence [arg] [:presence arg])
:revision (fn fn-revision [& args] (prepend :revision args))
:iso-date (fn fn-iso-date [& args] [:iso-date (str/join args)])
:reference (fn fn-reference [arg] [:reference arg])
:identity (fn fn-identity [& args] (prepend :identity args))
:typedef (fn fn-typedef [& args] (prepend :typedef args))
:container (fn fn-container [& args] (prepend :container args))
:rpc (fn fn-rpc [& args] (prepend :rpc args))
:input (fn fn-input [& args] (prepend :input args))
...<snip>...
}
と各機能を与えることを「内部名は」はるかに簡単、かなりのデバッグを行います。おそらく、これはClojureがより良いエラーメッセージを持っていれば不要かもしれませんが、それは長年の(&)未来の希望です。
あなたはここで詳細を見つけることができます。https://clojure.org/reference/special_forms#fn
をあなたが密接に読めば、それは、これはすでにuse-を解決したかどうかを確認するために実験する必要があるかもしれないが(defn foo [x] ...)
が
(def foo (fn foo [x] ...))
に展開されていると主張あなたが探している場合。それは我々が明示的に内部fn-fact
名前を避けるため、この例に見られるように、いずれかのように動作します:
(def fact (fn [x] ; fn-fact omitted here
(if (zero? x)
1
(* x (fact (dec x))))))
(fact 4) => 24
このバージョンでも動作します:
(def fact (fn fn-fact [x]
(if (zero? x)
1
(* x (fn-fact (dec x))))))
(fact 4) => 24
(fn-fact 4) => Unable to resolve symbol: fn-fact
だから我々は「内部名」fn-fact
が内部に隠されていることを確認しますその機能は外部からは見えない。
第2のアプローチは、マクロを使用する場合、ソースコードの行番号にアクセスする&form
グローバルデータを使用することです。
(deftest test-line-123 ; assuming this is on line 123 in source file
(is (= 3 (inc 2))))
代わりに手動で入力するevalutes
(dotest
(is (= 3 (inc 2))))
:In the Tupelo libraryこの技術は
(defmacro dotest [& body] ; #todo README & tests
(let [test-name-sym (symbol (str "test-line-" (:line (meta &form))))]
`(clojure.test/deftest ~test-name-sym [email protected])))
のエラー・メッセージを改善するために使用されるこの便利なマクロのようなユニットテストの使用を可能にします
(deftest t-addition
(is (= 3 (inc 2))))
(:line (meta &form))
などの情報にアクセスして、エラーメッセージや例外を問題のデバッグを試みている貧しい読者に役立つマクロにすることができます。
上記のマクロラッパーの例以外にも、同じテクニックの別の(より複雑な)例can be seen in the Plumatic Schema libraryが、clojure.core/defn
を拡張バージョンでラップします。
あなたはまた、Clojureのシンボルと機能の間の仲介として「匿名」VARをどのように使用するかを明確にするために、この質問を見たい場合があります( `それはどんな違いがあることWhen to use a Var instead of a function?
ないが、関数が再帰的にできるので、def hello [](println "hi")) 'は本当に'(def hello(fn hello [](println "hi")) 'です。 – Thumbnail
@AlanThompsonが大変感謝しています!私はここでポイントワイズの質問以外に多くのことを学びました。このディスカッションは本当に素晴らしいです – matanster
@thumbnailとAlanThompson私は最初のカップルのコメントは私が何かを間違って解釈していない場合は、答えが更新された後に現れているので、ちょっと混乱しているので、単にそれらを削除(削除)することをお勧めしますか? – matanster