Haddock: disambiguating types and values
Haskell has separate namespaces for types and values. When types and data constructors share a name, Haddock, Haskell’s documentation generator, can get confused. In this post I show how to disambiguate types and values in Haddock documentation.
Demonstrating the problem §
For demonstration purposes I created a simple module, ACME.Disamb
:
module ACME.Disamb (Foo(..), Bar(..), Quux, Xyxxy) where
data Foo = Foo
-- | A bar contains a 'Foo'. Example:
--
-- @let bar = 'Bar' 'Foo'@
--
data Bar = Bar Foo
data Quux
class Xyxxy a
Note that Foo
is the name of both a type and a data constructor.
Same for Bar
. Quux
is a type with no constructor and Xyxxy
is
a class. The Haddock
for type Bar
contains ambiguous references to both Bar
and
Foo
.
Let’s look at the HTML Haddock generated for each top-level declaration:
data Foo
§
<div class="top">
<p class="src">
<span class="keyword">data</span> <a id="t:Foo" class="def">Foo</a>
<a href="#t:Foo" class="selflink">#</a>
</p>
<div class="subs constructors">
<p class="caption">Constructors</p>
<table>
<tbody>
<tr>
<td class="src"><a id="v:Foo" class="def">Foo</a></td>
<td class="doc empty"> </td>
</tr>
</tbody>
</table>
</div>
</div>
The element representing type Foo
has id="t:Foo"
, whereas the
constructor has id="v:Foo"
. These identifiers can be used as
fragment identifiers in hyperlinks. Types and values are
disambiguated through the t:…
and v:…
identifier prefixes.
data Bar
§
<div class="top">
<p class="src">
<span class="keyword">data</span> <a id="t:Bar" class="def">Bar</a>
<a href="#t:Bar" class="selflink">#</a>
</p>
<div class="doc">
<p>
A bar contains a<code><a href="ACME-Disamb.html#t:Foo" title="ACME.Disamb">Foo</a></code>. Example:
</p>
<pre>let bar = <code><a href="ACME-Disamb.html#t:Bar" title="ACME.Disamb">Bar</a></code> <code><a href="ACME-Disamb.html#t:Foo" title="ACME.Disamb">Foo</a></code></pre>
</div>
<!-- constructors elided -->
</div>
Here we can see that all references to Foo
and Bar
in the
documentation I wrote all link to t:Foo
or t:Bar
. This is
not what I intended. The usage example should refer to the data
constructors.
In my example this is a minor nuisance, but recall that Foo
could
be the constructor of some other type. The type Foo
could be
unrelated!
data Quux
§
<div class="top">
<p class="src">
<span class="keyword">data</span> <a id="t:Quux" class="def">Quux</a>
<a href="#t:Quux" class="selflink">#</a>
</p>
</div>
Quux
has no constructor. As a result, there is no element with id="v:…"
.
class Xyxxy a
§
<div class="top">
<p class="src">
<span class="keyword">class</span> <a id="t:Xyzzy" class="def">Xyzzy</a> a
<a href="#t:Xyzzy" class="selflink">#</a>
</p>
</div>
Type class names inhabit the type namespace. Therefore the
corresponding element identifiers also use the t:…
prefix.
The solution §
To refer explicitly to a type or value, prefix the reference with
t
or v
. For example:
-- | A bar contains a 'Foo'. Example:
--
-- @let bar = v'Bar' v'Foo'@
--
data Bar = Bar Foo
The resulting HTML
…<div class="doc">
<p>
A bar contains a<code><a href="ACME-Disamb.html#t:Foo" title="ACME.Disamb">Foo</a></code>. Example:
</p>
<pre>let bar = <code><a href="ACME-Disamb.html#v:Bar" title="ACME.Disamb">Bar</a></code> <code><a href="ACME-Disamb.html#v:Foo" title="ACME.Disamb">Foo</a></code></pre>
</div>
…
This feature is available since haddock-2.23.0 (commit). The published user guide is out of date but you can read up-to-date documentation on GitHub.
Inter-module references §
Consider the following module:
-- | See also 'ACME.Disamb.Quux'.
module ACME.Disamb2 where
Unlike references within a module, inter-module references default to the value namespace:
<p class="caption">Description</p>
<div class="doc">
<p>
See also<code><a href="ACME-Disamb.html#v:Quux" title="ACME.Disamb">Quux</a></code>.
</p>
</div>
Recall that Quux
has no constructor. So the link doesn’t even
target the wrong identifier; it targets a non-existent identifier.
The solution is the same: prefix the whole reference with t
:
-- | See also t'ACME.Disamb.Quux'.
module ACME.Disamb2 where