Your docs are a program

Zainab Ali

https://kebab-ca.se/presentations.html

catphoto

case class Album(photos: Map[String, String] = Map.empty) {

  def addPhoto(catname: String, photo: String): Album =
    Album(photos + ((catname, photo)))

  def getPhoto(catname: String): String =
    photos(catname)
}
def add_photo(album, catname, photo) do
  Map.put(album, catname, photo)
end

def get_photo(album, catname) do
  album[catname]
end

Questions

  • How do I get started?

  • Why should I store cat photos?

  • How do I get my photo?

  • What should I pass to the addPhoto function?

Types of docs

  • Tutorial: Getting started

  • Conceptual guide: Motivation and model

  • How-to: Task focused

  • Reference: Code focused

Doc Tools

Why

Docs are artifacts

Reference docs

catphoto.scala

/** Gets the url of a random photo of a given cat.
  *
  * {{{
  * >>> val album = Album()
  * >>> val nextAlbum = album.addPhoto("Mao", "mao.png")
  *
  * >>> nextAlbum.getPhoto("Mao")
  * mao.png
  * }}}
  */
def getPhoto(name: String): String = ???

Reference docs

Doctest

catphoto.scala

/** Gets the url of a random photo of a given cat.
  *
  * {{{
  * >>> val album = Album()
  * >>> val nextAlbum = album.addPhoto("Mao", "mao.png")
  *
  * >>> nextAlbum.getPhoto("Mao")
  * mao.png
  * }}}
  */
def getPhoto(name: String): String = ...
sbt> test
+ CatPhoto.DocTest
[success]

Doctest

catphoto.scala

/** Gets the url of a random photo of a given cat.
  *
  * {{{
  * >>> val album = Album()
  * >>> val nextAlbum = album.addPhoto("Mao", "mao.png")
  *
  * >>> nextAlbum.getPhoto("Mao")
  * mao.png
  * }}}
  */
def getPhoto(name: String): Option[String] = ...
sbt> test
- CatPhoto.DocTest
[error] Failed tests

Doctest

catphoto.scala

/** Gets the url of a random photo of a given cat.
  *
  * {{{
  * >>> val album = Album()
  * >>> val nextAlbum = album.addPhoto("Mao", "mao.png")
  *
  * >>> nextAlbum.getPhoto("Mao")
  * Some("mao.png")
  * }}}
  */
def getPhoto(name: String): Option[String] = ???
sbt> test
+ CatPhoto.DocTest
[success]

Guides

getting-started.md

# Getting started

Create a photo album.
```scala
val album = Album()
```

Add a photo for a cat named `Mao`.
```scala
val nextAlbum = album.addPhoto("Mao", "mao.png")
```

mdoc

getting-started.md

# Getting started

Create a photo album.
```scala mdoc
val album = Album()
```

Add a photo for a cat named `Mao`.
```scala mdoc
val nextAlbum = album.addPhoto("Mao", "mao.png")
```
> mdoc --in getting-started.md
error: getting-started.md:6:13:
Not found: Album - did you mean album?
val album = Album()
            ^^^^^

getting-started.md

# Getting started

Import `catphoto.Album`.
```scala mdoc
import catphoto.Album
```
> mdoc --in getting-started.md
[success]

out/getting-started.md

# Getting started

Import `catphoto.Album`
```scala
import catphoto.Album
```
Create a photo album.

```scala
val album = Album()
// album: Album = Album()
```

Variable substitution

getting-started.md

# Getting started

This guide is for @@VERSION@@.
> mdoc --site.VERSION="0.1.42" --in getting-started.md

out/getting-started.md

# Getting started

This guide is for 0.1.42.

Displaying output

out/getting-started.md

Get a random photo for Mao.

```scala
nextAlbum.getPhoto("Mao")
// mao.png
```
// Can we display mao.png ?

CatPhotoModifier.scala

