1 Quick tour🔗ℹ

Suppose you have written a small package called tally:

tree

.

├── base.rkt

├── info.rkt

├── main.rkt

├── private

│   └── counter.rkt

└── scribblings

    └── tally.scrbl

 

3 directories, 5 files

The private module defines a counter:

"tally/private/counter.rkt"

#lang racket/base
 
(provide counter? make-counter counter-add! counter-value)
 
(struct counter ([n #:mutable]))
 
(define (make-counter)
  (counter 0))
 
(define (counter-add! c [k 1])
  (set-counter-n! c (+ (counter-n c) k)))
 
(define (counter-value c)
  (counter-n c))

The tally/base module re-exports just the counter, and the main tally module re-exports tally/base and adds two bindings of its own:

"tally/base.rkt"

#lang racket/base
 
;; Just the counters, without the rest of tally
(require "private/counter.rkt")
(provide (all-from-out "private/counter.rkt"))

"tally/main.rkt"

#lang racket/base
 
(require "base.rkt")
(provide (all-from-out "base.rkt")
         tally
         with-counter)
 
(define (tally pred lst)
  (for/sum ([v (in-list lst)])
    (if (pred v) 1 0)))
 
(define-syntax-rule (with-counter (c) body ...)
  (let ([c (make-counter)])
    body ...
    (counter-value c)))

The package’s docs cover most, but not all, of these bindings:

"tally/scribblings/tally.scrbl"

#lang scribble/manual
 
@(require (for-label racket/base tally))
 
@title{Tally}
 
@defmodule[tally]
 
@defproc[(counter? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is a counter.}
 
@defproc[(make-counter) counter?]{
Returns a new counter whose value is 0.}
 
@defproc[(counter-add! [c counter?] [k exact-integer? 1]) void?]{
Adds @racket[k] to the value of @racket[c].}
 
@defproc[(tally [pred (-> any/c any/c)] [lst list?]) exact-nonnegative-integer?]{
Returns the number of elements of @racket[lst] for which @racket[pred] returns a true value.}

After installing the package, which also builds its docs, check it with raco dover check:

raco dover check tally

Documentation coverage: tally

─────────────────────────────────────────────────────────────────

tally  [missing 2 of 6]:

  counter-value  (defined in tally/private/counter; exported by tally,

    tally/base)

  with-counter

 

tally █████████████████████████████████░░░░░░░░░░░░░░░░░  67% 4/6

─────────────────────────────────────────────────────────────────

Total █████████████████████████████████░░░░░░░░░░░░░░░░░  67% 4/6

 

Documentation warnings (3)

─────────────────────────────────────────────────────────────────

tally/base:

  These bindings are documented, but as exported by this module they don't

    connect to their docs:

    counter-add!

    counter?

    make-counter

  fix: add tally/private/counter to the #:use-sources of the defmodule or

      declare-exporting for tally

   or: if tally/base is internal, list it in dover-consider-private

Dover finds two bindings that have no documentation. Notice that it reports both under the tally module. One of those bindings, with-counter, is defined in "tally/main.rkt", so that makes sense. But the other one, counter-value, is defined in the private module, then re-exported from tally/base and from tally. So how does Dover decide that the documentation for counter-value belongs in the docs for the tally module?

When the same binding comes from multiple places, Dover tries to be smart about which module it reports it under. In this case it sees that we have already documented the other bindings from tally/private/counter (counter?, make-counter and counter-add!) in the Scribble docs for tally, and that the undocumented counter-value is also exported from tally. (See Where bindings are reported.)

Separately: there are warnings about the docs that do already exist. They say that uses of counter?, make-counter and counter-add!, when imported from tally/base, don’t connect to their docs, so a program that requires tally/base gets no links to them. This is because the defproc forms for those bindings are inside a section that declares it is documenting bindings from tally, and tally/base gets these bindings straight from the private module without going through tally. (See Documentation warnings.)

To start on the missing documentation, you can ask for stubs using the -f (or --format) flag and specifying stubs as the output format:

raco dover check -f stubs tally

@; ════════════════════════════════════════════════

@; Suggestion: add these under the existing defmodule for tally

 

@; ────────────────────────────────────────────────

@; Suggestion: add this near the existing docs for counter-add!, counter?,

    make-counter

@defproc[(counter-value [arg any/c]) any/c]{

 

Returns a value that ...}

 

@; ────────────────────────────────────────────────

@; Suggestion: add this near the existing docs for tally

@defform[(with-counter arg)

         #:contracts ([arg any/c])]{

 

Expands to code that ...}

 

The stubs are grouped by the module that each binding is reported under. Since tally already has docs, Dover doesn’t start a new defmodule for it, but suggests adding the stubs under the existing defmodule. It also suggests adding each stub near the docs for the bindings defined in the same module: counter-value near the other counter functions, and with-counter near tally. Since these differ, each gets its own group, after a separator line.

Now we’ll fix the docs by adding the missing stubs and filling them in, and by adding the #:use-sources that the warnings suggest:

"tally/scribblings/tally.scrbl"

#lang scribble/manual
 
@(require (for-label racket/base tally))
 
@title{Tally}
 
@defmodule[tally #:use-sources (tally/private/counter)]
 
@defproc[(counter? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is a counter.}
 
@defproc[(make-counter) counter?]{
Returns a new counter whose value is 0.}
 
@defproc[(counter-add! [c counter?] [k exact-integer? 1]) void?]{
Adds @racket[k] to the value of @racket[c].}
 

@defproc[(counter-value [c counter?]) exact-integer?]{

Returns the value of @racket[c].}

 
@defproc[(tally [pred (-> any/c any/c)] [lst list?]) exact-nonnegative-integer?]{
Returns the number of elements of @racket[lst] for which @racket[pred] returns a true value.}
 

@defform[(with-counter (id) body ...+)]{

Binds @racket[id] to a new counter, evaluates the @racket[body]s, and returns the counter's

final value.}

After rebuilding the docs, the check comes back clean:

  raco setup --pkgs tally

raco dover check tally

Documentation coverage: tally

─────────────────────────────────────────────────────────────────

tally ██████████████████████████████████████████████████ 100% 6/6

─────────────────────────────────────────────────────────────────

Total ██████████████████████████████████████████████████ 100% 6/6