Welcome! Please see the About page for a little more info on how this works.

0 votes
in Clojure by

Hi, I'm getting started with Clojure and I'm puzzled by the following:

(doc special-symbol?) states "Returns true if s names a special form"
(doc loop) states "clojure.core/loopSpecial Form: Evaluates the…"
But (special-symbol? 'loop) returns false.

Since (source loop) reveals it is a (defmacro …, I wonder if in the docs loop should be called a macro rather than a special form?

Also because (special-form? 'def) returns true, and def has no source, i.e. is not defined as a macro.

1 Answer

+1 vote

loop is a wacky thing, it's defined 2 times in the source, once on line 37:

;during bootstrap we don't have destructuring let, loop or fn, will redefine later

 ^{:macro true
   :added "1.0"}
 loop (fn* loop [&form &env & decl] (cons 'loop* decl)))

the docs you get are from the second def of loop
the first def of loop hooks into some compiler magic

and then on line 4599

(defmacro loop
  "Evaluates the exprs in a lexical context in which the symbols in
  the binding-forms are bound to their respective init-exprs or parts
  therein. Acts as a recur target."
  {:added "1.0", :special-form true, :forms '[(loop [bindings*] exprs*)]}
  [bindings & body]
      (vector? bindings) "a vector for its binding"
      (even? (count bindings)) "an even number of forms in binding vector")
    (let [db (destructure bindings)]
      (if (= db bindings)
        `(loop* ~bindings ~@body)
        (let [vs (take-nth 2 (drop 1 bindings))
              bs (take-nth 2 bindings)
              gs (map (fn [b] (if (symbol? b) b (gensym))) bs)
              bfs (reduce1 (fn [ret [b v g]]
                            (if (symbol? b)
                              (conj ret g v)
                              (conj ret g v b g)))
                          [] (map vector bs vs gs))]
          `(let ~bfs
             (loop* ~(vec (interleave gs gs))
               (let ~(vec (interleave bs gs))

Ok, that's interesting. However it doesn't dispel my doubts. If `loop` is really a special form as stated in the docs, on the website https://clojure.org/reference/special_forms, and also by its final definition (`:special-from true`), then why `(special-form? loop)` returns false?
because of the second def, likely.
Because `loop` isn't actually a special form. Anything defined in clojure.core is not a special form by definition. You can see the special forms are:

user=> (keys clojure.lang.Compiler/specials)
(& monitor-exit case* try reify* finally loop* do letfn* if clojure.core/import* new deftype* let* fn* recur set! . var quote catch throw monitor-enter def)

The reason `loop` is called a special form even though it is a macro follows this general style. Here's the (abbreviated) source of the `let` macro.

(defmacro let [bindings & body] `(let* ~(destructure bindings) ~@body))
Note that it is _almost_ a passthrough to the actual special form `let*`. It just adds destructuring. And that's the main difference in lots of these: they destructure in Clojure code and call out to a special form that doesn't need to worry about destructuring.
@dpsutton -- you should write this comment as an answer, and then we can upvote it and close this question out.
@dpsutton ok, so macros named "special forms" are thin wrappers around the real special forms. This makes sense.
As @Sean suggested, if you move your comment into an answer I'd be happy to accept it.