Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the rules for resolving symbols as macros vs. function parameters?

I was surprised to find that alpha-conversion is not safe in Clojure:

Clojure> ((fn [o] (o 3 2)) +)
5

Clojure> ((fn [or] (or 3 2)) +)
3

Clojure> ((fn [def] (def 3 2)) +)
java.lang.RuntimeException: First argument to def must be a Symbol

(I expected all three snippets to evaluate to 5).

What's the rule(s) for symbol resolution when shadowing and macros, and special forms are involved?

I'm using the version on Try Clojure.

like image 408
Matt Fenwick Avatar asked Aug 16 '12 13:08

Matt Fenwick


2 Answers

You cannot shadow special forms, according to the documentation:

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. 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.
    3. If in a local scope (i.e. in a function definition), 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.
    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.

And I'm not sure, but I interpret this passage (from the same page) to mean that you cannot shadow macros, either:

Macros are functions that manipulate forms, allowing for syntactic abstraction. If the operator of a call is a symbol that names a global var that is a macro function, that macro function is called and is passed the unevaluated operand forms. The return value of the macro is then evaluated in its place.

If the operator is not a special form or macro, the call is considered a function call.

like image 119
Matt Fenwick Avatar answered Nov 03 '22 01:11

Matt Fenwick


The first two both evaluate to 5 for me (Clojure 1.4 in Eclipse / Counterclockwise REPL). I suspect there is a bug in Try Clojure if you are getting 3.

You should be able to locally shadow over both function and macro names (though it often isn't a good idea since it can result in some subtle and confusing bugs!).

The third won't work because def is a special form. Special forms get special treatment from the Clojure reader and/or compiler, so you can't shadow them.

like image 21
mikera Avatar answered Nov 02 '22 23:11

mikera