concept form in category clojure

appears as: form, forms, forms, form, A form, The form, The forms
Clojure: The Essential Reference

This is an excerpt from Manning's book Clojure: The Essential Reference.

Since 2010, Chas Emerick has been asking the Clojure community a few questions in the form of an yearly survey to collect feedback about Clojure adoption in the industry. Cognitect, the company that is actively sponsoring the development of Clojure, is continuing the tradition with the last available results for 2019 published on their website [12]. Since the beginning of the survey, one of the major concern that people reported has been about the quantity and quality of the Clojure documentation.

(macroexpand '(defmacro simple [a] (str a))) ; #1

;; (do
;;   (clojure.core/defn simple ([&form &env a] (str a))) ; #2
;;   (. (var simple) (setMacro)) ; #3
;;   (var simple)) ; #4

4.1.2  &form

This one is almost self-descriptive: &form will resolve to the original form (as data) that the current macro has been invoked as. An example is worth a thousand words:

(defmacro just-print-me [& args] ; #1
  (println &form))

(just-print-me foo :bar 123)     ; #2
;; (just-print-me foo :bar 123)
;; nil

Some might be tempted to observe that the previous macro could be rewritten without any need for &form, like this:

(defmacro just-print-me [& args]
  (println (apply list 'just-print-me args)))

What’s the point of &form then? The answer is twofold:

  • &form is resilient to eventual re-naming. If we were to rename the previous macro, we’d have to rename its body as well to reflect that change.
  • &form gives us access to the original form metadata. This is perhaps the most important reason for its existence, allowing macro writers to propagate the metadata attached by users of the macro to the produced form. This is especially useful to preserve type-hints attached to macro invocations.
  • Get Programming with Clojure

    This is an excerpt from Manning's book Get Programming with Clojure.

    Compound expressions are always wrapped into parenthesis. Compound expressions are also called forms. The name of the form is given by the first element of the expression.

    For instance (+ 1 2) is a “+ form” and (def my-variable 10) is a “def form”.

    Figure 4.2  The structure of a nested arithmetic operation illustrated by an example that involves the * form and the + form.
    user=> (doc and)
     -------------------------
     clojure.core/and
     ([] [x] [x & next])
     Macro
       Evaluates exprs one at a time, from left to right. If a form
       returns logical false (nil or false), and returns that value and
       doesn't evaluate any of the other expressions, otherwise it returns
       the value of the last expr. (and) returns true.
     -------------------------
     Online doc: https://clojuredocs.org/clojure.core/and
     nil
    Clojure in Action, Second Edition

    This is an excerpt from Manning's book Clojure in Action, Second Edition.

    The outermost function is +, which has two arguments: 3 and the form (* 4 2). You know what 3 is all about, so let’s solve the (* 4 2). If you call the multiplication function * with arguments of 4 and 2, you get 8. Let’s write the expression again, solving the (* 4 2) step first, bolding the important parts to call your attention to them:

    As this prompt shows, Clojure puts you into the default namespace of user. You can type Clojure code at this prompt. When you finish typing in a form (a single valid expression, also called a symbolic expression or s-expression)[1] and press Enter, the Clojure reader accepts the stream of characters from the prompt (or any other source) and converts it into Clojure data structures. The data structures are evaluated to produce the result of the program, which is usually another data structure. The Clojure printer attempts to print the result in a format that can be read back by the reader. Finally, Clojure loops back and waits for more input.

    1If you want to be super-pedantic, there’s a distinction between “form” and “expression.” A form is a single readable unit, such as a number, a pair of matched quotation marks for a string, or a pair of matched parentheses. Forms are what matter when reading. An expression is something with a value, such as a data structure. Expressions are what matter when evaluating. This distinction between forms and expressions, reading and evaluating, is another symptom of Lisp’s nonsyntax as explained in chapter 1: it’s possible to read forms without knowing how to evaluate them as expressions!

    (defmacro assert-true [test-expr]
      (if-not (= 3 (count test-expr))
        (throw (RuntimeException.
             "Argument must be of the form
                   (operator test-expr expected-expr)")))
      (if-not (some #{(first test-expr)} '(< > <= >= = not=))
        (throw (RuntimeException.
           "operator must be one of < > <= >= = not=")))
      (let [[operator lhs rhs] test-expr]
        `(let [rhsv# ~rhs ret# ~test-expr]
           (if-not ret#
             (throw (RuntimeException.
                (str '~lhs " is not " '~operator " " rhsv#)))
             true))))
    The Joy of Clojure, Second Edition

    This is an excerpt from Manning's book The Joy of Clojure, Second Edition.

    Clojure provides a form of subtyping by allowing the creation of ad hoc hierarchies: inheritance relationships you can define among data types or even among symbols, which let you easily use the principles of polymorphism. We’ll delve into using the ad hoc hierarchy facility later, in section 9.2. As shown before, Clojure provides a capability similar to Java’s interfaces via its protocol mechanism. By defining a logically grouped set of functions, you can begin to define protocols to which data-type abstractions must adhere. This abstraction-oriented programming model is key in building large-scale applications, as you’ll discover in section 9.3 and beyond.

  • Macros combining forms
  • When viewed this way, the -> macro can be said to weave a sequence of forms into each in turn. This weaving can be done in any form and is always stitched in as the first argument to the outermost expression.[2] Observe how the placement of snowmen provides visual markers for the weave point:

    2 On the other hand, the ->> macro threads the form as the last argument.

    (-> (/ 144 12) (/  2 3) str keyword list)
    ;=> (:2)
    
    (-> (/ 144 12) (*  4 (/ 2 3)) str keyword (list  :33))
    ;=> (:32 :33)

    As shown via snowman, each expression is inserted into the following one at compile time, allowing you to write the whole expression inside-out. Using one of the arrow macros is useful when many sequential operations need to be applied to a single object. So this is a potential use case for macros: taking one form of an expression and transforming it into another form. In this chapter, we’ll also look at using macros to combine forms, change forms, control evaluation and resolution of arguments, manage resources, and build functions. But first, what does it mean that Clojure code is data, and why should you care?

    8.3. Macros combining forms

    Macros are often used for combining a number of forms and actions into one consistent view. You saw this behavior in the previous section with the do-until macro, but it’s more general. In this section, we’ll show how you can use macros to combine a number of tasks in order to simplify an API. Clojure’s defn macro is an instance of this type of macro because it aggregates the processes of creating a function, including the following: