Showing preview only (1,262K chars total). Download the full file or copy to clipboard to get everything.
Repository: hmemcpy/milewski-ctfp-pdf
Branch: master
Commit: 606bc62f6eb8
Files: 1997
Total size: 868.3 KB
Directory structure:
gitextract_i87k79kx/
├── .editorconfig
├── .envrc
├── .github/
│ ├── settings.yml
│ └── workflows/
│ ├── nix-flake-check.yaml
│ ├── nix-fmt-checks.yaml
│ ├── prettier-checks.yaml
│ └── release.yaml
├── .gitignore
├── .latexindent.yaml
├── .prettierignore
├── .prettierrc
├── LICENSE
├── Makefile
├── README.md
├── errata-1.0.0.md
├── errata-1.3.0.md
├── errata-scala.md
├── flake.nix
└── src/
├── acknowledgments.tex
├── category.tex
├── colophon.tex
├── content/
│ ├── 0.0/
│ │ └── preface.tex
│ ├── 1.1/
│ │ ├── category-the-essence-of-composition.tex
│ │ └── code/
│ │ ├── haskell/
│ │ │ ├── snippet01.hs
│ │ │ ├── snippet02.hs
│ │ │ ├── snippet03.hs
│ │ │ ├── snippet04.hs
│ │ │ ├── snippet05.hs
│ │ │ └── snippet06.hs
│ │ ├── ocaml/
│ │ │ ├── snippet01.ml
│ │ │ ├── snippet02.ml
│ │ │ ├── snippet03.ml
│ │ │ ├── snippet04.ml
│ │ │ ├── snippet05.ml
│ │ │ └── snippet06.ml
│ │ ├── reason/
│ │ │ ├── snippet01.re
│ │ │ ├── snippet02.re
│ │ │ ├── snippet03.re
│ │ │ ├── snippet04.re
│ │ │ ├── snippet05.re
│ │ │ └── snippet06.re
│ │ └── scala/
│ │ ├── snippet01.scala
│ │ ├── snippet02.scala
│ │ ├── snippet03.scala
│ │ ├── snippet04.scala
│ │ ├── snippet05.scala
│ │ └── snippet06.scala
│ ├── 1.10/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ ├── snippet25.hs
│ │ │ │ ├── snippet26.hs
│ │ │ │ └── snippet27.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ ├── snippet25.ml
│ │ │ │ ├── snippet26.ml
│ │ │ │ └── snippet27.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ ├── snippet25.re
│ │ │ │ ├── snippet26.re
│ │ │ │ └── snippet27.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ ├── snippet25.scala
│ │ │ ├── snippet26.scala
│ │ │ └── snippet27.scala
│ │ └── natural-transformations.tex
│ ├── 1.2/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ └── snippet11.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet010.ml
│ │ │ │ ├── snippet011.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ └── snippet11.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet010.re
│ │ │ │ ├── snippet011.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ └── snippet11.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ └── snippet11.scala
│ │ └── types-and-functions.tex
│ ├── 1.3/
│ │ ├── categories-great-and-small.tex
│ │ └── code/
│ │ ├── haskell/
│ │ │ ├── snippet01.hs
│ │ │ └── snippet02.hs
│ │ ├── ocaml/
│ │ │ ├── snippet01.ml
│ │ │ └── snippet02.ml
│ │ ├── reason/
│ │ │ ├── snippet01.re
│ │ │ └── snippet02.re
│ │ └── scala/
│ │ ├── snippet01.scala
│ │ └── snippet02.scala
│ ├── 1.4/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ └── snippet07.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ └── snippet07.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ └── snippet07.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ └── snippet07.scala
│ │ └── kleisli-categories.tex
│ ├── 1.5/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ ├── snippet25.hs
│ │ │ │ ├── snippet26.hs
│ │ │ │ ├── snippet27.hs
│ │ │ │ └── snippet28.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ ├── snippet25.ml
│ │ │ │ ├── snippet26.ml
│ │ │ │ ├── snippet27.ml
│ │ │ │ └── snippet28.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ ├── snippet25.re
│ │ │ │ ├── snippet26.re
│ │ │ │ ├── snippet27.re
│ │ │ │ └── snippet28.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ ├── snippet25.scala
│ │ │ ├── snippet26.scala
│ │ │ ├── snippet27.scala
│ │ │ └── snippet28.scala
│ │ └── products-and-coproducts.tex
│ ├── 1.6/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ ├── snippet25.hs
│ │ │ │ ├── snippet26.hs
│ │ │ │ ├── snippet27.hs
│ │ │ │ ├── snippet28.hs
│ │ │ │ ├── snippet29.hs
│ │ │ │ ├── snippet30.hs
│ │ │ │ ├── snippet31.hs
│ │ │ │ ├── snippet32.hs
│ │ │ │ ├── snippet33.hs
│ │ │ │ ├── snippet34.hs
│ │ │ │ ├── snippet35.hs
│ │ │ │ ├── snippet36.hs
│ │ │ │ └── snippet37.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ ├── snippet25.ml
│ │ │ │ ├── snippet26.ml
│ │ │ │ ├── snippet27.ml
│ │ │ │ ├── snippet28.ml
│ │ │ │ ├── snippet29.ml
│ │ │ │ ├── snippet30.ml
│ │ │ │ ├── snippet31.ml
│ │ │ │ ├── snippet32.ml
│ │ │ │ ├── snippet33.ml
│ │ │ │ ├── snippet34.ml
│ │ │ │ ├── snippet35.ml
│ │ │ │ ├── snippet36.ml
│ │ │ │ └── snippet37.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ ├── snippet25.re
│ │ │ │ ├── snippet26.re
│ │ │ │ ├── snippet27.re
│ │ │ │ ├── snippet28.re
│ │ │ │ ├── snippet29.re
│ │ │ │ ├── snippet30.re
│ │ │ │ ├── snippet31.re
│ │ │ │ ├── snippet32.re
│ │ │ │ ├── snippet33.re
│ │ │ │ ├── snippet34.re
│ │ │ │ ├── snippet35.re
│ │ │ │ ├── snippet36.re
│ │ │ │ └── snippet37.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ ├── snippet25.scala
│ │ │ ├── snippet26.scala
│ │ │ ├── snippet27.scala
│ │ │ ├── snippet28.scala
│ │ │ ├── snippet29.scala
│ │ │ ├── snippet30.scala
│ │ │ ├── snippet31.scala
│ │ │ ├── snippet32.scala
│ │ │ ├── snippet33.scala
│ │ │ ├── snippet34.scala
│ │ │ ├── snippet35.scala
│ │ │ ├── snippet36.scala
│ │ │ └── snippet37.scala
│ │ └── simple-algebraic-data-types.tex
│ ├── 1.7/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ ├── snippet25.hs
│ │ │ │ ├── snippet26.hs
│ │ │ │ ├── snippet27.hs
│ │ │ │ ├── snippet28.hs
│ │ │ │ ├── snippet29.hs
│ │ │ │ ├── snippet30.hs
│ │ │ │ ├── snippet31.hs
│ │ │ │ ├── snippet32.hs
│ │ │ │ ├── snippet33.hs
│ │ │ │ ├── snippet34.hs
│ │ │ │ └── snippet35.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ ├── snippet25.ml
│ │ │ │ ├── snippet26.ml
│ │ │ │ ├── snippet27.ml
│ │ │ │ ├── snippet28.ml
│ │ │ │ ├── snippet29.ml
│ │ │ │ ├── snippet30.ml
│ │ │ │ ├── snippet31.ml
│ │ │ │ ├── snippet32.ml
│ │ │ │ ├── snippet33.ml
│ │ │ │ ├── snippet34.ml
│ │ │ │ └── snippet35.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ ├── snippet25.re
│ │ │ │ ├── snippet26.re
│ │ │ │ ├── snippet27.re
│ │ │ │ ├── snippet28.re
│ │ │ │ ├── snippet29.re
│ │ │ │ ├── snippet30.re
│ │ │ │ ├── snippet31.re
│ │ │ │ ├── snippet32.re
│ │ │ │ ├── snippet33.re
│ │ │ │ ├── snippet34.re
│ │ │ │ └── snippet35.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ ├── snippet25.scala
│ │ │ ├── snippet26.scala
│ │ │ ├── snippet27.scala
│ │ │ ├── snippet28.scala
│ │ │ ├── snippet29.scala
│ │ │ ├── snippet30.scala
│ │ │ ├── snippet31.scala
│ │ │ ├── snippet32.scala
│ │ │ ├── snippet33.scala
│ │ │ ├── snippet34.scala
│ │ │ └── snippet35.scala
│ │ └── functors.tex
│ ├── 1.8/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ ├── snippet25.hs
│ │ │ │ ├── snippet26.hs
│ │ │ │ ├── snippet27.hs
│ │ │ │ ├── snippet28.hs
│ │ │ │ ├── snippet29.hs
│ │ │ │ └── snippet30.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ ├── snippet25.ml
│ │ │ │ ├── snippet26.ml
│ │ │ │ ├── snippet27.ml
│ │ │ │ ├── snippet28.ml
│ │ │ │ ├── snippet29.ml
│ │ │ │ └── snippet30.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ ├── snippet25.re
│ │ │ │ ├── snippet26.re
│ │ │ │ ├── snippet27.re
│ │ │ │ ├── snippet28.re
│ │ │ │ ├── snippet29.re
│ │ │ │ └── snippet30.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ ├── snippet25.scala
│ │ │ ├── snippet26.scala
│ │ │ ├── snippet27.scala
│ │ │ ├── snippet28.scala
│ │ │ ├── snippet29.scala
│ │ │ └── snippet30.scala
│ │ └── functoriality.tex
│ ├── 1.9/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ └── snippet15.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ └── snippet15.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ └── snippet15.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ └── snippet15.scala
│ │ └── function-types.tex
│ ├── 2.1/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ └── snippet02.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ └── snippet02.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ └── snippet02.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ └── snippet02.scala
│ │ └── declarative-programming.tex
│ ├── 2.2/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ └── snippet19.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ └── snippet19.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ └── snippet19.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ └── snippet19.scala
│ │ └── limits-and-colimits.tex
│ ├── 2.3/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ └── snippet10.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ └── snippet10.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ └── snippet10.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ └── snippet10.scala
│ │ └── free-monoids.tex
│ ├── 2.4/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ └── snippet15.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ └── snippet15.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ └── snippet15.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ └── snippet15.scala
│ │ └── representable-functors.tex
│ ├── 2.5/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ └── snippet07.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ └── snippet07.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ └── snippet07.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ └── snippet07.scala
│ │ └── the-yoneda-lemma.tex
│ ├── 2.6/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ └── snippet02.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ └── snippet02.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ └── snippet02.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ └── snippet02.scala
│ │ └── yoneda-embedding.tex
│ ├── 3.1/
│ │ └── its-all-about-morphisms.tex
│ ├── 3.10/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ └── snippet18.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ └── snippet18.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ └── snippet18.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ └── snippet18.scala
│ │ └── ends-and-coends.tex
│ ├── 3.11/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ └── snippet12.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ └── snippet12.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ └── snippet12.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ └── snippet12.scala
│ │ └── kan-extensions.tex
│ ├── 3.12/
│ │ └── enriched-categories.tex
│ ├── 3.13/
│ │ └── topoi.tex
│ ├── 3.14/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ └── snippet04.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ └── snippet04.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ └── snippet04.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ └── snippet04.scala
│ │ └── lawvere-theories.tex
│ ├── 3.15/
│ │ └── monads-monoids-and-categories.tex
│ ├── 3.2/
│ │ ├── adjunctions.tex
│ │ └── code/
│ │ ├── haskell/
│ │ │ ├── snippet01.hs
│ │ │ ├── snippet02.hs
│ │ │ ├── snippet03.hs
│ │ │ ├── snippet04.hs
│ │ │ ├── snippet05.hs
│ │ │ ├── snippet06.hs
│ │ │ ├── snippet07.hs
│ │ │ ├── snippet08.hs
│ │ │ └── snippet09.hs
│ │ ├── ocaml/
│ │ │ ├── snippet01.ml
│ │ │ ├── snippet02.ml
│ │ │ ├── snippet03.ml
│ │ │ ├── snippet04.ml
│ │ │ ├── snippet05.ml
│ │ │ ├── snippet06.ml
│ │ │ ├── snippet07.ml
│ │ │ ├── snippet08.ml
│ │ │ └── snippet09.ml
│ │ ├── reason/
│ │ │ ├── snippet01.re
│ │ │ ├── snippet02.re
│ │ │ ├── snippet03.re
│ │ │ ├── snippet04.re
│ │ │ ├── snippet05.re
│ │ │ ├── snippet06.re
│ │ │ ├── snippet07.re
│ │ │ ├── snippet08.re
│ │ │ └── snippet09.re
│ │ └── scala/
│ │ ├── snippet01.scala
│ │ ├── snippet02.scala
│ │ ├── snippet03.scala
│ │ ├── snippet04.scala
│ │ ├── snippet05.scala
│ │ ├── snippet06.scala
│ │ ├── snippet07.scala
│ │ ├── snippet08.scala
│ │ └── snippet09.scala
│ ├── 3.3/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ └── snippet02.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ └── snippet02.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ └── snippet02.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ └── snippet02.scala
│ │ └── free-forgetful-adjunctions.tex
│ ├── 3.4/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ └── snippet25.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ └── snippet25.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ └── snippet25.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ └── snippet25.scala
│ │ └── monads-programmers-definition.tex
│ ├── 3.5/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ ├── snippet25.hs
│ │ │ │ ├── snippet26.hs
│ │ │ │ ├── snippet27.hs
│ │ │ │ ├── snippet28.hs
│ │ │ │ ├── snippet29.hs
│ │ │ │ ├── snippet30.hs
│ │ │ │ ├── snippet31.hs
│ │ │ │ ├── snippet32.hs
│ │ │ │ ├── snippet33.hs
│ │ │ │ ├── snippet34.hs
│ │ │ │ ├── snippet35.hs
│ │ │ │ ├── snippet36.hs
│ │ │ │ └── snippet37.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ ├── snippet25.ml
│ │ │ │ ├── snippet26.ml
│ │ │ │ ├── snippet27.ml
│ │ │ │ ├── snippet28.ml
│ │ │ │ ├── snippet29.ml
│ │ │ │ ├── snippet30.ml
│ │ │ │ ├── snippet31.ml
│ │ │ │ ├── snippet32.ml
│ │ │ │ ├── snippet33.ml
│ │ │ │ ├── snippet34.ml
│ │ │ │ ├── snippet35.ml
│ │ │ │ ├── snippet36.ml
│ │ │ │ └── snippet37.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ ├── snippet25.re
│ │ │ │ ├── snippet26.re
│ │ │ │ ├── snippet27.re
│ │ │ │ ├── snippet28.re
│ │ │ │ ├── snippet29.re
│ │ │ │ ├── snippet30.re
│ │ │ │ ├── snippet31.re
│ │ │ │ ├── snippet32.re
│ │ │ │ ├── snippet33.re
│ │ │ │ ├── snippet34.re
│ │ │ │ ├── snippet35.re
│ │ │ │ ├── snippet36.re
│ │ │ │ └── snippet37.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ ├── snippet25.scala
│ │ │ ├── snippet26.scala
│ │ │ ├── snippet27.scala
│ │ │ ├── snippet28.scala
│ │ │ ├── snippet29.scala
│ │ │ ├── snippet30.scala
│ │ │ ├── snippet31.scala
│ │ │ ├── snippet32.scala
│ │ │ ├── snippet33.scala
│ │ │ ├── snippet34.scala
│ │ │ ├── snippet35.scala
│ │ │ ├── snippet36.scala
│ │ │ └── snippet37.scala
│ │ └── monads-and-effects.tex
│ ├── 3.6/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ └── snippet25.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ └── snippet25.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ └── snippet25.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ └── snippet25.scala
│ │ └── monads-categorically.tex
│ ├── 3.7/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ ├── snippet25.hs
│ │ │ │ ├── snippet26.hs
│ │ │ │ ├── snippet27.hs
│ │ │ │ ├── snippet28.hs
│ │ │ │ ├── snippet29.hs
│ │ │ │ ├── snippet30.hs
│ │ │ │ ├── snippet31.hs
│ │ │ │ ├── snippet32.hs
│ │ │ │ ├── snippet33.hs
│ │ │ │ ├── snippet34.hs
│ │ │ │ ├── snippet35.hs
│ │ │ │ ├── snippet36.hs
│ │ │ │ ├── snippet37.hs
│ │ │ │ ├── snippet38.hs
│ │ │ │ └── snippet39.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ ├── snippet25.ml
│ │ │ │ ├── snippet26.ml
│ │ │ │ ├── snippet27.ml
│ │ │ │ ├── snippet28.ml
│ │ │ │ ├── snippet29.ml
│ │ │ │ ├── snippet30.ml
│ │ │ │ ├── snippet31.ml
│ │ │ │ ├── snippet32.ml
│ │ │ │ ├── snippet33.ml
│ │ │ │ ├── snippet34.ml
│ │ │ │ ├── snippet35.ml
│ │ │ │ ├── snippet36.ml
│ │ │ │ ├── snippet37.ml
│ │ │ │ ├── snippet38.ml
│ │ │ │ └── snippet39.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ ├── snippet25.re
│ │ │ │ ├── snippet26.re
│ │ │ │ ├── snippet27.re
│ │ │ │ ├── snippet28.re
│ │ │ │ ├── snippet29.re
│ │ │ │ ├── snippet30.re
│ │ │ │ ├── snippet31.re
│ │ │ │ ├── snippet32.re
│ │ │ │ ├── snippet33.re
│ │ │ │ ├── snippet34.re
│ │ │ │ ├── snippet35.re
│ │ │ │ ├── snippet36.re
│ │ │ │ ├── snippet37.re
│ │ │ │ ├── snippet38.re
│ │ │ │ └── snippet39.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ ├── snippet25.scala
│ │ │ ├── snippet26.scala
│ │ │ ├── snippet27.scala
│ │ │ ├── snippet28.scala
│ │ │ ├── snippet29.scala
│ │ │ ├── snippet30.scala
│ │ │ ├── snippet31.scala
│ │ │ ├── snippet32.scala
│ │ │ ├── snippet33.scala
│ │ │ ├── snippet34.scala
│ │ │ ├── snippet35.scala
│ │ │ ├── snippet36.scala
│ │ │ ├── snippet37.scala
│ │ │ ├── snippet38.scala
│ │ │ └── snippet39.scala
│ │ └── comonads.tex
│ ├── 3.8/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ ├── snippet25.hs
│ │ │ │ ├── snippet26.hs
│ │ │ │ ├── snippet27.hs
│ │ │ │ ├── snippet28.hs
│ │ │ │ ├── snippet29.hs
│ │ │ │ ├── snippet30.hs
│ │ │ │ ├── snippet31.hs
│ │ │ │ ├── snippet32.hs
│ │ │ │ ├── snippet33.hs
│ │ │ │ ├── snippet34.hs
│ │ │ │ └── snippet35.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ ├── snippet25.ml
│ │ │ │ ├── snippet26.ml
│ │ │ │ ├── snippet27.ml
│ │ │ │ ├── snippet28.ml
│ │ │ │ ├── snippet29.ml
│ │ │ │ ├── snippet30.ml
│ │ │ │ ├── snippet31.ml
│ │ │ │ ├── snippet32.ml
│ │ │ │ ├── snippet33.ml
│ │ │ │ ├── snippet34.ml
│ │ │ │ └── snippet35.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ ├── snippet25.re
│ │ │ │ ├── snippet26.re
│ │ │ │ ├── snippet27.re
│ │ │ │ ├── snippet28.re
│ │ │ │ ├── snippet29.re
│ │ │ │ ├── snippet30.re
│ │ │ │ ├── snippet31.re
│ │ │ │ ├── snippet32.re
│ │ │ │ ├── snippet33.re
│ │ │ │ ├── snippet34.re
│ │ │ │ └── snippet35.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ ├── snippet25.scala
│ │ │ ├── snippet26.scala
│ │ │ ├── snippet27.scala
│ │ │ ├── snippet28.scala
│ │ │ ├── snippet29.scala
│ │ │ ├── snippet30.scala
│ │ │ ├── snippet31.scala
│ │ │ ├── snippet32.scala
│ │ │ ├── snippet33.scala
│ │ │ ├── snippet34.scala
│ │ │ └── snippet35.scala
│ │ └── f-algebras.tex
│ ├── 3.9/
│ │ ├── algebras-for-monads.tex
│ │ └── code/
│ │ ├── haskell/
│ │ │ ├── snippet01.hs
│ │ │ ├── snippet02.hs
│ │ │ ├── snippet03.hs
│ │ │ ├── snippet04.hs
│ │ │ ├── snippet05.hs
│ │ │ ├── snippet06.hs
│ │ │ ├── snippet07.hs
│ │ │ ├── snippet08.hs
│ │ │ ├── snippet09.hs
│ │ │ ├── snippet10.hs
│ │ │ ├── snippet11.hs
│ │ │ ├── snippet12.hs
│ │ │ ├── snippet13.hs
│ │ │ ├── snippet14.hs
│ │ │ ├── snippet15.hs
│ │ │ └── snippet16.hs
│ │ ├── ocaml/
│ │ │ ├── snippet01.ml
│ │ │ ├── snippet02.ml
│ │ │ ├── snippet03.ml
│ │ │ ├── snippet04.ml
│ │ │ ├── snippet05.ml
│ │ │ ├── snippet06.ml
│ │ │ ├── snippet07.ml
│ │ │ ├── snippet08.ml
│ │ │ ├── snippet09.ml
│ │ │ ├── snippet10.ml
│ │ │ ├── snippet11.ml
│ │ │ ├── snippet12.ml
│ │ │ ├── snippet13.ml
│ │ │ ├── snippet14.ml
│ │ │ ├── snippet15.ml
│ │ │ └── snippet16.ml
│ │ ├── reason/
│ │ │ ├── snippet01.re
│ │ │ ├── snippet02.re
│ │ │ ├── snippet03.re
│ │ │ ├── snippet04.re
│ │ │ ├── snippet05.re
│ │ │ ├── snippet06.re
│ │ │ ├── snippet07.re
│ │ │ ├── snippet08.re
│ │ │ ├── snippet09.re
│ │ │ ├── snippet10.re
│ │ │ ├── snippet11.re
│ │ │ ├── snippet12.re
│ │ │ ├── snippet13.re
│ │ │ ├── snippet14.re
│ │ │ ├── snippet15.re
│ │ │ └── snippet16.re
│ │ └── scala/
│ │ ├── snippet01.scala
│ │ ├── snippet02.scala
│ │ ├── snippet03.scala
│ │ ├── snippet04.scala
│ │ ├── snippet05.scala
│ │ ├── snippet06.scala
│ │ ├── snippet07.scala
│ │ ├── snippet08.scala
│ │ ├── snippet09.scala
│ │ ├── snippet10.scala
│ │ ├── snippet11.scala
│ │ ├── snippet12.scala
│ │ ├── snippet13.scala
│ │ ├── snippet14.scala
│ │ ├── snippet15.scala
│ │ └── snippet16.scala
│ ├── editor-note.tex
│ ├── ocaml/
│ │ ├── colophon.tex
│ │ └── editor-note.tex
│ ├── reason/
│ │ ├── colophon.tex
│ │ └── editor-note.tex
│ └── scala/
│ ├── colophon.tex
│ └── editor-note.tex
├── cover/
│ ├── blurb.tex
│ ├── cover-hardcover-ocaml.tex
│ ├── cover-hardcover-reason.tex
│ ├── cover-hardcover-scala.tex
│ ├── cover-hardcover.tex
│ ├── cover-paperback-ocaml.tex
│ ├── cover-paperback-reason.tex
│ ├── cover-paperback-scala.tex
│ ├── cover-paperback.tex
│ ├── ribbon-ocaml.tex
│ ├── ribbon-reason.tex
│ └── ribbon-scala.tex
├── ctfp-print-ocaml.tex
├── ctfp-print-reason.tex
├── ctfp-print-scala.tex
├── ctfp-print.tex
├── ctfp-reader-ocaml.tex
├── ctfp-reader-reason.tex
├── ctfp-reader-scala.tex
├── ctfp-reader.tex
├── ctfp.tex
├── free-software.tex
├── half-title.tex
├── index.tex
├── opt-ocaml.tex
├── opt-print-ustrade.tex
├── opt-reader-10in.tex
├── opt-reason.tex
├── opt-scala.tex
├── postamble.tex
├── preamble.tex
├── scraper.py
└── version.tex
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*]
indent_size = 4
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[Makefile]
indent_style = tab
[*.{tex,cls,lua,nix}]
indent_style = space
indent_size = 2
max_line_length = 80
[*.md]
trim_trailing_whitespace = false
indent_size = 2
max_line_length = 80
================================================
FILE: .envrc
================================================
use flake .
use flake github:loophp/nix-prettier
================================================
FILE: .github/settings.yml
================================================
# https://github.com/probot/settings
branches:
- name: master
protection:
enforce_admins: false
required_pull_request_reviews:
dismiss_stale_reviews: true
require_code_owner_reviews: true
required_approving_review_count: 1
restrictions: null
required_linear_history: true
required_status_checks:
strict: true
labels:
- name: typo
color: ee0701
- name: dependencies
color: 0366d6
- name: enhancement
color: 0e8a16
- name: question
color: cc317c
- name: security
color: ee0701
- name: stale
color: eeeeee
repository:
allow_merge_commit: true
allow_rebase_merge: true
allow_squash_merge: true
default_branch: master
description:
"Bartosz Milewski's 'Category Theory for Programmers' unofficial PDF and
LaTeX sources"
homepage: https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/
topics: pdf,haskell,scala,latex,cpp,functional-programming,ocaml,category-theory
has_downloads: true
has_issues: true
has_pages: false
has_projects: false
has_wiki: false
name: milewski-ctfp-pdf
private: false
================================================
FILE: .github/workflows/nix-flake-check.yaml
================================================
name: Check and build
on:
pull_request:
push:
branches:
- master
jobs:
dependencies:
name: Build dependencies
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Set up Git repository
uses: actions/checkout@v3
- name: Create global variables
id: version
run:
echo "version=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
determine-matrix:
name: Figure out the packages we need to build
runs-on: ubuntu-latest
needs: [dependencies]
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Set up Git repository
uses: actions/checkout@v3
- name: Install the Nix package manager
uses: cachix/install-nix-action@v20
- id: set-matrix
run: |
echo "matrix=$(
nix eval --json .#packages.x86_64-linux --apply builtins.attrNames
)" >> $GITHUB_OUTPUT
build:
name: Build
needs: determine-matrix
runs-on: ubuntu-latest
strategy:
matrix:
packages: ${{fromJson(needs.determine-matrix.outputs.matrix)}}
steps:
- name: Set up Git repository
uses: actions/checkout@v3
- name: Install Nix
uses: cachix/install-nix-action@v20
- name: Nix flake check
run: nix flake check
- name: Build ${{ matrix.packages }}.pdf
run: nix build .#${{ matrix.packages }}
================================================
FILE: .github/workflows/nix-fmt-checks.yaml
================================================
name: Nix formatter checks
on:
pull_request:
jobs:
format-check:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Install Nix
uses: cachix/install-nix-action@v18
- name: Run nix formatter tool
run: nix fmt . -- --check
================================================
FILE: .github/workflows/prettier-checks.yaml
================================================
name: Prettier checks
on:
pull_request:
jobs:
prettier:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install the Nix package manager
uses: cachix/install-nix-action@v18
- name: Checks
run: nix run nixpkgs#nodePackages.prettier -- --check .
================================================
FILE: .github/workflows/release.yaml
================================================
name: Release PDFs
on:
push:
tags:
- "**"
jobs:
determine-matrix:
name: Figure out the assets we need to build
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Set up Git repository
uses: actions/checkout@v3
- name: Install the Nix package manager
uses: cachix/install-nix-action@v20
- id: set-matrix
run: |
echo "matrix=$(
nix eval --json .#packages.x86_64-linux --apply builtins.attrNames
)" >> $GITHUB_OUTPUT
build:
name: Build documents
needs: determine-matrix
runs-on: ubuntu-latest
strategy:
matrix:
packages: ${{fromJson(needs.determine-matrix.outputs.matrix)}}
steps:
- name: Set up Git repository
uses: actions/checkout@v3
- name: Install Nix
uses: cachix/install-nix-action@v20
- name: Build ${{ matrix.packages }}.pdf
run: |
nix build .#${{ matrix.packages }}
mkdir -p out
cp -ar ./result/* out/
- name: Upload build assets (${{ matrix.packages }}.pdf)
uses: actions/upload-artifact@v2
with:
name: ctfp
path: out/*
release:
name: "Create Github pre-release"
runs-on: ubuntu-latest
needs: [build]
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: Create Github pre-release (${{ github.ref }})
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
draft: true
assets:
name: Upload release assets
runs-on: ubuntu-latest
needs: [determine-matrix, release]
strategy:
matrix:
packages: ${{fromJson(needs.determine-matrix.outputs.matrix)}}
steps:
- name: Download build assets (${{ matrix.packages }}.pdf)
uses: actions/download-artifact@v4.1.7
with:
name: ctfp
path: ctfp
- name:
Upload release assets (${{ matrix.packages }}--${{ github.ref
}}.pdf)
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.release.outputs.upload_url }}
asset_path: ctfp/${{ matrix.packages }}.pdf
asset_name: ${{ matrix.packages }}--${{ github.ref }}.pdf
asset_content_type: application/pdf
================================================
FILE: .gitignore
================================================
/.vscode/
/.direnv/
/build/
/result
.DS_Store
src/.dotty-ide-disabled
src/.metals
out/
_minted*
*.fls
out/*
*.fdb_latexmk
*.bak*
*.log
*.pdf
*.xdv
*.gz
*.pyg
*.synctex(busy)
*.aux
*.idx
*.ilg
*.lig
*.ind
*.out
*.toc
================================================
FILE: .latexindent.yaml
================================================
defaultIndent: " "
verbatimEnvironments:
verbatim: 1
lstlisting: 1
minted: 1
snip: 1
snipv: 1
================================================
FILE: .prettierignore
================================================
/.direnv/
/.idea/
/vendor/
/docs/
/build/
CHANGELOG.md
flake.lock
================================================
FILE: .prettierrc
================================================
{
"proseWrap": "always"
}
================================================
FILE: LICENSE
================================================
LICENSING TERMS
1. Output PDF, *.tex, all files in content/* and fig/* are licensed
under Creative Commons Attribution-ShareAlike 4.0 International License
(http://creativecommons.org/licenses/by-sa/4.0/).
2. The script files scraper.py and additional scripts are under GNU GPL v.3:
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
================================================
FILE: Makefile
================================================
OUTPUT ?= $(shell basename "$(shell dirname "$(INPUT)")")
OUTPUT_DIRECTORY = $(shell pwd)/build
LATEXMK_ARGS ?= -f -file-line-error -shell-escape -logfilewarninglist -interaction=nonstopmode -halt-on-error -norc -pdflatex="xelatex %O %S" -pdfxe
TEXINPUTS = ""
TEXLIVE_RUN = TEXINPUTS=$(TEXINPUTS)
LATEXMK_COMMAND = $(TEXLIVE_RUN) latexmk $(LATEXMK_ARGS)
# Make does not offer a recursive wildcard function, so here's one:
rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
ctfp:
cd src; $(LATEXMK_COMMAND) -jobname=ctfp ctfp-reader.tex
ctfp-ocaml:
cd src; $(LATEXMK_COMMAND) -jobname=ctfp-ocaml ctfp-reader-ocaml.tex
ctfp-scala:
cd src; $(LATEXMK_COMMAND) -jobname=ctfp-scala ctfp-reader-scala.tex
ctfp-print:
cd src; $(LATEXMK_COMMAND) -jobname=ctfp-print ctfp-print.tex
ctfp-print-ocaml:
cd src; $(LATEXMK_COMMAND) -jobname=ctfp-print-ocaml ctfp-print-ocaml.tex
ctfp-print-scala:
cd src; $(LATEXMK_COMMAND) -jobname=ctfp-print-scala ctfp-print-scala.tex
lint:
$(foreach file, $(call rwildcard,$(shell dirname "$(INPUT)"),*.tex), latexindent -l -w $(file);)
================================================
FILE: README.md
================================================
![GitHub stars][github stars]
[![GitHub Workflow Status][github workflow status]][github actions link]
[![Download][download badge]][github latest release]
[![License][license badge]][github latest release]
# Category Theory For Programmers
An _unofficial_ PDF version of "**C**ategory **T**heory **F**or **P**rogrammers"
by [Bartosz Milewski][bartosz github], converted from his [blogpost
series][blogpost series] (_with permission!_).
![Category Theory for Programmers][ctfp image]
## Buy the book
- **[Standard edition in full-color hardcover
print][buy regular edition on blurb]**
- Publish date: 12 August, 2019.
- Based off release tag [v1.3.0][v1.3.0 github release link]. See
[errata-1.3.0](errata-1.3.0.md) for changes and fixes since print.
- **[Scala Edition in paperback][buy scala edition on blurb]**
- Publish date: 12 August, 2019.
- Based off release tag [v1.3.0][v1.3.0 github release link]. See
[errata-scala](errata-scala.md) for changes and fixes since print.
## Build the book
The building workflow requires [Nix][nix website]. After [installing
Nix][nix download website], you need to enable the upcoming "flake" feature
which must be [enabled manually][nixos wiki flake] the time being. This is
needed to expose the new Nix commands and flakes support that are hidden behind
feature-flags.
Afterwards, type `nix flake show` in the root directory of the project to see
all the available versions of this book. Then type `nix build .#<edition>` to
build the edition you want (Scala, OCaml, Reason and their printed versions).
For example, to build the Scala edition you'll have to type
`nix build .#ctfp-scala`. For Haskell (the original version) that is just
`nix build .#ctfp`.
Upon successful compilation, the PDF file will be placed in the `result`
directory.
The command `nix develop` will provide a shell containing all the required
dependencies to build the book manually using the provided `Makefile`. To build
the `ctfp-scala` edition, just run `make ctfp-scala`.
## Contribute
Contributors are welcome to contribute to this book by sending pull-requests.
Once reviewed, the changes are merged in the main branch and will be
incorporated in the next release.
**Note from [Bartosz][bartosz github]**: I really appreciate all your
contributions. You made this book much better than I could have imagined. Thank
you!
Find the [list of contributors on Github][contributors].
## Acknowledgements
PDF LaTeX source and the tools to create it are based on the work by [Andres
Raba][andres raba github]. The book content is taken, with permission, from
[Bartosz Milewski][bartosz github]'s blogpost series, and adapted to the LaTeX
format.
The original blog post acknowledgments by Bartosz are consolidated in the
_Acknowledgments_ page at the end of the book.
## License
The PDF book, `.tex` files, and associated images and figures in directories
`src/fig` and `src/content` are licensed under [Creative Commons
Attribution-ShareAlike 4.0 International License][license cc by sa].
The script files `scraper.py` and others are licensed under [GNU General Public
License version 3][license gnu gpl].
[download badge]:
https://img.shields.io/badge/Download-latest-green.svg?style=flat-square
[github actions link]: https://github.com/hmemcpy/milewski-ctfp-pdf/actions
[github stars]:
https://img.shields.io/github/stars/hmemcpy/milewski-ctfp-pdf.svg?style=flat-square
[github workflow status]:
https://img.shields.io/github/actions/workflow/status/hmemcpy/milewski-ctfp-pdf/nix-flake-check.yaml?branch=master&style=flat-square
[github latest release]:
https://github.com/hmemcpy/milewski-ctfp-pdf/releases/latest
[license badge]:
https://img.shields.io/badge/License-CC_By_SA-green.svg?style=flat-square
[ctfp image]:
https://user-images.githubusercontent.com/601206/47271389-8eea0900-d581-11e8-8e81-5b932e336336.png
[bartosz github]: https://github.com/BartoszMilewski
[nixos wiki flake]: https://wiki.nixos.org/wiki/Flakes
[andres raba github]: https://github.com/sarabander
[contributors]: https://github.com/hmemcpy/milewski-ctfp-pdf/graphs/contributors
[license cc by sa]: https://spdx.org/licenses/CC-BY-SA-4.0.html
[license gnu gpl]: https://spdx.org/licenses/GPL-3.0.html
[blogpost series]:
https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/
[buy regular edition on blurb]:
https://www.blurb.com/b/9621951-category-theory-for-programmers-new-edition-hardco
[buy scala edition on blurb]:
https://www.blurb.com/b/9603882-category-theory-for-programmers-scala-edition-pape
[v1.3.0 github release link]:
https://github.com/hmemcpy/milewski-ctfp-pdf/releases/tag/v1.3.0
[nix website]: https://nixos.org/nix/
[nix download website]: https://nixos.org/download.html
================================================
FILE: errata-1.0.0.md
================================================
## A list of typos/mistakes that were fixed after the initial printed book release.
### Preface
- [#155](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/155) - Typo
(physicist -> physicists)
### 6. Simple Algebraic Data Types
- [#176](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/176) - Typo
(statements -> statement)
### 8. Functoriality
- [9a3a5a3](https://github.com/hmemcpy/milewski-ctfp-pdf/commit/9a3a5a386e98ef8f926bccd08f572cc19b1a6367) -
added clarifications on bifunctoriality vs. separate functoriality (fix by
Bartosz)
### 9. Function Types
- [#182](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/182) - Fix typo
(chose -> choose)
### 10. Natural Transformations
- [#157](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/157) - Adding
paragraph indent
### 12. Limits and Colimits
- [#162](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/162) - Fix
grammatical error
### 14. Representable Functors
- [#162](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/162) - Fix
grammatical error
### 18. Adjunctions
- [#160](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/160) - Fix spelling
of "counit"
### 19. Free/Forgetful Adjunctions
- [#156](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/156) - an instance of
the category name **Mon** is appearing as **arg**
- [#160](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/160) - Fix spelling
of "isomorphism"
### 20. Monads - Programmer's Definition
- [#160](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/160) - Fix
grammatical error
- [#162](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/162) - Fix
grammatical error
### 22. Monads Categorically
- [#162](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/162) - Fix
grammatical error
### 23. Comonads
- [#158](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/158) - fixed
incorrect typesetting of `set`
- [23f522e](https://github.com/hmemcpy/milewski-ctfp-pdf/commit/23f522ec083c2c98f28f15935ff2893ccd1fa76c) -
adjusted `Prod`/`Product` names (fix by Bartosz)
### 25. Algebras for Monads
- [#158](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/158) - fixed
incorrect typesetting of `set`
- [#159](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/159) - fixed
incorrect typesetting of category terms
- [#160](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/160) - Fix spelling
of "counit" and "morphisms", fix subscript spacing
- [#162](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/162) - Fix
grammatical errors
### 26. Ends and Coends
- [#159](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/159) - fixed
incorrect typesetting of category terms
- [#160](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/160) - Fix spelling
of "coequalizer", fix subscript spacing
### 27. Kan Extensions
- [#160](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/160) - Fix subscript
spacing
- [31821e5](https://github.com/hmemcpy/milewski-ctfp-pdf/commit/31821e5ded0dacf059e1fcb985be406e8a495107) -
postcomposition -> precomposition (fix by Bartosz)
### 28. Enriched Categories
- [#160](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/160) - Fix subscript
spacing
- [#162](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/162) - Fix
grammatical error
### 29. Topoi
- [#162](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/162) - Fix
grammatical error
### 30. Lawvere Theories
- [#160](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/160) - Fix spelling
of "coequalizer"
- [#162](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/162) - Fix
grammatical errors and a typesetting error
================================================
FILE: errata-1.3.0.md
================================================
## A list of typos/mistakes that were fixed after the release of the new edition (1.3.0) (12 August, 2019).
(see errata for the original edition until 1.3.0
[here](https://github.com/hmemcpy/milewski-ctfp-pdf/blob/master/errata-1.0.0.md))
### Preface
- [#278](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/278) - Fixed
reference to Saunders Mac Lane's _Categories for the Working Mathematician_.
Was previously misreferenced as "_Category Theory_ for the Working
Mathematician."
### 12. Limits and Colimits
- [#278](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/278) - Fixed
formatting of quotation marks around "selecting." Were previously pointing the
wrong direction.
### 18. Adjunctions
- [#228](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/228) - Typo
(adjuncion -> adjunction)
### 30. Lawvere Theories
- [#226](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/226) - fix type in
diagram of monads as coends
================================================
FILE: errata-scala.md
================================================
## A list of typos/mistakes that were fixed after the initial printed book release (12 August, 2019).
### 7. Functors
- [3d29cd9](https://github.com/hmemcpy/milewski-ctfp-pdf/commit/3d29cd99f34ce1205ed9a68aeae038d9d47c7145) -
Added `LazyList` example, supported since Scala 2.13
- [#210](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/210) - Section 6.4 -
`prodToSum` snippet. Explicitly Tupling return type to avoid adapted args
warning, which is deprecated behavior
- [#243](https://github.com/hmemcpy/milewski-ctfp-pdf/pull/243) - Section 8.7 -
Change bimap to dimap in Profunctor definition
================================================
FILE: flake.nix
================================================
{
description = "Category Theory for Programmers";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
inputs.systems.url = "github:nix-systems/default";
outputs = inputs@{ self, flake-parts, nixpkgs, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = import inputs.systems;
perSystem = { config, pkgs, system, lib, ... }:
let
pkgs = nixpkgs.legacyPackages.${system};
########################################################################
# LaTeX Font
inconsolata-lgc-latex = pkgs.stdenvNoCC.mkDerivation {
name = "inconsolata-lgc-latex";
pname = "inconsolata-lgc-latex";
src = pkgs.inconsolata-lgc;
dontConfigure = true;
sourceRoot = ".";
installPhase = ''
runHook preInstall
find $src -name '*.ttf' -exec install -m644 -Dt $out/fonts/truetype/public/inconsolata-lgc/ {} \;
find $src -name '*.otf' -exec install -m644 -Dt $out/fonts/opentype/public/inconsolata-lgc/ {} \;
runHook postInstall
'';
tlType = "run";
};
########################################################################
# LaTeX Environment
texliveEnv = pkgs.texlive.combine {
inherit
(pkgs.texlive)
adjustbox
alegreya
babel
bookcover
catchfile
chngcntr
collectbox
currfile
emptypage
enumitem
environ
fgruler
fontaxes
framed
fvextra
idxlayout
ifmtarg
ifnextok
ifplatform
imakeidx
import
inconsolata
l3packages
lettrine
libertine
libertinus-fonts
listings
mdframed
microtype
minifp
minted
mweights
needspace
newtx
noindentafter
nowidow
scheme-medium
subfigure
subfiles
textpos
tcolorbox
tikz-cd
titlecaps
titlesec
todonotes
trimspaces
upquote
wrapfig
xifthen
xpatch
xstring
zref
;
inconsolata-lgc-latex = {
pkgs = [ inconsolata-lgc-latex ];
};
};
commonAttrs = {
nativeBuildInputs = [
texliveEnv
(
pkgs.python3.withPackages (p: [ p.pygments p.pygments-style-github ])
)
pkgs.which
];
};
mkLatex = variant: edition:
let
maybeVariant = lib.optionalString (variant != null) "-${variant}";
maybeEdition = lib.optionalString (edition != null) "-${edition}";
variantStr =
if variant == null
then "reader"
else variant;
basename = "ctfp-${variantStr}${maybeEdition}";
version = self.shortRev or self.lastModifiedDate;
suffix = maybeVariant + maybeEdition;
fullname = "ctfp${suffix}";
in
pkgs.stdenvNoCC.mkDerivation (commonAttrs
// {
inherit basename version;
name = basename;
src = "${self}/src";
configurePhase = ''
runHook preConfigure
substituteInPlace "version.tex" --replace "dev" "${version}"
runHook postConfigure
'';
buildPhase = ''
runHook preBuild
latexmk -file-line-error -shell-escape -logfilewarninglist \
-interaction=nonstopmode -halt-on-error -norc \
-jobname=ctfp -pdflatex="xelatex %O %S" -pdfxe \
"$basename.tex"
runHook postBuild
'';
installPhase = "
runHook preInstall
install -m 0644 -vD ctfp.pdf \"$out/${fullname}.pdf\"
runHook postInstall
";
passthru.packageName = fullname;
});
editions = [ null "scala" "ocaml" "reason" ];
variants = [ null "print" ];
in
{
formatter = pkgs.nixpkgs-fmt;
packages = lib.listToAttrs (lib.concatMap
(variant:
map
(edition: rec {
value = mkLatex variant edition;
name = value.packageName;
})
editions)
variants);
# nix develop .
devShells.default = pkgs.mkShellNoCC (commonAttrs
// {
nativeBuildInputs =
commonAttrs.nativeBuildInputs
++ [
pkgs.git
pkgs.gnumake
];
});
};
};
}
================================================
FILE: src/acknowledgments.tex
================================================
\noindent
I’d like to thank Edward Kmett and Gershom Bazerman for checking my math
and logic. I'm grateful to many volunteers who corrected my mistakes and improved the book.
\vspace{1.0em}
\noindent
I’d like to thank Andrew Sutton for rewriting my C++ monoid concept
code according to his and Bjarne Stroustrup’s latest proposal.
\vspace{1.0em}
\noindent
I'm grateful to Eric Niebler for reading the draft and providing the
clever implementation of \code{compose} that uses advanced features of
C++14 to drive type inference. I was able to cut the whole section of
old fashioned template magic that did the same thing using type traits.
Good riddance!
================================================
FILE: src/category.tex
================================================
\newcommand{\cat}{%
\symbf%
}
\newcommand{\idarrow}[1][]{%
\mathbf{id}_{#1}%
}
\newcommand{\Lim}[1][]{%
\mathbf{Lim}{#1}%
}
\newcommand{\Set}{\cat{Set}}
\newcommand{\Rel}{\cat{Rel}}
\newcommand{\Cat}{\cat{Cat}}
\newcommand{\id}{\mathbf{id}}
\newcommand{\Ran}{\mathbf{Ran}}
\newcommand{\Lan}{\mathbf{Lan}}
\newcommand{\Hask}{\mathbf{Hask}}
\newcommand{\Fop}{\cat{F}^\mathit{op}}
================================================
FILE: src/colophon.tex
================================================
\lettrine[lraise=-0.03,loversize=0.08]{T}{his book} was compiled by \urlref{https://hmemcpy.com}{Igal Tabachnik}, by converting the original text by Bartosz Milewski into \LaTeX{} format, by
first scraping the original WordPress blog posts using \urlref{https://mercury.postlight.com/web-parser/}{Mercury Web Parser}
to get a clean HTML content, modifying and tweaking with \urlref{https://www.crummy.com/software/BeautifulSoup/}{Beautiful Soup},
finally, converting to \LaTeX{} with \urlref{https://pandoc.org/}{Pandoc}.
The typefaces are Linux Libertine for body text and Linux Biolinum for headings, both by Philipp H. Poll. Typewriter face is Inconsolata
created by Raph Levien and supplemented by Dimosthenis Kaponis and Takashi Tanigawa in the form of Inconsolata \acronym{LGC}. The cover page
typeface is Alegreya, designed by Juan Pablo del Peral.
Original book layout design and typography are done by Andres Raba. Syntax highlighting is using ``GitHub'' style for Pygments by
\urlref{https://github.com/hugomaiavieira/pygments-style-github}{Hugo Maia Vieira}.
\ifdefined\OPTCustomLanguage{%
\input{content/\OPTCustomLanguage/colophon}
}
\fi
================================================
FILE: src/content/0.0/preface.tex
================================================
% !TEX root = ../../ctfp-print.tex
\begin{quote}
For some time now I've been floating the idea of writing a book about
category theory that would be targeted at programmers. Mind you, not
computer scientists but programmers --- engineers rather than
scientists. I know this sounds crazy and I am properly scared. I can't
deny that there is a huge gap between science and engineering because I
have worked on both sides of the divide. But I've always felt a very
strong compulsion to explain things. I have tremendous admiration for
Richard Feynman who was the master of simple explanations. I know I'm no
Feynman, but I will try my best. I'm starting by publishing this preface
--- which is supposed to motivate the reader to learn category theory
--- in hopes of starting a discussion and soliciting feedback.\footnote{
You may also watch me teach this material to a live audience, at
\href{https://goo.gl/GT2UWU}{https://goo.gl/GT2UWU} (or search
``bartosz milewski category theory'' on YouTube.)}
\end{quote}
\lettrine[lhang=0.17]{I}{will attempt}, in the space of a few paragraphs,
to convince you that this book is written for you, and whatever
objections you might have to learning one of the most abstract branches
of mathematics in your ``copious spare time'' are totally unfounded.
My optimism is based on several observations. First, category theory is
a treasure trove of extremely useful programming ideas. Haskell
programmers have been tapping this resource for a long time, and the
ideas are slowly percolating into other languages, but this process is
too slow. We need to speed it up.
Second, there are many different kinds of math, and they appeal to
different audiences. You might be allergic to calculus or algebra, but
it doesn't mean you won't enjoy category theory. I would go as far as
to argue that category theory is the kind of math that is particularly
well suited for the minds of programmers. That's because category theory
--- rather than dealing with particulars --- deals with structure. It
deals with the kind of structure that makes programs composable.
Composition is at the very root of category theory --- it's part of the
definition of the category itself. And I will argue strongly that
composition is the essence of programming. We've been composing things
forever, long before some great engineer came up with the idea of a
subroutine. Some time ago the principles of structured programming
revolutionized programming because they made blocks of code composable.
Then came object oriented programming, which is all about composing
objects. Functional programming is not only about composing functions
and algebraic data structures --- it makes concurrency composable ---
something that's virtually impossible with other programming paradigms.
Third, I have a secret weapon, a butcher's knife, with which I will
butcher math to make it more palatable to programmers. When you're a
professional mathematician, you have to be very careful to get all your
assumptions straight, qualify every statement properly, and construct
all your proofs rigorously. This makes mathematical papers and books
extremely hard to read for an outsider. I'm a physicist by training, and
in physics we made amazing advances using informal reasoning.
Mathematicians laughed at the Dirac delta function, which was made up on
the spot by the great physicist P. A. M. Dirac to solve some
differential equations. They stopped laughing when they discovered a
completely new branch of calculus called distribution theory that
formalized Dirac's insights.
Of course when using hand-waving arguments you run the risk of saying
something blatantly wrong, so I will try to make sure that there is
solid mathematical theory behind informal arguments in this book. I do
have a worn-out copy of Saunders Mac Lane's \emph{Categories for
the Working Mathematician} on my nightstand.
Since this is category theory \emph{for programmers} I will illustrate
all major concepts using computer code. You are probably aware that
functional languages are closer to math than the more popular imperative
languages. They also offer more abstracting power. So a natural
temptation would be to say: You must learn Haskell before the bounty of
category theory becomes available to you. But that would imply that
category theory has no application outside of functional programming and
that's simply not true. So I will provide a lot of C++ examples.
Granted, you'll have to overcome some ugly syntax, the patterns might
not stand out from the background of verbosity, and you might be forced
to do some copy and paste in lieu of higher abstraction, but that's just
the lot of a C++ programmer.
But you're not off the hook as far as Haskell is concerned. You don't
have to become a Haskell programmer, but you need it as a language for
sketching and documenting ideas to be implemented in C++. That's exactly
how I got started with Haskell. I found its terse syntax and powerful
type system a great help in understanding and implementing C++
templates, data structures, and algorithms. But since I can't expect the
readers to already know Haskell, I will introduce it slowly and explain
everything as I go.
If you're an experienced programmer, you might be asking yourself: I've
been coding for so long without worrying about category theory or
functional methods, so what's changed? Surely you can't help but notice
that there's been a steady stream of new functional features invading
imperative languages. Even Java, the bastion of object-oriented
programming, let the lambdas in. C++ has recently been evolving at a
frantic pace --- a new standard every few years --- trying to catch up
with the changing world. All this activity is in preparation for a
disruptive change or, as we physicists call it, a phase transition. If
you keep heating water, it will eventually start boiling. We are now in
the position of a frog that must decide if it should continue swimming
in increasingly hot water, or start looking for some alternatives.
\begin{figure}[H]
\centering
\includegraphics[width=0.5\textwidth]{images/img_1299.jpg}
\end{figure}
\noindent
One of the forces that are driving the big change is the multicore
revolution. The prevailing programming paradigm, object oriented
programming, doesn't buy you anything in the realm of concurrency and
parallelism, and instead encourages dangerous and buggy design. Data
hiding, the basic premise of object orientation, when combined with
sharing and mutation, becomes a recipe for data races. The idea of
combining a mutex with the data it protects is nice but, unfortunately,
locks don't compose, and lock hiding makes deadlocks more likely and
harder to debug.
But even in the absence of concurrency, the growing complexity of
software systems is testing the limits of scalability of the imperative
paradigm. To put it simply, side effects are getting out of hand.
Granted, functions that have side effects are often convenient and easy
to write. Their effects can in principle be encoded in their names and
in the comments. A function called SetPassword or WriteFile is obviously
mutating some state and generating side effects, and we are used to
dealing with that. It's only when we start composing functions that have
side effects on top of other functions that have side effects, and so
on, that things start getting hairy. It's not that side effects are
inherently bad --- it's the fact that they are hidden from view that
makes them impossible to manage at larger scales. Side effects don't
scale, and imperative programming is all about side effects.
Changes in hardware and the growing complexity of software are forcing
us to rethink the foundations of programming. Just like the builders of
Europe's great gothic cathedrals we've been honing our craft to the
limits of material and structure. There is an unfinished gothic
\urlref{http://en.wikipedia.org/wiki/Beauvais_Cathedral}{cathedral in
Beauvais}, France, that stands witness to this deeply human struggle
with limitations. It was intended to beat all previous records of height
and lightness, but it suffered a series of collapses. Ad hoc measures
like iron rods and wooden supports keep it from disintegrating, but
obviously a lot of things went wrong. From a modern perspective, it's a
miracle that so many gothic structures had been successfully completed
without the help of modern material science, computer modelling, finite
element analysis, and general math and physics. I hope future
generations will be as admiring of the programming skills we've been
displaying in building complex operating systems, web servers, and the
internet infrastructure. And, frankly, they should, because we've done
all this based on very flimsy theoretical foundations. We have to fix
those foundations if we want to move forward.
\begin{figure}
\centering
\includegraphics[totalheight=0.5\textheight]{images/beauvais_interior_supports.jpg}
\caption{Ad hoc measures preventing the Beauvais cathedral from collapsing.}
\end{figure}
================================================
FILE: src/content/1.1/category-the-essence-of-composition.tex
================================================
% !TEX root = ../../ctfp-print.tex
\lettrine[lhang=0.17]{A}{category} is an embarrassingly simple concept.
A category consists of \newterm{objects} and \newterm{arrows} that go between them. That's
why categories are so easy to represent pictorially. An object can be
drawn as a circle or a point, and an arrow\ldots{} is an arrow. (Just
for variety, I will occasionally draw objects as piggies and arrows as
fireworks.) But the essence of a category is \emph{composition}. Or, if you
prefer, the essence of composition is a category. Arrows compose, so
if you have an arrow from object $A$ to object $B$, and another arrow from
object $B$ to object $C$, then there must be an arrow --- their composition
--- that goes from $A$ to $C$.
\begin{figure}
\centering
\includegraphics[width=0.8\textwidth]{images/img_1330.jpg}
\caption{In a category, if there is an arrow going from $A$ to $B$ and an arrow going from $B$ to $C$
then there must also be a direct arrow from $A$ to $C$ that is their composition. This diagram is not a full
category because it’s missing identity morphisms (see later).}
\end{figure}
\section{Arrows as Functions}
Is this already too much abstract nonsense? Do not despair. Let's talk
concretes. Think of arrows, which are also called \newterm{morphisms}, as
functions. You have a function $f$ that takes an argument of type $A$ and
returns a $B$. You have another function $g$ that takes a $B$ and returns a $C$.
You can compose them by passing the result of $f$ to $g$. You have just
defined a new function that takes an $A$ and returns a $C$.
In math, such composition is denoted by a small circle between
functions: $g \circ f$. Notice the right to left order of composition. For some
people this is confusing. You may be familiar with the pipe notation in
Unix, as in:
\begin{snip}{text}
lsof | grep Chrome
\end{snip}
or the chevron \code{>>} in F\#, which both
go from left to right. But in mathematics and in Haskell functions
compose right to left. It helps if you read $g \circ f$ as ``g \emph{after} f.''
Let's make this even more explicit by writing some C code. We have one
function \code{f} that takes an argument of type \code{A} and
returns a value of type \code{B}:
\begin{snip}{text}
B f(A a);
\end{snip}
and another:
\begin{snip}{text}
C g(B b);
\end{snip}
Their composition is:
\begin{snip}{text}
C g_after_f(A a)
{
return g(f(a));
}
\end{snip}
Here, again, you see right-to-left composition: \code{g(f(a))}; this
time in C.
I wish I could tell you that there is a template in the C++ Standard
Library that takes two functions and returns their composition, but
there isn't one. So let's try some Haskell for a change. Here's the
declaration of a function from A to B:
\src{snippet01}
Similarly:
\src{snippet02}
Their composition is:
\src{snippet03}
Once you see how simple things are in Haskell, the inability to express
straightforward functional concepts in C++ is a little embarrassing. In
fact, Haskell will let you use Unicode characters so you can write
composition as:
% don't 'mathify' this block
\begin{snip}{text}
g ◦ f
\end{snip}
You can even use Unicode double colons and arrows:
% don't 'mathify' this block
\begin{snipv}
f \ensuremath{\Colon} A → B
\end{snipv}
So here's the first Haskell lesson: Double colon means ``has the type
of\ldots{}'' A function type is created by inserting an arrow between
two types. You compose two functions by inserting a period between them
(or a Unicode circle).
\section{Properties of Composition}
There are two extremely important properties that the composition in any
category must satisfy.
\begin{enumerate}
\item
Composition is associative. If you have three morphisms, $f$, $g$, and $h$,
that can be composed (that is, their objects match end-to-end), you
don't need parentheses to compose them. In math notation this is
expressed as:
\[h \circ (g \circ f) = (h \circ g) \circ f = h \circ g \circ f\]
In (pseudo) Haskell:
\src{snippet04}[b]
(I said ``pseudo,'' because equality is not defined for functions.)
Associativity is pretty obvious when dealing with functions, but it may
be not as obvious in other categories.
\item
For every object $A$ there is an arrow which is a unit of composition.
This arrow loops from the object to itself. Being a unit of composition
means that, when composed with any arrow that either starts at $A$ or ends
at $A$, respectively, it gives back the same arrow. The unit arrow for
object A is called $\idarrow[A]$ (\newterm{identity} on $A$). In math
notation, if $f$ goes from $A$ to $B$ then
\[f \circ \idarrow[A] = f\]
and
\[\idarrow[B] \circ f = f\]
\end{enumerate}
When dealing with functions, the identity arrow is implemented as the
identity function that just returns back its argument. The
implementation is the same for every type, which means this function is
universally polymorphic. In C++ we could define it as a template:
\begin{snip}{cpp}
template<typename T> T id(T x) { return x; }
\end{snip}
Of course, in C++ nothing is that simple, because you have to take into
account not only what you're passing but also how (that is, by value, by
reference, by const reference, by move, and so on).
In Haskell, the identity function is part of the standard library
(called Prelude). Here's its declaration and definition:
\src{snippet05}
As you can see, polymorphic functions in Haskell are a piece of cake. In
the declaration, you just replace the type with a type variable. Here's
the trick: names of concrete types always start with a capital letter,
names of type variables start with a lowercase letter. So here
\code{a} stands for all types.
Haskell function definitions consist of the name of the function
followed by formal parameters --- here just one, \code{x}. The body of
the function follows the equal sign. This terseness is often shocking to
newcomers but you will quickly see that it makes perfect sense. Function
definition and function call are the bread and butter of functional
programming so their syntax is reduced to the bare minimum. Not only are
there no parentheses around the argument list but there are no commas
between arguments (you'll see that later, when we define functions of
multiple arguments).
The body of a function is always an expression --- there are no
statements in functions. The result of a function is this expression ---
here, just \code{x}.
This concludes our second Haskell lesson.
The identity conditions can be written (again, in pseudo-Haskell) as:
\src{snippet06}
You might be asking yourself the question: Why would anyone bother with
the identity function --- a function that does nothing? Then again, why
do we bother with the number zero? Zero is a symbol for nothing. Ancient
Romans had a number system without a zero and they were able to build
excellent roads and aqueducts, some of which survive to this day.
Neutral values like zero or $\id$ are extremely useful when
working with symbolic variables. That's why Romans were not very good at
algebra, whereas the Arabs and the Persians, who were familiar with the
concept of zero, were. So the identity function becomes very handy as an
argument to, or a return from, a higher-order function. Higher order
functions are what make symbolic manipulation of functions possible.
They are the algebra of functions.
To summarize: A category consists of objects and arrows (morphisms).
Arrows can be composed, and the composition is associative. Every object
has an identity arrow that serves as a unit under composition.
\section{Composition is the Essence of Programming}
Functional programmers have a peculiar way of approaching problems. They
start by asking very Zen-like questions. For instance, when designing an
interactive program, they would ask: What is interaction? When
implementing Conway's Game of Life, they would probably ponder about the
meaning of life. In this spirit, I'm going to ask: What is programming?
At the most basic level, programming is about telling the computer what
to do. ``Take the contents of memory address x and add it to the
contents of the register EAX.'' But even when we program in assembly,
the instructions we give the computer are an expression of something
more meaningful. We are solving a non-trivial problem (if it were
trivial, we wouldn't need the help of the computer). And how do we solve
problems? We decompose bigger problems into smaller problems. If the
smaller problems are still too big, we decompose them further, and so
on. Finally, we write code that solves all the small problems. And then
comes the essence of programming: we compose those pieces of code to
create solutions to larger problems. Decomposition wouldn't make sense
if we weren't able to put the pieces back together.
This process of hierarchical decomposition and recomposition is not
imposed on us by computers. It reflects the limitations of the human
mind. Our brains can only deal with a small number of concepts at a
time. One of the most cited papers in psychology,
\urlref{http://en.wikipedia.org/wiki/The_Magical_Number_Seven,_Plus_or_Minus_Two}{The
Magical Number Seven, Plus or Minus Two}, postulated that we can only
keep $7 \pm 2$ ``chunks'' of information in our minds. The details of our
understanding of the human short-term memory might be changing, but we
know for sure that it's limited. The bottom line is that we are unable
to deal with the soup of objects or the spaghetti of code. We need
structure not because well-structured programs are pleasant to look at,
but because otherwise our brains can't process them efficiently. We
often describe some piece of code as elegant or beautiful, but what we
really mean is that it's easy to process by our limited human minds.
Elegant code creates chunks that are just the right size and come in
just the right number for our mental digestive system to assimilate
them.
So what are the right chunks for the composition of programs? Their
surface area has to increase slower than their volume. (I like this
analogy because of the intuition that the surface area of a geometric
object grows with the square of its size --- slower than the volume,
which grows with the cube of its size.) The surface area is the
information we need in order to compose chunks. The volume is the
information we need in order to implement them. The idea is that, once a
chunk is implemented, we can forget about the details of its
implementation and concentrate on how it interacts with other chunks. In
object-oriented programming, the surface is the class declaration of the
object, or its abstract interface. In functional programming, it's the
declaration of a function. (I'm simplifying things a bit, but that's the
gist of it.)
Category theory is extreme in the sense that it actively discourages us
from looking inside the objects. An object in category theory is an
abstract nebulous entity. All you can ever know about it is how it
relates to other objects --- how it connects with them using arrows. This
is how internet search engines rank web sites by analyzing incoming and
outgoing links (except when they cheat). In object-oriented programming,
an idealized object is only visible through its abstract interface (pure
surface, no volume), with methods playing the role of arrows. The moment
you have to dig into the implementation of the object in order to
understand how to compose it with other objects, you've lost the
advantages of your programming paradigm.
\section{Challenges}
\begin{enumerate}
\tightlist
\item
Implement, as best as you can, the identity function in your favorite
language (or the second favorite, if your favorite language happens to
be Haskell).
\item
Implement the composition function in your favorite language. It takes
two functions as arguments and returns a function that is their
composition.
\item
Write a program that tries to test that your composition function
respects identity.
\item
Is the world-wide web a category in any sense? Are links morphisms?
\item
Is Facebook a category, with people as objects and friendships as
morphisms?
\item
When is a directed graph a category?
\end{enumerate}
================================================
FILE: src/content/1.1/code/haskell/snippet01.hs
================================================
f :: A -> B
================================================
FILE: src/content/1.1/code/haskell/snippet02.hs
================================================
g :: B -> C
================================================
FILE: src/content/1.1/code/haskell/snippet03.hs
================================================
g . f
================================================
FILE: src/content/1.1/code/haskell/snippet04.hs
================================================
f :: A -> B
g :: B -> C
h :: C -> D
h . (g . f) == (h . g) . f == h . g . f
================================================
FILE: src/content/1.1/code/haskell/snippet05.hs
================================================
id :: a -> a
id x = x
================================================
FILE: src/content/1.1/code/haskell/snippet06.hs
================================================
f . id == f
id . f == f
================================================
FILE: src/content/1.1/code/ocaml/snippet01.ml
================================================
module type Polymorphic_Function_F = sig
type a
type b
val f : a -> b
end
================================================
FILE: src/content/1.1/code/ocaml/snippet02.ml
================================================
module type Polymorphic_Function_G = sig
type b
type c
val g : b -> c
end
================================================
FILE: src/content/1.1/code/ocaml/snippet03.ml
================================================
module Compose_Example
(F : Polymorphic_Function_F)
(G : Polymorphic_Function_G with type b = F.b) =
struct
(** OCaml doesn't have a compose operator. So, creating one. **)
let ( >> ) g f x = g (f x)
let compose : 'a -> 'c = G.g >> F.f
end
================================================
FILE: src/content/1.1/code/ocaml/snippet04.ml
================================================
module Compose_Three_GF = functor(F:Polymorphic_Function_F)(G:Polymorphic_Function_G with type b = F.b)(H:Polymorphic_Function_H with type c = G.c) -> struct
let compose : 'a -> 'd = H.h >> (G.g >> F.f)
end
module Compose_Three_HG = functor(F:Polymorphic_Function_F)(G:Polymorphic_Function_G with type b = F.b)(H:Polymorphic_Function_H with type c = G.c) -> struct
let compose : 'a -> 'd = (H.h >> G.g) >> F.f
end
Compose_Three_GF.compose = Compose_Three_HG.compose
================================================
FILE: src/content/1.1/code/ocaml/snippet05.ml
================================================
let id x = x
================================================
FILE: src/content/1.1/code/ocaml/snippet06.ml
================================================
f >> id
id >> f
================================================
FILE: src/content/1.1/code/reason/snippet01.re
================================================
module type Polymorphic_Function_F = {
type a;
type b;
let f: a => b;
};
================================================
FILE: src/content/1.1/code/reason/snippet02.re
================================================
module type Polymorphic_Function_G = {
type b;
type c;
let g: b => c;
};
================================================
FILE: src/content/1.1/code/reason/snippet03.re
================================================
module Compose_Example =
(
F: Polymorphic_Function_F,
G: Polymorphic_Function_G with type b = F.b,
) => {
/** ReasonML doesn't have a compose operator. So, creating one. **/
let (>>) = (g, f, x) => g(f(x));
let compose: 'a => 'c = (G.g >> F.f: 'a => 'c);
};
================================================
FILE: src/content/1.1/code/reason/snippet04.re
================================================
module Compose_Three_GF =
(
F: Polymorphic_Function_F,
G: Polymorphic_Function_G with type b = F.b,
H: Polymorphic_Function_H with type c = G.c,
) => {
let compose: 'a => 'd = H.h >> (G.g >> F.f);
};
module Compose_Three_HG =
(
F: Polymorphic_Function_F,
G: Polymorphic_Function_G with type b = F.b,
H: Polymorphic_Function_H with type c = G.c,
) => {
let compose: 'a => 'd = H.h >> G.g >> F.f;
};
Compose_Three_GF.compose == Compose_Three_HG.compose
================================================
FILE: src/content/1.1/code/reason/snippet05.re
================================================
let id = x => x;
================================================
FILE: src/content/1.1/code/reason/snippet06.re
================================================
f >> id;
id >> f;
================================================
FILE: src/content/1.1/code/scala/snippet01.scala
================================================
val f: A => B
================================================
FILE: src/content/1.1/code/scala/snippet02.scala
================================================
val g: B => C
================================================
FILE: src/content/1.1/code/scala/snippet03.scala
================================================
g compose f
================================================
FILE: src/content/1.1/code/scala/snippet04.scala
================================================
val f: A => B
val g: B => C
val h: C => D
h compose (g compose f) === (h compose g) compose f === h compose g compose f
================================================
FILE: src/content/1.1/code/scala/snippet05.scala
================================================
def identity[A](a: A): A = a
================================================
FILE: src/content/1.1/code/scala/snippet06.scala
================================================
f compose identity[A] == f
identity[B] _ compose f == f
================================================
FILE: src/content/1.10/code/haskell/snippet01.hs
================================================
alpha :: forall a . F a -> G a
================================================
FILE: src/content/1.10/code/haskell/snippet02.hs
================================================
alpha :: F a -> G a
================================================
FILE: src/content/1.10/code/haskell/snippet03.hs
================================================
alpha :: F a -> G a
================================================
FILE: src/content/1.10/code/haskell/snippet04.hs
================================================
safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:xs) = Just x
================================================
FILE: src/content/1.10/code/haskell/snippet05.hs
================================================
fmap f . safeHead = safeHead . fmap f
================================================
FILE: src/content/1.10/code/haskell/snippet06.hs
================================================
fmap f (safeHead []) = fmap f Nothing = Nothing
================================================
FILE: src/content/1.10/code/haskell/snippet07.hs
================================================
safeHead (fmap f []) = safeHead [] = Nothing
================================================
FILE: src/content/1.10/code/haskell/snippet08.hs
================================================
fmap f (safeHead (x:xs)) = fmap f (Just x) = Just (f x)
================================================
FILE: src/content/1.10/code/haskell/snippet09.hs
================================================
safeHead (fmap f (x:xs)) = safeHead (f x : fmap f xs) = Just (f x)
================================================
FILE: src/content/1.10/code/haskell/snippet10.hs
================================================
fmap f [] = []
fmap f (x:xs) = f x : fmap f xs
================================================
FILE: src/content/1.10/code/haskell/snippet11.hs
================================================
fmap f Nothing = Nothing
fmap f (Just x) = Just (f x)
================================================
FILE: src/content/1.10/code/haskell/snippet12.hs
================================================
length :: [a] -> Const Int a
length [] = Const 0
length (x:xs) = Const (1 + unConst (length xs))
================================================
FILE: src/content/1.10/code/haskell/snippet13.hs
================================================
unConst :: Const c a -> c
unConst (Const x) = x
================================================
FILE: src/content/1.10/code/haskell/snippet14.hs
================================================
length :: [a] -> Int
================================================
FILE: src/content/1.10/code/haskell/snippet15.hs
================================================
scam :: Const Int a -> Maybe a
scam (Const x) = Nothing
================================================
FILE: src/content/1.10/code/haskell/snippet16.hs
================================================
newtype Reader e a = Reader (e -> a)
================================================
FILE: src/content/1.10/code/haskell/snippet17.hs
================================================
instance Functor (Reader e) where
fmap f (Reader g) = Reader (\x -> f (g x))
================================================
FILE: src/content/1.10/code/haskell/snippet18.hs
================================================
alpha :: Reader () a -> Maybe a
================================================
FILE: src/content/1.10/code/haskell/snippet19.hs
================================================
dumb (Reader _) = Nothing
================================================
FILE: src/content/1.10/code/haskell/snippet20.hs
================================================
obvious (Reader g) = Just (g ())
================================================
FILE: src/content/1.10/code/haskell/snippet21.hs
================================================
newtype Op r a = Op (a -> r)
================================================
FILE: src/content/1.10/code/haskell/snippet22.hs
================================================
instance Contravariant (Op r) where
contramap f (Op g) = Op (g . f)
================================================
FILE: src/content/1.10/code/haskell/snippet23.hs
================================================
predToStr (Op f) = Op (\x -> if f x then "T" else "F")
================================================
FILE: src/content/1.10/code/haskell/snippet24.hs
================================================
contramap f . predToStr = predToStr . contramap f
================================================
FILE: src/content/1.10/code/haskell/snippet25.hs
================================================
contramap :: (b -> a) -> (Op Bool a -> Op Bool b)
================================================
FILE: src/content/1.10/code/haskell/snippet26.hs
================================================
a -> a
================================================
FILE: src/content/1.10/code/haskell/snippet27.hs
================================================
(a -> a) -> f a
================================================
FILE: src/content/1.10/code/ocaml/snippet01.ml
================================================
val alpha : 'a . 'a f -> 'a g
================================================
FILE: src/content/1.10/code/ocaml/snippet02.ml
================================================
val alpha : 'a f -> 'a g
================================================
FILE: src/content/1.10/code/ocaml/snippet03.ml
================================================
val alpha : 'a f -> 'a g
================================================
FILE: src/content/1.10/code/ocaml/snippet04.ml
================================================
let safe_head = function
| [] -> None
| x :: xs -> Some x
;;
================================================
FILE: src/content/1.10/code/ocaml/snippet05.ml
================================================
compose (fmap f) safe_head = compose safe_head (fmap f)
================================================
FILE: src/content/1.10/code/ocaml/snippet06.ml
================================================
(* Starting with empty list *)
let fmap f (safe_head []) = fmap f None = None
================================================
FILE: src/content/1.10/code/ocaml/snippet07.ml
================================================
let safe_head (fmap f []) = safe_head [] = None
================================================
FILE: src/content/1.10/code/ocaml/snippet08.ml
================================================
let fmap f (safe_head (x :: xs)) = fmap f (Some x)= Some (f x)
================================================
FILE: src/content/1.10/code/ocaml/snippet09.ml
================================================
let safe_head (fmap f (x :: xs)) = safe_head (f x :: f xs) = Some (f x)
================================================
FILE: src/content/1.10/code/ocaml/snippet10.ml
================================================
let rec fmap f = function
| [] -> []
| x :: xs -> f x :: fmap f xs
;;
================================================
FILE: src/content/1.10/code/ocaml/snippet11.ml
================================================
let rec fmap f = function
| None -> None
| Some x -> Some (f x)
;;
================================================
FILE: src/content/1.10/code/ocaml/snippet12.ml
================================================
(** OCaml requires mutually recursive functions to be defined together *)
let rec length : 'a list -> (int, 'a) const = function
| [] -> Const 0
| _ :: xs -> Const (1 + un_const (length xs))
and un_const : 'c 'a. ('c, 'a) const -> 'c = function
| Const c -> c
;;
================================================
FILE: src/content/1.10/code/ocaml/snippet13.ml
================================================
let un_const : 'c 'a. ('c, 'a) const -> 'c = function
| Const c -> c
;;
================================================
FILE: src/content/1.10/code/ocaml/snippet14.ml
================================================
val length : 'a list -> int
================================================
FILE: src/content/1.10/code/ocaml/snippet15.ml
================================================
let scam : 'a. ('int, 'a) const -> 'a option = function
| Const a -> None
;;
================================================
FILE: src/content/1.10/code/ocaml/snippet16.ml
================================================
type ('e, 'a) reader = Reader of ('e -> 'a)
================================================
FILE: src/content/1.10/code/ocaml/snippet17.ml
================================================
module Reader_Functor (T : sig
type e
end) : Functor = struct
type 'a t = (T.e, 'a) reader
let fmap : 'a 'b. ('a -> 'b) -> 'a t -> 'b t =
fun f -> function
| Reader r -> Reader (compose f r)
;;
end
================================================
FILE: src/content/1.10/code/ocaml/snippet18.ml
================================================
val alpha : (unit, 'a) reader -> 'a option
================================================
FILE: src/content/1.10/code/ocaml/snippet19.ml
================================================
let dumb : 'a. (unit, 'a) reader -> 'a option = function
| Reader _ -> None
;;
================================================
FILE: src/content/1.10/code/ocaml/snippet20.ml
================================================
let obvious : 'a. (unit, 'a) reader -> 'a option = function
| Reader f -> Some (f ())
;;
================================================
FILE: src/content/1.10/code/ocaml/snippet21.ml
================================================
type ('r, 'a) op = Op of ('a -> 'r)
================================================
FILE: src/content/1.10/code/ocaml/snippet22.ml
================================================
module Op_Contravariant (T : sig
type r
end) : Contravariant = struct
type 'a t = (T.r, 'a) op
let contramap : ('b -> 'a) -> 'a t -> 'b t =
fun f -> function
| Op g -> Op (compose g f)
;;
end
================================================
FILE: src/content/1.10/code/ocaml/snippet23.ml
================================================
let pred_to_str = function
| Op f -> Op (fun x -> if f x then "T" else "F")
;;
================================================
FILE: src/content/1.10/code/ocaml/snippet24.ml
================================================
compose (contramap f) pred_to_str = compose pred_to_str (contramap f)
================================================
FILE: src/content/1.10/code/ocaml/snippet25.ml
================================================
module Op_Bool = Op_Contravariant (struct
type r = bool
end)
let op_bool_contramap : ('b -> 'a) -> 'a Op_Bool.t -> 'b Op_Bool.t =
Op_Bool.contramap
;;
================================================
FILE: src/content/1.10/code/ocaml/snippet26.ml
================================================
'a -> 'a
================================================
FILE: src/content/1.10/code/ocaml/snippet27.ml
================================================
('a -> 'a) -> 'a f
================================================
FILE: src/content/1.10/code/reason/snippet01.re
================================================
let alpha: 'a . f('a) => g('a);
================================================
FILE: src/content/1.10/code/reason/snippet02.re
================================================
let alpha: f('a) => g('a);
================================================
FILE: src/content/1.10/code/reason/snippet03.re
================================================
let alpha: f('a) => g('a);
================================================
FILE: src/content/1.10/code/reason/snippet04.re
================================================
let safe_head =
fun
| [] => None
| [x, ...xs] => Some(x);
================================================
FILE: src/content/1.10/code/reason/snippet05.re
================================================
compose(fmap(f), safe_head) == compose(safe_head, fmap(f));
================================================
FILE: src/content/1.10/code/reason/snippet06.re
================================================
/* Starting with empty list */
let fmap = (f, safe_head([])) == fmap(f, None) == None;
================================================
FILE: src/content/1.10/code/reason/snippet07.re
================================================
let safe_head = (fmap, f, []) == safe_head([]) == None;
================================================
FILE: src/content/1.10/code/reason/snippet08.re
================================================
let fmap = (f, safe_head([x, ... xs])) == fmap(f(Some(x))) == Some(f(x));
================================================
FILE: src/content/1.10/code/reason/snippet09.re
================================================
let safe_head =
(fmap([x, ...xs])) == safe_head(f([x, ... xs])) == Some(f(x));
================================================
FILE: src/content/1.10/code/reason/snippet10.re
================================================
let rec fmap = f =>
fun
| [] => []
| [x, ...xs] => [f(x), ...fmap(f, xs)];
================================================
FILE: src/content/1.10/code/reason/snippet11.re
================================================
let rec fmap = f =>
fun
| None => None
| Some(x) => Some(f(x));
================================================
FILE: src/content/1.10/code/reason/snippet12.re
================================================
/* ReasonML requires mutually recursive functions
* to be defined together */
let rec length: list('a) => const(int, 'a) = (
fun
| [] => Const(0)
| [_, ...xs] => Const(1 + un_const(length(xs))):
list('a) => const(int, 'a)
)
and un_const: 'c 'a. const('c, 'a) => 'c =
fun
| Const(c) => c;
================================================
FILE: src/content/1.10/code/reason/snippet13.re
================================================
let un_const: 'c 'a. const('c, 'a) => 'c =
fun
| Const(c) => c;
================================================
FILE: src/content/1.10/code/reason/snippet14.re
================================================
let length: list('a) => int;
================================================
FILE: src/content/1.10/code/reason/snippet15.re
================================================
let scam: 'a. const('int, 'a) => option('a) =
fun
| Const(a) => None;
================================================
FILE: src/content/1.10/code/reason/snippet16.re
================================================
type reader('e, 'a) =
| Reader('e => 'a);
================================================
FILE: src/content/1.10/code/reason/snippet17.re
================================================
module Reader_Functor = (T: {type e;}) : Functor => {
type t('a) = reader(T.e, 'a);
let fmap: 'a 'b. ('a => 'b, t('a)) => t('b) =
f =>
fun
| Reader(r) => Reader(compose(f, r));
};
================================================
FILE: src/content/1.10/code/reason/snippet18.re
================================================
let alpha: reader(unit, 'a) => option('a);
================================================
FILE: src/content/1.10/code/reason/snippet19.re
================================================
let dumb: 'a. reader(unit, 'a) => option('a) =
fun
| Reader(_) => None;
================================================
FILE: src/content/1.10/code/reason/snippet20.re
================================================
let obvious: 'a. reader(unit, 'a) => option('a) =
fun
| Reader(f) => Some(f());
================================================
FILE: src/content/1.10/code/reason/snippet21.re
================================================
type op('r, 'a) =
| Op('a => 'r);
================================================
FILE: src/content/1.10/code/reason/snippet22.re
================================================
module Op_Contravariant = (T: {type r;}) : Contravariant => {
type t('a) = op(T.r, 'a);
let contramap: ('b => 'a, t('a)) => t('b) =
f =>
fun
| Op(g) => Op(compose(g, f));
};
================================================
FILE: src/content/1.10/code/reason/snippet23.re
================================================
let pred_to_str =
fun
| Op(f) => Op(x => (f(x)) ? "T" : "F");
================================================
FILE: src/content/1.10/code/reason/snippet24.re
================================================
compose(contramap(f), pred_to_str) == compose(pred_to_str, contramap(f));
================================================
FILE: src/content/1.10/code/reason/snippet25.re
================================================
module Op_Bool = Op_Contravariant({type r = bool;});
let op_bool_contramap: ('b => 'a, Op_Bool.t('a)) => Op_Bool.t('b) = (
Op_Bool.contramap: ('b => 'a, Op_Bool.t('a)) => Op_Bool.t('b)
);
================================================
FILE: src/content/1.10/code/reason/snippet26.re
================================================
'a => 'a
================================================
FILE: src/content/1.10/code/reason/snippet27.re
================================================
('a => 'a) => f('a)
================================================
FILE: src/content/1.10/code/scala/snippet01.scala
================================================
def alpha[A]: F[A] => G[A]
================================================
FILE: src/content/1.10/code/scala/snippet02.scala
================================================
val alpha: F[A] => G[A]
================================================
FILE: src/content/1.10/code/scala/snippet03.scala
================================================
val alpha: F[A] => G[A]
================================================
FILE: src/content/1.10/code/scala/snippet04.scala
================================================
def safeHead[A]: List[A] => Option[A] = {
case Nil => None
case x :: xs => Some(x)
}
================================================
FILE: src/content/1.10/code/scala/snippet05.scala
================================================
(fmap(f) compose safeHead) == (safeHead compose fmap(f))
================================================
FILE: src/content/1.10/code/scala/snippet06.scala
================================================
fmap(f)(safeHead(List.empty)) == fmap(f)(None) == None
================================================
FILE: src/content/1.10/code/scala/snippet07.scala
================================================
safeHead(fmap(f)(List.empty)) == safeHead(List.empty) == None
================================================
FILE: src/content/1.10/code/scala/snippet08.scala
================================================
fmap(f)(safeHead(x :: xs)) == fmap(f)(Some(x)) == Some(f(x))
================================================
FILE: src/content/1.10/code/scala/snippet09.scala
================================================
safeHead(fmap(f)(x :: xs)) == safeHead(f(1) :: fmap(f)(xs)) == Some(f(x))
================================================
FILE: src/content/1.10/code/scala/snippet10.scala
================================================
def fmap[A, B](f: A => B): List[A] => List[B] = {
case Nil => Nil
case x :: xs => f(x) :: fmap(f)(xs)
}
================================================
FILE: src/content/1.10/code/scala/snippet11.scala
================================================
def fmap[A, B](f: A => B): Option[A] => Option[B] = {
case None => None
case Some(x) => Some(f(x))
}
================================================
FILE: src/content/1.10/code/scala/snippet12.scala
================================================
def length[A]: List[A] => Const[Int, A] = {
case Nil => Const(0)
case x :: xs => Const(1 + unConst(length(xs)))
}
================================================
FILE: src/content/1.10/code/scala/snippet13.scala
================================================
def unConst[C, A]: Const[C, A] => C = {
case Const(x) => x
}
================================================
FILE: src/content/1.10/code/scala/snippet14.scala
================================================
def length[A]: List[A] => Int
================================================
FILE: src/content/1.10/code/scala/snippet15.scala
================================================
def scam[A]: Const[Int, A] => Option[A] = {
case Const(x) => None
}
================================================
FILE: src/content/1.10/code/scala/snippet16.scala
================================================
case class Reader[E, A](run: E => A)
================================================
FILE: src/content/1.10/code/scala/snippet17.scala
================================================
implicit def readerFunctor[E] = new Functor[Reader[E, ?]] {
def fmap[A, B](f: A => B)(g: Reader[E, A]): Reader[E, B] =
Reader(x => f(g.run(x)))
}
================================================
FILE: src/content/1.10/code/scala/snippet18.scala
================================================
def alpha[A]: Reader[Unit, A] => Option[A]
================================================
FILE: src/content/1.10/code/scala/snippet19.scala
================================================
def dumb[A]: Reader[Unit, A] => Option[A] = {
case Reader(_) => None
}
================================================
FILE: src/content/1.10/code/scala/snippet20.scala
================================================
def obvious[A]: Reader[Unit, A] => Option[A] = {
case Reader(g) => Some(g())
}
================================================
FILE: src/content/1.10/code/scala/snippet21.scala
================================================
case class Op[R, A](f: A => R)
================================================
FILE: src/content/1.10/code/scala/snippet22.scala
================================================
implicit def opContravariant[R] = new Contravariant[Op[R, ?]] {
def contramap[A, B](f: B => A): Op[R, A] => Op[R, B] = {
case Op(g) => Op(g compose f)
}
}
================================================
FILE: src/content/1.10/code/scala/snippet23.scala
================================================
def predToStr[A]: Op[Boolean, A] => Op[String, A] = {
case Op(f) => Op(x => if (f(x)) "T" else "F")
}
================================================
FILE: src/content/1.10/code/scala/snippet24.scala
================================================
(op.contramap(func) compose predToStr) == (predToStr compose op.contramap(func))
================================================
FILE: src/content/1.10/code/scala/snippet25.scala
================================================
def contramap[A, B](f: B => A): Op[Boolean, A] => Op[Boolean, B] = {
case Op(g) => Op(g compose f)
}
================================================
FILE: src/content/1.10/code/scala/snippet26.scala
================================================
A => A
================================================
FILE: src/content/1.10/code/scala/snippet27.scala
================================================
(A => A) => F[A]
================================================
FILE: src/content/1.10/natural-transformations.tex
================================================
% !TEX root = ../../ctfp-print.tex
\lettrine[lhang=0.17]{W}{e talked about} functors as mappings between categories that preserve
their structure.
A functor ``embeds'' one category in another. It may
collapse multiple things into one, but it never breaks connections. One
way of thinking about it is that with a functor we are modeling one
category inside another. The source category serves as a model, a
blueprint, for some structure that's part of the target category.
\begin{figure}[H]
\centering\includegraphics[width=0.4\textwidth]{images/1_functors.jpg}
\end{figure}
\noindent
There may be many ways of embedding one category in another. Sometimes
they are equivalent, sometimes very different. One may collapse the
whole source category into one object, another may map every object to a
different object and every morphism to a different morphism. The same
blueprint may be realized in many different ways. Natural
transformations help us compare these realizations. They are mappings of
functors --- special mappings that preserve their functorial nature.
Consider two functors $F$ and $G$ between categories
$\cat{C}$ and $\cat{D}$. If you focus on just one object $a$ in
$\cat{C}$, it is mapped to two objects: $F a$ and $G a$.
A mapping of functors should therefore map $F a$ to
$G a$.
\begin{figure}[H]
\centering
\includegraphics[width=0.3\textwidth]{images/2_natcomp.jpg}
\end{figure}
\noindent
Notice that $F a$ and $G a$ are objects in the same
category $\cat{D}$. Mappings between objects in the same category should
not go against the grain of the category. We don't want to make
artificial connections between objects. So it's \emph{natural} to use
existing connections, namely morphisms. A natural transformation is a
selection of morphisms: for every object $a$, it picks one
morphism from $F a$ to $G a$. If we call the natural
transformation $\alpha$, this morphism is called the \newterm{component}
of $\alpha$ at $a$, or $\alpha_a$.
\[\alpha_a \Colon F a \to G a\]
Keep in mind that $a$ is an object in $\cat{C}$ while $\alpha_a$
is a morphism in $\cat{D}$.
If, for some $a$, there is no morphism between $F a$ and
$G a$ in $\cat{D}$, there can be no natural transformation
between $F$ and $G$.
Of course that's only half of the story, because functors not only map
objects, they map morphisms as well. So what does a natural
transformation do with those mappings? It turns out that the mapping of
morphisms is fixed --- under any natural transformation between $F$ and $G$,
$F f$ must be transformed into $G f$. What's more, the
mapping of morphisms by the two functors drastically restricts the
choices we have in defining a natural transformation that's compatible
with it. Consider a morphism $f$ between two objects $a$
and $b$ in $\cat{C}$. It's mapped to two morphisms, $F f$
and $G f$ in $\cat{D}$:
\begin{gather*}
F f \Colon F a \to F b \\
G f \Colon G a \to G b
\end{gather*}
The natural transformation $\alpha$ provides two additional morphisms
that complete the diagram in $\cat{D}$:
\begin{gather*}
\alpha_a \Colon F a \to G a \\
\alpha_b \Colon F b \to G b
\end{gather*}
\begin{figure}[H]
\centering
\includegraphics[width=0.4\textwidth]{images/3_naturality.jpg}
\end{figure}
\noindent
Now we have two ways of getting from $F a$ to $G b$. To
make sure that they are equal, we must impose the \newterm{naturality
condition} that holds for any $f$:
\[G f \circ \alpha_a = \alpha_b \circ F f\]
The naturality condition is a pretty stringent requirement. For
instance, if the morphism $F f$ is invertible, naturality
determines $\alpha_b$ in terms of $\alpha_a$. It \emph{transports}
$\alpha_a$ along $f$:
\[\alpha_b = (G f) \circ \alpha_a \circ (F f)^{-1}\]
\begin{figure}[H]
\centering
\includegraphics[width=0.4\textwidth]{images/4_transport.jpg}
\end{figure}
\noindent
If there is more than one invertible morphism between two objects, all
these transports have to agree. In general, though, morphisms are not
invertible; but you can see that the existence of natural
transformations between two functors is far from guaranteed. So the
scarcity or the abundance of functors that are related by natural
transformations may tell you a lot about the structure of categories
between which they operate. We'll see some examples of that when we talk
about limits and the Yoneda lemma.
Looking at a natural transformation component-wise, one may say that it
maps objects to morphisms. Because of the naturality condition, one may
also say that it maps morphisms to commuting squares --- there is one
commuting naturality square in $\cat{D}$ for every morphism in $\cat{C}$.
\begin{figure}[H]
\centering
\includegraphics[width=0.4\textwidth]{images/naturality.jpg}
\end{figure}
\noindent
This property of natural transformations comes in very handy in a lot of
categorical constructions, which often include commuting diagrams. With
a judicious choice of functors, a lot of these commutativity conditions
may be transformed into naturality conditions. We'll see examples of
that when we get to limits, colimits, and adjunctions.
Finally, natural transformations may be used to define isomorphisms of
functors. Saying that two functors are naturally isomorphic is almost
like saying they are the same. \newterm{Natural isomorphism} is defined as
a natural transformation whose components are all isomorphisms
(invertible morphisms).
\section{Polymorphic Functions}
We talked about the role of functors (or, more specifically,
endofunctors) in programming. They correspond to type constructors that
map types to types. They also map functions to functions, and this
mapping is implemented by a higher order function \code{fmap} (or
\code{transform}, \code{then}, and the like in C++).
To construct a natural transformation we start with an object, here a
type, \code{a}. One functor, \code{F}, maps it to the type
$F a$. Another functor, \code{G}, maps it to $G a$.
The component of a natural transformation \code{alpha} at \code{a}
is a function from $F a$ to $G a$. In pseudo-Haskell:
\begin{snipv}
alpha\textsubscript{a} :: F a -> G a
\end{snipv}
A natural transformation is a polymorphic function that is defined for
all types \code{a}:
\src{snippet01}
The \code{forall a} is optional in Haskell (and in fact requires
turning on the language extension \code{ExplicitForAll}). Normally,
you would write it like this:
\src{snippet02}
Keep in mind that it's really a family of functions parameterized by
\code{a}. This is another example of the terseness of the Haskell
syntax. A similar construct in C++ would be slightly more verbose:
\begin{snip}{cpp}
template<typename A> G<A> alpha(F<A>);
\end{snip}
There is a more profound difference between Haskell's polymorphic
functions and C++ generic functions, and it's reflected in the way these
functions are implemented and type-checked. In Haskell, a polymorphic
function must be defined uniformly for all types. One formula must work
across all types. This is called \newterm{parametric polymorphism}.
C++, on the other hand, supports by default \newterm{ad hoc polymorphism},
which means that a template doesn't have to be well-defined for all
types. Whether a template will work for a given type is decided at
instantiation time, where a concrete type is substituted for the type
parameter. Type checking is deferred, which unfortunately often leads to
incomprehensible error messages.
In C++, there is also a mechanism for function overloading and template
specialization, which allows different definitions of the same function
for different types. In Haskell this functionality is provided by type
classes and type families.
Haskell's parametric polymorphism has an unexpected consequence: any
polymorphic function of the type:
\src{snippet03}
where \code{F} and \code{G} are functors, automatically satisfies
the naturality condition. Here it is in categorical notation ($f$
is a function $f \Colon a \to b$):
\[G f \circ \alpha_a = \alpha_b \circ F f\]
In Haskell, the action of a functor \code{G} on a morphism \code{f}
is implemented using \code{fmap}. I'll first write it in
pseudo-Haskell, with explicit type annotations:
\begin{snipv}
fmap\textsubscript{G} f . alpha\textsubscript{a} = alpha\textsubscript{b} . fmap\textsubscript{F} f
\end{snipv}
Because of type inference, these annotations are not necessary, and the
following equation holds:
\begin{snip}{text}
fmap f . alpha = alpha . fmap f
\end{snip}
This is still not real Haskell --- function equality is not expressible
in code --- but it's an identity that can be used by the programmer in
equational reasoning; or by the compiler, to implement optimizations.
The reason why the naturality condition is automatic in Haskell has to
do with ``theorems for free.'' Parametric polymorphism, which is used to
define natural transformations in Haskell, imposes very strong
limitations on the implementation --- one formula for all types. These
limitations translate into equational theorems about such functions. In
the case of functions that transform functors, free theorems are the
naturality conditions.\footnote{
You may read more about free theorems in my
blog \href{https://bartoszmilewski.com/2014/09/22/parametricity-money-for-nothing-and-theorems-for-free/}{``Parametricity:
Money for Nothing and Theorems for Free}.''}
One way of thinking about functors in Haskell that I mentioned earlier
is to consider them generalized containers. We can continue this analogy
and consider natural transformations to be recipes for repackaging the
contents of one container into another container. We are not touching
the items themselves: we don't modify them, and we don't create new
ones. We are just copying (some of) them, sometimes multiple times, into
a new container.
The naturality condition becomes the statement that it doesn't matter
whether we modify the items first, through the application of
\code{fmap}, and repackage later; or repackage first, and then modify
the items in the new container, with its own implementation of
\code{fmap}. These two actions, repackaging and \code{fmap}ping, are
orthogonal. ``One moves the eggs, the other boils them.''
Let's see a few examples of natural transformations in Haskell. The
first is between the list functor, and the \code{Maybe} functor. It
returns the head of the list, but only if the list is non-empty:
\src{snippet04}
It's a function polymorphic in \code{a}. It works for any type
\code{a}, with no limitations, so it is an example of parametric
polymorphism. Therefore it is a natural transformation between the two
functors. But just to convince ourselves, let's verify the naturality
condition.
\src{snippet05}
We have two cases to consider; an empty list:
\src{snippet06}
\src{snippet07}
and a non-empty list:
\src{snippet08}
\src{snippet09}
I used the implementation of \code{fmap} for lists:
\src{snippet10}
and for \code{Maybe}:
\src{snippet11}
An interesting case is when one of the functors is the trivial
\code{Const} functor. A natural transformation from or to a
\code{Const} functor looks just like a function that's either
polymorphic in its return type or in its argument type.
For instance, \code{length} can be thought of as a natural
transformation from the list functor to the \code{Const Int} functor:
\src{snippet12}
Here, \code{unConst} is used to peel off the \code{Const}
constructor:
\src{snippet13}
Of course, in practice \code{length} is defined as:
\src{snippet14}
which effectively hides the fact that it's a natural transformation.
Finding a parametrically polymorphic function \emph{from} a
\code{Const} functor is a little harder, since it would require the
creation of a value from nothing. The best we can do is:
\src{snippet15}
Another common functor that we've seen already, and which will play an
important role in the Yoneda lemma, is the \code{Reader} functor. I
will rewrite its definition as a \code{newtype}:
\src{snippet16}
It is parameterized by two types, but is (covariantly) functorial only
in the second one:
\src{snippet17}
For every type \code{e}, you can define a family of natural
transformations from \code{Reader e} to any other functor \code{f}.
We'll see later that the members of this family are always in one to one
correspondence with the elements of \code{f e} (the
\hyperref[the-yoneda-lemma]{Yoneda lemma}).
For instance, consider the somewhat trivial unit type \code{()} with
one element \code{()}. The functor \code{Reader ()} takes any type
\code{a} and maps it into a function type \code{() -> a}.
These are just all the functions that pick a single element from the set
\code{a}. There are as many of these as there are elements in
\code{a}. Now let's consider natural transformations from this functor
to the \code{Maybe} functor:
\src{snippet18}
There are only two of these, \code{dumb} and \code{obvious}:
\src{snippet19}
and
\src{snippet20}
(The only thing you can do with \code{g} is to apply it to the unit
value \code{()}.)
And, indeed, as predicted by the Yoneda lemma, these correspond to the
two elements of the \code{Maybe ()} type, which are \code{Nothing}
and \code{Just ()}. We'll come back to the Yoneda lemma later ---
this was just a little teaser.
\section{Beyond Naturality}
A parametrically polymorphic function between two functors (including
the edge case of the \code{Const} functor) is always a natural
transformation. Since all standard algebraic data types are functors,
any polymorphic function between such types is a natural transformation.
We also have function types at our disposal, and those are functorial in
their return type. We can use them to build functors (like the
\code{Reader} functor) and define natural transformations that are
higher-order functions.
However, function types are not covariant in the argument type. They are
\newterm{contravariant}. Of course contravariant functors are equivalent to
covariant functors from the opposite category. Polymorphic functions
between two contravariant functors are still natural transformations in
the categorical sense, except that they work on functors from the
opposite category to Haskell types.
You might remember the example of a contravariant functor we've looked
at before:
\src{snippet21}
This functor is contravariant in \code{a}:
\src{snippet22}
We can write a polymorphic function from, say, \code{Op Bool} to
\code{Op String}:
\src{snippet23}
But since the two functors are not covariant, this is not a natural
transformation in $\Hask$. However, because they are both
contravariant, they satisfy the ``opposite'' naturality condition:
\src{snippet24}[b]
Notice that the function \code{f} must go in the opposite direction
than what you'd use with \code{fmap}, because of the signature of
\code{contramap}:
\src{snippet25}
Are there any type constructors that are not functors, whether covariant
or contravariant? Here's one example:
\src{snippet26}
This is not a functor because the same type \code{a} is used both in
the negative (contravariant) and positive (covariant) position. You
can't implement \code{fmap} or \code{contramap} for this type.
Therefore a function of the signature:
\src{snippet27}
where \code{f} is an arbitrary functor, cannot be a natural
transformation. Interestingly, there is a generalization of natural
transformations, called dinatural transformations, that deals with such
cases. We'll get to them when we discuss ends.
\section{Functor Category}
Now that we have mappings between functors --- natural transformations
--- it's only natural to ask the question whether functors form a
category. And indeed they do! There is one category of functors for each
pair of categories, $\cat{C}$ and $\cat{D}$. Objects in this category are functors from
$\cat{C}$ to $\cat{D}$, and morphisms are natural transformations between those
functors.
We have to define composition of two natural transformations, but that's
quite easy. The components of natural transformations are morphisms, and
we know how to compose morphisms.
Indeed, let's take a natural transformation $\alpha$ from functor $F$ to $G$. Its
component at object $a$ is some morphism:
\[\alpha_a \Colon F a \to G a\]
We'd like to compose $\alpha$ with $\beta$, which is a natural transformation from
functor $G$ to $H$. The component of $\beta$ at $a$ is a morphism:
\[\beta_a \Colon G a \to H a\]
These morphisms are composable and their composition is another
morphism:
\[\beta_a \circ \alpha_a \Colon F a \to H a\]
We will use this morphism as the component of the natural transformation
$\beta \cdot \alpha$ --- the composition of two natural transformations $\beta$ after $\alpha$:
\[(\beta \cdot \alpha)_a = \beta_a \circ \alpha_a\]
\begin{figure}[H]
\centering
\includegraphics[width=0.4\textwidth]{images/5_vertical.jpg}
\end{figure}
\noindent
One (long) look at a diagram convinces us that the result of this
composition is indeed a natural transformation from F to H:
\[H f \circ (\beta \cdot \alpha)_a = (\beta \cdot \alpha)_b \circ F f\]
\begin{figure}[H]
\centering
\includegraphics[width=0.35\textwidth]{images/6_verticalnaturality.jpg}
\end{figure}
\noindent
Composition of natural transformations is associative, because their
components, which are regular morphisms, are associative with respect to
their composition.
Finally, for each functor $F$ there is an identity natural transformation
$1_F$ whose components are the identity morphisms:
\[\id_{F a} \Colon F a \to F a\]
So, indeed, functors form a category.
A word about notation. Following Saunders Mac Lane I use the dot for the
kind of natural transformation composition I have just described. The
problem is that there are two ways of composing natural transformations.
This one is called the vertical composition, because the functors are
usually stacked up vertically in the diagrams that describe it. Vertical
composition is important in defining the functor category. I'll explain
horizontal composition shortly.
\begin{figure}[H]
\centering
\includegraphics[width=0.3\textwidth]{images/6a_vertical.jpg}
\end{figure}
\noindent
The functor category between categories $\cat{C}$ and $\cat{D}$ is written as
$\cat{Fun(C, D)}$, or $\cat{{[}C, D{]}}$, or sometimes as
$\cat{D^C}$. This last notation suggests that a functor category itself
might be considered a function object (an exponential) in some other
category. Is this indeed the case?
Let's have a look at the hierarchy of abstractions that we've been
building so far. We started with a category, which is a collection of
objects and morphisms. Categories themselves (or, strictly speaking
\emph{small} categories, whose objects form sets) are themselves objects
in a higher-level category $\Cat$. Morphisms in that category are
functors. A Hom-set in $\Cat$ is a set of functors. For instance
$\cat{Cat(C, D)}$ is a set of functors between two categories $\cat{C}$ and $\cat{D}$.
\begin{figure}[H]
\centering
\includegraphics[width=0.3\textwidth]{images/7_cathomset.jpg}
\end{figure}
\noindent
A functor category $\cat{{[}C, D{]}}$ is also a set of functors between two
categories (plus natural transformations as morphisms). Its objects are
the same as the members of $\cat{Cat(C, D)}$. Moreover, a functor category,
being a category, must itself be an object of $\Cat$ (it so
happens that the functor category between two small categories is itself
small). We have a relationship between a Hom-set in a category and an
object in the same category. The situation is exactly like the
exponential object that we've seen in the last section. Let's see how we
can construct the latter in $\Cat$.
As you may remember, in order to construct an exponential, we need to
first define a product. In $\Cat$, this turns out to be relatively
easy, because small categories are \emph{sets} of objects, and we know
how to define Cartesian products of sets. So an object in a product
category $\cat{C\times D}$ is just a pair of objects, $(c, d)$, one from $\cat{C}$
and one from $\cat{D}$. Similarly, a morphism between two such pairs,
$(c, d)$ and $(c', d')$, is a pair of morphisms, $(f, g)$, where
$f \Colon c \to c'$ and $g \Colon d \to d'$. These pairs of morphisms
compose component-wise, and there is always an identity pair that is
just a pair of identity morphisms. To make the long story short,
$\Cat$ is a full-blown Cartesian closed category in which there is
an exponential object $\cat{D^C}$ for any pair of categories.
And by ``object'' in $\Cat$ I mean a category, so
$\cat{D^C}$ is a category, which we can identify with the
functor category between $\cat{C}$ and $\cat{D}$.
\section{2-Categories}
With that out of the way, let's have a closer look at $\Cat$. By
definition, any Hom-set in $\Cat$ is a set of functors. But, as we
have seen, functors between two objects have a richer structure than
just a set. They form a category, with natural transformations acting as
morphisms. Since functors are considered morphisms in $\Cat$,
natural transformations are morphisms between morphisms.
This richer structure is an example of a $\cat{2}$-category, a generalization of
a category where, besides objects and morphisms (which might be called
$1$-morphisms in this context), there are also $2$-morphisms, which are
morphisms between morphisms.
In the case of $\Cat$ seen as a $\cat{2}$-category we have:
\begin{itemize}
\tightlist
\item
Objects: (Small) categories
\item
1-morphisms: Functors between categories
\item
2-morphisms: Natural transformations between functors.
\end{itemize}
\begin{figure}[H]
\centering
\includegraphics[width=0.3\textwidth]{images/8_cat-2-cat.jpg}
\end{figure}
\noindent
Instead of a Hom-set between two categories $\cat{C}$ and $\cat{D}$, we have a
Hom-category --- the functor category $\cat{D^C}$. We have
regular functor composition: a functor $F$ from $\cat{D^C}$
composes with a functor $G$ from $\cat{E^D}$ to give $G \circ F$ from
$\cat{E^C}$. But we also have composition inside each
Hom-category --- vertical composition of natural transformations, or
2-morphisms, between functors.
With two kinds of composition in a $\cat{2}$-category, the question arises: How
do they interact with each other?
Let's pick two functors, or 1-morphisms, in $\Cat$:
\begin{gather*}
F \Colon \cat{C} \to \cat{D} \\
G \Colon \cat{D} \to \cat{E}
\end{gather*}
and their composition:
\[G \circ F \Colon \cat{C} \to \cat{E}\]
Suppose we have two natural transformations, $\alpha$ and $\beta$, that act,
respectively, on functors $F$ and $G$:
\begin{gather*}
\alpha \Colon F \to F' \\
\beta \Colon G \to G'
\end{gather*}
\begin{figure}[H]
\centering
\includegraphics[width=0.4\textwidth]{images/10_horizontal.jpg}
\end{figure}
\noindent
Notice that we cannot apply vertical composition to this pair, because
the target of $\alpha$ is different from the source of $\beta$. In fact they are
members of two different functor categories: $\cat{D^C}$ and $\cat{E^D}$.
We can, however, apply composition to the functors
$F'$ and $G'$, because the target of $F'$ is the source of $G'$ --- it's the
category $\cat{D}$. What's the relation between the functors $G' \circ F'$ and $G \circ F$?
Having $\alpha$ and $\beta$ at our disposal, can we define a natural transformation
from $G \circ F$ to $G' \circ F'$? Let me sketch the construction.
\begin{figure}[H]
\centering
\includegraphics[width=0.5\textwidth]{images/9_horizontal.jpg}
\end{figure}
\noindent
As usual, we start with an object $a$ in $\cat{C}$. Its image splits into
two objects in $\cat{D}$: $F a$ and $F'a$. There is also a
morphism, a component of $\alpha$, connecting these two objects:
\[\alpha_a \Colon F a \to F'a\]
When going from $\cat{D}$ to $\cat{E}$, these two objects split further into four
objects: $G (F a)$, $G'(F a)$, $G (F'a)$, $G'(F'a)$.
We also have four morphisms forming a square. Two of these morphisms are
the components of the natural transformation $\beta$:
\begin{gather*}
\beta_{F a} \Colon G (F a) \to G'(F a) \\
\beta_{F'a} \Colon G (F'a) \to G'(F'a)
\end{gather*}
The other two are the images of $\alpha_a$ under the two
functors (functors map morphisms):
\begin{gather*}
G \alpha_a \Colon G (F a) \to G (F'a) \\
G'\alpha_a \Colon G'(F a) \to G'(F'a)
\end{gather*}
That's a lot of morphisms. Our goal is to find a morphism that goes from
$G (F a)$ to $G'(F'a)$, a candidate for the
component of a natural transformation connecting the two functors $G \circ F$
and $G' \circ F'$. In fact there's not one but two paths we can take from
$G (F a)$ to $G'(F'a)$:
\begin{gather*}
G'\alpha_a \circ \beta_{F a} \\
\beta_{F'a} \circ G \alpha_a
\end{gather*}
Luckily for us, they are equal, because the square we have formed turns
out to be the naturality square for $\beta$.
We have just defined a component of a natural transformation from $G \circ F$
to $G' \circ F'$. The proof of naturality for this transformation is pretty
straightforward, provided you have enough patience.
We call this natural transformation the \newterm{horizontal composition} of
$\alpha$ and $\beta$:
\[\beta \circ \alpha \Colon G \circ F \to G' \circ F'\]
Again, following Mac Lane I use the small circle for horizontal
composition, although you may also encounter star in its place.
Here's a categorical rule of thumb: Every time you have composition, you
should look for a category. We have vertical composition of natural
transformations, and it's part of the functor category. But what about
the horizontal composition? What category does that live in?
The way to figure this out is to look at $\Cat$ sideways. Look at
natural transformations not as arrows between functors but as arrows
between categories. A natural transformation sits between two
categories, the ones that are connected by the functors it transforms.
We can think of it as connecting these two categories.
\begin{figure}[H]
\centering
\includegraphics[width=0.5\textwidth]{images/sideways.jpg}
\end{figure}
\noindent
Let's focus on two objects of $\Cat$ --- categories $\cat{C}$ and $\cat{D}$. There
is a set of natural transformations that go between functors that
connect $\cat{C}$ to $\cat{D}$. These natural transformations are our new arrows from $\cat{C}$
to $\cat{D}$. By the same token, there are natural transformations going between
functors that connect $\cat{D}$ to $\cat{E}$, which we can treat as new arrows going
from $\cat{D}$ to $\cat{E}$. Horizontal composition is the composition of these arrows.
We also have an identity arrow going from $\cat{C}$ to $\cat{C}$. It's the identity
natural transformation that maps the identity functor on $\cat{C}$ to itself.
Notice that the identity for horizontal composition is also the identity
for vertical composition, but not vice versa.
Finally, the two compositions satisfy the interchange law:
\[(\beta' \cdot \alpha') \circ (\beta \cdot \alpha) = (\beta' \circ \beta) \cdot (\alpha' \circ \alpha)\]
I will quote Saunders Mac Lane here: The reader may enjoy writing down
the evident diagrams needed to prove this fact.
There is one more piece of notation that might come in handy in the
future. In this new sideways interpretation of $\Cat$ there are
two ways of getting from object to object: using a functor or using a
natural transformation. We can, however, re-interpret the functor arrow
as a special kind of natural transformation: the identity natural
transformation acting on this functor. So you'll often see this
notation:
\[F \circ \alpha\]
where $F$ is a functor from $\cat{D}$ to $\cat{E}$, and $\alpha$ is a natural transformation
between two functors going from $\cat{C}$ to $\cat{D}$. Since you can't compose a
functor with a natural transformation, this is interpreted as a
horizontal composition of the identity natural transformation
$1_F$ after $\alpha$.
Similarly:
\[\alpha \circ F\]
is a horizontal composition of $\alpha$ after $1_F$.
\section{Conclusion}
This concludes the first part of the book. We've learned the basic
vocabulary of category theory. You may think of objects and categories
as nouns; and morphisms, functors, and natural transformations as verbs.
Morphisms connect objects, functors connect categories, natural
transformations connect functors.
But we've also seen that, what appears as an action at one level of
abstraction, becomes an object at the next level. A set of morphisms
turns into a function object. As an object, it can be a source or a
target of another morphism. That's the idea behind higher order
functions.
A functor maps objects to objects, so we can use it as a type
constructor, or a parametric type. A functor also maps morphisms, so it
is a higher order function --- \code{fmap}. There are some simple
functors, like \code{Const}, product, and coproduct, that can be used
to generate a large variety of algebraic data types. Function types are
also functorial, both covariant and contravariant, and can be used to
extend algebraic data types.
Functors may be looked upon as objects in the functor category. As such,
they become sources and targets of morphisms: natural transformations. A
natural transformation is a special type of polymorphic function.
\section{Challenges}
\begin{enumerate}
\tightlist
\item
Define a natural transformation from the \code{Maybe} functor to the
list functor. Prove the naturality condition for it.
\item
Define at least two different natural transformations between
\code{Reader ()} and the list functor. How many different lists of
\code{()} are there?
\item
Continue the previous exercise with \code{Reader Bool} and
\code{Maybe}.
\item
Show that horizontal composition of natural transformation satisfies
the naturality condition (hint: use components). It's a good exercise
in diagram chasing.
\item
Write a short essay about how you may enjoy writing down the evident
diagrams needed to prove the interchange law.
\item
Create a few test cases for the opposite naturality condition of
transformations between different \code{Op} functors. Here's one
choice:
\begin{snip}{haskell}
op :: Op Bool Int
op = Op (\x -> x > 0)
\end{snip}
and
\begin{snip}{haskell}
f :: String -> Int
f x = read x
\end{snip}
\end{enumerate}
================================================
FILE: src/content/1.2/code/haskell/snippet01.hs
================================================
x :: Integer
================================================
FILE: src/content/1.2/code/haskell/snippet02.hs
================================================
f :: Bool -> Bool
================================================
FILE: src/content/1.2/code/haskell/snippet03.hs
================================================
f :: Bool -> Bool
f x = undefined
================================================
FILE: src/content/1.2/code/haskell/snippet04.hs
================================================
f :: Bool -> Bool
f = undefined
================================================
FILE: src/content/1.2/code/haskell/snippet05.hs
================================================
fact n = product [1..n]
================================================
FILE: src/content/1.2/code/haskell/snippet06.hs
================================================
absurd :: Void -> a
================================================
FILE: src/content/1.2/code/haskell/snippet07.hs
================================================
f44 :: () -> Integer
f44 () = 44
================================================
FILE: src/content/1.2/code/haskell/snippet08.hs
================================================
fInt :: Integer -> ()
fInt x = ()
================================================
FILE: src/content/1.2/code/haskell/snippet09.hs
================================================
fInt :: Integer -> ()
fInt _ = ()
================================================
FILE: src/content/1.2/code/haskell/snippet10.hs
================================================
unit :: a -> ()
unit _ = ()
================================================
FILE: src/content/1.2/code/haskell/snippet11.hs
================================================
data Bool = True | False
================================================
FILE: src/content/1.2/code/ocaml/snippet01.ml
================================================
module type Chapter2_DeclareVariable = sig
val x : int
end
================================================
FILE: src/content/1.2/code/ocaml/snippet010.ml
================================================
let unit _ = ()
================================================
FILE: src/content/1.2/code/ocaml/snippet011.ml
================================================
type bool =
| false
| true
================================================
FILE: src/content/1.2/code/ocaml/snippet02.ml
================================================
module type Chapter2_DeclareFunction = sig
val f : bool -> bool
end
================================================
FILE: src/content/1.2/code/ocaml/snippet03.ml
================================================
module Chapter2_Bottom : Chapter2_DeclareFunction = struct
let f (b : bool) : bool = failwith "Not Implemented"
end
================================================
FILE: src/content/1.2/code/ocaml/snippet04.ml
================================================
module Chapter2_Bottom : Chapter2_DeclareFunction = struct
let f : bool -> bool = fun _ -> failwith "Not implemented"
end
================================================
FILE: src/content/1.2/code/ocaml/snippet05.ml
================================================
let fact n = List.fold (List.range 1 n) ~init:1 ~f:( * )
================================================
FILE: src/content/1.2/code/ocaml/snippet06.ml
================================================
type void
let rec absurd (x : void) = absurd x
================================================
FILE: src/content/1.2/code/ocaml/snippet07.ml
================================================
let f44 () : int = 44
================================================
FILE: src/content/1.2/code/ocaml/snippet08.ml
================================================
let f_int (x : int) = ()
================================================
FILE: src/content/1.2/code/ocaml/snippet09.ml
================================================
let f_int (_ : int) = ()
================================================
FILE: src/content/1.2/code/ocaml/snippet10.ml
================================================
let unit _ = ()
================================================
FILE: src/content/1.2/code/ocaml/snippet11.ml
================================================
type bool =
| false
| true
================================================
FILE: src/content/1.2/code/reason/snippet01.re
================================================
module type Chapter2_DeclareVariable = {let x: int;};
================================================
FILE: src/content/1.2/code/reason/snippet010.re
================================================
let unit = _ => ();
================================================
FILE: src/content/1.2/code/reason/snippet011.re
================================================
type bool =
| false
| true;
================================================
FILE: src/content/1.2/code/reason/snippet02.re
================================================
module type Chapter2_DeclareFunction = {let f: bool => bool;};
================================================
FILE: src/content/1.2/code/reason/snippet03.re
================================================
module Chapter2_Bottom: Chapter2_DeclareFunction = {
let f = (b: bool): bool => failwith("Not Implemented");
};
================================================
FILE: src/content/1.2/code/reason/snippet04.re
================================================
module Chapter2_Bottom: Chapter2_DeclareFunction = {
let f: bool => bool = _ => failwith("Not implemented");
};
================================================
FILE: src/content/1.2/code/reason/snippet05.re
================================================
let fact = n => List.fold(List.range(1, n), ~init=1, ~f=( * ));
================================================
FILE: src/content/1.2/code/reason/snippet06.re
================================================
type void;
let rec absurd = (x: void) => absurd(x);
================================================
FILE: src/content/1.2/code/reason/snippet07.re
================================================
let f44 = (): int => 44;
================================================
FILE: src/content/1.2/code/reason/snippet08.re
================================================
let f_int = (x: int) => ();
================================================
FILE: src/content/1.2/code/reason/snippet09.re
================================================
let f_int = (_: int) => ();
================================================
FILE: src/content/1.2/code/reason/snippet10.re
================================================
let unit = _ => ();
================================================
FILE: src/content/1.2/code/reason/snippet11.re
================================================
type bool =
| false
| true;
================================================
FILE: src/content/1.2/code/scala/snippet01.scala
================================================
val x: BigInt
================================================
FILE: src/content/1.2/code/scala/snippet02.scala
================================================
val f: Boolean => Boolean
================================================
FILE: src/content/1.2/code/scala/snippet03.scala
================================================
val f: Boolean => Boolean = x => ???
================================================
FILE: src/content/1.2/code/scala/snippet04.scala
================================================
def f: Boolean => Boolean = ???
================================================
FILE: src/content/1.2/code/scala/snippet05.scala
================================================
val fact = (n: Int) => (1 to n).product
================================================
FILE: src/content/1.2/code/scala/snippet06.scala
================================================
def absurd[A]: Nothing => A
================================================
FILE: src/content/1.2/code/scala/snippet07.scala
================================================
val f44: Unit => BigInt = _ => 44
================================================
FILE: src/content/1.2/code/scala/snippet08.scala
================================================
val fInt: BigInt => Unit = x => ()
================================================
FILE: src/content/1.2/code/scala/snippet09.scala
================================================
val fInt: BigInt => Unit = _ => ()
================================================
FILE: src/content/1.2/code/scala/snippet10.scala
================================================
def unit[A]: A => Unit = _ => ()
================================================
FILE: src/content/1.2/code/scala/snippet11.scala
================================================
sealed trait Bool
case object True extends Bool
case object False extends Bool
================================================
FILE: src/content/1.2/types-and-functions.tex
================================================
% !TEX root = ../../ctfp-print.tex
\lettrine[lhang=0.17]{T}{he category of types and functions} plays an important role in
programming, so let's talk about what types are and why we need them.
\section{Who Needs Types?}
There seems to be some controversy about the advantages of static vs.
dynamic and strong vs. weak typing. Let me illustrate these choices with
a thought experiment. Imagine millions of monkeys at computer keyboards
happily hitting random keys, producing programs, compiling, and running
them.
\begin{figure}[H]
\centering
\includegraphics[width=0.3\textwidth]{images/img_1329.jpg}
\end{figure}
\noindent
With machine language, any combination of bytes produced by monkeys
would be accepted and run. But with higher level languages, we do
appreciate the fact that a compiler is able to detect lexical and
grammatical errors. Lots of monkeys will go without bananas, but the
remaining programs will have a better chance of being useful. Type
checking provides yet another barrier against nonsensical programs.
Moreover, whereas in a dynamically typed language, type mismatches would
be discovered at runtime, in strongly typed statically checked languages,
type mismatches are discovered at compile time, eliminating lots of
incorrect programs before they have a chance to run.
So the question is, do we want to make monkeys happy, or do we want to
produce correct programs?
The usual goal in the typing monkeys thought experiment is the
production of the complete works of Shakespeare. Having a spell checker
and a grammar checker in the loop would drastically increase the odds.
The analog of a type checker would go even further by making sure that,
once Romeo is declared a human being, he doesn't sprout leaves or trap
photons in his powerful gravitational field.
\section{Types Are About Composability}
Category theory is about composing arrows. But not any two arrows can be
composed. The target object of one arrow must be the same as the source
object of the next arrow. In programming we pass the results of
one function to another. The program will not work if the target
function is not able to correctly interpret the data produced by the
source function. The two ends must fit for the composition to work. The
stronger the type system of the language, the better this match can be
described and mechanically verified.
The only serious argument I hear against strong static type checking is
that it might eliminate some programs that are semantically correct. In
practice, this happens extremely rarely and, in any case, every language
provides some kind of a backdoor to bypass the type system when that's
really necessary. Even Haskell has \code{unsafeCoerce}. But such
devices should be used judiciously. Franz Kafka's character, Gregor
Samsa, breaks the type system when he metamorphoses into a giant bug,
and we all know how it ends.
Another argument I hear a lot is that dealing with types imposes too
much burden on the programmer. I could sympathize with this sentiment
after having to write a few declarations of iterators in C++ myself,
except that there is a technology called \newterm{type inference} that lets
the compiler deduce most of the types from the context in which they are
used. In C++, you can now declare a variable \code{auto} and let the
compiler figure out its type.
In Haskell, except on rare occasions, type annotations are purely
optional. Programmers tend to use them anyway, because they can tell a
lot about the semantics of code, and they make compilation errors easier
to understand. It's a common practice in Haskell to start a project by
designing the types. \sloppy{Later, type annotations drive the implementation
and become compiler-enforced comments.}
Strong static typing is often used as an excuse for not testing the
code. You may sometimes hear Haskell programmers saying, ``If it
compiles, it must be correct.'' Of course, there is no guarantee that a
type-correct program is correct in the sense of producing the right
output. The result of this cavalier attitude is that in several studies
Haskell didn't come as strongly ahead of the pack in code quality as one
would expect. It seems that, in the commercial setting, the pressure to
fix bugs is applied only up to a certain quality level, which has
everything to do with the economics of software development and the
tolerance of the end user, and very little to do with the programming
language or methodology. A better criterion would be to measure how many
projects fall behind schedule or are delivered with drastically reduced
functionality.
As for the argument that unit testing can replace strong typing,
consider the common refactoring practice in strongly typed languages:
changing the type of an argument of a particular function. In a strongly
typed language, it's enough to modify the declaration of that function
and then fix all the build breaks. In a weakly typed language, the fact
that a function now expects different data cannot be propagated to call
sites. Unit testing may catch some of the mismatches, but testing is
almost always a probabilistic rather than a deterministic process.
Testing is a poor substitute for proof.
\section{What Are Types?}
The simplest intuition for types is that they are sets of values. The
type \code{Bool} (remember, concrete types start with a capital letter
in Haskell) is a two-element set of \code{True} and \code{False}.
Type \code{Char} is a set of all Unicode characters like
\code{a} or \code{ą}.
Sets can be finite or infinite. The type of \code{String}, which is a
synonym for a list of \code{Char}, is an example of an infinite set.
When we declare \code{x} to be an \code{Integer}:
\src{snippet01}
we are saying that it's an element of the set of integers.
\code{Integer} in Haskell is an infinite set, and it can be used to do
arbitrary precision arithmetic. There is also a finite-set \code{Int}
that corresponds to machine type, just like the C++ \code{int}.
There are some subtleties that make this identification of types and
sets tricky. There are problems with polymorphic functions that involve
circular definitions, and with the fact that you can't have a set of all
sets; but as I promised, I won't be a stickler for math. The great thing
is that there is a category of sets, which is called $\Set$, and
we'll just work with it. In $\Set$, objects are sets and morphisms
(arrows) are functions.
$\Set$ is a very special category, because we can actually peek
inside its objects and get a lot of intuitions from doing that. For
instance, we know that an empty set has no elements. We know that there
are special one-element sets. We know that functions map elements of one
set to elements of another set. They can map two elements to one, but
not one element to two. We know that an identity function maps each
element of a set to itself, and so on. The plan is to gradually forget
all this information and instead express all those notions in purely
categorical terms, that is in terms of objects and arrows.
In the ideal world we would just say that Haskell types are sets and
Haskell functions are mathematical functions between sets. There is just
one little problem: A mathematical function does not execute any code
--- it just knows the answer. A Haskell function has to calculate the
answer. It's not a problem if the answer can be obtained in a finite
number of steps --- however big that number might be. But there are some
calculations that involve recursion, and those might never terminate. We
can't just ban non-terminating functions from Haskell because
distinguishing between terminating and non-terminating functions is
undecidable --- the famous halting problem. That's why computer
scientists came up with a brilliant idea, or a major hack, depending on
your point of view, to extend every type by one more special value
called the \newterm{bottom} and denoted by \code{\_|\_}, or
Unicode $\bot$. This ``value'' corresponds to a non-terminating computation.
So a function declared as:
\src{snippet02}
may return \code{True}, \code{False}, or \code{\_|\_};
the latter meaning that it would never terminate.
Interestingly, once you accept the bottom as part of the type system, it
is convenient to treat every runtime error as a bottom, and even allow
functions to return the bottom explicitly. The latter is usually done
using the expression \code{undefined}, as in:
\src{snippet03}
This definition type checks because \code{undefined} evaluates to
bottom, which is a member of any type, including \code{Bool}. You can
even write:
\src{snippet04}
(without the \code{x}) because the bottom is also a member of the type
\code{Bool -> Bool}.
Functions that may return bottom are called partial, as opposed to total
functions, which return valid results for every possible argument.
Because of the bottom, you'll see the category of Haskell types and
functions referred to as $\Hask$ rather than $\Set$. From
the theoretical point of view, this is the source of never-ending
complications, so at this point I will use my butcher's knife and
terminate this line of reasoning. From the pragmatic point of view, it's
okay to ignore non-terminating functions and bottoms, and treat
$\Hask$ as bona fide $\Set$.\footnote{Nils Anders Danielsson,
John Hughes, Patrik Jansson, Jeremy Gibbons, \href{http://www.cs.ox.ac.uk/jeremy.gibbons/publications/fast+loose.pdf}{
Fast and Loose Reasoning is Morally Correct}. This paper provides justification for ignoring bottoms in most contexts.}
\section{Why Do We Need a Mathematical Model?}
As a programmer you are intimately familiar with the syntax and grammar
of your programming language. These aspects of the language are usually
described using formal notation at the very beginning of the language
spec. But the meaning, or semantics, of the language is much harder to
describe; it takes many more pages, is rarely formal enough, and almost
never complete. Hence the never ending discussions among language
lawyers, and a whole cottage industry of books dedicated to the exegesis
of the finer points of language standards.
There are formal tools for describing the semantics of a language but,
because of their complexity, they are mostly used with simplified
academic languages, not real-life programming behemoths. One such tool
called \newterm{operational semantics} describes the mechanics of program
execution. It defines a formalized idealized interpreter. The semantics
of industrial languages, such as C++, is usually described using
informal operational reasoning, often in terms of an ``abstract
machine.''
The problem is that it's very hard to prove things about programs using
operational semantics. To show a property of a program you essentially
have to ``run it'' through the idealized interpreter.
It doesn't matter that programmers never perform formal proofs of
correctness. We always ``think'' that we write correct programs. Nobody
sits at the keyboard saying, ``Oh, I'll just throw a few lines of code
and see what happens.'' We think that the code we write will perform
certain actions that will produce desired results. We are usually quite
surprised when it doesn't. That means we do reason about programs we
write, and we usually do it by running an interpreter in our heads. It's
just really hard to keep track of all the variables. Computers are good
at running programs --- humans are not! If we were, we wouldn't need
computers.
But there is an alternative. It's called \newterm{denotational semantics}
and it's based on math. In denotational semantics every programming
construct is given its mathematical interpretation. With that, if you
want to prove a property of a program, you just prove a mathematical
theorem. You might think that theorem proving is hard, but the fact is
that we humans have been building up mathematical methods for thousands
of years, so there is a wealth of accumulated knowledge to tap into.
Also, as compared to the kind of theorems that professional
mathematicians prove, the problems that we encounter in programming are
usually quite simple, if not trivial.
Consider the definition of a factorial function in Haskell, which is a
language quite amenable to denotational semantics:
\src{snippet05}
The expression \code{{[}1..n{]}} is a list of integers from \code{1} to \code{n}.
The function \code{product} multiplies all elements of a list. That's
just like a definition of factorial taken from a math text. Compare this
with C:
\begin{snip}{c}
int fact(int n) {
int i;
int result = 1;
for (i = 2; i <= n; ++i)
result *= i;
return result;
}
\end{snip}
Need I say more?
Okay, I'll be the first to admit that this was a cheap shot! A factorial
function has an obvious mathematical denotation. An astute reader might
ask: What's the mathematical model for reading a character from the
keyboard or sending a packet across the network? For the longest time
that would have been an awkward question leading to a rather convoluted
explanation. It seemed like denotational semantics wasn't the best fit
for a considerable number of important tasks that were essential for
writing useful programs, and which could be easily tackled by
operational semantics. The breakthrough came from category theory.
Eugenio Moggi discovered that computational effect can be mapped to
monads. This turned out to be an important observation that not only
gave denotational semantics a new lease on life and made pure functional
programs more usable, but also shed new light on traditional
programming. I'll talk about monads later, when we develop more
categorical tools.
One of the important advantages of having a mathematical model for
programming is that it's possible to perform formal proofs of
correctness of software. This might not seem so important when you're
writing consumer software, but there are areas of programming where the
price of failure may be exorbitant, or where human life is at stake. But
even when writing web applications for the health system, you may
appreciate the thought that functions and algorithms from the Haskell
standard library come with proofs of correctness.
\section{Pure and Dirty Functions}
The things we call functions in C++ or any other imperative language,
are not the same things mathematicians call functions. A mathematical
function is just a mapping of values to values.
We can implement a mathematical function in a programming language: Such
a function, given an input value will calculate the output value. A
function to produce a square of a number will probably multiply the
input value by itself. It will do it every time it's called, and it's
guaranteed to produce the same output every time it's called with the
same input. The square of a number doesn't change with the phases of the
Moon.
Also, calculating the square of a number should not have a side effect
of dispensing a tasty treat for your dog. A ``function'' that does that
cannot be easily modelled as a mathematical function.
In programming languages, functions that always produce the same result
given the same input and have no side effects are called \newterm{pure
functions}. In a pure functional language like Haskell all functions are
pure. Because of that, it's easier to give these languages denotational
semantics and model them using category theory. As for other languages,
it's always possible to restrict yourself to a pure subset, or reason
about side effects separately. Later we'll see how monads let us model
all kinds of effects using only pure functions. So we really don't lose
anything by restricting ourselves to mathematical functions.
\section{Examples of Types}
Once you realize that types are sets, you can think of some rather
exotic types. For instance, what's the type corresponding to an empty
set? No, it's not C++ \code{void}, although this type \emph{is} called
\code{Void} in Haskell. It's a type that's not inhabited by any
values. You can define a function that takes \code{Void}, but you can
never call it. To call it, you would have to provide a value of the type
\code{Void}, and there just aren't any. As for what this function can
return, there are no restrictions whatsoever. It can return any type
(although it never will, because it can't be called). In other words
it's a function that's polymorphic in the return type. Haskellers have a
name for it:
\src{snippet06}
(Remember, \code{a} is a type variable that can stand for any type.)
The name is not coincidental. There is deeper interpretation of types
and functions in terms of logic called the Curry-Howard isomorphism. The
type \code{Void} represents falsity, and the type of the function
\code{absurd} corresponds to the statement that from falsity follows
anything, as in the Latin adage ``ex falso sequitur quodlibet.''
Next is the type that corresponds to a singleton set. It's a type that
has only one possible value. This value just ``is.'' You might not
immediately recognize it as such, but that is the C++ \code{void}.
Think of functions from and to this type. A function from \code{void}
can always be called. If it's a pure function, it will always return the
same result. Here's an example of such a function:
\begin{snip}{c}
int f44() { return 44; }
\end{snip}
You might think of this function as taking ``nothing'', but as we've
just seen, a function that takes ``nothing'' can never be called because
there is no value representing ``nothing.'' So what does this function
take? Conceptually, it takes a dummy value of which there is only one
instance ever, so we don't have to mention it explicitly. In Haskell,
however, there is a symbol for this value: an empty pair of parentheses,
\code{()}. So, by a funny coincidence (or is it a coincidence?), the
call to a function of void looks the same in C++ and in Haskell. Also,
because of the Haskell's love of terseness, the same symbol \code{()}
is used for the type, the constructor, and the only value corresponding
to a singleton set. So here's this function in Haskell:
\src{snippet07}
The first line declares that \code{f44} takes the type \code{()},
pronounced ``unit,'' into the type \code{Integer}. The second line
defines \code{f44} by pattern matching the only constructor for unit,
namely \code{()}, and producing the number 44. You call this function
by providing the unit value \code{()}:
\begin{snip}{c}
f44 ()
\end{snip}
Notice that every function of unit is equivalent to picking a single
element from the target type (here, picking the \code{Integer} 44). In
fact you could think of \code{f44} as a different representation for
the number 44. This is an example of how we can replace explicit mention
of elements of a set by talking about functions (arrows) instead.
Functions from unit to any type $A$ are in one-to-one correspondence with
the elements of that set $A$.
What about functions with the \code{void} return type, or, in Haskell,
with the unit return type? In C++ such functions are used for side
effects, but we know that these are not real functions in the
mathematical sense of the word. A pure function that returns unit does
nothing: it discards its argument.
Mathematically, a function from a set $A$ to a singleton set maps every
element of $A$ to the single element of that singleton set. For every $A$
there is exactly one such function. Here's this function for
\code{Integer}:
\src{snippet08}
You give it any integer, and it gives you back a unit. In the spirit of
terseness, Haskell lets you use the wildcard pattern, the underscore,
for an argument that is discarded. This way you don't have to invent a
name for it. So the above can be rewritten as:
\src{snippet09}
Notice that the implementation of this function not only doesn't depend
on the value passed to it, but it doesn't even depend on the type of the
argument.
Functions that can be implemented with the same formula for any type are
called parametrically polymorphic. You can implement a whole family of
such functions with one equation using a type parameter instead of a
concrete type. What should we call a polymorphic function from any type
to unit type? Of course we'll call it \code{unit}:
\src{snippet10}
In C++ you would write this function as:
\begin{snip}{cpp}
template<typename T>
void unit(T) {}
\end{snip}
Next in the typology of types is a two-element set. In C++ it's called
\code{bool} and in Haskell, predictably, \code{Bool}. The difference
is that in C++ \code{bool} is a built-in type, whereas in Haskell it
can be defined as follows:
\src{snippet11}
(The way to read this definition is that \code{Bool} is either
\code{True} or \code{False}.) In principle, one should also be able
to define a Boolean type in C++ as an enumeration:
\begin{snip}{cpp}
enum bool {
true,
false
};
\end{snip}
but C++ \code{enum} is secretly an integer. The C++11
``\code{enum class}'' could have been used instead, but then you
would have to qualify its values with the class name, as in
\code{bool::true} and \code{bool::false}, not to mention having to
include the appropriate header in every file that uses it.
Pure functions from \code{Bool} just pick two values from the target
type, one corresponding to \code{True} and another to \code{False}.
Functions to \code{Bool} are called \newterm{predicates}. For instance,
the Haskell library \code{Data.Char} is full of predicates like
\code{isAlpha} or \code{isDigit}. In C++ there is a similar library
\code{} that defines, among others, \code{isalpha} and
\code{isdigit}, but these return an \code{int} rather than a
Boolean. The actual predicates are defined in \code{std::ctype} and
have the form \code{ctype::is(alpha, c)}, \code{ctype::is(digit, c)}, etc.
\section{Challenges}
\begin{enumerate}
\tightlist
\item
Define a higher-order function (or a function object) \code{memoize}
in your favorite language. This function takes a pure function
\code{f} as an argument and returns a function that behaves almost
exactly like \code{f}, except that it only calls the original
function once for every argument, stores the result internally, and
subsequently returns this stored result every time it's called with
the same argument. You can tell the memoized function from the
original by watching its performance. For instance, try to memoize a
function that takes a long time to evaluate. You'll have to wait for
the result the first time you call it, but on subsequent calls, with
the same argument, you should get the result immediately.
\item
Try to memoize a function from your standard library that you normally
use to produce random numbers. Does it work?
\item
Most random number generators can be initialized with a seed.
Implement a function that takes a seed, calls the random number
generator with that seed, and returns the result. Memoize that
function. Does it work?
\item
Which of these C++ functions are pure? Try to memoize them and observe
what happens when you call them multiple times: memoized and not.
\begin{enumerate}
\tightlist
\item
The factorial function from the example in the text.
\item
\begin{minted}{cpp}
std::getchar()
\end{minted}
\item
\begin{minted}{cpp}
bool f() {
std::cout << "Hello!" << std::endl;
return true;
}
\end{minted}
\item
\begin{minted}{cpp}
int f(int x) {
static int y = 0;
y += x;
return y;
}
\end{minted}
\end{enumerate}
\item
How many different functions are there from \code{Bool} to
\code{Bool}? Can you implement them all?
\item
Draw a picture of a category whose only objects are the types
\code{Void}, \code{()} (unit), and \code{Bool}; with arrows
corresponding to all possible functions between these types. Label the
arrows with the names of the functions.
\end{enumerate}
================================================
FILE: src/content/1.3/categories-great-and-small.tex
================================================
% !TEX root = ../../ctfp-print.tex
\lettrine[lhang=0.17]{Y}{ou can get} real appreciation for categories by studying a variety of
examples. Categories come in all shapes and sizes and often pop up in
unexpected places. We'll start with something really simple.
\section{No Objects}
The most trivial category is one with zero objects and, consequently,
zero morphisms. It's a very sad category by itself, but it may be
important in the context of other categories, for instance, in the
category of all categories (yes, there is one). If you think that an
empty set makes sense, then why not an empty category?
\section{Simple Graphs}
You can build categories just by connecting objects with arrows. You can
imagine starting with any directed graph and making it into a category
by simply adding more arrows. First, add an identity arrow at each node.
Then, for any two arrows such that the end of one coincides with the
beginning of the other (in other words, any two \newterm{composable}
arrows), add a new arrow to serve as their composition. Every time you
add a new arrow, you have to also consider its composition with any
other arrow (except for the identity arrows) and itself. You usually end
up with infinitely many arrows, but that's okay.
Another way of looking at this process is that you're creating a
category, which has an object for every node in the graph, and all
possible \newterm{chains} of composable graph edges as morphisms. (You may
even consider identity morphisms as special cases of chains of length
zero.)
Such a category is called a \newterm{free category} generated by a given
graph. It's an example of a free construction, a process of completing a
given structure by extending it with a minimum number of items to
satisfy its laws (here, the laws of a category). We'll see more examples
of it in the future.
\section{Orders}
And now for something completely different! A category where a morphism
is a relation between objects: the relation of being less than or equal.
Let's check if it indeed is a category. Do we have identity morphisms?
Every object is less than or equal to itself: check! Do we have
composition? If $a \leqslant b$ and $b \leqslant c$ then $a \leqslant c$: check! Is composition associative? Check! A set with a
relation like this is called a \newterm{preorder}, so a preorder is indeed
a category.
You can also have a stronger relation, that satisfies an additional
condition that, if $a \leqslant b$ and $b \leqslant a$ then $a$ must be
the same as $b$. That's called a \newterm{partial order}.
Finally, you can impose the condition that any two objects are in a
relation with each other, one way or another; and that gives you a
\newterm{linear order} or \newterm{total order}.
Let's characterize these ordered sets as categories. A preorder is a
category where there is at most one morphism going from any object $a$ to
any object $b$. Another name for such a category is ``thin.'' A preorder
is a thin category.
A set of morphisms from object $a$ to object $b$ in a category $\cat{C}$ is called a
\newterm{hom-set} and is written as $\cat{C}(a, b)$ (or, sometimes,
$\mathbf{Hom}_{\cat{C}}(a, b)$). So every hom-set in a preorder is either
empty or a singleton. That includes the hom-set $\cat{C}(a, a)$, the set of
morphisms from $a$ to $a$, which must be a singleton, containing only the
identity, in any preorder. You may, however, have cycles in a preorder.
Cycles are forbidden in a partial order.
It's very important to be able to recognize preorders, partial orders,
and total orders because of sorting. Sorting algorithms, such as
quicksort, bubble sort, merge sort, etc., can only work correctly on
total orders. Partial orders can be sorted using topological sort.
\section{Monoid as Set}
Monoid is an embarrassingly simple but amazingly powerful concept. It's
the concept behind basic arithmetics: Both addition and multiplication
form a monoid. Monoids are ubiquitous in programming. They show up as
strings, lists, foldable data structures, futures in concurrent
programming, events in functional reactive programming, and so on.
Traditionally, a monoid is defined as a set with a binary operation. All
that's required from this operation is that it's associative, and that
there is one special element that behaves like a unit with respect to
it.
For instance, natural numbers with zero form a monoid under addition.
Associativity means that:
\[(a + b) + c = a + (b + c)\]
(In other words, we can skip parentheses when adding numbers.)
The neutral element is zero, because:
\[0 + a = a\]
and
\[a + 0 = a\]
The second equation is redundant, because addition is commutative $(a + b
= b + a)$, but commutativity is not part of the definition of a monoid.
For instance, string concatenation is not commutative and yet it forms a
monoid. The neutral element for string concatenation, by the way, is an
empty string, which can be attached to either side of a string without
changing it.
In Haskell we can define a type class for monoids --- a type for which
there is a neutral element called \code{mempty} and a binary operation
called \code{mappend}:
\src{snippet01}
The type signature for a two-argument function,
\code{m -> m -> m}, might look strange at first,
but it will make perfect sense after we talk about currying. You may
interpret a signature with multiple arrows in two basic ways: as a
function of multiple arguments, with the rightmost type being the return
type; or as a function of one argument (the leftmost one), returning a
function. The latter interpretation may be emphasized by adding
parentheses (which are redundant, because the arrow is
right-associative), as in: \code{m -> (m -> m)}.
We'll come back to this interpretation in a moment.
Notice that, in Haskell, there is no way to express the monoidal
properties of \code{mempty} and \code{mappend} (i.e., the fact that
\code{mempty} is neutral and that \code{mappend} is associative).
It's the responsibility of the programmer to make sure they are
satisfied.
Haskell classes are not as intrusive as C++ classes. When you're
defining a new type, you don't have to specify its class up front. You
are free to procrastinate and declare a given type to be an instance of
some class much later. As an example, let's declare \code{String} to
be a monoid by providing the implementation of \code{mempty} and
\code{mappend} (this is, in fact, done for you in the standard
Prelude):
\src{snippet02}
Here, we have reused the list concatenation operator \code{(++)},
because a \code{String} is just a list of characters.
A word about Haskell syntax: Any infix operator can be turned into a
two-argument function by surrounding it with parentheses. Given two
strings, you can concatenate them by inserting \code{++} between them:
\begin{snip}{haskell}
"Hello " ++ "world!"
\end{snip}
or by passing them as two arguments to the parenthesized \code{(++)}:
\begin{snip}{haskell}
(++) "Hello " "world!"
\end{snip}
Notice that arguments to a function are not separated by commas or
surrounded by parentheses. (This is probably the hardest thing to get
used to when learning Haskell.)
It's worth emphasizing that Haskell lets you express equality of
functions, as in:
\begin{snip}{haskell}
mappend = (++)
\end{snip}
Conceptually, this is different than expressing the equality of values
produced by functions, as in:
\begin{snip}{haskell}
mappend s1 s2 = (++) s1 s2
\end{snip}
The former translates into equality of morphisms in the category
$\Hask$ (or $\Set$, if we ignore bottoms, which is the name
for never-ending calculations). Such equations are not only more
succinct, but can often be generalized to other categories. The latter
is called \newterm{extensional} equality, and states the fact that for any
two input strings, the outputs of \code{mappend} and \code{(++)} are
the same. Since the values of arguments are sometimes called
\newterm{points} (as in: the value of $f$ at point $x$), this is called
point-wise equality. Function equality without specifying the arguments
is described as \newterm{point-free}. (Incidentally, point-free equations
often involve composition of functions, which is symbolized by a point,
so this might be a little confusing to the beginner.)
The closest one can get to declaring a monoid in C++ would be to use the
C++20 standard's concept feature.
\begin{snip}{cpp}
template<typename T>
struct mempty;
template<typename T>
T mappend(T, T) = delete;
template<typename M>
concept Monoid = requires (M m) {
{ mempty<M>::value() } -> std::same_as<M>;
{ mappend(m, m) } -> std::same_as<M>;
};
\end{snip}
The first definition is a structure meant to hold the neutral element for each
specialization.
The keyword \code{delete} means that there is no default value
defined: It will have to be specified on a case-by-case basis.
Similarly, there is no default for \code{mappend}.
The concept \code{Monoid} tests whether there exist appropriate definitions of
\code{mempty} and \code{mappend} for a given type \code{M}.
An instantiation of the Monoid concept can be accomplished by providing
appropriate specializations and overloads:
\begin{snip}{cpp}
template<>
struct mempty<std::string> {
static std::string value() { return ""; }
};
template<>
std::string mappend(std::string s1, std::string s2) {
return s1 + s2;
}
\end{snip}
\section{Monoid as Category}
That was the ``familiar'' definition of the monoid in terms of elements
of a set. But as you know, in category theory we try to get away from
sets and their elements, and instead talk about objects and morphisms.
So let's change our perspective a bit and think of the application of
the binary operator as ``moving'' or ``shifting'' things around the set.
For instance, there is the operation of adding 5 to every natural
number. It maps 0 to 5, 1 to 6, 2 to 7, and so on. That's a function
defined on the set of natural numbers. That's good: we have a function
and a set. In general, for any number n there is a function of adding $n$
--- the ``adder'' of $n$.
How do adders compose? The composition of the function that adds 5 with
the function that adds 7 is a function that adds 12. So the composition
of adders can be made equivalent to the rules of addition. That's good
too: we can replace addition with function composition.
But wait, there's more: There is also the adder for the neutral element,
zero. Adding zero doesn't move things around, so it's the identity
function in the set of natural numbers.
Instead of giving you the traditional rules of addition, I could as well
give you the rules of composing adders, without any loss of information.
Notice that the composition of adders is associative, because the
composition of functions is associative; and we have the zero adder
corresponding to the identity function.
An astute reader might have noticed that the mapping from integers to
adders follows from the second interpretation of the type signature of
\code{mappend} as \code{m -> (m -> m)}. It
tells us that \code{mappend} maps an element of a monoid set to a
function acting on that set.
Now I want you to forget that you are dealing with the set of natural
numbers and just think of it as a single object, a blob with a bunch of
morphisms --- the adders. A monoid is a single object category. In fact
the name monoid comes from Greek \emph{mono}, which means single. Every
monoid can be described as a single object category with a set of
morphisms that follow appropriate rules of composition.
\begin{figure}[H]
\centering
\includegraphics[width=0.35\textwidth]{images/monoid.jpg}
\end{figure}
\noindent
String concatenation is an interesting case, because we have a choice of
defining right appenders and left appenders (or \emph{prependers}, if
you will). The composition tables of the two models are a mirror reverse
of each other. You can easily convince yourself that appending ``bar''
after ``foo'' corresponds to prepending ``foo'' after prepending
``bar''.
You might ask the question whether every categorical monoid --- a
one-object category --- defines a unique set-with-binary-operator
monoid. It turns out that we can always extract a set from a
single-object category. This set is the set of morphisms --- the adders
in our example. In other words, we have the hom-set $\cat{M}(m, m)$ of the
single object $m$ in the category $\cat{M}$. We can easily define a binary
operator in this set: The monoidal product of two set-elements is the
element corresponding to the composition of the corresponding morphisms.
If you give me two elements of $\cat{M}(m, m)$ corresponding to $f$ and
$g$, their product will correspond to the composition
$f \circ g$. The composition always exists, because the source and the
target for these morphisms are the same object. And it's associative by
the rules of category. The identity morphism is the neutral element of
this product. So we can always recover a set monoid from a category
monoid. For all intents and purposes they are one and the same.
\begin{figure}[H]
\centering
\includegraphics[width=0.4\textwidth]{images/monoidhomset.jpg}
\caption{Monoid hom-set seen as morphisms and as points in a set.}
\end{figure}
\noindent
There is just one little nit for mathematicians to pick: morphisms don't
have to form a set. In the world of categories there are things larger
than sets. A category in which morphisms between any two objects form a
set is called locally small. As promised, I will be mostly ignoring such
subtleties, but I thought I should mention them for the record.
A lot of interesting phenomena in category theory have their root in the
fact that elements of a hom-set can be seen both as morphisms, which
follow the rules of composition, and as points in a set. Here,
composition of morphisms in $\cat{M}$ translates into monoidal product in the
set $\cat{M}(m, m)$.
\section{Challenges}
\begin{enumerate}
\tightlist
\item
Generate a free category from:
\begin{enumerate}
\tightlist
\item
A graph with one node and no edges
\item
A graph with one node and one (directed) edge (hint: this edge can
be composed with itself)
\item
A graph with two nodes and a single arrow between them
\item
A graph with a single node and 26 arrows marked with the letters of
the alphabet: a, b, c \ldots{} z.
\end{enumerate}
\item
What kind of order is this?
\begin{enumerate}
\tightlist
\item
A set of sets with the inclusion relation: $A$ is included in $B$ if
every element of $A$ is also an element of $B$.
\item
C++ types with the following subtyping relation: \code{T1} is a subtype of
\code{T2} if a pointer to \code{T1} can be passed to a function that expects a
pointer to \code{T2} without triggering a compilation error.
\end{enumerate}
\item
Considering that \code{Bool} is a set of two values \code{True} and \code{False}, show that
it forms two (set-theoretical) monoids with respect to, respectively,
operator \code{\&\&} (AND) and \code{||} (OR).
\item
Represent the \code{Bool} monoid with the AND operator as a category: List
the morphisms and their rules of composition.
\item
Represent addition modulo 3 as a monoid category.
\end{enumerate}
================================================
FILE: src/content/1.3/code/haskell/snippet01.hs
================================================
class Monoid m where
mempty :: m
mappend :: m -> m -> m
================================================
FILE: src/content/1.3/code/haskell/snippet02.hs
================================================
instance Monoid String where
mempty = ""
mappend = (++)
================================================
FILE: src/content/1.3/code/ocaml/snippet01.ml
================================================
module type Monoid = sig
type a
val mempty : a
val mappend : a -> a -> a
end
================================================
FILE: src/content/1.3/code/ocaml/snippet02.ml
================================================
module StringMonoid : Monoid = struct
type a = string
let mempty = ""
let mappend = ( ^ )
end
================================================
FILE: src/content/1.3/code/reason/snippet01.re
================================================
module type Monoid = {
type a;
let mempty: a;
let mappend: (a, a) => a;
};
================================================
FILE: src/content/1.3/code/reason/snippet02.re
================================================
module StringMonoid: Monoid = {
type a = string;
let mempty = "";
let mappend = (++);
};
================================================
FILE: src/content/1.3/code/scala/snippet01.scala
================================================
trait Monoid[M] {
def combine(m1: M, m2: M): M
def empty: M
}
================================================
FILE: src/content/1.3/code/scala/snippet02.scala
================================================
object Monoid {
implicit val stringMonoid: Monoid[String] = new Monoid[String] {
def combine(m1: String, m2: String): String = m1 + m2
def empty: String = ""
}
}
================================================
FILE: src/content/1.4/code/haskell/snippet01.hs
================================================
type Writer a = (a, String)
================================================
FILE: src/content/1.4/code/haskell/snippet02.hs
================================================
a -> Writer b
================================================
FILE: src/content/1.4/code/haskell/snippet03.hs
================================================
(>=>) :: (a -> Writer b) -> (b -> Writer c) -> (a -> Writer c)
================================================
FILE: src/content/1.4/code/haskell/snippet04.hs
================================================
m1 >=> m2 = \x ->
let (y, s1) = m1 x
(z, s2) = m2 y
in (z, s1 ++ s2)
================================================
FILE: src/content/1.4/code/haskell/snippet05.hs
================================================
return :: a -> Writer a
return x = (x, "")
================================================
FILE: src/content/1.4/code/haskell/snippet06.hs
================================================
upCase :: String -> Writer String
upCase s = (map toUpper s, "upCase ")
toWords :: String -> Writer [String]
toWords s = (words s, "toWords ")
================================================
FILE: src/content/1.4/code/haskell/snippet07.hs
================================================
process :: String -> Writer [String]
process = upCase >=> toWords
================================================
FILE: src/content/1.4/code/ocaml/snippet01.ml
================================================
type 'a writer = 'a * string
================================================
FILE: src/content/1.4/code/ocaml/snippet02.ml
================================================
'a -> 'b writer
================================================
FILE: src/content/1.4/code/ocaml/snippet03.ml
================================================
module type Kleisli = sig
type a
type b
type c
val ( >=> ) : (a -> b writer) -> (b -> c writer) -> a -> c writer
end
================================================
FILE: src/content/1.4/code/ocaml/snippet04.ml
================================================
let pure x = x, ""
================================================
FILE: src/content/1.4/code/ocaml/snippet05.ml
================================================
let up_case : string -> string writer =
fun s -> String.uppercase s, "up_case "
;;
================================================
FILE: src/content/1.4/code/ocaml/snippet06.ml
================================================
let to_words : string -> string list writer =
fun s -> String.split s ~on:' ', "to_words "
;;
================================================
FILE: src/content/1.4/code/ocaml/snippet07.ml
================================================
module KleisiExample
(K : Kleisli
with type a = string
and type b = string
and type c = string list) =
struct
let up_case_and_to_words : string -> string list writer =
K.( >=> ) up_case to_words
;;
end
================================================
FILE: src/content/1.4/code/reason/snippet01.re
================================================
type writer('a) = ('a, string);
================================================
FILE: src/content/1.4/code/reason/snippet02.re
================================================
'a => writer('b)
====
gitextract_i87k79kx/
├── .editorconfig
├── .envrc
├── .github/
│ ├── settings.yml
│ └── workflows/
│ ├── nix-flake-check.yaml
│ ├── nix-fmt-checks.yaml
│ ├── prettier-checks.yaml
│ └── release.yaml
├── .gitignore
├── .latexindent.yaml
├── .prettierignore
├── .prettierrc
├── LICENSE
├── Makefile
├── README.md
├── errata-1.0.0.md
├── errata-1.3.0.md
├── errata-scala.md
├── flake.nix
└── src/
├── acknowledgments.tex
├── category.tex
├── colophon.tex
├── content/
│ ├── 0.0/
│ │ └── preface.tex
│ ├── 1.1/
│ │ ├── category-the-essence-of-composition.tex
│ │ └── code/
│ │ ├── haskell/
│ │ │ ├── snippet01.hs
│ │ │ ├── snippet02.hs
│ │ │ ├── snippet03.hs
│ │ │ ├── snippet04.hs
│ │ │ ├── snippet05.hs
│ │ │ └── snippet06.hs
│ │ ├── ocaml/
│ │ │ ├── snippet01.ml
│ │ │ ├── snippet02.ml
│ │ │ ├── snippet03.ml
│ │ │ ├── snippet04.ml
│ │ │ ├── snippet05.ml
│ │ │ └── snippet06.ml
│ │ ├── reason/
│ │ │ ├── snippet01.re
│ │ │ ├── snippet02.re
│ │ │ ├── snippet03.re
│ │ │ ├── snippet04.re
│ │ │ ├── snippet05.re
│ │ │ └── snippet06.re
│ │ └── scala/
│ │ ├── snippet01.scala
│ │ ├── snippet02.scala
│ │ ├── snippet03.scala
│ │ ├── snippet04.scala
│ │ ├── snippet05.scala
│ │ └── snippet06.scala
│ ├── 1.10/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ ├── snippet25.hs
│ │ │ │ ├── snippet26.hs
│ │ │ │ └── snippet27.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ ├── snippet25.ml
│ │ │ │ ├── snippet26.ml
│ │ │ │ └── snippet27.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ ├── snippet25.re
│ │ │ │ ├── snippet26.re
│ │ │ │ └── snippet27.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ ├── snippet25.scala
│ │ │ ├── snippet26.scala
│ │ │ └── snippet27.scala
│ │ └── natural-transformations.tex
│ ├── 1.2/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ └── snippet11.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet010.ml
│ │ │ │ ├── snippet011.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ └── snippet11.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet010.re
│ │ │ │ ├── snippet011.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ └── snippet11.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ └── snippet11.scala
│ │ └── types-and-functions.tex
│ ├── 1.3/
│ │ ├── categories-great-and-small.tex
│ │ └── code/
│ │ ├── haskell/
│ │ │ ├── snippet01.hs
│ │ │ └── snippet02.hs
│ │ ├── ocaml/
│ │ │ ├── snippet01.ml
│ │ │ └── snippet02.ml
│ │ ├── reason/
│ │ │ ├── snippet01.re
│ │ │ └── snippet02.re
│ │ └── scala/
│ │ ├── snippet01.scala
│ │ └── snippet02.scala
│ ├── 1.4/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ └── snippet07.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ └── snippet07.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ └── snippet07.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ └── snippet07.scala
│ │ └── kleisli-categories.tex
│ ├── 1.5/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ ├── snippet25.hs
│ │ │ │ ├── snippet26.hs
│ │ │ │ ├── snippet27.hs
│ │ │ │ └── snippet28.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ ├── snippet25.ml
│ │ │ │ ├── snippet26.ml
│ │ │ │ ├── snippet27.ml
│ │ │ │ └── snippet28.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ ├── snippet25.re
│ │ │ │ ├── snippet26.re
│ │ │ │ ├── snippet27.re
│ │ │ │ └── snippet28.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ ├── snippet25.scala
│ │ │ ├── snippet26.scala
│ │ │ ├── snippet27.scala
│ │ │ └── snippet28.scala
│ │ └── products-and-coproducts.tex
│ ├── 1.6/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ ├── snippet25.hs
│ │ │ │ ├── snippet26.hs
│ │ │ │ ├── snippet27.hs
│ │ │ │ ├── snippet28.hs
│ │ │ │ ├── snippet29.hs
│ │ │ │ ├── snippet30.hs
│ │ │ │ ├── snippet31.hs
│ │ │ │ ├── snippet32.hs
│ │ │ │ ├── snippet33.hs
│ │ │ │ ├── snippet34.hs
│ │ │ │ ├── snippet35.hs
│ │ │ │ ├── snippet36.hs
│ │ │ │ └── snippet37.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ ├── snippet25.ml
│ │ │ │ ├── snippet26.ml
│ │ │ │ ├── snippet27.ml
│ │ │ │ ├── snippet28.ml
│ │ │ │ ├── snippet29.ml
│ │ │ │ ├── snippet30.ml
│ │ │ │ ├── snippet31.ml
│ │ │ │ ├── snippet32.ml
│ │ │ │ ├── snippet33.ml
│ │ │ │ ├── snippet34.ml
│ │ │ │ ├── snippet35.ml
│ │ │ │ ├── snippet36.ml
│ │ │ │ └── snippet37.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ ├── snippet25.re
│ │ │ │ ├── snippet26.re
│ │ │ │ ├── snippet27.re
│ │ │ │ ├── snippet28.re
│ │ │ │ ├── snippet29.re
│ │ │ │ ├── snippet30.re
│ │ │ │ ├── snippet31.re
│ │ │ │ ├── snippet32.re
│ │ │ │ ├── snippet33.re
│ │ │ │ ├── snippet34.re
│ │ │ │ ├── snippet35.re
│ │ │ │ ├── snippet36.re
│ │ │ │ └── snippet37.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ ├── snippet25.scala
│ │ │ ├── snippet26.scala
│ │ │ ├── snippet27.scala
│ │ │ ├── snippet28.scala
│ │ │ ├── snippet29.scala
│ │ │ ├── snippet30.scala
│ │ │ ├── snippet31.scala
│ │ │ ├── snippet32.scala
│ │ │ ├── snippet33.scala
│ │ │ ├── snippet34.scala
│ │ │ ├── snippet35.scala
│ │ │ ├── snippet36.scala
│ │ │ └── snippet37.scala
│ │ └── simple-algebraic-data-types.tex
│ ├── 1.7/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ ├── snippet25.hs
│ │ │ │ ├── snippet26.hs
│ │ │ │ ├── snippet27.hs
│ │ │ │ ├── snippet28.hs
│ │ │ │ ├── snippet29.hs
│ │ │ │ ├── snippet30.hs
│ │ │ │ ├── snippet31.hs
│ │ │ │ ├── snippet32.hs
│ │ │ │ ├── snippet33.hs
│ │ │ │ ├── snippet34.hs
│ │ │ │ └── snippet35.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ ├── snippet25.ml
│ │ │ │ ├── snippet26.ml
│ │ │ │ ├── snippet27.ml
│ │ │ │ ├── snippet28.ml
│ │ │ │ ├── snippet29.ml
│ │ │ │ ├── snippet30.ml
│ │ │ │ ├── snippet31.ml
│ │ │ │ ├── snippet32.ml
│ │ │ │ ├── snippet33.ml
│ │ │ │ ├── snippet34.ml
│ │ │ │ └── snippet35.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ ├── snippet25.re
│ │ │ │ ├── snippet26.re
│ │ │ │ ├── snippet27.re
│ │ │ │ ├── snippet28.re
│ │ │ │ ├── snippet29.re
│ │ │ │ ├── snippet30.re
│ │ │ │ ├── snippet31.re
│ │ │ │ ├── snippet32.re
│ │ │ │ ├── snippet33.re
│ │ │ │ ├── snippet34.re
│ │ │ │ └── snippet35.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ ├── snippet25.scala
│ │ │ ├── snippet26.scala
│ │ │ ├── snippet27.scala
│ │ │ ├── snippet28.scala
│ │ │ ├── snippet29.scala
│ │ │ ├── snippet30.scala
│ │ │ ├── snippet31.scala
│ │ │ ├── snippet32.scala
│ │ │ ├── snippet33.scala
│ │ │ ├── snippet34.scala
│ │ │ └── snippet35.scala
│ │ └── functors.tex
│ ├── 1.8/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ ├── snippet25.hs
│ │ │ │ ├── snippet26.hs
│ │ │ │ ├── snippet27.hs
│ │ │ │ ├── snippet28.hs
│ │ │ │ ├── snippet29.hs
│ │ │ │ └── snippet30.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ ├── snippet25.ml
│ │ │ │ ├── snippet26.ml
│ │ │ │ ├── snippet27.ml
│ │ │ │ ├── snippet28.ml
│ │ │ │ ├── snippet29.ml
│ │ │ │ └── snippet30.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ ├── snippet25.re
│ │ │ │ ├── snippet26.re
│ │ │ │ ├── snippet27.re
│ │ │ │ ├── snippet28.re
│ │ │ │ ├── snippet29.re
│ │ │ │ └── snippet30.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ ├── snippet25.scala
│ │ │ ├── snippet26.scala
│ │ │ ├── snippet27.scala
│ │ │ ├── snippet28.scala
│ │ │ ├── snippet29.scala
│ │ │ └── snippet30.scala
│ │ └── functoriality.tex
│ ├── 1.9/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ └── snippet15.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ └── snippet15.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ └── snippet15.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ └── snippet15.scala
│ │ └── function-types.tex
│ ├── 2.1/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ └── snippet02.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ └── snippet02.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ └── snippet02.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ └── snippet02.scala
│ │ └── declarative-programming.tex
│ ├── 2.2/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ └── snippet19.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ └── snippet19.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ └── snippet19.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ └── snippet19.scala
│ │ └── limits-and-colimits.tex
│ ├── 2.3/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ └── snippet10.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ └── snippet10.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ └── snippet10.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ └── snippet10.scala
│ │ └── free-monoids.tex
│ ├── 2.4/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ └── snippet15.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ └── snippet15.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ └── snippet15.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ └── snippet15.scala
│ │ └── representable-functors.tex
│ ├── 2.5/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ └── snippet07.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ └── snippet07.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ └── snippet07.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ └── snippet07.scala
│ │ └── the-yoneda-lemma.tex
│ ├── 2.6/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ └── snippet02.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ └── snippet02.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ └── snippet02.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ └── snippet02.scala
│ │ └── yoneda-embedding.tex
│ ├── 3.1/
│ │ └── its-all-about-morphisms.tex
│ ├── 3.10/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ └── snippet18.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ └── snippet18.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ └── snippet18.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ └── snippet18.scala
│ │ └── ends-and-coends.tex
│ ├── 3.11/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ └── snippet12.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ └── snippet12.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ └── snippet12.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ └── snippet12.scala
│ │ └── kan-extensions.tex
│ ├── 3.12/
│ │ └── enriched-categories.tex
│ ├── 3.13/
│ │ └── topoi.tex
│ ├── 3.14/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ └── snippet04.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ └── snippet04.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ └── snippet04.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ └── snippet04.scala
│ │ └── lawvere-theories.tex
│ ├── 3.15/
│ │ └── monads-monoids-and-categories.tex
│ ├── 3.2/
│ │ ├── adjunctions.tex
│ │ └── code/
│ │ ├── haskell/
│ │ │ ├── snippet01.hs
│ │ │ ├── snippet02.hs
│ │ │ ├── snippet03.hs
│ │ │ ├── snippet04.hs
│ │ │ ├── snippet05.hs
│ │ │ ├── snippet06.hs
│ │ │ ├── snippet07.hs
│ │ │ ├── snippet08.hs
│ │ │ └── snippet09.hs
│ │ ├── ocaml/
│ │ │ ├── snippet01.ml
│ │ │ ├── snippet02.ml
│ │ │ ├── snippet03.ml
│ │ │ ├── snippet04.ml
│ │ │ ├── snippet05.ml
│ │ │ ├── snippet06.ml
│ │ │ ├── snippet07.ml
│ │ │ ├── snippet08.ml
│ │ │ └── snippet09.ml
│ │ ├── reason/
│ │ │ ├── snippet01.re
│ │ │ ├── snippet02.re
│ │ │ ├── snippet03.re
│ │ │ ├── snippet04.re
│ │ │ ├── snippet05.re
│ │ │ ├── snippet06.re
│ │ │ ├── snippet07.re
│ │ │ ├── snippet08.re
│ │ │ └── snippet09.re
│ │ └── scala/
│ │ ├── snippet01.scala
│ │ ├── snippet02.scala
│ │ ├── snippet03.scala
│ │ ├── snippet04.scala
│ │ ├── snippet05.scala
│ │ ├── snippet06.scala
│ │ ├── snippet07.scala
│ │ ├── snippet08.scala
│ │ └── snippet09.scala
│ ├── 3.3/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ └── snippet02.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ └── snippet02.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ └── snippet02.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ └── snippet02.scala
│ │ └── free-forgetful-adjunctions.tex
│ ├── 3.4/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ └── snippet25.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ └── snippet25.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ └── snippet25.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ └── snippet25.scala
│ │ └── monads-programmers-definition.tex
│ ├── 3.5/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ ├── snippet25.hs
│ │ │ │ ├── snippet26.hs
│ │ │ │ ├── snippet27.hs
│ │ │ │ ├── snippet28.hs
│ │ │ │ ├── snippet29.hs
│ │ │ │ ├── snippet30.hs
│ │ │ │ ├── snippet31.hs
│ │ │ │ ├── snippet32.hs
│ │ │ │ ├── snippet33.hs
│ │ │ │ ├── snippet34.hs
│ │ │ │ ├── snippet35.hs
│ │ │ │ ├── snippet36.hs
│ │ │ │ └── snippet37.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ ├── snippet25.ml
│ │ │ │ ├── snippet26.ml
│ │ │ │ ├── snippet27.ml
│ │ │ │ ├── snippet28.ml
│ │ │ │ ├── snippet29.ml
│ │ │ │ ├── snippet30.ml
│ │ │ │ ├── snippet31.ml
│ │ │ │ ├── snippet32.ml
│ │ │ │ ├── snippet33.ml
│ │ │ │ ├── snippet34.ml
│ │ │ │ ├── snippet35.ml
│ │ │ │ ├── snippet36.ml
│ │ │ │ └── snippet37.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ ├── snippet25.re
│ │ │ │ ├── snippet26.re
│ │ │ │ ├── snippet27.re
│ │ │ │ ├── snippet28.re
│ │ │ │ ├── snippet29.re
│ │ │ │ ├── snippet30.re
│ │ │ │ ├── snippet31.re
│ │ │ │ ├── snippet32.re
│ │ │ │ ├── snippet33.re
│ │ │ │ ├── snippet34.re
│ │ │ │ ├── snippet35.re
│ │ │ │ ├── snippet36.re
│ │ │ │ └── snippet37.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ ├── snippet25.scala
│ │ │ ├── snippet26.scala
│ │ │ ├── snippet27.scala
│ │ │ ├── snippet28.scala
│ │ │ ├── snippet29.scala
│ │ │ ├── snippet30.scala
│ │ │ ├── snippet31.scala
│ │ │ ├── snippet32.scala
│ │ │ ├── snippet33.scala
│ │ │ ├── snippet34.scala
│ │ │ ├── snippet35.scala
│ │ │ ├── snippet36.scala
│ │ │ └── snippet37.scala
│ │ └── monads-and-effects.tex
│ ├── 3.6/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ └── snippet25.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ └── snippet25.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ └── snippet25.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ └── snippet25.scala
│ │ └── monads-categorically.tex
│ ├── 3.7/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ ├── snippet25.hs
│ │ │ │ ├── snippet26.hs
│ │ │ │ ├── snippet27.hs
│ │ │ │ ├── snippet28.hs
│ │ │ │ ├── snippet29.hs
│ │ │ │ ├── snippet30.hs
│ │ │ │ ├── snippet31.hs
│ │ │ │ ├── snippet32.hs
│ │ │ │ ├── snippet33.hs
│ │ │ │ ├── snippet34.hs
│ │ │ │ ├── snippet35.hs
│ │ │ │ ├── snippet36.hs
│ │ │ │ ├── snippet37.hs
│ │ │ │ ├── snippet38.hs
│ │ │ │ └── snippet39.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ ├── snippet25.ml
│ │ │ │ ├── snippet26.ml
│ │ │ │ ├── snippet27.ml
│ │ │ │ ├── snippet28.ml
│ │ │ │ ├── snippet29.ml
│ │ │ │ ├── snippet30.ml
│ │ │ │ ├── snippet31.ml
│ │ │ │ ├── snippet32.ml
│ │ │ │ ├── snippet33.ml
│ │ │ │ ├── snippet34.ml
│ │ │ │ ├── snippet35.ml
│ │ │ │ ├── snippet36.ml
│ │ │ │ ├── snippet37.ml
│ │ │ │ ├── snippet38.ml
│ │ │ │ └── snippet39.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ ├── snippet25.re
│ │ │ │ ├── snippet26.re
│ │ │ │ ├── snippet27.re
│ │ │ │ ├── snippet28.re
│ │ │ │ ├── snippet29.re
│ │ │ │ ├── snippet30.re
│ │ │ │ ├── snippet31.re
│ │ │ │ ├── snippet32.re
│ │ │ │ ├── snippet33.re
│ │ │ │ ├── snippet34.re
│ │ │ │ ├── snippet35.re
│ │ │ │ ├── snippet36.re
│ │ │ │ ├── snippet37.re
│ │ │ │ ├── snippet38.re
│ │ │ │ └── snippet39.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ ├── snippet25.scala
│ │ │ ├── snippet26.scala
│ │ │ ├── snippet27.scala
│ │ │ ├── snippet28.scala
│ │ │ ├── snippet29.scala
│ │ │ ├── snippet30.scala
│ │ │ ├── snippet31.scala
│ │ │ ├── snippet32.scala
│ │ │ ├── snippet33.scala
│ │ │ ├── snippet34.scala
│ │ │ ├── snippet35.scala
│ │ │ ├── snippet36.scala
│ │ │ ├── snippet37.scala
│ │ │ ├── snippet38.scala
│ │ │ └── snippet39.scala
│ │ └── comonads.tex
│ ├── 3.8/
│ │ ├── code/
│ │ │ ├── haskell/
│ │ │ │ ├── snippet01.hs
│ │ │ │ ├── snippet02.hs
│ │ │ │ ├── snippet03.hs
│ │ │ │ ├── snippet04.hs
│ │ │ │ ├── snippet05.hs
│ │ │ │ ├── snippet06.hs
│ │ │ │ ├── snippet07.hs
│ │ │ │ ├── snippet08.hs
│ │ │ │ ├── snippet09.hs
│ │ │ │ ├── snippet10.hs
│ │ │ │ ├── snippet11.hs
│ │ │ │ ├── snippet12.hs
│ │ │ │ ├── snippet13.hs
│ │ │ │ ├── snippet14.hs
│ │ │ │ ├── snippet15.hs
│ │ │ │ ├── snippet16.hs
│ │ │ │ ├── snippet17.hs
│ │ │ │ ├── snippet18.hs
│ │ │ │ ├── snippet19.hs
│ │ │ │ ├── snippet20.hs
│ │ │ │ ├── snippet21.hs
│ │ │ │ ├── snippet22.hs
│ │ │ │ ├── snippet23.hs
│ │ │ │ ├── snippet24.hs
│ │ │ │ ├── snippet25.hs
│ │ │ │ ├── snippet26.hs
│ │ │ │ ├── snippet27.hs
│ │ │ │ ├── snippet28.hs
│ │ │ │ ├── snippet29.hs
│ │ │ │ ├── snippet30.hs
│ │ │ │ ├── snippet31.hs
│ │ │ │ ├── snippet32.hs
│ │ │ │ ├── snippet33.hs
│ │ │ │ ├── snippet34.hs
│ │ │ │ └── snippet35.hs
│ │ │ ├── ocaml/
│ │ │ │ ├── snippet01.ml
│ │ │ │ ├── snippet02.ml
│ │ │ │ ├── snippet03.ml
│ │ │ │ ├── snippet04.ml
│ │ │ │ ├── snippet05.ml
│ │ │ │ ├── snippet06.ml
│ │ │ │ ├── snippet07.ml
│ │ │ │ ├── snippet08.ml
│ │ │ │ ├── snippet09.ml
│ │ │ │ ├── snippet10.ml
│ │ │ │ ├── snippet11.ml
│ │ │ │ ├── snippet12.ml
│ │ │ │ ├── snippet13.ml
│ │ │ │ ├── snippet14.ml
│ │ │ │ ├── snippet15.ml
│ │ │ │ ├── snippet16.ml
│ │ │ │ ├── snippet17.ml
│ │ │ │ ├── snippet18.ml
│ │ │ │ ├── snippet19.ml
│ │ │ │ ├── snippet20.ml
│ │ │ │ ├── snippet21.ml
│ │ │ │ ├── snippet22.ml
│ │ │ │ ├── snippet23.ml
│ │ │ │ ├── snippet24.ml
│ │ │ │ ├── snippet25.ml
│ │ │ │ ├── snippet26.ml
│ │ │ │ ├── snippet27.ml
│ │ │ │ ├── snippet28.ml
│ │ │ │ ├── snippet29.ml
│ │ │ │ ├── snippet30.ml
│ │ │ │ ├── snippet31.ml
│ │ │ │ ├── snippet32.ml
│ │ │ │ ├── snippet33.ml
│ │ │ │ ├── snippet34.ml
│ │ │ │ └── snippet35.ml
│ │ │ ├── reason/
│ │ │ │ ├── snippet01.re
│ │ │ │ ├── snippet02.re
│ │ │ │ ├── snippet03.re
│ │ │ │ ├── snippet04.re
│ │ │ │ ├── snippet05.re
│ │ │ │ ├── snippet06.re
│ │ │ │ ├── snippet07.re
│ │ │ │ ├── snippet08.re
│ │ │ │ ├── snippet09.re
│ │ │ │ ├── snippet10.re
│ │ │ │ ├── snippet11.re
│ │ │ │ ├── snippet12.re
│ │ │ │ ├── snippet13.re
│ │ │ │ ├── snippet14.re
│ │ │ │ ├── snippet15.re
│ │ │ │ ├── snippet16.re
│ │ │ │ ├── snippet17.re
│ │ │ │ ├── snippet18.re
│ │ │ │ ├── snippet19.re
│ │ │ │ ├── snippet20.re
│ │ │ │ ├── snippet21.re
│ │ │ │ ├── snippet22.re
│ │ │ │ ├── snippet23.re
│ │ │ │ ├── snippet24.re
│ │ │ │ ├── snippet25.re
│ │ │ │ ├── snippet26.re
│ │ │ │ ├── snippet27.re
│ │ │ │ ├── snippet28.re
│ │ │ │ ├── snippet29.re
│ │ │ │ ├── snippet30.re
│ │ │ │ ├── snippet31.re
│ │ │ │ ├── snippet32.re
│ │ │ │ ├── snippet33.re
│ │ │ │ ├── snippet34.re
│ │ │ │ └── snippet35.re
│ │ │ └── scala/
│ │ │ ├── snippet01.scala
│ │ │ ├── snippet02.scala
│ │ │ ├── snippet03.scala
│ │ │ ├── snippet04.scala
│ │ │ ├── snippet05.scala
│ │ │ ├── snippet06.scala
│ │ │ ├── snippet07.scala
│ │ │ ├── snippet08.scala
│ │ │ ├── snippet09.scala
│ │ │ ├── snippet10.scala
│ │ │ ├── snippet11.scala
│ │ │ ├── snippet12.scala
│ │ │ ├── snippet13.scala
│ │ │ ├── snippet14.scala
│ │ │ ├── snippet15.scala
│ │ │ ├── snippet16.scala
│ │ │ ├── snippet17.scala
│ │ │ ├── snippet18.scala
│ │ │ ├── snippet19.scala
│ │ │ ├── snippet20.scala
│ │ │ ├── snippet21.scala
│ │ │ ├── snippet22.scala
│ │ │ ├── snippet23.scala
│ │ │ ├── snippet24.scala
│ │ │ ├── snippet25.scala
│ │ │ ├── snippet26.scala
│ │ │ ├── snippet27.scala
│ │ │ ├── snippet28.scala
│ │ │ ├── snippet29.scala
│ │ │ ├── snippet30.scala
│ │ │ ├── snippet31.scala
│ │ │ ├── snippet32.scala
│ │ │ ├── snippet33.scala
│ │ │ ├── snippet34.scala
│ │ │ └── snippet35.scala
│ │ └── f-algebras.tex
│ ├── 3.9/
│ │ ├── algebras-for-monads.tex
│ │ └── code/
│ │ ├── haskell/
│ │ │ ├── snippet01.hs
│ │ │ ├── snippet02.hs
│ │ │ ├── snippet03.hs
│ │ │ ├── snippet04.hs
│ │ │ ├── snippet05.hs
│ │ │ ├── snippet06.hs
│ │ │ ├── snippet07.hs
│ │ │ ├── snippet08.hs
│ │ │ ├── snippet09.hs
│ │ │ ├── snippet10.hs
│ │ │ ├── snippet11.hs
│ │ │ ├── snippet12.hs
│ │ │ ├── snippet13.hs
│ │ │ ├── snippet14.hs
│ │ │ ├── snippet15.hs
│ │ │ └── snippet16.hs
│ │ ├── ocaml/
│ │ │ ├── snippet01.ml
│ │ │ ├── snippet02.ml
│ │ │ ├── snippet03.ml
│ │ │ ├── snippet04.ml
│ │ │ ├── snippet05.ml
│ │ │ ├── snippet06.ml
│ │ │ ├── snippet07.ml
│ │ │ ├── snippet08.ml
│ │ │ ├── snippet09.ml
│ │ │ ├── snippet10.ml
│ │ │ ├── snippet11.ml
│ │ │ ├── snippet12.ml
│ │ │ ├── snippet13.ml
│ │ │ ├── snippet14.ml
│ │ │ ├── snippet15.ml
│ │ │ └── snippet16.ml
│ │ ├── reason/
│ │ │ ├── snippet01.re
│ │ │ ├── snippet02.re
│ │ │ ├── snippet03.re
│ │ │ ├── snippet04.re
│ │ │ ├── snippet05.re
│ │ │ ├── snippet06.re
│ │ │ ├── snippet07.re
│ │ │ ├── snippet08.re
│ │ │ ├── snippet09.re
│ │ │ ├── snippet10.re
│ │ │ ├── snippet11.re
│ │ │ ├── snippet12.re
│ │ │ ├── snippet13.re
│ │ │ ├── snippet14.re
│ │ │ ├── snippet15.re
│ │ │ └── snippet16.re
│ │ └── scala/
│ │ ├── snippet01.scala
│ │ ├── snippet02.scala
│ │ ├── snippet03.scala
│ │ ├── snippet04.scala
│ │ ├── snippet05.scala
│ │ ├── snippet06.scala
│ │ ├── snippet07.scala
│ │ ├── snippet08.scala
│ │ ├── snippet09.scala
│ │ ├── snippet10.scala
│ │ ├── snippet11.scala
│ │ ├── snippet12.scala
│ │ ├── snippet13.scala
│ │ ├── snippet14.scala
│ │ ├── snippet15.scala
│ │ └── snippet16.scala
│ ├── editor-note.tex
│ ├── ocaml/
│ │ ├── colophon.tex
│ │ └── editor-note.tex
│ ├── reason/
│ │ ├── colophon.tex
│ │ └── editor-note.tex
│ └── scala/
│ ├── colophon.tex
│ └── editor-note.tex
├── cover/
│ ├── blurb.tex
│ ├── cover-hardcover-ocaml.tex
│ ├── cover-hardcover-reason.tex
│ ├── cover-hardcover-scala.tex
│ ├── cover-hardcover.tex
│ ├── cover-paperback-ocaml.tex
│ ├── cover-paperback-reason.tex
│ ├── cover-paperback-scala.tex
│ ├── cover-paperback.tex
│ ├── ribbon-ocaml.tex
│ ├── ribbon-reason.tex
│ └── ribbon-scala.tex
├── ctfp-print-ocaml.tex
├── ctfp-print-reason.tex
├── ctfp-print-scala.tex
├── ctfp-print.tex
├── ctfp-reader-ocaml.tex
├── ctfp-reader-reason.tex
├── ctfp-reader-scala.tex
├── ctfp-reader.tex
├── ctfp.tex
├── free-software.tex
├── half-title.tex
├── index.tex
├── opt-ocaml.tex
├── opt-print-ustrade.tex
├── opt-reader-10in.tex
├── opt-reason.tex
├── opt-scala.tex
├── postamble.tex
├── preamble.tex
├── scraper.py
└── version.tex
SYMBOL INDEX (5 symbols across 1 files) FILE: src/scraper.py function get_toc (line 14) | def get_toc(): function get_content (line 30) | def get_content(html): function write_content (line 35) | def write_content(file, content): function save_images (line 39) | def save_images(markup, path): function save_url (line 53) | def save_url(chapter, title, url):
Condensed preview — 1997 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,061K chars).
[
{
"path": ".editorconfig",
"chars": 316,
"preview": "root = true\n\n[*]\nindent_size = 4\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace ="
},
{
"path": ".envrc",
"chars": 49,
"preview": "use flake .\nuse flake github:loophp/nix-prettier\n"
},
{
"path": ".github/settings.yml",
"chars": 1274,
"preview": "# https://github.com/probot/settings\n\nbranches:\n - name: master\n protection:\n enforce_admins: false\n "
},
{
"path": ".github/workflows/nix-flake-check.yaml",
"chars": 1727,
"preview": "name: Check and build\n\non:\n pull_request:\n push:\n branches:\n - master\njobs:\n dependencies:\n "
},
{
"path": ".github/workflows/nix-fmt-checks.yaml",
"chars": 368,
"preview": "name: Nix formatter checks\n\non:\n pull_request:\n\njobs:\n format-check:\n runs-on: ubuntu-latest\n steps:"
},
{
"path": ".github/workflows/prettier-checks.yaml",
"chars": 383,
"preview": "name: Prettier checks\n\non:\n pull_request:\n\njobs:\n prettier:\n runs-on: ubuntu-latest\n\n steps:\n "
},
{
"path": ".github/workflows/release.yaml",
"chars": 3049,
"preview": "name: Release PDFs\n\non:\n push:\n tags:\n - \"**\"\n\njobs:\n determine-matrix:\n name: Figure out"
},
{
"path": ".gitignore",
"chars": 216,
"preview": "/.vscode/\n/.direnv/\n/build/\n/result\n.DS_Store\nsrc/.dotty-ide-disabled\nsrc/.metals\nout/\n_minted*\n*.fls\nout/*\n*.fdb_latexm"
},
{
"path": ".latexindent.yaml",
"chars": 115,
"preview": "defaultIndent: \" \"\nverbatimEnvironments:\n verbatim: 1\n lstlisting: 1\n minted: 1\n snip: 1\n snipv: 1\n"
},
{
"path": ".prettierignore",
"chars": 66,
"preview": "/.direnv/\n/.idea/\n/vendor/\n/docs/\n/build/\nCHANGELOG.md\nflake.lock\n"
},
{
"path": ".prettierrc",
"chars": 30,
"preview": "{\n \"proseWrap\": \"always\"\n}\n"
},
{
"path": "LICENSE",
"chars": 35451,
"preview": "\n LICENSING TERMS\n \n 1. Output PDF, *.tex, all files in content/* and fig/* are licensed\n under Creative Commons A"
},
{
"path": "Makefile",
"chars": 1105,
"preview": "OUTPUT ?= $(shell basename \"$(shell dirname \"$(INPUT)\")\")\nOUTPUT_DIRECTORY = $(shell pwd)/build\nLATEXMK_ARGS ?= -f -file"
},
{
"path": "README.md",
"chars": 4779,
"preview": "![GitHub stars][github stars]\n[![GitHub Workflow Status][github workflow status]][github actions link]\n[![Download][down"
},
{
"path": "errata-1.0.0.md",
"chars": 3615,
"preview": "## A list of typos/mistakes that were fixed after the initial printed book release.\n\n### Preface\n\n- [#155](https://githu"
},
{
"path": "errata-1.3.0.md",
"chars": 950,
"preview": "## A list of typos/mistakes that were fixed after the release of the new edition (1.3.0) (12 August, 2019).\n\n(see errata"
},
{
"path": "errata-scala.md",
"chars": 610,
"preview": "## A list of typos/mistakes that were fixed after the initial printed book release (12 August, 2019).\n\n### 7. Functors\n\n"
},
{
"path": "flake.nix",
"chars": 5335,
"preview": "{\n description = \"Category Theory for Programmers\";\n\n inputs.nixpkgs.url = \"github:NixOS/nixpkgs/nixpkgs-unstable\";\n "
},
{
"path": "src/acknowledgments.tex",
"chars": 654,
"preview": "\\noindent\nI’d like to thank Edward Kmett and Gershom Bazerman for checking my math\nand logic. I'm grateful to many volun"
},
{
"path": "src/category.tex",
"chars": 384,
"preview": "\\newcommand{\\cat}{%\n \\symbf%\n}\n\\newcommand{\\idarrow}[1][]{%\n \\mathbf{id}_{#1}%\n}\n\\newcommand{\\Lim}[1][]{%\n \\mathbf{Li"
},
{
"path": "src/colophon.tex",
"chars": 1158,
"preview": "\\lettrine[lraise=-0.03,loversize=0.08]{T}{his book} was compiled by \\urlref{https://hmemcpy.com}{Igal Tabachnik}, by con"
},
{
"path": "src/content/0.0/preface.tex",
"chars": 9091,
"preview": "% !TEX root = ../../ctfp-print.tex\n\n\\begin{quote}\n For some time now I've been floating the idea of writing a book abou"
},
{
"path": "src/content/1.1/category-the-essence-of-composition.tex",
"chars": 12390,
"preview": "% !TEX root = ../../ctfp-print.tex\n\n\\lettrine[lhang=0.17]{A}{category} is an embarrassingly simple concept.\nA category c"
},
{
"path": "src/content/1.1/code/haskell/snippet01.hs",
"chars": 11,
"preview": "f :: A -> B"
},
{
"path": "src/content/1.1/code/haskell/snippet02.hs",
"chars": 11,
"preview": "g :: B -> C"
},
{
"path": "src/content/1.1/code/haskell/snippet03.hs",
"chars": 5,
"preview": "g . f"
},
{
"path": "src/content/1.1/code/haskell/snippet04.hs",
"chars": 75,
"preview": "f :: A -> B\ng :: B -> C\nh :: C -> D\nh . (g . f) == (h . g) . f == h . g . f"
},
{
"path": "src/content/1.1/code/haskell/snippet05.hs",
"chars": 21,
"preview": "id :: a -> a\nid x = x"
},
{
"path": "src/content/1.1/code/haskell/snippet06.hs",
"chars": 23,
"preview": "f . id == f\nid . f == f"
},
{
"path": "src/content/1.1/code/ocaml/snippet01.ml",
"chars": 81,
"preview": "module type Polymorphic_Function_F = sig\n type a\n type b\n\n val f : a -> b\nend\n"
},
{
"path": "src/content/1.1/code/ocaml/snippet02.ml",
"chars": 81,
"preview": "module type Polymorphic_Function_G = sig\n type b\n type c\n\n val g : b -> c\nend\n"
},
{
"path": "src/content/1.1/code/ocaml/snippet03.ml",
"chars": 255,
"preview": "module Compose_Example\n (F : Polymorphic_Function_F)\n (G : Polymorphic_Function_G with type b = F.b) =\nstruct\n (*"
},
{
"path": "src/content/1.1/code/ocaml/snippet04.ml",
"chars": 472,
"preview": "module Compose_Three_GF = functor(F:Polymorphic_Function_F)(G:Polymorphic_Function_G with type b = F.b)(H:Polymorphic_Fu"
},
{
"path": "src/content/1.1/code/ocaml/snippet05.ml",
"chars": 13,
"preview": "let id x = x\n"
},
{
"path": "src/content/1.1/code/ocaml/snippet06.ml",
"chars": 16,
"preview": "f >> id\nid >> f\n"
},
{
"path": "src/content/1.1/code/reason/snippet01.re",
"chars": 82,
"preview": "module type Polymorphic_Function_F = {\n type a;\n type b;\n \n let f: a => b;\n};\n"
},
{
"path": "src/content/1.1/code/reason/snippet02.re",
"chars": 82,
"preview": "module type Polymorphic_Function_G = {\n type b;\n type c;\n \n let g: b => c;\n};\n"
},
{
"path": "src/content/1.1/code/reason/snippet03.re",
"chars": 297,
"preview": "module Compose_Example =\n (\n F: Polymorphic_Function_F,\n G: Polymorphic_Function_G with type b = F"
},
{
"path": "src/content/1.1/code/reason/snippet04.re",
"chars": 538,
"preview": "module Compose_Three_GF =\n (\n F: Polymorphic_Function_F,\n G: Polymorphic_Function_G with type b = "
},
{
"path": "src/content/1.1/code/reason/snippet05.re",
"chars": 17,
"preview": "let id = x => x;\n"
},
{
"path": "src/content/1.1/code/reason/snippet06.re",
"chars": 18,
"preview": "f >> id;\nid >> f;\n"
},
{
"path": "src/content/1.1/code/scala/snippet01.scala",
"chars": 13,
"preview": "val f: A => B"
},
{
"path": "src/content/1.1/code/scala/snippet02.scala",
"chars": 13,
"preview": "val g: B => C"
},
{
"path": "src/content/1.1/code/scala/snippet03.scala",
"chars": 11,
"preview": "g compose f"
},
{
"path": "src/content/1.1/code/scala/snippet04.scala",
"chars": 120,
"preview": "val f: A => B\nval g: B => C\nval h: C => D\n\nh compose (g compose f) === (h compose g) compose f === h compose g compose f"
},
{
"path": "src/content/1.1/code/scala/snippet05.scala",
"chars": 28,
"preview": "def identity[A](a: A): A = a"
},
{
"path": "src/content/1.1/code/scala/snippet06.scala",
"chars": 55,
"preview": "f compose identity[A] == f\nidentity[B] _ compose f == f"
},
{
"path": "src/content/1.10/code/haskell/snippet01.hs",
"chars": 30,
"preview": "alpha :: forall a . F a -> G a"
},
{
"path": "src/content/1.10/code/haskell/snippet02.hs",
"chars": 19,
"preview": "alpha :: F a -> G a"
},
{
"path": "src/content/1.10/code/haskell/snippet03.hs",
"chars": 19,
"preview": "alpha :: F a -> G a"
},
{
"path": "src/content/1.10/code/haskell/snippet04.hs",
"chars": 73,
"preview": "safeHead :: [a] -> Maybe a\nsafeHead [] = Nothing\nsafeHead (x:xs) = Just x"
},
{
"path": "src/content/1.10/code/haskell/snippet05.hs",
"chars": 37,
"preview": "fmap f . safeHead = safeHead . fmap f"
},
{
"path": "src/content/1.10/code/haskell/snippet06.hs",
"chars": 47,
"preview": "fmap f (safeHead []) = fmap f Nothing = Nothing"
},
{
"path": "src/content/1.10/code/haskell/snippet07.hs",
"chars": 44,
"preview": "safeHead (fmap f []) = safeHead [] = Nothing"
},
{
"path": "src/content/1.10/code/haskell/snippet08.hs",
"chars": 55,
"preview": "fmap f (safeHead (x:xs)) = fmap f (Just x) = Just (f x)"
},
{
"path": "src/content/1.10/code/haskell/snippet09.hs",
"chars": 66,
"preview": "safeHead (fmap f (x:xs)) = safeHead (f x : fmap f xs) = Just (f x)"
},
{
"path": "src/content/1.10/code/haskell/snippet10.hs",
"chars": 46,
"preview": "fmap f [] = []\nfmap f (x:xs) = f x : fmap f xs"
},
{
"path": "src/content/1.10/code/haskell/snippet11.hs",
"chars": 53,
"preview": "fmap f Nothing = Nothing\nfmap f (Just x) = Just (f x)"
},
{
"path": "src/content/1.10/code/haskell/snippet12.hs",
"chars": 96,
"preview": "length :: [a] -> Const Int a\nlength [] = Const 0\nlength (x:xs) = Const (1 + unConst (length xs))"
},
{
"path": "src/content/1.10/code/haskell/snippet13.hs",
"chars": 47,
"preview": "unConst :: Const c a -> c\nunConst (Const x) = x"
},
{
"path": "src/content/1.10/code/haskell/snippet14.hs",
"chars": 20,
"preview": "length :: [a] -> Int"
},
{
"path": "src/content/1.10/code/haskell/snippet15.hs",
"chars": 55,
"preview": "scam :: Const Int a -> Maybe a\nscam (Const x) = Nothing"
},
{
"path": "src/content/1.10/code/haskell/snippet16.hs",
"chars": 36,
"preview": "newtype Reader e a = Reader (e -> a)"
},
{
"path": "src/content/1.10/code/haskell/snippet17.hs",
"chars": 80,
"preview": "instance Functor (Reader e) where\n fmap f (Reader g) = Reader (\\x -> f (g x))"
},
{
"path": "src/content/1.10/code/haskell/snippet18.hs",
"chars": 31,
"preview": "alpha :: Reader () a -> Maybe a"
},
{
"path": "src/content/1.10/code/haskell/snippet19.hs",
"chars": 25,
"preview": "dumb (Reader _) = Nothing"
},
{
"path": "src/content/1.10/code/haskell/snippet20.hs",
"chars": 32,
"preview": "obvious (Reader g) = Just (g ())"
},
{
"path": "src/content/1.10/code/haskell/snippet21.hs",
"chars": 28,
"preview": "newtype Op r a = Op (a -> r)"
},
{
"path": "src/content/1.10/code/haskell/snippet22.hs",
"chars": 71,
"preview": "instance Contravariant (Op r) where\n contramap f (Op g) = Op (g . f)"
},
{
"path": "src/content/1.10/code/haskell/snippet23.hs",
"chars": 54,
"preview": "predToStr (Op f) = Op (\\x -> if f x then \"T\" else \"F\")"
},
{
"path": "src/content/1.10/code/haskell/snippet24.hs",
"chars": 49,
"preview": "contramap f . predToStr = predToStr . contramap f"
},
{
"path": "src/content/1.10/code/haskell/snippet25.hs",
"chars": 49,
"preview": "contramap :: (b -> a) -> (Op Bool a -> Op Bool b)"
},
{
"path": "src/content/1.10/code/haskell/snippet26.hs",
"chars": 6,
"preview": "a -> a"
},
{
"path": "src/content/1.10/code/haskell/snippet27.hs",
"chars": 15,
"preview": "(a -> a) -> f a"
},
{
"path": "src/content/1.10/code/ocaml/snippet01.ml",
"chars": 30,
"preview": "val alpha : 'a . 'a f -> 'a g\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet02.ml",
"chars": 25,
"preview": "val alpha : 'a f -> 'a g\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet03.ml",
"chars": 25,
"preview": "val alpha : 'a f -> 'a g\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet04.ml",
"chars": 65,
"preview": "let safe_head = function\n | [] -> None\n | x :: xs -> Some x\n;;\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet05.ml",
"chars": 56,
"preview": "compose (fmap f) safe_head = compose safe_head (fmap f)\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet06.ml",
"chars": 78,
"preview": "(* Starting with empty list *)\nlet fmap f (safe_head []) = fmap f None = None\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet07.ml",
"chars": 48,
"preview": "let safe_head (fmap f []) = safe_head [] = None\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet08.ml",
"chars": 63,
"preview": "let fmap f (safe_head (x :: xs)) = fmap f (Some x)= Some (f x)\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet09.ml",
"chars": 72,
"preview": "let safe_head (fmap f (x :: xs)) = safe_head (f x :: f xs) = Some (f x)\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet10.ml",
"chars": 74,
"preview": "let rec fmap f = function\n | [] -> []\n | x :: xs -> f x :: fmap f xs\n;;\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet11.ml",
"chars": 71,
"preview": "let rec fmap f = function\n | None -> None\n | Some x -> Some (f x)\n;;\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet12.ml",
"chars": 270,
"preview": "(** OCaml requires mutually recursive functions to be defined together *)\nlet rec length : 'a list -> (int, 'a) const = "
},
{
"path": "src/content/1.10/code/ocaml/snippet13.ml",
"chars": 74,
"preview": "let un_const : 'c 'a. ('c, 'a) const -> 'c = function\n | Const c -> c\n;;\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet14.ml",
"chars": 28,
"preview": "val length : 'a list -> int\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet15.ml",
"chars": 79,
"preview": "let scam : 'a. ('int, 'a) const -> 'a option = function\n | Const a -> None\n;;\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet16.ml",
"chars": 44,
"preview": "type ('e, 'a) reader = Reader of ('e -> 'a)\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet17.ml",
"chars": 213,
"preview": "module Reader_Functor (T : sig\n type e\nend) : Functor = struct\n type 'a t = (T.e, 'a) reader\n\n let fmap : 'a 'b. ('a "
},
{
"path": "src/content/1.10/code/ocaml/snippet18.ml",
"chars": 43,
"preview": "val alpha : (unit, 'a) reader -> 'a option\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet19.ml",
"chars": 81,
"preview": "let dumb : 'a. (unit, 'a) reader -> 'a option = function\n | Reader _ -> None\n;;\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet20.ml",
"chars": 91,
"preview": "let obvious : 'a. (unit, 'a) reader -> 'a option = function\n | Reader f -> Some (f ())\n;;\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet21.ml",
"chars": 36,
"preview": "type ('r, 'a) op = Op of ('a -> 'r)\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet22.ml",
"chars": 207,
"preview": "module Op_Contravariant (T : sig\n type r\nend) : Contravariant = struct\n type 'a t = (T.r, 'a) op\n\n let contramap : ('"
},
{
"path": "src/content/1.10/code/ocaml/snippet23.ml",
"chars": 81,
"preview": "let pred_to_str = function\n | Op f -> Op (fun x -> if f x then \"T\" else \"F\")\n;;\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet24.ml",
"chars": 70,
"preview": "compose (contramap f) pred_to_str = compose pred_to_str (contramap f)\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet25.ml",
"chars": 156,
"preview": "module Op_Bool = Op_Contravariant (struct\n type r = bool\nend)\n\nlet op_bool_contramap : ('b -> 'a) -> 'a Op_Bool.t -> 'b"
},
{
"path": "src/content/1.10/code/ocaml/snippet26.ml",
"chars": 9,
"preview": "'a -> 'a\n"
},
{
"path": "src/content/1.10/code/ocaml/snippet27.ml",
"chars": 19,
"preview": "('a -> 'a) -> 'a f\n"
},
{
"path": "src/content/1.10/code/reason/snippet01.re",
"chars": 31,
"preview": "let alpha: 'a . f('a) => g('a);"
},
{
"path": "src/content/1.10/code/reason/snippet02.re",
"chars": 27,
"preview": "let alpha: f('a) => g('a);\n"
},
{
"path": "src/content/1.10/code/reason/snippet03.re",
"chars": 27,
"preview": "let alpha: f('a) => g('a);\n"
},
{
"path": "src/content/1.10/code/reason/snippet04.re",
"chars": 64,
"preview": "let safe_head =\n fun\n | [] => None\n | [x, ...xs] => Some(x);\n"
},
{
"path": "src/content/1.10/code/reason/snippet05.re",
"chars": 60,
"preview": "compose(fmap(f), safe_head) == compose(safe_head, fmap(f));\n"
},
{
"path": "src/content/1.10/code/reason/snippet06.re",
"chars": 86,
"preview": "/* Starting with empty list */\nlet fmap = (f, safe_head([])) == fmap(f, None) == None;"
},
{
"path": "src/content/1.10/code/reason/snippet07.re",
"chars": 55,
"preview": "let safe_head = (fmap, f, []) == safe_head([]) == None;"
},
{
"path": "src/content/1.10/code/reason/snippet08.re",
"chars": 73,
"preview": "let fmap = (f, safe_head([x, ... xs])) == fmap(f(Some(x))) == Some(f(x));"
},
{
"path": "src/content/1.10/code/reason/snippet09.re",
"chars": 81,
"preview": "let safe_head = \n (fmap([x, ...xs])) == safe_head(f([x, ... xs])) == Some(f(x));"
},
{
"path": "src/content/1.10/code/reason/snippet10.re",
"chars": 81,
"preview": "let rec fmap = f =>\n fun\n | [] => []\n | [x, ...xs] => [f(x), ...fmap(f, xs)];\n"
},
{
"path": "src/content/1.10/code/reason/snippet11.re",
"chars": 70,
"preview": "let rec fmap = f =>\n fun\n | None => None\n | Some(x) => Some(f(x));\n"
},
{
"path": "src/content/1.10/code/reason/snippet12.re",
"chars": 303,
"preview": "/* ReasonML requires mutually recursive functions \n* to be defined together */\nlet rec length: list('a) => const(int, 'a"
},
{
"path": "src/content/1.10/code/reason/snippet13.re",
"chars": 68,
"preview": "let un_const: 'c 'a. const('c, 'a) => 'c =\n fun\n | Const(c) => c;\n"
},
{
"path": "src/content/1.10/code/reason/snippet14.re",
"chars": 29,
"preview": "let length: list('a) => int;\n"
},
{
"path": "src/content/1.10/code/reason/snippet15.re",
"chars": 74,
"preview": "let scam: 'a. const('int, 'a) => option('a) =\n fun\n | Const(a) => None;\n"
},
{
"path": "src/content/1.10/code/reason/snippet16.re",
"chars": 44,
"preview": "type reader('e, 'a) =\n | Reader('e => 'a);\n"
},
{
"path": "src/content/1.10/code/reason/snippet17.re",
"chars": 203,
"preview": "module Reader_Functor = (T: {type e;}) : Functor => {\n type t('a) = reader(T.e, 'a);\n \n let fmap: 'a 'b. ('a => 'b, t"
},
{
"path": "src/content/1.10/code/reason/snippet18.re",
"chars": 43,
"preview": "let alpha: reader(unit, 'a) => option('a);\n"
},
{
"path": "src/content/1.10/code/reason/snippet19.re",
"chars": 76,
"preview": "let dumb: 'a. reader(unit, 'a) => option('a) =\n fun\n | Reader(_) => None;\n"
},
{
"path": "src/content/1.10/code/reason/snippet20.re",
"chars": 84,
"preview": "let obvious: 'a. reader(unit, 'a) => option('a) =\n fun\n | Reader(f) => Some(f());\n"
},
{
"path": "src/content/1.10/code/reason/snippet21.re",
"chars": 36,
"preview": "type op('r, 'a) =\n | Op('a => 'r);\n"
},
{
"path": "src/content/1.10/code/reason/snippet22.re",
"chars": 195,
"preview": "module Op_Contravariant = (T: {type r;}) : Contravariant => {\n type t('a) = op(T.r, 'a);\n\n let contramap: ('b => 'a, t"
},
{
"path": "src/content/1.10/code/reason/snippet23.re",
"chars": 66,
"preview": "let pred_to_str =\n fun\n | Op(f) => Op(x => (f(x)) ? \"T\" : \"F\");\n"
},
{
"path": "src/content/1.10/code/reason/snippet24.re",
"chars": 74,
"preview": "compose(contramap(f), pred_to_str) == compose(pred_to_str, contramap(f));\n"
},
{
"path": "src/content/1.10/code/reason/snippet25.re",
"chars": 191,
"preview": "module Op_Bool = Op_Contravariant({type r = bool;});\n\nlet op_bool_contramap: ('b => 'a, Op_Bool.t('a)) => Op_Bool.t('b) "
},
{
"path": "src/content/1.10/code/reason/snippet26.re",
"chars": 8,
"preview": "'a => 'a"
},
{
"path": "src/content/1.10/code/reason/snippet27.re",
"chars": 20,
"preview": "('a => 'a) => f('a)\n"
},
{
"path": "src/content/1.10/code/scala/snippet01.scala",
"chars": 26,
"preview": "def alpha[A]: F[A] => G[A]"
},
{
"path": "src/content/1.10/code/scala/snippet02.scala",
"chars": 23,
"preview": "val alpha: F[A] => G[A]"
},
{
"path": "src/content/1.10/code/scala/snippet03.scala",
"chars": 23,
"preview": "val alpha: F[A] => G[A]"
},
{
"path": "src/content/1.10/code/scala/snippet04.scala",
"chars": 88,
"preview": "def safeHead[A]: List[A] => Option[A] = {\n case Nil => None\n case x :: xs => Some(x)\n}"
},
{
"path": "src/content/1.10/code/scala/snippet05.scala",
"chars": 56,
"preview": "(fmap(f) compose safeHead) == (safeHead compose fmap(f))"
},
{
"path": "src/content/1.10/code/scala/snippet06.scala",
"chars": 54,
"preview": "fmap(f)(safeHead(List.empty)) == fmap(f)(None) == None"
},
{
"path": "src/content/1.10/code/scala/snippet07.scala",
"chars": 61,
"preview": "safeHead(fmap(f)(List.empty)) == safeHead(List.empty) == None"
},
{
"path": "src/content/1.10/code/scala/snippet08.scala",
"chars": 60,
"preview": "fmap(f)(safeHead(x :: xs)) == fmap(f)(Some(x)) == Some(f(x))"
},
{
"path": "src/content/1.10/code/scala/snippet09.scala",
"chars": 73,
"preview": "safeHead(fmap(f)(x :: xs)) == safeHead(f(1) :: fmap(f)(xs)) == Some(f(x))"
},
{
"path": "src/content/1.10/code/scala/snippet10.scala",
"chars": 107,
"preview": "def fmap[A, B](f: A => B): List[A] => List[B] = {\n case Nil => Nil\n case x :: xs => f(x) :: fmap(f)(xs)\n}"
},
{
"path": "src/content/1.10/code/scala/snippet11.scala",
"chars": 104,
"preview": "def fmap[A, B](f: A => B): Option[A] => Option[B] = {\n case None => None\n case Some(x) => Some(f(x))\n}"
},
{
"path": "src/content/1.10/code/scala/snippet12.scala",
"chars": 117,
"preview": "def length[A]: List[A] => Const[Int, A] = {\n case Nil => Const(0)\n case x :: xs => Const(1 + unConst(length(xs)))\n}"
},
{
"path": "src/content/1.10/code/scala/snippet13.scala",
"chars": 62,
"preview": "def unConst[C, A]: Const[C, A] => C = {\n case Const(x) => x\n}"
},
{
"path": "src/content/1.10/code/scala/snippet14.scala",
"chars": 29,
"preview": "def length[A]: List[A] => Int"
},
{
"path": "src/content/1.10/code/scala/snippet15.scala",
"chars": 69,
"preview": "def scam[A]: Const[Int, A] => Option[A] = {\n case Const(x) => None\n}"
},
{
"path": "src/content/1.10/code/scala/snippet16.scala",
"chars": 36,
"preview": "case class Reader[E, A](run: E => A)"
},
{
"path": "src/content/1.10/code/scala/snippet17.scala",
"chars": 151,
"preview": "implicit def readerFunctor[E] = new Functor[Reader[E, ?]] {\n def fmap[A, B](f: A => B)(g: Reader[E, A]): Reader[E, B] ="
},
{
"path": "src/content/1.10/code/scala/snippet18.scala",
"chars": 42,
"preview": "def alpha[A]: Reader[Unit, A] => Option[A]"
},
{
"path": "src/content/1.10/code/scala/snippet19.scala",
"chars": 72,
"preview": "def dumb[A]: Reader[Unit, A] => Option[A] = {\n case Reader(_) => None\n}"
},
{
"path": "src/content/1.10/code/scala/snippet20.scala",
"chars": 80,
"preview": "def obvious[A]: Reader[Unit, A] => Option[A] = {\n case Reader(g) => Some(g())\n}"
},
{
"path": "src/content/1.10/code/scala/snippet21.scala",
"chars": 30,
"preview": "case class Op[R, A](f: A => R)"
},
{
"path": "src/content/1.10/code/scala/snippet22.scala",
"chars": 162,
"preview": "implicit def opContravariant[R] = new Contravariant[Op[R, ?]] {\n def contramap[A, B](f: B => A): Op[R, A] => Op[R, B] ="
},
{
"path": "src/content/1.10/code/scala/snippet23.scala",
"chars": 103,
"preview": "def predToStr[A]: Op[Boolean, A] => Op[String, A] = {\n case Op(f) => Op(x => if (f(x)) \"T\" else \"F\")\n}"
},
{
"path": "src/content/1.10/code/scala/snippet24.scala",
"chars": 80,
"preview": "(op.contramap(func) compose predToStr) == (predToStr compose op.contramap(func))"
},
{
"path": "src/content/1.10/code/scala/snippet25.scala",
"chars": 102,
"preview": "def contramap[A, B](f: B => A): Op[Boolean, A] => Op[Boolean, B] = {\n case Op(g) => Op(g compose f)\n}"
},
{
"path": "src/content/1.10/code/scala/snippet26.scala",
"chars": 6,
"preview": "A => A"
},
{
"path": "src/content/1.10/code/scala/snippet27.scala",
"chars": 16,
"preview": "(A => A) => F[A]"
},
{
"path": "src/content/1.10/natural-transformations.tex",
"chars": 30341,
"preview": "% !TEX root = ../../ctfp-print.tex\n\n\\lettrine[lhang=0.17]{W}{e talked about} functors as mappings between categories tha"
},
{
"path": "src/content/1.2/code/haskell/snippet01.hs",
"chars": 12,
"preview": "x :: Integer"
},
{
"path": "src/content/1.2/code/haskell/snippet02.hs",
"chars": 17,
"preview": "f :: Bool -> Bool"
},
{
"path": "src/content/1.2/code/haskell/snippet03.hs",
"chars": 33,
"preview": "f :: Bool -> Bool\nf x = undefined"
},
{
"path": "src/content/1.2/code/haskell/snippet04.hs",
"chars": 31,
"preview": "f :: Bool -> Bool\nf = undefined"
},
{
"path": "src/content/1.2/code/haskell/snippet05.hs",
"chars": 23,
"preview": "fact n = product [1..n]"
},
{
"path": "src/content/1.2/code/haskell/snippet06.hs",
"chars": 19,
"preview": "absurd :: Void -> a"
},
{
"path": "src/content/1.2/code/haskell/snippet07.hs",
"chars": 32,
"preview": "f44 :: () -> Integer\nf44 () = 44"
},
{
"path": "src/content/1.2/code/haskell/snippet08.hs",
"chars": 33,
"preview": "fInt :: Integer -> ()\nfInt x = ()"
},
{
"path": "src/content/1.2/code/haskell/snippet09.hs",
"chars": 33,
"preview": "fInt :: Integer -> ()\nfInt _ = ()"
},
{
"path": "src/content/1.2/code/haskell/snippet10.hs",
"chars": 27,
"preview": "unit :: a -> ()\nunit _ = ()"
},
{
"path": "src/content/1.2/code/haskell/snippet11.hs",
"chars": 24,
"preview": "data Bool = True | False"
},
{
"path": "src/content/1.2/code/ocaml/snippet01.ml",
"chars": 61,
"preview": "module type Chapter2_DeclareVariable = sig\n val x : int\nend\n"
},
{
"path": "src/content/1.2/code/ocaml/snippet010.ml",
"chars": 16,
"preview": "let unit _ = ()\n"
},
{
"path": "src/content/1.2/code/ocaml/snippet011.ml",
"chars": 31,
"preview": "type bool =\n | false\n | true\n"
},
{
"path": "src/content/1.2/code/ocaml/snippet02.ml",
"chars": 70,
"preview": "module type Chapter2_DeclareFunction = sig\n val f : bool -> bool\nend\n"
},
{
"path": "src/content/1.2/code/ocaml/snippet03.ml",
"chars": 118,
"preview": "module Chapter2_Bottom : Chapter2_DeclareFunction = struct\n let f (b : bool) : bool = failwith \"Not Implemented\"\nend\n"
},
{
"path": "src/content/1.2/code/ocaml/snippet04.ml",
"chars": 124,
"preview": "module Chapter2_Bottom : Chapter2_DeclareFunction = struct\n let f : bool -> bool = fun _ -> failwith \"Not implemented\"\n"
},
{
"path": "src/content/1.2/code/ocaml/snippet05.ml",
"chars": 57,
"preview": "let fact n = List.fold (List.range 1 n) ~init:1 ~f:( * )\n"
},
{
"path": "src/content/1.2/code/ocaml/snippet06.ml",
"chars": 48,
"preview": "type void\n\nlet rec absurd (x : void) = absurd x\n"
},
{
"path": "src/content/1.2/code/ocaml/snippet07.ml",
"chars": 22,
"preview": "let f44 () : int = 44\n"
},
{
"path": "src/content/1.2/code/ocaml/snippet08.ml",
"chars": 25,
"preview": "let f_int (x : int) = ()\n"
},
{
"path": "src/content/1.2/code/ocaml/snippet09.ml",
"chars": 25,
"preview": "let f_int (_ : int) = ()\n"
},
{
"path": "src/content/1.2/code/ocaml/snippet10.ml",
"chars": 16,
"preview": "let unit _ = ()\n"
},
{
"path": "src/content/1.2/code/ocaml/snippet11.ml",
"chars": 31,
"preview": "type bool =\n | false\n | true\n"
},
{
"path": "src/content/1.2/code/reason/snippet01.re",
"chars": 54,
"preview": "module type Chapter2_DeclareVariable = {let x: int;};\n"
},
{
"path": "src/content/1.2/code/reason/snippet010.re",
"chars": 20,
"preview": "let unit = _ => ();\n"
},
{
"path": "src/content/1.2/code/reason/snippet011.re",
"chars": 32,
"preview": "type bool =\n | false\n | true;\n"
},
{
"path": "src/content/1.2/code/reason/snippet02.re",
"chars": 63,
"preview": "module type Chapter2_DeclareFunction = {let f: bool => bool;};\n"
},
{
"path": "src/content/1.2/code/reason/snippet03.re",
"chars": 114,
"preview": "module Chapter2_Bottom: Chapter2_DeclareFunction = {\n let f = (b: bool): bool => failwith(\"Not Implemented\");\n};\n"
},
{
"path": "src/content/1.2/code/reason/snippet04.re",
"chars": 113,
"preview": "module Chapter2_Bottom: Chapter2_DeclareFunction = {\n let f: bool => bool = _ => failwith(\"Not implemented\");\n};"
},
{
"path": "src/content/1.2/code/reason/snippet05.re",
"chars": 64,
"preview": "let fact = n => List.fold(List.range(1, n), ~init=1, ~f=( * ));\n"
},
{
"path": "src/content/1.2/code/reason/snippet06.re",
"chars": 53,
"preview": "type void;\n\nlet rec absurd = (x: void) => absurd(x);\n"
},
{
"path": "src/content/1.2/code/reason/snippet07.re",
"chars": 25,
"preview": "let f44 = (): int => 44;\n"
},
{
"path": "src/content/1.2/code/reason/snippet08.re",
"chars": 28,
"preview": "let f_int = (x: int) => ();\n"
},
{
"path": "src/content/1.2/code/reason/snippet09.re",
"chars": 28,
"preview": "let f_int = (_: int) => ();\n"
},
{
"path": "src/content/1.2/code/reason/snippet10.re",
"chars": 20,
"preview": "let unit = _ => ();\n"
},
{
"path": "src/content/1.2/code/reason/snippet11.re",
"chars": 32,
"preview": "type bool =\n | false\n | true;\n"
},
{
"path": "src/content/1.2/code/scala/snippet01.scala",
"chars": 13,
"preview": "val x: BigInt"
},
{
"path": "src/content/1.2/code/scala/snippet02.scala",
"chars": 25,
"preview": "val f: Boolean => Boolean"
},
{
"path": "src/content/1.2/code/scala/snippet03.scala",
"chars": 36,
"preview": "val f: Boolean => Boolean = x => ???"
},
{
"path": "src/content/1.2/code/scala/snippet04.scala",
"chars": 31,
"preview": "def f: Boolean => Boolean = ???"
},
{
"path": "src/content/1.2/code/scala/snippet05.scala",
"chars": 39,
"preview": "val fact = (n: Int) => (1 to n).product"
},
{
"path": "src/content/1.2/code/scala/snippet06.scala",
"chars": 27,
"preview": "def absurd[A]: Nothing => A"
},
{
"path": "src/content/1.2/code/scala/snippet07.scala",
"chars": 33,
"preview": "val f44: Unit => BigInt = _ => 44"
}
]
// ... and 1797 more files (download for full content)
About this extraction
This page contains the full source code of the hmemcpy/milewski-ctfp-pdf GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 1997 files (868.3 KB), approximately 326.6k tokens, and a symbol index with 5 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.