Search code examples
clojure

What is clojure's "doc" command printing out?


I'm a bit confused by clojure's doc.

The output is a specification of arguments, using the traditional basic regular expression markup, which is:

  • *: 0..n instances of the marked-up preceding element ("many")
  • ?: 0..1 instances of the marked-up preceding element ("optional")
  • +: 1..n instances of the marked-up preceding element ("at least one")

Parentheses () and square brackets [] are, however, to be used "as given" to form the correct expression or form, and are not used for grouping of any regular expression elements.

And if there are several valid argument combinations, they are listed on several lines.

But observe for example the output for (doc fn):

clojure.core/fn
  (fn name? [params*] exprs*)
  (fn name? ([params*] exprs*) +)
([& sigs])
Special Form
  params => positional-params* , or positional-params* & next-param
  positional-param => binding-form
  next-param => binding-form
  name => symbol

  Defines a function

  Please see http://clojure.org/special_forms#fn
  params => positional-params* , or positional-params* & next-param
  positional-param => binding-form
  next-param => binding-form
  name => symbol

  Defines a function
Spec
  args: (cat :fn-name (? simple-symbol?) :fn-tail (alt :arity-1 :clojure.core.specs.alpha/params+body :arity-n (+ (spec :clojure.core.specs.alpha/params+body))))
  ret: any?
nil

The first three lines make sense as:

  1. Package and name of var or special form
  2. A valid expression structure (fn name? [params*] exprs*)
  3. Another possible expression structure (fn name? ([params*] exprs*) +)

But then there is a non-indented ([& sigs]). What's that about?

The subsequent output also seems to be in need of cleanup. And what's the "Spec"?

For (doc doc):

clojure.repl/doc
([name])
Macro
  Prints documentation for a var or special form given its name,
   or for a spec if given a keyword
nil

I don't understand the non-indented ([name]).

Or let's try (doc letfn):

clojure.core/letfn
  (letfn [fnspecs*] exprs*)
([fnspecs & body])
Special Form
  fnspec ==> (fname [params*] exprs) or (fname ([params*] exprs)+)

  Takes a vector of function specs and a body, and generates a set of
  bindings of functions to their names. All of the names are available
  in all of the definitions of the functions, as well as the body.
  fnspec ==> (fname [params*] exprs) or (fname ([params*] exprs)+)

  Takes a vector of function specs and a body, and generates a set of
  bindings of functions to their names. All of the names are available
  in all of the definitions of the functions, as well as the body.
nil

Again the meaning of ([fnspecs & body]) is unclear to me.


Solution

  • Those are the parameter lists for the functions.

    From the source:

    Code from the definition of the 'fn' macro

    ([& sigs]) says that it takes a variadic list of "(sig)natures". "Signature" in this case is referring to the example forms on the previous lines. Either ([params] body) for creating a function with a single parameter list, or a list of (([params] body) ([other-params] body) to create a function with multiple parameter lists.

    The doc one is just saying that doc accepts a name as an argument.

    Finally, letfn is saying that it takes a "fnspecs" vector, and a variadic body of expressions. You can see that in how it's used:

    (letfn [(some-function [param] ; The vector on these two lines is "fnspecs"
                            (println param))]
      (println "PRINTING!") ; These two lines are grouped into "body"
      (some-function 1))
    

    It has an outer set of parenthesis because it's showing a list of all the available parameter lists. doc shows ([name]) (a list of one vector) because it only has one valid parameter list. If you look at the docs for a function like max though:

    clojure.core/max
    ([x] [x y] [x y & more])
      Returns the greatest of the nums.
    

    Its listing multiple parameter lists because max has multiple arities.


    As for why letfn shows ([fnspec & body]), that's the way that it's defined in the source:

    Code from the definition of the 'letfn' macro

    fnspecs is the first parameter, and any arguments supplied after that are grouped into the body var-arg list. You may need to look over Clojure's var-arg syntax.