Skip to content

Instantly share code, notes, and snippets.

@VladUreche
Created January 13, 2014 08:39
Show Gist options
  • Save VladUreche/8396624 to your computer and use it in GitHub Desktop.
Save VladUreche/8396624 to your computer and use it in GitHub Desktop.
Scaladoc tutorial for docs.scala-lang.org, in a pitiful state
# Scaladoc Developer Guide
## Introduction
Scaladoc is the tool that enables developers to automatically generate documentation for their Scala (and Java) projects. It is Scala's equivalent of the widely-used Javadoc tool. This means that Javadoc (and even doxygen) users will be familiar with Scaladoc from day 1: for them, it is most beneficial to check out the Scaladoc/Javadoc comparison tables and if necessary, skim through this document to understand specific features.
The rest of this tutorial is aimed at developers new to Scaladoc and other similar tools. It assumes a basic understanding of the Scala language, which is necessary to follow the examples given throughout the tutorial. For the user perspective on the Scaladoc-generated documentation, such as finding a class, understanding the page layout, navigating through diagrams, please refer to the Scaladoc User Guide.
The tutorial will start by a short motivation and then will explain the main concept in Scaladoc: the doc comment.
### Why document?
As a developer with lots of experience you are probably more familiar with the source code than with documentation. You can probably just find the definition site in a quick all-files lookup, deftly follow what happens inside a method and then go on with your code. Congratulations, that's how the hackers do it! But you may forget that:
1) not everyone has the skills to quickly find what they need in someone else's code
2) someone who just started out might not have such a good understanding of the language that they can immediately infer how to use other code
3) even you may get in trouble if the implementation is changes, and by looking at the code you made an assumption that another library does not guarantee
For the cases above, there's a quick way to make sure all the other developers are productive with your code: write documentation and publish it. In the case of Scala, an automated tool called Scaladoc can extract comments from your code and generate an easy-to-navigate website that you can publish on the Internet or on your corporate network. How does this work? We'll see that in the next section.
### How to document?
The documentation in Scala is interleaved with code. This makes it easy to update and read while editing and browsing the code. The easiest way to include the documentation is to have comments that describe entities in the program or library that you're developing. There are three major types of comments in Scala:
// This is a single line comment that does not add any documentation
/* This comment can span multiple lines
* but it isn't considered as documentation.
* It ends with star slash, like so */
/** This comment will document the next entity in the code,
* and is called a doc comment or documentation comment.
* Like the multiline comment, it can span several lines
* and ends with star slash, but it starts with two stars
* instead of one, to signal its a documentation status.
*/
case class Greeter(name: String) {
def sayHi = println(s"Hi $name!")
def sayBye = println(s"Bye $name!")
}
The documentation comments can span multiple lines and always start with /** and end with */. They always affect the next entity in the code below themselves. By entity, we can understand class, trait, object definitions and member definitions or declarations. We could in fact documnt each entry in the class above:
/** The Greeter class offers convenience methods to greet
* a user during a console session.
* @param name The user's first name or nickname */
case class Greeter(name: String) {
/** Greets the user when the console session starts. */
def sayHi = println(s"Hi $name!")
/** Greets the user when the console session ends. */
def sayBye = println(s"Bye $name!")
}
As you can see above, we've added doc comments for all the definitions in our example: The Greeter case class and the session start/end greeting methods. You can see a screenshot of the documentation generated by Scaladoc below:
TODO: Add screenshot
Before we delve into deeper into the doc comment syntax and explain what the strange @param tag does, we'll look at starting scaladoc for your project.
### Starting Scaladoc the first time
Scaladoc is part of the Scala compiler package. If the scalac compiler and the scala interactive prompt (REPL) are in your path, you'll be able to call scaladoc too:
scaladoc Greeter.scala OtherClasses.scala
This will invoke the Scaladoc tool with two source files. If in order to compile these source files you need a library in the classpath, you can add it using -classpath:
scaladoc -classpath <path to library> Greeter.scala OtherClasses.scala
The rest of the scaladoc command-line options will be explained in one of the final chapters, Scaladoc command-line flags roundup.
# Using Scaladoc
Scaladoc is able to generate documentation for your code based on the doc comments you added in the source code. But there are two aspects that can make your documentation awesome:
* write clear and concise doc comments that offer the right ammount of information: enough to understand the pre- and postconditions, but not more
* don't go around describing the implementation -- that's what normal comments are for, and they should be read by people going through your code
* always state the performance characteristics of data structures: is an operation O(1), O(n), O(n^2)? Make that clear
* finally, use the available markup and tags to help your comments best deliver the information
The rest of the tutorial describes the mechanisms offered by Scaladoc to improve documentation:
* The markup syntax of Scaladoc that enables text to be structured
* Links to other pages in Scaladoc or to the Internet
* Tags that allow the documented to further split the comment into sections: main comment, comments for each parameter, etc
* Variables that allow a piece of information to be reused in different locations
* Use cases that allow the documenter to override a complex signature with a simpler one
* Implicits make the implicit conversions that can take place explicit and enumerate all the members that can be called on a class
* Diagrams that allow capturing the inheritance relations in interactive UML-like graphs
* Groups enable documenters to group semantically-related members on a page and provide an overview for each group.
The next sections will look at each of the topics above in detail.
## Wiki Syntax (Markup)
The scaladoc wiki syntax follows the trac wiki syntax. A number of features are available:
* headings
* paragraphs with bold, italic, monospace, underline, superscript and subscript
* blocks of code (with Scala syntax highlighting)
* unnumbered and numbered lists
We'll use an example to explain the syntax:
/** = This is an h1 heading =
* == This is an h2 heading ==
* === This is an h3 heading ===
*
* This is a paragraph. Paragraph separation is done using a blank line.
*
* This is another paragraph containing '''bold''', ''italic'', `monospace`,
* __underline__, ^superscript^, and ,,subscript,, words.
*
* {{{
* Multi-line code can be inserted as a block and will be printed as monospace text.
* It is parsed as Scala code, with keyword highlighting.
* }}}
*
* This is a bullet list:
* - element 1
* - element 2
* - subelement, just indent more
*
* You can also create numbered lists. The trick is to keep the same bullet
* character sequence -- the parser will increase the number automatically:
* a. letters
* i. small letters (use `a.` as bullet)
* i. caps letters (use `A.` as bullet)
* a. numbers
* A. arabic (use `1.` as bullet)
* A. roman (use `I.` as bullet)
* A. small roman (use `i.` as bullet)
*/
object WikiSyntax
The example in this section can be downloaded from here.
## HTML Tags in Scaladoc
HTML element tags are not recommended in Scaladoc, but are supported to maintain compatibility with existing documentation or when the wiki syntax is insufficient. If you are using HTML element tags in your documentation, you must be aware of the following.
The following block-level HTML elements are sanitised by Scaladoc, which internally converts them into equivalent wiki syntax: div, ol, ul, li, p and all headings. A sanitised element will loose its attributes. Other block-level elements (pre, table, etc.) are not sanitised. However, a comment that starts with a non-sanitised block-level element may not be rendered correctly; make sure to have a least one paragraph, heading, or similar block before it.
For an HTML element in a comment to be preserved as such in the documentation page, it must be recognised as valid. The following elements are recognised (in addition to the sanitised elements above):
a, abbr, acronym, address, area, b, bdo, big, blockquote, br, button, caption, code, cite, col, colgroup, dd, del, dfn, em, fieldset, form, hr, img, input, i, ins, kbd, label, legend, link, map, object, optgroup, option, param, pre, q, samp, select, small, span, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, var. Furthermore, any string that has the structure of an HTML entity (as accepted by regular expression &\w+;) will also be recognised.
Any other HTML/XML element will be encoded so as to be visibly printed in the documentation.
In a wiki-syntax code block (using {{{), all HTML/XML elements will be encoded so as to be visibly printed in the documentation, including the recognised elements above. They will not function as markup elements. However, if a code block is defined using the pre element, recognised elements inside the block will be preserved.
Keep in mind that you rely on the documentation being generated in HTML, with a specific stylesheet. Scaladoc has an extensible documentation generator so that other formats may be generated. For this reason, recognised but non-sanitised HTML elements are not guaranteed to be kept by all generators. If you want to use HTML elements in your comment, we recommend you do not rely on them to obtain readable documentation, but instead use them for non-critial improvements.
## Links
Links in scaladoc can be included in documentation comment and are created by using two pairs of square brackets: `[[scala.collection.immutable.List]]`. The links are of three main types:
* internal scaladoc links - both the entity that links and the entity that is linked are part of the same library and are documented together (e.g. A class linking to one of its members)
* external scaladoc links - the linked entity was previously documented by scaladoc and the pages are published on the Internet (e.g. Linking to a class in the Scala library)
* external links - a link to a random external webpage, not necessarely generated by scaladoc
Additionally, scaladoc will generate links in types whenever this is possible. For example, if the scala library is externally linked, a method returning `List[Int]` will have List and Int point to the corresponding members in library scaladoc.
### Common Links Format
Links in scaladoc are included in doc comments and start with `[[`, contain a the link target, either an entity or an url, optionally followed by a space and the link text, and closed by `]]`. For example:
```
/**
* [[com.acme.lib.C an internal scaladoc link to case class C]]
* [[scala.collection.Map an external scaladoc link to the Map trait in the Scala library]]
* [[http://scala-lang.org an external link to the Scala website]]
*/
package com.acme.lib {
case class C(i: Int)
}
```
### Internal Scaladoc Links
This is the most common type of links: They make it possible to reference other classes, objects, traits in the project, or even their members. Links have a very simple syntax for most cases, but can become pretty complex if you need to select overloaded members:
We'll use the following code as a running example:
```
package scaladoc.example {
case class C[T](t: T)
object O
trait T {
type C = C[Int]
type D = C[String]
val D = ???
def foo(c: C): C
def bar(i: Int): Int
def bar(s: String): String
def bar[R[X]](r: R[Int]): Int
}
trait Z extends T {
override type C = C[Int] with NonNull
}
}
```
#### Absolute and relative links
In this example, writing `[[scaladoc.example.C]]` in any doc comment will create a link to the `case class C` page. This is what's called an absolute link: we enumerate all the packages the class is in.
But we can also write `[[C]]`.
**What will this point to?** It depends where we are: if we're writing it in the doc comment for `case class C` it will point to the class itself. If we write it in `trait T` or any of its members, it will point to the `type C = C[Int]` member in `trait T`. If we write it in the doc comment for `trait Z`, it will point to the overridden `type C = C[Int] with NonNull` in `trait Z`.
Using relative links makes it possible to link to the correct member even in the presence of doc comment inhertiance. For example, adding a link `[[C]]` for `def foo` in `trait T` will be inherited in `trait Z`, along with the method. But in `trait Z`, the link will point to the overridden type `type C = C[Int] with NonNull`. Knowing this, a documentation developer should make sure all links to members in classes and traits are relative links, so they are updated when members are inherited.
**How to make it always point to C?** This is a valid question: we might want a link to always point to the same entity: we can do that by using absolute links or by adding more prefixes to a relative link. For example, `[[example.C]]` will point to `case class C[T]` as long as there's no other `[[example.C]]` closer in the class. Let's look at how the link search algorithm works if we add `[[example.C]]` in the doc comment of method `foo` of `trait T`:
1. first treat the link as absolute. There's no `example` package, so jump to step 2.
2. treat the link as relative to the current entity: ``scaladoc.example.T#foo``. There's no package `example` in method `foo`: continue searching.
3. drop an entity from the current path: ``scaladoc.example.T``. There's no package `example` in trait `T`: continue searching.
4. drop an entity from the current path: ``scaladoc.example``. There's no package `example` in package `example`: continue searching.
5. drop an entity from the current path: ``scaladoc``. Found package `example`. Inside it, found case class `C`: link resolved.
**Can I use relative links to members imported in scope** No, scaladoc relative links only refer to the current entity, and do not search inside imports. For example, although all source files have object `scala.Predef` automatically imported in scope, referring to method `???` of object `scala.Predef` needs the full prefix: `[[scala.Predef.???]]`.
#### Separators
Inheriting from Java, scaladoc links use dot (`.`) to separate packages, classes, traits or objects. They also use hash (`#`) to link to members. In fact, the specialized roles are relaxed in scaladoc: the two separators can be used to separate anything, thanks to the link search algorith we'll see in the next sections.
**What if my class is called hashhash (`##`)?** You can still include dot and hash in a name by prefixing them with a slash: `[[my.library.\#\#]]`.
With this knowledge, it is be possible to link to most entities in scaladoc. The next section treats the problem of selecting the right element to link to in case a name is overloaded and can be usually skipped.
#### Linking to right entity
Defining a case class automatically creates a companion object in the same pacakge. So in our case, package `example` should contain both `case class C` and `object C` that allows us to omit `new` when creating a new case class. At this point two valid questions arise:
**How to link to the companion object of `case class C[T](t: T)`?** To answer this question we need to distinguish between types and values: traits, classes and type definitions are types. Packages, objects, values and definitions are terms. We can hint in the link we want to link to a type or term by following the link by "!" or "$". Thus `[[example.C$]]` will link to the object while `[[example.C!]]` will link to the class. The previous format, `[[example.C]]` will first look for a type and if it doesn't exist, will look for a term. So if there's only a term with the name, but no type, one can use `[[example.O]]` to link to it. The same theory is valid for members, so `[[example.T.D]]` will point to the type alias, unless suffixed by `$`.
**How to link to one of the `bar` methods, as they are both terms?``** Links allow selection on signatures: we can point to `[[example.T.bar(i:Int):Int*]]` or `[[example.T.bar(s:String):String*]]`. The signature matching is triggered by suffixing the link with an asterisk (`*`). To find the exact signature text, link to `[[example.T.bar]]`: scaladoc will complain the link is ambiguous and will output all possible signatures. To use type prefixes in the types, you need to prefix dots by slashes: `[[example.T.foo(c:scaladoc\.example\.C):scaladoc\.example\.C*]]`. Signatures can also contain type parameters, which require the use of square brackets, which may interact with the link syntax. This can be overcome by adding as many square brackets as necessary to avoid interaction: `[[[[example.T.bar[R[X]](r:R[Int]):Int]]]]`.
### External Scaladoc Links
External scaladoc links point to other libaries' documentations. The most common example is pointing to the Scala library. The syntax and search for external scaladoc links is the same as for internal links. The only difference is that the link generated points to a scaladoc page published on the Internet. In order to have scaladoc external links, one needs to specify the locations for the external scaladoc sites and the packages they document. This is done in the command line:
```
scaladoc test.scala -external-urls scala=http://www.scala-lang.org/api/current/
```
This will enable scaladoc to point any entity in the scala package to the URL given. There is no guarantee that the URL is accessible and that all entities are documented at the given URL. The responsibilty to check external scaladoc links falls back to the documentation developer.
There are a couple of known bugs in the external links:
- they are unable to link to use cases
- they are unable to detect areas of the documentation not documented using `-skip-packages`
- they are unable to link to type members documented with `@template` to produce pages
These are hard limitations of the current external scaladoc links are are not possible to solve without a complete redesign of the external linking.
### External Links
## Tags
-- take from wiki
## Variables
-- take from wiki
## Use cases
-- take from wiki
## Comment inheritance
Comments are inerhited form a commented entity to the overriding entity in two situations:
- if the overriding entity has no doc comment itself, in which case it's Implicit Comment Inheritance
- if the overriding entity has a comment but uses `@inheritdoc` to inherit the overridden comment - Explicit Comment Inheriance
### Implicit comment inheritance
A member that overrides or implements a commented member will automatically inherit the comment if it does not have one itself.
Parameter comments are inherited separately, so that the overall comment can be changed in the overriding member whilst still inheriting parameter comments, or vice-versa. If a member has multiple parameters and only some are present in the overriding member's comment, the comments for other parameters will be inherited. Inheritance of parameter comments is based on parameter names; if parameter names are changed by overriding, comments will not be inherited.
In the following example, the comment for the Count class will not be inherited, as Count extends but does not override Func. On the other hand, the apply method in Count overrides the method apply in Func, so the comment will be inherited.
/** A class representing a function from values of type `T` to values of type `R`
* @tparam T the function's parameter type
* @tparam R the return type of the function
*/
abstract class Func[T, R] {
/** The function application method.
* @param t The value of type `T` to which the function will be applied
* @return The result of the function applied to `t`
*/
def apply(t: T): R
}
abstract class Count[T] extends Func[T, Int] {
override def apply(t: T): Int
}
Finally, adding the `ListElemCountOne` and `ListElemCountTwo` classes showcase the selective parameter inheritance:
class ListElementCountOne extends Count[List[_]] {
override def apply(l: List[_]): Int = l.length
}
class ListElementCountTwo extends Count[List[_]] {
/** @return The List `t`'s element count */
override def apply(t: List[_]): Int = t.length
}
`ListElemCountOne.apply` has a new name for its parameter, `l`, so the inherited description is not inherited anymore. On the other hand, the main comment is present. Contrarily, in `ListElemCountTwo.apply` we get the inherited description for parameter `t` and the new return value.
Still, there's one thing missing: `ListElemCountTwo.apply` is missing the main comment. This happens because scaladoc assumes the new main comment is empty and followed by a parameter definition. The next section will show how the comment inheritance can be better controlled.
### Explicit comment inheritance
NOTE: This feature was added in Scala 2.10.
As we have seen in the previous section, whenever we redefine a member, scaladoc assumes we've also overridden the main comment. But what if we want to keep the old comment? Or, better yet, keep the old comment and add some more information to the main comment? The answer is explicit inheritance: `@inheritdoc`:
class ListElementCountThree extends Count[List[_]] {
/** @inheritdoc
*
* In the case of `ListElementCountThree`, the input is a `List` of anything and the method returns the number of elements in the list.
* @param t A list who's elements will be counted
* @return @inheritdoc, the element count.
*/
override def apply(l: List[_]): Int = l.length
}
In this above example, the main comment of `ListElementCountThree.apply` will be:
The function application method.
In the case of ListElementCountThree, the input is a List of anything and the method returns the number of elements in the list.
And the return of the method is described as:
The result of the function applied to t, the element count.
The `@inheritdoc` tag will expand to the corresponding comment in the overridden entity:
* if used in the main comment, it will expand to the overridden member's main comment
* if used in a `@param <x>` tag, it will expand to the comment of parameter `<x>` in the overridden member (same goes for `@throws <Exception>` and `@tparam <T>`)
* if used in a `@return`, `@note`, `@author` tag, it will expand to the corresponding comment in the overridden member
The example in this section can be downloaded from here.
## Implicits
NOTE: This feature was added in Scala 2.10.
Scaladoc can list the members inherited by implicit conversions in each class and trait. To use this feature, you need to explicitly pass the `-implicits` flag to the tool.
The implicit conversions a class or trait is involved in are taken from the default scope of the entity:
- the companion object
- all package objects of packages that contain the class or trait
- the scala package and the scala.Predef object (which are imported by default)
If you define an implicit conversion anywhere outside the default scope, it will only affect the class or trait if it is explicitly imported - thus, it is dependent on your use case and should not be always documented in scaladoc.
### Constraints
Some implicit conversions may only take place if the type parameters are constrained to some value or is certain type classe are available in the scope:
<pre>
implicit def toSum\[T: Numeric\](l: List[T]): T = ??? // not implemented yet...
</pre>
### Shadowing and ambiguation
Implicitly inherited members may be ambiguous or shadowed. Ambiguous implicit members are members with overlapping signatures that are impossible to distinguish on the compiler side:
<pre>
scala> implicit def intToFoo(i: Int) = new { def foo = 1 }
intToFoo: (i: Int)Object{def foo: Int}
scala> implicit def intToFoo2(i: Int) = new { def foo = "1" }
intToFoo2: (i: Int)Object{def foo: String}
scala> 1.foo
<console>:10: error: type mismatch;
found : Int(1)
required: ?{def foo: ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method intToFoo of type (i: Int)Object{def foo: Int}
and method intToFoo2 of type (i: Int)Object{def foo: String}
are possible conversion functions from Int(1) to ?{def foo: ?}
1.foo
^
</pre>
Shadowed members are the implicitly inherited members that cannot be called at all because similar members already exist in the class:
<pre>
scala> implicit def stringToTrim(s: String) = new { def trim = "surprise!!!" }
stringToTrim: (s: String)Object{def trim: String}
scala> "no surprises here".trim
res1: String = no surprises here
</pre>
## Diagrams
NOTE: This feature was added in Scala 2.10.
WE NEED TO WRITE THAT
## Groups
NOTE: This feature was added in Scala 2.10.
WE NEED TO WRITE THAT
Questions:
- two groups
- group name with spaces
- locally replacing a group name
- name, desc, prio scopes (scala.reflect example)
## Scaladoc command-line flags roundup
## Scaladoc wiki syntax annotations roundup
@jgogstad
Copy link

jgogstad commented Apr 3, 2020

Thank you! I think this gist is as relevant today as it was in 2016. Changed the extension to .md and added more backticks around code blocks: https://gist.github.com/jgogstad/38f1e58f6c43f6cac0efd2ec96537cff

For those who prefer rendered markdown instead

Edit: Just found this, it probably covers this gist: https://docs.scala-lang.org/overviews/scaladoc/for-library-authors.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment