1. hahn
    1. Introduction
      1. fibonacci
        1. Examples
    2. Syntax
      1. Variables
      2. Procedures
      3. Special tags
        1. @example
        2. @example-no-eval
        3. @internal
        4. @no-source
        5. @to
      4. Top-level directives
        1. author
        2. description
        3. egg
        4. email
        5. example
        6. example-no-eval
        7. heading
        8. noop
        9. repo
        10. source
        11. subheading
        12. subsubheading
        13. text
        14. title
        15. username
    3. Complete example
      1. The .meta file
      2. The module file
      3. The source file
      4. The .setup file
    4. Limitations


Hahn is a mechanism for documenting Scheme in the source-code itself; similar to Doxygen, Javadoc or Roxygen.

As an example, let's take this naïve Fibonacci:

(define (fibonacci n)
  @("Computes the nth [[http://en.wikipedia.org/wiki/Fibonacci_number|Fibonacci]]."
    "This naïve algorithm runs in ''O(2^n)''; using e.g. memoization,
we could bring it down to ''O(n)''."
    (n "The nth number to calculate")
    (@to "integer")
     "Computing the 6th Fibonnaci number (starting from 0)"
     (fibonacci 6)))
  (case n
    ((0) 0)
    ((1) 1)
    (else (+ (fibonacci (- n 1))
             (fibonacci (- n 2))))))

It produces the following output:


[procedure] (fibonacci n) → number

Computes the nth Fibonacci number.

This naïve algorithm runs in O(2^n); using e.g. memoization, we could bring it down to O(n).

The nth number to calculate
(define (fibonacci n)
  (case n ((0) 0) ((1) 1) (else (+ (fibonacci (- n 1)) (fibonacci (- n 2))))))

Computing the 6th Fibonnaci number (starting from 0)

(fibonacci 6)
=> 8


To document definitions, insert a so-called document-expression ("docexpr") after the variable (or variable-formals) and before the body of the definition.

The docexpr is an ampersand-prefixed expression containing a description; and optionally a longer description, parameters, return values and examples.


Variables such as constants and parameters, for example, only require a description:

(define k @("The Boltzmann constant") 1.38e-23)

The general form for variables is therefore something like:

(define <variable> @(<description> [<longer description>]) <expression>)


Procedures, on the other hand, can provide parameters, return-values and examples; parameters are specified with key-value lists containing the parameter and a description of the parameter; whereas return-values and examples are key-value lists containing the special keys @to and @example respectively.

