Why/how name shadowing the function name within the function works

I found the answer to my question, and I’d like to share it here.

Clojure allows the param name of a function that shadows the function name. In the example below, both names of the function and the input param are bar. Within the function body, the name bar resolves to the input param:

user=> (defn bar [bar] bar)
#'user/bar
user=> (bar 42)
42

When there’s no name-shadowing, within the function body, the name bar resolves to the function object itself.

user=> (defn bar [foo] bar)
#'user/bar
user=> (bar 42)
#object[user$bar 0x7428de63 "[email protected]"]

To explain this, we need to know the rules of how Clojure resolves a symbol. See Evaluation:

A Symbol is resolved:

  • If it is namespace-qualified, the value is the value of the binding of the global var named by the symbol. It is an error if there is no global var named by the symbol, or if the reference is to a non-public var in a different namespace.
  • If it is package-qualified, the value is the Java class named by the symbol. It is an error if there is no Class named by the symbol.
  • Else, it is not qualified and the first of the following applies:
    1. If it names a special form it is considered a special form, and must be utilized accordingly.
    2. If in a local scope (e.g. in a function definition or a let form), a lookup is done to see if it names a local binding (e.g. a function argument or let-bound name). If so, the value is the value of the local binding.
    3. A lookup is done in the current namespace to see if there is a mapping from the symbol to a class. If so, the symbol is considered to name a Java class object. Note that class names normally denote class objects, but are treated specially in certain special forms, e.g. . and new.
    4. A lookup is done in the current namespace to see if there is a mapping from the symbol to a var. If so, the value is the value of the binding of the var referred-to by the symbol.
    5. It is an error.

In the first case, the symbol bar is considered #2, a local binding.
In the second case, the symbol bar is considered #4, a var in the current namespace.

P.S. The original tweet that sparked this quest is here: https://twitter.com/dawranliou/status/1171063623847792647

2 Likes