class CatPhotoModifier extends PostModifier {
  val name = "catphoto"
  def process(ctx: PostModifierContext): String = {
    ctx.lastValue match {
      case url: String =>
        s"""```scala
           |${ctx.outputCode}
           |```
           |![A cat photo]($url)"""
      case _ => ""
    }
  }
}

getting-started.md

Get a random photo for Mao.

```scala mdoc:catphoto
nextAlbum.getPhoto("Mao")
```

out/getting-started.md

Get a random photo for Mao.

```scala
nextAlbum.getPhoto("Mao")
// mao.png
```

![A cat photo](mao.png)

Docs are artifacts

  • Compile, test and validate

  • Integrate into CI pipelines

  • Variable substitution

  • Manipulate output

Docs are trees

# Getting started

Import `catphoto.Album`
<html>
<h1>Getting started</h1>
<p>
  Import
  <span>catphoto.Album</span>
</p>
</html>

Docs are interactive

Livebooks

getting-started.livemd

## Cat Photo Tutorial

Let's create a cat photo album.

```elixir
album = CatPhoto.new()
```

## Adding a photo to the photo library

You can add a photo to the library with `add_photo`.

```elixir
album = album
  |> CatPhoto.add_photo(:mao, "mao.png")
```

Visualizations

defmodule CatPhoto.Kino do
  use Kino.JS

  def new(album) do
    htmls = for {_, v} <- album do
      "<img src='#{v}'/>"
    end
    html = Enum.join(htmls)
    Kino.JS.new(__MODULE__, html)
  end

  asset "main.js" do
    """
    export function init(ctx, html) {
      ctx.root.innerHTML = html;
    }
    """
  end
end

Using the browser

Docs are programs

getting-started.md

# Getting started

This guide is for @@VERSION@@.

Import `catphoto.Album`.

```scala mdoc
import catphoto.Album
```
...

Add a photo for a cat named `Mao`.
```scala mdoc
val nextAlbum = album.addPhoto("Mao", "mao.png")
```

thank-you.md

# Thank you!
The following cats need lots of treats:

 - Mao
 - Popcorn

Bugs

getting-started.md

Add a photo for Cinder.
<html>
<h1>Getting started</h1>
<p>
  Import
  <span>catphoto.Album</span>
</p>
</html>
(html
(h1 Getting started  h1)
(p
  Import
  (span catphoto.Album  span)
  p)
  html)
(html
(h1 Getting started    )
(p
  Import
  (span catphoto.Album      )
   )
     )

Racket

cats.rkt

#lang racket

(define myCat "Mao")

(define cats '("Mao" "Popcorn"))

(define (stroke name) (string-append "Stroking " name))
> racket
λ>
λ> (require "cats.rkt")
λ> myCat
"Mao"
λ> (stroke myCat)
"Stroking Mao"

A language to write languages

thank-you.html.pmd

#lang pollen

# Thank you!
The following cats need lots of treats:

 - Mao
 - Popcorn
> racket
λ> (require "thank-you.html.pmd")
λ> doc
'(root
  (h1 ((id "thank-you")) "Thank you!")
  (p "The following cats need lots of treats:")
  (ul (li "Mao") (li "Popcorn")))
λ> (->html doc)
"<h1 id=\"thank-you\">Thank you!</h1><p>The following cats …"

catname.rkt

#lang racket

(require "thank-you.html.pmd")

;; Get the cats from the doc
(define cats (select* 'li doc))

;; catname is a function
(define (catname name)
   (if (member name cats)
       name
       (error "A cat wasn’t thanked!" name)))

getting-started.html.pmd

#lang pollen

(require "catname.rkt")

Add a photo for (catname "Cinder").
λ> (require "getting-started.html.pmd")
A cat wasn't thanked! "Cinder"
  • Docs are artifacts

  • Docs are trees

  • Docs are interactive

  • Docs are programs

Where next?

Explore

Find me

Thank you!

Questions?