(define (add x y)
  @("Adds two numbers."
    (x "The augend")
    (y "The addend")
    (@to "number")
    (@example "Adding two imprecise binary numbers"
              (add #b1# #b1##)))
  (+ x y))

Notice that @example takes a description, too; the general form for procedures is therefore something like:

(define (<variable> <formals>)
    [<longer description>]
    [(<formal-0> <formal-description-0>) ...
     (<formal-n> <formal-description-n>)]
    [(@to <return-type>)]
    [(@example <example-description>
               <example-expression-0> ... <example-expression-n>)]))

Special tags


The @example tag is useful for providing examples of procedure-application.

(define (quadratic-diophantine z)
  @("Finds a solution to the quadratic Diophantine equation x^2 + y^2 = z^2, given z."
    "Returns two values, x and y."
    (z "The known side")
    (@to "number, number")
    (@example "An example from Arithmetica II.VIII"
              (quadratic-diophantine 16)))
  (let* ((m (random (inexact->exact (floor (sqrt z)))))
         (n (sqrt (- z (expt m 2)))))
    (let ((x (- (expt m 2) (expt n 2)))
          (y (* 2 m n)))
      (values x y))))

The @example-no-eval tag is also useful for providing examples of procedure-application; the difference is that hahn does not attempt to evaluate them when rendering the documentation.

@Example-no-eval is useful when the examples are incomplete or pathological.

(define (find-fermat-counterexample)
  @("Finds positive integers a, b, c and n > 2 for which a^n + b^n = c^n."
    (@example-no-eval "Warning: this should never terminate."
  ;; The testable range is pretty small.
  (let ((range 8))
    (until (let ((a (+ (random range) 1))
                 (b (+ (random range) 1))
                 (n (+ (random range) 3)))
             (integer? (expt (+ (expt a n) (expt b n)) (/ 1 n)))))))

The @internal tag signifies that the documentation for the given expression should be suppressed; it is useful for internal documentation.

(define cat-alive?
  @("Qubit representing whether or not our cat is alive"
    "{{Cat-alive?}} is internal so that observers are forced to use
the {{observe!}} procedure."

The @no-source tag turns off the source-code listing that accompanies documented expressions.

(define (vote! candidate)
  @("[[http://youtu.be/IoWJkrlptNs|Votes]] for your candidate!"
    "This black-box voting procedure is the trade-secret of Biedolb,
Inc.; the source-code has been suppressed."
    (candidate "The candidate for which to vote")
  (register-vote! 'president-mccain))

The @to tag is optional and specifies the return value of a procedure; in the absence of @to, the return value is considered to be unspecified.

This procedure, for instance, has an unspecified return type:

(define (entangle! register . qs)
  @("Entangles qubits in a register."
    (register "The register in which to entangle")
    (qs "The qubits to be entangled"))
  (for-each (lambda (q) (set-register! q register)) qs))

whereas this one returns a specific type:

(define (apply-gate gate . qs)
  @("Applies the quantum-gate to the qubits."
    (gate "The quantum gate to apply")
    (qs "The qubits on which to apply it")
    (@to "qubit"))
  (make-qubit (matrix-multiply (apply quantum-state qs) gate)))

Top-level directives

Some of the top-level directives deal with metadata already gleaned from the .meta file and are therefore redundant; others deal with presentation, and are useful for crafting introductions, &c.


Author is the author of the egg; it overrides (author "Egg Author") from the .meta file.

@(author "Diophantus of Alexandria")

Description describes the egg; it overrides (synopsis "Egg synopsis") from the .meta file.

@(description "To divide a given square number into two squares")

Egg is the name of the egg; it overrides the filename of the .meta file (i.e. "name" from name.meta).

@(egg "arithmetica")

Email is the author's email; it overrides (email "author@example.com") from the .meta file.

@(email "diophantus@alexandria.net")

Example is a stand-alone example, as opposed to the @example tag that accompanies procedures.

@(example "Riastradh once asked why this does what it does; no one had
a satisfactory answer."
  (let* ((yin ((lambda (y) (newline) y)
                       (call/cc call/cc)))
                 (yang ((lambda (y) (write-char #\*) y)
                        (call/cc call/cc))))
            (yin yang)))

Example-no-eval is a stand-alone, unevaluated example; as opposed to the @example-no-eval tag that accompanies procedures.

@(example-no-eval "This will never terminate; thanks, Eli!"
                  ((lambda (x) (x x)) (lambda (x) (x x))))

Heading designates a section of the documentation right below the level of title.

@(heading "Arithmeticorum Liber II")

Noop is an artifact required to separate presentation-based directives from source-code that they don't belong to.

For instance:

@(heading "Abstract")
@(text "This is the body of the abstract.")

(define phi
  @("The heading and text above do not belong to this variable.")
  (/ (+ 1 (sqrt 5)) 2))

Repo overrides (repo "https://example.com/repo.git") from the .meta file.

(TODO: Implement this.)


Source provides a stand-alone source-code listing.

@(text "Through an aggressive heuristic, we've managed to solve the
halting problem: let's assume that if it doesn't finish in 1 second,
it never will.")

  (define (terminate? thunk)
    (let ((thread (thread-start! (make-thread thunk))))
      (and (thread-join! thread 1 #f)
           (thread-terminate! thread)

Subheading designates a section of the documentation right below the level of heading.

@(subheading "Quaestio VIII")

Subsubheading designates a section of the documentation right below the level of subheading.

@(subsubheading "Observatio domini Petri de Fermat")

Text is used for free-form text and can be useful for abstracts and explanatory material.

@(text "I have discovered a truly marvellous proof of this, which this
margin is too narrow to contain.")

Title overrides the egg-name as the title of the document.

@(title "Arithmetica")

Username is the username of the author on Chicken's wiki; it overrides (user "chicken-user") from the .meta file.

@(username "pfermat")

(TODO: Let's rename this user.)

Complete example

To tie everything together, here's a complete example; see the resulting documentation.

The .meta file

Hahn reads the metadata from the .meta file such as: synopsis, author, email, user, repo, depends.

((synopsis "Use the Landauer limit to calculate my program's entropy.")
 (author "Peter Danenberg")
 (email "pcd@roxygen.org")
 (user "klutometis")
 (repo "https://github.com/klutometis/landauer")
 (category math)
 (license "BSD")
 (depends hahn)
 (test-depends test)

The module file

The module file is a suitable place for putting introductory material about the egg; such as background information, abstract, &c.

It is also suitable for a high-level overview of what the module does.

@(heading "Landauer's principle")

@(text "[[http://en.wikipedia.org/wiki/Landauer%27s_principle|Landauer's
principle]] states that every irreversible operation produces
entropy; erasing one bit, for instance, generates at least ''kT'' ln
2 J of heat.")

@(text "We can use Landauer's principle to calculate a lower-bound on
the energy released by our program, given some number of

@(heading "Documentation")

(module landauer
  @("The Landauer module contains contains some constants, parameters
and procedures for calculating a lower-bound on the heat-dissipation
of programs.")
  (import chicken scheme)
  (include "landauer-core.scm"))

The source file

The source file contains the documentation of individual constants, parameters, records, procedures.

(define k @("The Boltzmann constant") 1.38e-23)

(define room-temperature @("Room temperature in K")
  (make-parameter 298.15))

(define (heat operations)
  @("Calculate a lower-bound on the heat dissipated by some number
of irreversible bit-operations."
    "Room-temperature is governed by the [[#room-temperature]]
    (operations "The number of irreversible bit-operations")
    (@to "number"))
  (* operations k (room-temperature) (log 2)))

The .setup file

The .setup file does two things:

  1. compiles each extension with -X hahn; and
  2. generates documention.

Extensions should be compiled with -X hahn; this strips the documentation from the source before compilation so that the compiler is not confused.

The hahn binary from hahn-utils generates the actual documentation; the hahn egg provides a convenience macro run-hahn so that installation does not fail for users who haven't installed hahn-utils.

There is a soft-dependency on the otherwise dependency-heavy egg hahn-utils: users don't have to have it unless they want to generate docs themselves, for some reason.

(use hahn setup-helper-mod)

 (extension-version "0.0.1")
 compile-options: '(-X hahn))

(run-hahn -o landauer.wiki landauer.scm landauer-core.scm)