6

これが私が期待するように機能することを考えると:

(do
  (println (resolve 'a)) ; nil 
  (def a "a")
  (println (resolve 'a))) ; #'user/a

なぜこれが行われないのかを理解したいと思います。

(future
  (println (resolve 'b)) ; #'user/b (shouldn't it be still undefined at this point?)
  (def b "b")
  (println (resolve 'b))) ; #'user/b

これが適切な解決策であるかどうかも知りたいです(同じ問題を正確に解決するわけではありませんが、私のコンテキストで同等の仕事をしています):

(def c (atom nil))
(future
  (println @c) ; nil
  (reset! c "c")
  (println @c)) ; c
4

1 に答える 1

7

この動作は、defフォームがコンパイルされる方法の結果として発生します。

defトップレベルではない (またはおそらくトップレベル内 -- この場合の詳細なコメントについては以下を参照) フォームを使用するletことは、スタイルの問題として嫌われることに注意してください。一方、Atom を使用したスニペットは問題ありません。必要な機能があれば、使用しない理由はありません。

def話に移ります:

  1. defフォームの編集:

    フォームが検出されると、その時点でdef適切な名前の Var がコンパイラによって現在の名前空間に作成されます。(名前空間で修飾されたシンボルを name 引数として使用して、現在の名前空間の外にある Varを試行すると、例外が発生します)。その Var は最初はバインドされておらず、実際に実行されるまでバインドされていません。トップレベルの場合、それはすぐに行われますが、関数の本体内(またはフォーム内-以下を参照)の非表示の場合、それは関数が呼び出されたときです:defdefdefdefdeflet

    ;;; in the user namespace:
    
    (defn foo []
      (def bar "asdf")
     :done)
    ; => #'user/foo
    
    bar
    ; => #<Unbound Unbound: #'user/bar>
    
    ;;; let's change the namespace and call foo:
    
    (ns some.ns)
    (user/foo)
    ; => :done
    
    bar
    ; exception, the bar Var was created in the user namespace!
    
    user/bar
    ; => "asdf"
    ; the Var's namespace is fixed at compile time
    
  2. 最初の例 --do形式:

    トップ レベルdoの は、そのコンテンツが発生した場所でコード フローに結合されたかのように扱われdoます。したがって、REPL で入力すると、最初の式、次に、次に 2 番目の式(do (println ...) (def ...) (println ...))を入力するのと同じことになります(ただし、REPL は新しいプロンプトを 1 つしか生成しません)。printlndefprintln

  3. 2 番目の例 -- を使用future:

    (future ...)に近いものに展開し(future-call (fn [] ...))ます。...フォームが含まれている場合def、上記の方法でコンパイルされます。匿名関数が独自のスレッドで実行されるまでに、Var が作成されているresolveため、それを見つけることができます。

  4. 補足として、同様のスニペットとその出力を見てみましょう。

    (let []
      (println (resolve 'c)) 
      (def c "c") 
      (println (resolve 'c)))
    ; #'user/c
    ; #'user/c
    ; => nil
    

    その理由は、let最初にコンパイルされてから全体として実行される余分なポイントの前と同じです。これは、内部に定義があるトップレベルのフォームを使用する際に心に留めておくべきことletです。定義に副作用のあるコードが混ざっていない限り、通常は問題ありません。それ以外の場合は、特に注意する必要があります。

于 2012-08-21T18:16:56.460 に答える