再バインドする予定のグローバル変数を作成するたびに、その変数にアクセスするすべての関数に暗黙の引数を追加します。適切な(明示的な)引数とは異なり、この非表示の引数は関数のシグネチャに表示されず、関数がそれを使用していることを示すものはほとんどない場合があります。コードの「機能」が低下します。同じ引数を使用して同じ関数を呼び出すと、これらのグローバル動的変数の現在の状態に基づいて異なる戻り値が返される可能性があります。
グローバル変数の利点は、デフォルト値を簡単に指定できることです。また、その変数を使用するすべての関数にその変数を渡す必要がないため、怠惰になる可能性があります。
欠点は、コードの読み取り、テスト、使用、およびデバッグが難しいことです。また、コードは潜在的にエラーが発生しやすくなります。session
varを使用する関数を呼び出す前に、varをバインドまたは再バインドすることを忘れるのは簡単ですが、パラメーターがarglistにあるときにパラメーターを渡すのを忘れるのはそれほど簡単ではありません。
そのため、謎のバグや、関数間の奇妙な暗黙の依存関係が発生します。このシナリオを考えてみましょう。
user> (defn foo [] (when-not (:logged-in *session*) (throw (Exception. "Access denied!"))))
#'user/foo
user> (defn bar [] (foo))
#'user/bar
user> (defn quux [] (bar))
#'user/quux
user> (quux)
; Evaluation aborted. ;; Access denied!
の動作は、値を持つセッションに暗黙的に依存しますが、すべての関数呼び出し、およびそれらの関数が呼び出すすべてのquux
関数を掘り下げない限り、それはわかりません。quux
深さ10または20レベルのコールチェーンを想像してみてください。下部にある関数は、に依存し*session*
ます。それをデバッグして楽しんでください。
代わりに、、、を持っていた場合は、(defn foo [session] ...)
電話をかけると、セッションの準備ができていることがすぐにわかります。(defn bar [session] ...)
(defn quux [session] ...)
quux
個人的には、大量の関数が使用する強力で正常なデフォルト値がない限り、明示的な引数を使用します。これは、非常にまれに、または再バインドしないことを計画していました。(たとえば、何かを出力したいすべての関数に明示的な引数としてSTDOUTを渡すのはばかげています。)