2017-04-16 13 views
1

clojureでは、関数の定義のための新しいラッパーを導入することなく、その内部で関数名を慣用的に取得できますか?関数の:test属性の本文の中の関数の名前にもアクセスできますか?関数の本体名を取得するか、テスト本体

モチベーションのために、これは特定のロギング状況、および本体の内容を変更しないように:testの本体を保持するのに役立ちます。

metaに最も近い短い解説は次のようになります。私が知る限り、クロージャにはmetaにはthisという概念はありません。

(defn a [] (:name (meta (var a)))) 

明らかに、ラッパーマクロで達成するのは簡単です。

編集:幸いにも誰も今までに言及したラムダコンビネータ。

答えて

2

質問には2つの方法があります。しかし、あなたがやりたいことを完全に自動化するには、独自のカスタムdefn置き換え/ラッパーを定義する必要があると私は思っています。


最初に気付くべきことは、すべての機能が匿名であることです。我々は、入力するとき:

(defn hello [] (println "hi")) 

を私たちは本当に入力している:私たちはそのターンポイントで無名関数に匿名varを指すシンボルhelloを作成している

(def hello (fn [] (println "hi")) 

。しかし、我々はそうのような機能に「内部名」を与えることができます。

(def hello (fn fn-hello [] (println "hi"))) 

だから今我々はhellofn-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?

+1

ないが、関数が再帰的にできるので、def hello [](println "hi")) 'は本当に'(def hello(fn hello [](println "hi")) 'です。 – Thumbnail

+0

@AlanThompsonが大変感謝しています!私はここでポイントワイズの質問以外に多くのことを学びました。このディスカッションは本当に素晴らしいです – matanster

+0

@thumbnailとAlanThompson私は最初のカップルのコメントは私が何かを間違って解釈していない場合は、答えが更新された後に現れているので、ちょっと混乱しているので、単にそれらを削除(削除)することをお勧めしますか? – matanster

関連する問題