0

While toying with Clojure I wrote a function, which relied on someSymbol being undefined at first run as (resolve someSymbol) would return nil. Turns out, that by defn-ing funcion with def somewhere in it causes the symbol to be defined:

 (resolve 'someSymbol)
 (defn resolvePokus []
   (prn "I was evaluated")
   (def someSymbol 1)
   )
 (resolve 'someSymbol)

in REPL yields:

nil
#'user/resolvePokus
#'user/someSymbol

Does that mean, that some special expressions are evaluated on running defn? Which ones?

Brief look into defn's source didn't reveal anything to me, except for there is one TODO comment in this core function :)

Igand
  • 1,161
  • 1
  • 15
  • 25
  • 1
    To be very clear, by the way, rebinding a Var's root value from inside a function is a pretty serious code smell. (Which is to say -- this isn't something you're *supposed* to be doing). – Charles Duffy Apr 17 '17 at 17:20
  • 1
    Also, you might actually look at what `someSymbol` resolves to -- you'll note that while it resolves, it's unbound -- not bound to 1 -- until the function is *actually called*. – Charles Duffy Apr 17 '17 at 17:21
  • Yes, you are right - I was wondering if I should further prolong the question by adding that it is not a good idea to do that in the first place, but decided not to. – Igand Apr 17 '17 at 17:24
  • Ah - nice point, thanks. I actually noticed that, but didn't realize, it means, that the expression with def wasn't actually evaluated. Still even on first run of function the resolve will return truthy value, which was suprising to me... – Igand Apr 17 '17 at 17:26
  • Everybody always frowns upon that, but saying `(def foo ...)` merely creates a global variable. I don't see that changing that global value via a 2nd `def` is that much different than changing a global value via `swap!` or `alter-var-root` or `with-redefs`.... – Alan Thompson Apr 17 '17 at 17:26
  • 1
    @AlanThompson, the concurrency semantics by which they differ are explicitly documented. Re-`def`-ing a var is a potentially blocking operation. – Charles Duffy Apr 17 '17 at 17:29
  • 1
    @AlanThompson, ...also, unlike `alter-var-root`, `def` offers no atomicity guarantees. See http://stackoverflow.com/questions/16447621/difference-between-using-def-to-update-a-var-and-alter-var-root – Charles Duffy Apr 17 '17 at 17:34
  • 1
    FYI - functions are complied into byte-code when entered into REPL. The compiler [interns vars it encounters in def expressions in during compile time](https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L538). – Aleph Aleph Apr 17 '17 at 20:12

1 Answers1

1

I assume you are running in the repl. I do not see the behavior you describe:

clj.core=> (resolve 'someSymbol)
nil
clj.core=>  (defn resolvePokus []
      #_=>    (prn "I was evaluated")
      #_=>    (def someSymbol 1)
      #_=>    )
#'clj.core/resolvePokus

clj.core=> (resolve 'someSymbol)
#'clj.core/someSymbol

; try to use it -> error "Unbound..."
clj.core=> someSymbol
#object[clojure.lang.Var$Unbound 0x542f6481 "Unbound: #'clj.core/someSymbol"]

clj.core=> (resolvePokus)             ; run the function
"I was evaluated"
#'clj.core/someSymbol
clj.core=>  (resolve 'someSymbol)     ; still can resolve
#'clj.core/someSymbol
clj.core=> someSymbol                 ; now we can use it
1

clj.core=> (declare xyz)          ; creates a var, but unbound
#'clj.core/xyz
clj.core=> (resolve 'xyz)         ; we can see resolve it
#'clj.core/xyz

clj.core=> xyz    ; try to use it -> error "Unbound"

#object[clojure.lang.Var$Unbound 0x2d1d436f "Unbound: #'clj.core/xyz"]

clj.core=> (def xyz 5)     ; define it
#'clj.core/xyz
clj.core=> (resolve 'xyz)  ; still can resolve
#'clj.core/xyz
clj.core=> xyz             ; now we can use it
5

So when I define the function, after typing the final parentheses the repl prints that #'clj.core/resolvePokus is defined but not someSymbol. The final resolve call still returns nil.

However, if you read further it seems that Clojure does the equivalent of a (declare someSymbol) when it first sees the (def someSymbol 1). You can see the same behavior when I manually (declare xyz) and later give it a value via (def xyz 5)

You may wish to look at this answer. The details involve the "hidden" var and how it is the anonymous intermediary between the symbol xyz and the value 5.


P.S. The above example was run on Ubuntu 16.04, Clojure 1.8, Java 1.8

Community
  • 1
  • 1
Alan Thompson
  • 29,276
  • 6
  • 41
  • 48
  • Oh, sorry.I miss-pasted - there is someSymb instead of someSymbol on the last line. Correction on the way... – Igand Apr 17 '17 at 17:17
  • Are you sure about final resolve still returning nil ? My REPL doesn't think so: user=> (resolve 'someSymbol) nil user=> (defn resolvePokus [] #_=> (prn "I was evaluated") #_=> (def someSymbol 1) #_=> ) #'user/resolvePokus user=> (resolve 'someSymb) nil user=> (resolve 'someSymbol) #'user/someSymbol user=> (resolve 'someSymbol) #'user/someSymbol – Igand Apr 17 '17 at 17:34
  • Look closely (& maybe refresh the page). `(resolve 'someSymbol)` doesn't returns `nil` except the 1st time. However, the repl doesn't print anything about `someSymbol` when the `(defn resolvePokus ...)` is completed. – Alan Thompson Apr 17 '17 at 17:37
  • Well I do and the first resolve does return nil (as expected). Then I understand (now), in defn happens just some declare of necessary symbols (symbol comes to existence, but no value is stored in underlying var) and the final resolve does confirm the symbols existence (while it is still unbound). Right? Thanks for yours and Charles D. time – Igand Apr 17 '17 at 17:45