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. 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. Copyright (C) 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 . 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: Copyright (C) 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 . 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 . ================================================ 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 .#` 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 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 G alpha(F); \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 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 struct mempty; template T mappend(T, T) = delete; template concept Monoid = requires (M m) { { mempty::value() } -> std::same_as; { mappend(m, m) } -> std::same_as; }; \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 { 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) ================================================ FILE: src/content/1.4/code/reason/snippet03.re ================================================ module type Kleisli = { type a; type b; type c; let (>=>): (a => writer(b), b => writer(c), a) => writer(c); }; ================================================ FILE: src/content/1.4/code/reason/snippet04.re ================================================ let pure = x => (x, ""); ================================================ FILE: src/content/1.4/code/reason/snippet05.re ================================================ let up_case: string => writer(string) = s => (String.uppercase(s), "up_case "); ================================================ FILE: src/content/1.4/code/reason/snippet06.re ================================================ let to_words: string => writer(list(string)) = ( s => (String.split(s, ~on=' '), "to_words "): string => writer(list(string)) ); ================================================ FILE: src/content/1.4/code/reason/snippet07.re ================================================ module KleisiExample = (K: Kleisli with type a = string and type b = string and type c = list(string) ) => { let up_case_and_to_words: string => writer(list(string)) = ( K.(>=>)(up_case, to_words): string => writer(list(string)) ); }; ================================================ FILE: src/content/1.4/code/scala/snippet01.scala ================================================ type Writer[A] = (A, String) ================================================ FILE: src/content/1.4/code/scala/snippet02.scala ================================================ A => Writer[B] ================================================ FILE: src/content/1.4/code/scala/snippet03.scala ================================================ def >=>[A, B, C](m1: A => Writer[B], m2: B => Writer[C]): A => Writer[C] ================================================ FILE: src/content/1.4/code/scala/snippet04.scala ================================================ object kleisli { //allows us to use >=> as an infix operator implicit class KleisliOps[A, B](m1: A => Writer[B]) { def >=>[C](m2: B => Writer[C]): A => Writer[C] = x => { val (y, s1) = m1(x) val (z, s2) = m2(y) (z, s1 + s2) } } } ================================================ FILE: src/content/1.4/code/scala/snippet05.scala ================================================ def pure[A](x: A): Writer[A] = (x, "") ================================================ FILE: src/content/1.4/code/scala/snippet06.scala ================================================ val upCase: String => Writer[String] = s => (s.toUpperCase, "upCase ") val toWords: String => Writer[List[String]] = s => (s.split(' ').toList, "toWords ") ================================================ FILE: src/content/1.4/code/scala/snippet07.scala ================================================ val process: String => Writer[List[String]] = { import kleisli._ upCase >=> toWords } ================================================ FILE: src/content/1.4/kleisli-categories.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{Y}{ou've seen how to model} types and pure functions as a category. I also mentioned that there is a way to model side effects, or non-pure functions, in category theory. Let's have a look at one such example: functions that log or trace their execution. Something that, in an imperative language, would likely be implemented by mutating some global state, as in: \begin{snip}{cpp} string logger; bool negate(bool b) { logger += "Not so! "; return !b; } \end{snip} You know that this is not a pure function, because its memoized version would fail to produce a log. This function has \newterm{side effects}. In modern programming, we try to stay away from global mutable state as much as possible --- if only because of the complications of concurrency. And you would never put code like this in a library. Fortunately for us, it's possible to make this function pure. You just have to pass the log explicitly, in and out. Let's do that by adding a string argument, and pairing regular output with a string that contains the updated log: \begin{snip}{cpp} pair negate(bool b, string logger) { return make_pair(!b, logger + "Not so! "); } \end{snip} This function is pure, it has no side effects, it returns the same pair every time it's called with the same arguments, and it can be memoized if necessary. However, considering the cumulative nature of the log, you'd have to memoize all possible histories that can lead to a given call. There would be a separate memo entry for: \begin{snip}{cpp} negate(true, "It was the best of times. "); \end{snip} and \begin{snip}{cpp} negate(true, "It was the worst of times. "); \end{snip} and so on. It's also not a very good interface for a library function. The callers are free to ignore the string in the return type, so that's not a huge burden; but they are forced to pass a string as input, which might be inconvenient. Is there a way to do the same thing less intrusively? Is there a way to separate concerns? In this simple example, the main purpose of the function \code{negate} is to turn one Boolean into another. The logging is secondary. Granted, the message that is logged is specific to the function, but the task of aggregating the messages into one continuous log is a separate concern. We still want the function to produce a string, but we'd like to unburden it from producing a log. So here's the compromise solution: \begin{snip}{cpp} pair negate(bool b) { return make_pair(!b, "Not so! "); } \end{snip} The idea is that the log will be aggregated \emph{between} function calls. To see how this can be done, let's switch to a slightly more realistic example. We have one function from string to string that turns lower case characters to upper case: \begin{snip}{cpp} string toUpper(string s) { string result; int (*toupperp)(int) = &toupper; // toupper is overloaded transform(begin(s), end(s), back_inserter(result), toupperp); return result; } \end{snip} and another that splits a string into a vector of strings, breaking it on whitespace boundaries: \begin{snip}{cpp} vector toWords(string s) { return words(s); } \end{snip} The actual work is done in the auxiliary function \code{words}: \begin{snip}{cpp} vector words(string s) { vector result{""}; for (auto i = begin(s); i != end(s); ++i) { if (isspace(*i)) result.push_back(""); else result.back() += *i; } return result; } \end{snip} We want to modify the functions \code{toUpper} and \code{toWords} so that they piggyback a message string on top of their regular return values. \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/piggyback.jpg} \end{figure} \noindent We will ``embellish'' the return values of these functions. Let's do it in a generic way by defining a template \code{Writer} that encapsulates a pair whose first component is a value of arbitrary type \code{A} and the second component is a string: \begin{snip}{cpp} template using Writer = pair; \end{snip} Here are the embellished functions: \begin{snip}{cpp} Writer toUpper(string s) { string result; int (*toupperp)(int) = &toupper; transform(begin(s), end(s), back_inserter(result), toupperp); return make_pair(result, "toUpper "); } Writer> toWords(string s) { return make_pair(words(s), "toWords "); } \end{snip} We want to compose these two functions into another embellished function that uppercases a string and splits it into words, all the while producing a log of those actions. Here's how we may do it: \begin{snip}{cpp} Writer> process(string s) { auto p1 = toUpper(s); auto p2 = toWords(p1.first); return make_pair(p2.first, p1.second + p2.second); } \end{snip} We have accomplished our goal: The aggregation of the log is no longer the concern of the individual functions. They produce their own messages, which are then, externally, concatenated into a larger log. Now imagine a whole program written in this style. It's a nightmare of repetitive, error-prone code. But we are programmers. We know how to deal with repetitive code: we abstract it! This is, however, not your run of the mill abstraction --- we have to abstract \newterm{function composition} itself. But composition is the essence of category theory, so before we write more code, let's analyze the problem from the categorical point of view. \section{The Writer Category} The idea of embellishing the return types of a bunch of functions in order to piggyback some additional functionality turns out to be very fruitful. We'll see many more examples of it. The starting point is our regular category of types and functions. We'll leave the types as objects, but redefine our morphisms to be the embellished functions. For instance, suppose that we want to embellish the function \code{isEven} that goes from \code{int} to \code{bool}. We turn it into a morphism that is represented by an embellished function. The important point is that this morphism is still considered an arrow between the objects \code{int} and \code{bool}, even though the embellished function returns a pair: \begin{snip}{cpp} pair isEven(int n) { return make_pair(n % 2 == 0, "isEven "); } \end{snip} By the laws of a category, we should be able to compose this morphism with another morphism that goes from the object \code{bool} to whatever. In particular, we should be able to compose it with our earlier \code{negate}: \begin{snip}{cpp} pair negate(bool b) { return make_pair(!b, "Not so! "); } \end{snip} Obviously, we cannot compose these two morphisms the same way we compose regular functions, because of the input/output mismatch. Their composition should look more like this: \begin{snip}{cpp} pair isOdd(int n) { pair p1 = isEven(n); pair p2 = negate(p1.first); return make_pair(p2.first, p1.second + p2.second); } \end{snip} So here's the recipe for the composition of two morphisms in this new category we are constructing: \begin{enumerate} \tightlist \item Execute the embellished function corresponding to the first morphism \item Extract the first component of the result pair and pass it to the embellished function corresponding to the second morphism \item Concatenate the second component (the string) of the first result and the second component (the string) of the second result \item Return a new pair combining the first component of the final result with the concatenated string. \end{enumerate} If we want to abstract this composition as a higher order function in C++, we have to use a template parameterized by three types corresponding to three objects in our category. It should take two embellished functions that are composable according to our rules, and return a third embellished function: \begin{snip}{cpp} template function(A)> compose(function(A)> m1, function(B)> m2) { return [m1, m2](A x) { auto p1 = m1(x); auto p2 = m2(p1.first); return make_pair(p2.first, p1.second + p2.second); }; } \end{snip} Now we can go back to our earlier example and implement the composition of \code{toUpper} and \code{toWords} using this new template: \begin{snip}{cpp} Writer> process(string s) { return compose>( toUpper, toWords)(s); } \end{snip} There is still a lot of noise with the passing of types to the \code{compose} template. This can be avoided as long as you have a C++14-compliant compiler that supports generalized lambda functions with return type deduction (credit for this code goes to Eric Niebler): \begin{snip}{cpp} auto const compose = [](auto m1, auto m2) { return [m1, m2](auto x) { auto p1 = m1(x); auto p2 = m2(p1.first); return make_pair(p2.first, p1.second + p2.second); }; }; \end{snip} In this new definition, the implementation of \code{process} simplifies to: \begin{snip}{cpp} Writer> process(string s) { return compose(toUpper, toWords)(s); } \end{snip} But we are not finished yet. We have defined composition in our new category, but what are the identity morphisms? These are not our regular identity functions! They have to be morphisms from type A back to type A, which means they are embellished functions of the form: \begin{snip}{cpp} Writer identity(A); \end{snip} They have to behave like units with respect to composition. If you look at our definition of composition, you'll see that an identity morphism should pass its argument without change, and only contribute an empty string to the log: \begin{snip}{cpp} template Writer identity(A x) { return make_pair(x, ""); } \end{snip} You can easily convince yourself that the category we have just defined is indeed a legitimate category. In particular, our composition is trivially associative. If you follow what's happening with the first component of each pair, it's just a regular function composition, which is associative. The second components are being concatenated, and concatenation is also associative. An astute reader may notice that it would be easy to generalize this construction to any monoid, not just the string monoid. We would use \code{mappend} inside \code{compose} and \code{mempty} inside \code{identity} (in place of \code{+} and \code{""}). There really is no reason to limit ourselves to logging just strings. A good library writer should be able to identify the bare minimum of constraints that make the library work --- here the logging library's only requirement is that the log have monoidal properties. \section{Writer in Haskell} The same thing in Haskell is a little more terse, and we also get a lot more help from the compiler. Let's start by defining the \code{Writer} type: \src{snippet01} Here I'm just defining a type alias, an equivalent of a \code{typedef} (or \code{using}) in C++. The type \code{Writer} is parameterized by a type variable \code{a} and is equivalent to a pair of \code{a} and \code{String}. The syntax for pairs is minimal: just two items in parentheses, separated by a comma. Our morphisms are functions from an arbitrary type to some \code{Writer} type: \src{snippet02} We'll declare the composition as a funny infix operator, sometimes called the ``fish'': \src{snippet03} It's a function of two arguments, each being a function on its own, and returning a function. The first argument is of the type \code{(a -> Writer b)}, the second is \code{(b -> Writer c)}, and the result is \code{(a -> Writer c)}. Here's the definition of this infix operator --- the two arguments \code{m1} and \code{m2} appearing on either side of the fishy symbol: \src{snippet04} The result is a lambda function of one argument \code{x}. The lambda is written as a backslash --- think of it as the Greek letter $\lambda$ with an amputated leg. The \code{let} expression lets you declare auxiliary variables. Here the result of the call to \code{m1} is pattern matched to a pair of variables \code{(y, s1)}; and the result of the call to \code{m2}, with the argument \code{y} from the first pattern, is matched to \code{(z, s2)}. It is common in Haskell to pattern match pairs, rather than use accessors, as we did in C++. Other than that there is a pretty straightforward correspondence between the two implementations. The overall value of the \code{let} expression is specified in its \code{in} clause: here it's a pair whose first component is \code{z} and the second component is the concatenation of two strings, \code{s1++s2}. I will also define the identity morphism for our category, but for reasons that will become clear much later, I will call it \code{return}. \src{snippet05} For completeness, let's have the Haskell versions of the embellished functions \code{upCase} and \code{toWords}: \src{snippet06} The function \code{map} corresponds to the C++ \code{transform}. It applies the character function \code{toUpper} to the string \code{s}. The auxiliary function \code{words} is defined in the standard Prelude library. Finally, the composition of the two functions is accomplished with the help of the fish operator: \src{snippet07} \section{Kleisli Categories} You might have guessed that I haven't invented this category on the spot. It's an example of the so called Kleisli category --- a category based on a monad. We are not ready to discuss monads yet, but I wanted to give you a taste of what they can do. For our limited purposes, a Kleisli category has, as objects, the types of the underlying programming language. Morphisms from type $A$ to type $B$ are functions that go from $A$ to a type derived from $B$ using the particular embellishment. Each Kleisli category defines its own way of composing such morphisms, as well as the identity morphisms with respect to that composition. (Later we'll see that the imprecise term ``embellishment'' corresponds to the notion of an endofunctor in a category.) The particular monad that I used as the basis of the category in this chapter is called the \newterm{writer monad} and it's used for logging or tracing the execution of functions. It's also an example of a more general mechanism for embedding effects in pure computations. You've seen previously that we could model programming-language types and functions in the category of sets (disregarding bottoms, as usual). Here we have extended this model to a slightly different category, a category where morphisms are represented by embellished functions, and their composition does more than just pass the output of one function to the input of another. We have one more degree of freedom to play with: the composition itself. It turns out that this is exactly the degree of freedom which makes it possible to give simple denotational semantics to programs that in imperative languages are traditionally implemented using side effects. \section{Challenge} A function that is not defined for all possible values of its argument is called a partial function. It's not really a function in the mathematical sense, so it doesn't fit the standard categorical mold. It can, however, be represented by a function that returns an embellished type \code{optional}: \begin{snip}{cpp} template class optional { bool _isValid; A _value; public: optional() : _isValid(false) {} optional(A v) : _isValid(true), _value(v) {} bool isValid() const { return _isValid; } A value() const { return _value; } }; \end{snip} For example, here's the implementation of the embellished function \code{safe\_root}: \begin{snip}{cpp} optional safe_root(double x) { if (x >= 0) return optional{sqrt(x)}; else return optional{}; } \end{snip} Here's the challenge: \begin{enumerate} \tightlist \item Construct the Kleisli category for partial functions (define composition and identity). \item Implement the embellished function \code{safe\_reciprocal} that returns a valid reciprocal of its argument, if it's different from zero. \item Compose the functions \code{safe\_root} and \code{safe\_reciprocal} to implement \code{safe\_root\_reciprocal} that calculates \code{sqrt(1/x)} whenever possible. \end{enumerate} ================================================ FILE: src/content/1.5/code/haskell/snippet01.hs ================================================ absurd :: Void -> a ================================================ FILE: src/content/1.5/code/haskell/snippet02.hs ================================================ unit :: a -> () unit _ = () ================================================ FILE: src/content/1.5/code/haskell/snippet03.hs ================================================ yes :: a -> Bool yes _ = True ================================================ FILE: src/content/1.5/code/haskell/snippet04.hs ================================================ no :: a -> Bool no _ = False ================================================ FILE: src/content/1.5/code/haskell/snippet05.hs ================================================ f . g = id g . f = id ================================================ FILE: src/content/1.5/code/haskell/snippet06.hs ================================================ fst :: (a, b) -> a fst (x, y) = x ================================================ FILE: src/content/1.5/code/haskell/snippet07.hs ================================================ snd :: (a, b) -> b snd (x, y) = y ================================================ FILE: src/content/1.5/code/haskell/snippet08.hs ================================================ fst (x, _) = x snd (_, y) = y ================================================ FILE: src/content/1.5/code/haskell/snippet09.hs ================================================ p :: c -> a q :: c -> b ================================================ FILE: src/content/1.5/code/haskell/snippet10.hs ================================================ p :: Int -> Int p x = x q :: Int -> Bool q _ = True ================================================ FILE: src/content/1.5/code/haskell/snippet11.hs ================================================ p :: (Int, Int, Bool) -> Int p (x, _, _) = x q :: (Int, Int, Bool) -> Bool q (_, _, b) = b ================================================ FILE: src/content/1.5/code/haskell/snippet12.hs ================================================ p' = p . m q' = q . m ================================================ FILE: src/content/1.5/code/haskell/snippet13.hs ================================================ m :: Int -> (Int, Bool) m x = (x, True) ================================================ FILE: src/content/1.5/code/haskell/snippet14.hs ================================================ p x = fst (m x) = x q x = snd (m x) = True ================================================ FILE: src/content/1.5/code/haskell/snippet15.hs ================================================ m (x, _, b) = (x, b) ================================================ FILE: src/content/1.5/code/haskell/snippet16.hs ================================================ fst = p . m' snd = q . m' ================================================ FILE: src/content/1.5/code/haskell/snippet17.hs ================================================ m' (x, b) = (x, x, b) ================================================ FILE: src/content/1.5/code/haskell/snippet18.hs ================================================ m' (x, b) = (x, 42, b) ================================================ FILE: src/content/1.5/code/haskell/snippet19.hs ================================================ m :: c -> (a, b) m x = (p x, q x) ================================================ FILE: src/content/1.5/code/haskell/snippet20.hs ================================================ factorizer :: (c -> a) -> (c -> b) -> (c -> (a, b)) factorizer p q = \x -> (p x, q x) ================================================ FILE: src/content/1.5/code/haskell/snippet21.hs ================================================ i :: a -> c j :: b -> c ================================================ FILE: src/content/1.5/code/haskell/snippet22.hs ================================================ i' = m . i j' = m . j ================================================ FILE: src/content/1.5/code/haskell/snippet23.hs ================================================ data Contact = PhoneNum Int | EmailAddr String ================================================ FILE: src/content/1.5/code/haskell/snippet24.hs ================================================ helpdesk :: Contact helpdesk = PhoneNum 2222222 ================================================ FILE: src/content/1.5/code/haskell/snippet25.hs ================================================ data Either a b = Left a | Right b ================================================ FILE: src/content/1.5/code/haskell/snippet26.hs ================================================ factorizer :: (a -> c) -> (b -> c) -> Either a b -> c factorizer i j (Left a) = i a factorizer i j (Right b) = j b ================================================ FILE: src/content/1.5/code/haskell/snippet27.hs ================================================ p = fst . m q = snd . m ================================================ FILE: src/content/1.5/code/haskell/snippet28.hs ================================================ p () = fst (m ()) q () = snd (m ()) ================================================ FILE: src/content/1.5/code/ocaml/snippet01.ml ================================================ type void (* Uninhabited type *) val absurd : void -> 'a ================================================ FILE: src/content/1.5/code/ocaml/snippet02.ml ================================================ let unit x = () ================================================ FILE: src/content/1.5/code/ocaml/snippet03.ml ================================================ let yes _ = true ================================================ FILE: src/content/1.5/code/ocaml/snippet04.ml ================================================ let no _ = false ================================================ FILE: src/content/1.5/code/ocaml/snippet05.ml ================================================ compose f g = id compose g f = id ================================================ FILE: src/content/1.5/code/ocaml/snippet06.ml ================================================ let fst (a, b) = a ================================================ FILE: src/content/1.5/code/ocaml/snippet07.ml ================================================ let snd (a, b) = b ================================================ FILE: src/content/1.5/code/ocaml/snippet08.ml ================================================ let fst (a, _) = a let snd (_, b) = b ================================================ FILE: src/content/1.5/code/ocaml/snippet09.ml ================================================ module type Chapter5_Product = sig type a type c type b val p : c -> a val q : c -> b end ================================================ FILE: src/content/1.5/code/ocaml/snippet10.ml ================================================ module Chapter5_Product_Example : Chapter5_Product with type a = int and type b = bool and type c = int = struct type a = int type b = bool type c = int let p x = x let q _ = true end ================================================ FILE: src/content/1.5/code/ocaml/snippet11.ml ================================================ module Chapter5_Product_Example2 : Chapter5_Product = struct type a = int type b = bool type c = int * int * bool let p (x, _, _) = x let q (_, _, b) = b end ================================================ FILE: src/content/1.5/code/ocaml/snippet12.ml ================================================ let p' = compose Chapter5_Product_Example.p m let q' = compose Chapter5_Product_Example.q m ================================================ FILE: src/content/1.5/code/ocaml/snippet13.ml ================================================ let m (x : int) = x, true ================================================ FILE: src/content/1.5/code/ocaml/snippet14.ml ================================================ let p x = fst (m x) let q x = snd (m x) ================================================ FILE: src/content/1.5/code/ocaml/snippet15.ml ================================================ let m ((x, _, b) : int * int * bool) = x, b ================================================ FILE: src/content/1.5/code/ocaml/snippet16.ml ================================================ fst = compose p m' snd = compose q m' ================================================ FILE: src/content/1.5/code/ocaml/snippet17.ml ================================================ let m' ((x, b) : int * bool) = x, x, b ================================================ FILE: src/content/1.5/code/ocaml/snippet18.ml ================================================ let m' ((x, b) : int * bool) = x, 42, b ================================================ FILE: src/content/1.5/code/ocaml/snippet19.ml ================================================ module type Chapter5_product_projection_example = functor (Product : Chapter5_Product) -> sig val m : Product.c -> Product.a * Product.b end module ProjectionImpl (Product : Chapter5_Product) = struct let m c = Product.p c, Product.q c end ================================================ FILE: src/content/1.5/code/ocaml/snippet20.ml ================================================ module type Factorizer = functor (Product : Chapter5_Product) -> sig val factorizer : (Product.c -> Product.a) -> (Product.c -> Product.b) -> Product.c -> Product.a * Product.b end module FactorizerImpl (Product : Chapter5_Product) = struct let factorizer ca cb = Product.p ca, Product.q cb end ================================================ FILE: src/content/1.5/code/ocaml/snippet21.ml ================================================ module type CoProduct = sig type a type b type c val i : a -> c val j : b -> c end ================================================ FILE: src/content/1.5/code/ocaml/snippet22.ml ================================================ i' == compose m i j' == compose m j ================================================ FILE: src/content/1.5/code/ocaml/snippet23.ml ================================================ type contact = | PhoneNum of int | EmailAddr of string ================================================ FILE: src/content/1.5/code/ocaml/snippet24.ml ================================================ let helpdesk = PhoneNum 2222222 ================================================ FILE: src/content/1.5/code/ocaml/snippet25.ml ================================================ type ('a, 'b) either = | Left of 'a | Right of 'b ================================================ FILE: src/content/1.5/code/ocaml/snippet26.ml ================================================ let factorizer i j = function | Left a -> i a | Right b -> j b ;; ================================================ FILE: src/content/1.5/code/ocaml/snippet27.ml ================================================ p = compose fst m q = compose snd m ================================================ FILE: src/content/1.5/code/ocaml/snippet28.ml ================================================ p () = fst (m ()) q () = snd (m ()) ================================================ FILE: src/content/1.5/code/reason/snippet01.re ================================================ type void; /* Uninhabited type */ type absurd = void => 'a = ; ================================================ FILE: src/content/1.5/code/reason/snippet02.re ================================================ let unit = x => (); ================================================ FILE: src/content/1.5/code/reason/snippet03.re ================================================ let yes = _ => true; ================================================ FILE: src/content/1.5/code/reason/snippet04.re ================================================ let no = _ => false; ================================================ FILE: src/content/1.5/code/reason/snippet05.re ================================================ compose(f, g) == id; compose(g, f) == id; ================================================ FILE: src/content/1.5/code/reason/snippet06.re ================================================ let fst = ((a, b)) => a; ================================================ FILE: src/content/1.5/code/reason/snippet07.re ================================================ let snd = ((a, b)) => b; ================================================ FILE: src/content/1.5/code/reason/snippet08.re ================================================ let fst = ((a, _)) => a; let snd = ((_, b)) => b; ================================================ FILE: src/content/1.5/code/reason/snippet09.re ================================================ module type Chapter5_Product = { type a; type c; type b; let p: c => a; let q: c => b; }; ================================================ FILE: src/content/1.5/code/reason/snippet10.re ================================================ module Chapter5_Product_Example: Chapter5_Product with type a = int and type b = bool and type c = int = { type a = int; type b = bool; type c = int; let p = x => x; let q = _ => true; }; ================================================ FILE: src/content/1.5/code/reason/snippet11.re ================================================ module Chapter5_Product_Example2: Chapter5_Product = { type a = int; type b = bool; type c = (int, int, bool); let p = ((x, _, _)) => x; let q = ((_, _, b)) => b; }; ================================================ FILE: src/content/1.5/code/reason/snippet12.re ================================================ let p' = compose(Chapter5_Product_Example.p, m); let q' = compose(Chapter5_Product_Example.q, m); ================================================ FILE: src/content/1.5/code/reason/snippet13.re ================================================ let m = (x: int) => (x, true); ================================================ FILE: src/content/1.5/code/reason/snippet14.re ================================================ let p = x => fst(m(x)); let q = x => snd(m(x)); ================================================ FILE: src/content/1.5/code/reason/snippet15.re ================================================ let m = ((x, _, b): (int, int, bool)) => (x, b); ================================================ FILE: src/content/1.5/code/reason/snippet16.re ================================================ fst == compose(p, m'); snd == compose(q, m'); ================================================ FILE: src/content/1.5/code/reason/snippet17.re ================================================ let m' = ((x, b): (int, bool)) => (x, x, b); ================================================ FILE: src/content/1.5/code/reason/snippet18.re ================================================ let m' = ((x, b): (int, bool)) => (x, 42, b); ================================================ FILE: src/content/1.5/code/reason/snippet19.re ================================================ module type Chapter5_product_projection_example = (Product: Chapter5_Product) => { let m: Product.c => (Product.a, Product.b); }; module ProjectionImpl = (Product: Chapter5_Product) => { let m = c => (Product.p(c), Product.q(c)); }; ================================================ FILE: src/content/1.5/code/reason/snippet20.re ================================================ module type Factorizer = (Product: Chapter5_Product) => { let factorizer: (Product.c => Product.a, Product.c => Product.b, Product.c) => (Product.a, Product.b); }; module FactorizerImpl = (Product: Chapter5_Product) => { let factorizer = (ca, cb) => (Product.p(ca), Product.q(cb)); }; ================================================ FILE: src/content/1.5/code/reason/snippet21.re ================================================ module type CoProduct = { type a; type b; type c; let i: a => c; let j: b => c; }; ================================================ FILE: src/content/1.5/code/reason/snippet22.re ================================================ i' === compose(m, i) j' === compose(m, j); ================================================ FILE: src/content/1.5/code/reason/snippet23.re ================================================ type contact = | PhoneNum(int) | EmailAddr(string); ================================================ FILE: src/content/1.5/code/reason/snippet24.re ================================================ let helpdesk = PhoneNum(2222222); ================================================ FILE: src/content/1.5/code/reason/snippet25.re ================================================ type either('a, 'b) = | Left('a) | Right('b); ================================================ FILE: src/content/1.5/code/reason/snippet26.re ================================================ let factorizer = (i, j) => fun | Left(a) => i(a) | Right(b) => j(b); ================================================ FILE: src/content/1.5/code/reason/snippet27.re ================================================ p == compose(fst, m); q == compose(snd, m); ================================================ FILE: src/content/1.5/code/reason/snippet28.re ================================================ p() == fst(m()); q() == snd(m()); ================================================ FILE: src/content/1.5/code/scala/snippet01.scala ================================================ def absurd[A]: Nothing => A ================================================ FILE: src/content/1.5/code/scala/snippet02.scala ================================================ def unit[A]: A => Unit = _ => () ================================================ FILE: src/content/1.5/code/scala/snippet03.scala ================================================ def yes[A]: A => Boolean = _ => true ================================================ FILE: src/content/1.5/code/scala/snippet04.scala ================================================ def no[A]: A => Boolean = _ => false ================================================ FILE: src/content/1.5/code/scala/snippet05.scala ================================================ f compose g == identity _ g compose f == identity _ ================================================ FILE: src/content/1.5/code/scala/snippet06.scala ================================================ def fst[A, B]: ((A, B)) => A = { case (x, y) => x } ================================================ FILE: src/content/1.5/code/scala/snippet07.scala ================================================ def snd[A, B]: ((A, B)) => B = { case (x, y) => y } ================================================ FILE: src/content/1.5/code/scala/snippet08.scala ================================================ def fst[A, B]: ((A, B)) => A = _._1 def snd[A, B]: ((A, B)) => B = _._2 ================================================ FILE: src/content/1.5/code/scala/snippet09.scala ================================================ def p: C => A def q: C => B ================================================ FILE: src/content/1.5/code/scala/snippet10.scala ================================================ def p: Int => Int = x => x def q: Int => Boolean = _ => true ================================================ FILE: src/content/1.5/code/scala/snippet11.scala ================================================ def p: ((Int, Int, Boolean)) => Int = _._1 def q: ((Int, Int, Boolean)) => Boolean = _._3 ================================================ FILE: src/content/1.5/code/scala/snippet12.scala ================================================ p1 == p compose m q1 == q compose m ================================================ FILE: src/content/1.5/code/scala/snippet13.scala ================================================ def m: Int => (Int, Boolean) = x => (x, true) ================================================ FILE: src/content/1.5/code/scala/snippet14.scala ================================================ def p: Int => Int = x => fst(m(x)) // == x def q: Int => Boolean = x => snd(m(x)) // == true ================================================ FILE: src/content/1.5/code/scala/snippet15.scala ================================================ def m: ((Int, Int, Boolean)) => (Int, Boolean) = { case (x, _, b) => (x, b) } ================================================ FILE: src/content/1.5/code/scala/snippet16.scala ================================================ fst == p compose m1 snd == q compose m1 ================================================ FILE: src/content/1.5/code/scala/snippet17.scala ================================================ def m1: ((Int, Boolean)) => (Int, Int, Boolean) = { case (x, b) => (x, x, b) } ================================================ FILE: src/content/1.5/code/scala/snippet18.scala ================================================ def m1: ((Int, Boolean)) => (Int, Int, Boolean) = { case (x, b) => (x, 42, b) } ================================================ FILE: src/content/1.5/code/scala/snippet19.scala ================================================ def m: C => (A, B) = x => (p(x), q(x)) ================================================ FILE: src/content/1.5/code/scala/snippet20.scala ================================================ def factorizer[A, B, C]: (C => A) => (C => B) => (C => (A, B)) = p => q => x => (p(x), q(x)) ================================================ FILE: src/content/1.5/code/scala/snippet21.scala ================================================ def i: A => C def j: B => C ================================================ FILE: src/content/1.5/code/scala/snippet22.scala ================================================ i1 == m compose i j1 == m compose j ================================================ FILE: src/content/1.5/code/scala/snippet23.scala ================================================ sealed trait Contact case class PhoneNum(num: Int) extends Contact case class EmailAddr(addr: String) extends Contact ================================================ FILE: src/content/1.5/code/scala/snippet24.scala ================================================ def helpdesk: Contact = PhoneNum(2222222) ================================================ FILE: src/content/1.5/code/scala/snippet25.scala ================================================ sealed trait Either[A, B] case class Left[A](v: A) extends Either[A, Nothing] case class Right[B](v: B) extends Either[Nothing, B] ================================================ FILE: src/content/1.5/code/scala/snippet26.scala ================================================ def factorizer[A, B, C]: (A => C) => (B => C) => Either[A, B] => C = i => j => { case Left(a) => i(a) case Right(b) => j(b) } ================================================ FILE: src/content/1.5/code/scala/snippet27.scala ================================================ p == fst compose m q == snd compose m ================================================ FILE: src/content/1.5/code/scala/snippet28.scala ================================================ p(()) == fst(m(())) q(()) == snd(m(())) ================================================ FILE: src/content/1.5/products-and-coproducts.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{T}{he Ancient Greek} playwright Euripides once said: ``Every man is like the company he is wont to keep.'' We are defined by our relationships. Nowhere is this more true than in category theory. If we want to single out a particular object in a category, we can only do this by describing its pattern of relationships with other objects (and itself). These relationships are defined by morphisms. There is a common construction in category theory called the \newterm{universal construction} for defining objects in terms of their relationships. One way of doing this is to pick a pattern, a particular shape constructed from objects and morphisms, and look for all its occurrences in the category. If it's a common enough pattern, and the category is large, chances are you'll have lots and lots of hits. The trick is to establish some kind of ranking among those hits, and pick what could be considered the best fit. This process is reminiscent of the way we do web searches. A query is like a pattern. A very general query will give you large \emph{recall}: lots of hits. Some may be relevant, others not. To eliminate irrelevant hits, you refine your query. That increases its \emph{precision}. Finally, the search engine will rank the hits and, hopefully, the one result that you're interested in will be at the top of the list. \section{Initial Object} The simplest shape is a single object. Obviously, there are as many instances of this shape as there are objects in a given category. That's a lot to choose from. We need to establish some kind of ranking and try to find the object that tops this hierarchy. The only means at our disposal are morphisms. If you think of morphisms as arrows, then it's possible that there is an overall net flow of arrows from one end of the category to another. This is true in ordered categories, for instance in partial orders. We could generalize that notion of object precedence by saying that object $a$ is ``more initial'' than object $b$, if there is an arrow (a morphism) going from $a$ to $b$. We would then define \emph{the} initial object as one that has arrows going to all other objects. Obviously there is no guarantee that such an object exists, and that's okay. A bigger problem is that there may be too many such objects: The recall is good, but precision is lacking. The solution is to take a hint from ordered categories --- they allow at most one arrow between any two objects: there is only one way of being less-than or equal-to another object. Which leads us to this definition of the initial object: \begin{quote} The \textbf{initial object} is the object that has one and only one morphism going to any object in the category. \end{quote} \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/initial.jpg} \end{figure} \noindent However, even that doesn't guarantee the uniqueness of the initial object (if one exists). But it guarantees the next best thing: uniqueness \newterm{up to isomorphism}. Isomorphisms are very important in category theory, so I'll talk about them shortly. For now, let's just agree that uniqueness up to isomorphism justifies the use of ``the'' in the definition of the initial object. Here are some examples: The initial object in a partially ordered set (often called a \newterm{poset}) is its least element. Some posets don't have an initial object --- like the set of all integers, positive and negative, with less-than-or-equal relation for morphisms. In the category of sets and functions, the initial object is the empty set. Remember, an empty set corresponds to the Haskell type \code{Void} (there is no corresponding type in C++) and the unique polymorphic function from \code{Void} to any other type is called \code{absurd}: \src{snippet01} It's this family of morphisms that makes \code{Void} the initial object in the category of types. \section{Terminal Object} Let's continue with the single-object pattern, but let's change the way we rank the objects. We'll say that object $a$ is ``more terminal'' than object $b$ if there is a morphism going from $b$ to $a$ (notice the reversal of direction). We'll be looking for an object that's more terminal than any other object in the category. Again, we will insist on uniqueness: \begin{quote} The \textbf{terminal object} is the object with one and only one morphism coming to it from any object in the category. \end{quote} \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/final.jpg} \end{figure} \noindent And again, the terminal object is unique, up to isomorphism, which I will show shortly. But first let's look at some examples. In a poset, the terminal object, if it exists, is the biggest object. In the category of sets, the terminal object is a singleton. We've already talked about singletons --- they correspond to the \code{void} type in C++ and the unit type \code{()} in Haskell. It's a type that has only one value --- implicit in C++ and explicit in Haskell, denoted by \code{()}. We've also established that there is one and only one pure function from any type to the unit type: \src{snippet02} so all the conditions for the terminal object are satisfied. Notice that in this example the uniqueness condition is crucial, because there are other sets (actually, all of them, except for the empty set) that have incoming morphisms from every set. For instance, there is a Boolean-valued function (a predicate) defined for every type: \src{snippet03} But \code{Bool} is not a terminal object. There is at least one more \code{Bool}-valued function from every type (except \code{Void}, for which both functions are equal to \code{absurd}): \src{snippet04} Insisting on uniqueness gives us just the right precision to narrow down the definition of the terminal object to just one type. \section{Duality} You can't help but to notice the symmetry between the way we defined the initial object and the terminal object. The only difference between the two was the direction of morphisms. It turns out that for any category $\cat{C}$ we can define the \newterm{opposite category} $\cat{C}^\mathit{op}$ just by reversing all the arrows. The opposite category automatically satisfies all the requirements of a category, as long as we simultaneously redefine composition. If original morphisms $f \Colon a \to b$ and $g \Colon b \to c$ composed to $h \Colon a \to c$ with $h = g \circ f$, then the reversed morphisms $f^\mathit{op} \Colon b \to a$ and $g^\mathit{op} \Colon c \to b$ will compose to $h^\mathit{op} \Colon c \to a$ with $h^\mathit{op} = f^\mathit{op} \circ g^\mathit{op}$. And reversing the identity arrows is a (pun alert!) no-op. Duality is a very important property of categories because it doubles the productivity of every mathematician working in category theory. For every construction you come up with, there is its opposite; and for every theorem you prove, you get one for free. The constructions in the opposite category are often prefixed with ``co'', so you have products and coproducts, monads and comonads, cones and cocones, limits and colimits, and so on. There are no cocomonads though, because reversing the arrows twice gets us back to the original state. It follows then that a terminal object is the initial object in the opposite category. \section{Isomorphisms} As programmers, we are well aware that defining equality is a nontrivial task. What does it mean for two objects to be equal? Do they have to occupy the same location in memory (pointer equality)? Or is it enough that the values of all their components are equal? Are two complex numbers equal if one is expressed as the real and imaginary part, and the other as modulus and angle? You'd think that mathematicians would have figured out the meaning of equality, but they haven't. They have the same problem of multiple competing definitions for equality. There is the propositional equality, intensional equality, extensional equality, and equality as a path in homotopy type theory. And then there are the weaker notions of isomorphism, and even weaker of equivalence. The intuition is that isomorphic objects look the same --- they have the same shape. It means that every part of one object corresponds to some part of another object in a one-to-one mapping. As far as our instruments can tell, the two objects are a perfect copy of each other. Mathematically it means that there is a mapping from object $a$ to object $b$, and there is a mapping from object $b$ back to object $a$, and they are the inverse of each other. In category theory we replace mappings with morphisms. An isomorphism is an invertible morphism; or a pair of morphisms, one being the inverse of the other. We understand the inverse in terms of composition and identity: Morphism $g$ is the inverse of morphism $f$ if their composition is the identity morphism. These are actually two equations because there are two ways of composing two morphisms: \src{snippet05} When I said that the initial (terminal) object was unique up to isomorphism, I meant that any two initial (terminal) objects are isomorphic. That's actually easy to see. Let's suppose that we have two initial objects $i_{1}$ and $i_{2}$. Since $i_{1}$ is initial, there is a unique morphism $f$ from $i_{1}$ to $i_{2}$. By the same token, since $i_{2}$ is initial, there is a unique morphism $g$ from $i_{2}$ to $i_{1}$. What's the composition of these two morphisms? \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/uniqueness.jpg} \caption{All morphisms in this diagram are unique.} \end{figure} \noindent The composition $g \circ f$ must be a morphism from $i_{1}$ to $i_{1}$. But $i_{1}$ is initial so there can only be one morphism going from $i_{1}$ to $i_{1}$. Since we are in a category, we know that there is an identity morphism from $i_{1}$ to $i_{1}$, and since there is room for only one, that must be it. Therefore $g \circ f$ is equal to identity. Similarly, $f \circ g$ must be equal to identity, because there can be only one morphism from $i_{2}$ back to $i_{2}$. This proves that $f$ and $g$ must be the inverse of each other. Therefore any two initial objects are isomorphic. Notice that in this proof we used the uniqueness of the morphism from the initial object to itself. Without that we couldn't prove the ``up to isomorphism'' part. But why do we need the uniqueness of $f$ and $g$? Because not only is the initial object unique up to isomorphism, it is unique up to \emph{unique} isomorphism. In principle, there could be more than one isomorphism between two objects, but that's not the case here. This ``uniqueness up to unique isomorphism'' is the important property of all universal constructions. \section{Products} The next universal construction is that of a product. We know what a Cartesian product of two sets is: it's a set of pairs. But what's the pattern that connects the product set with its constituent sets? If we can figure that out, we'll be able to generalize it to other categories. All we can say is that there are two functions, the projections, from the product to each of the constituents. In Haskell, these two functions are called \code{fst} and \code{snd} and they pick, respectively, the first and the second component of a pair: \src{snippet06} \src{snippet07} Here, the functions are defined by pattern matching their arguments: the pattern that matches any pair is \code{(x, y)}, and it extracts its components into variables \code{x} and \code{y}. These definitions can be simplified even further with the use of wildcards: \src{snippet08} In C++, we would use template functions, for instance: \begin{snip}{cpp} template A fst(pair const & p) { return p.first; } \end{snip} Equipped with this seemingly very limited knowledge, let's try to define a pattern of objects and morphisms in the category of sets that will lead us to the construction of a product of two sets, $a$ and $b$. This pattern consists of an object $c$ and two morphisms $p$ and $q$ connecting it to $a$ and $b$, respectively: \src{snippet09} \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/productpattern.jpg} \end{figure} \noindent All $c$s that fit this pattern will be considered candidates for the product. There may be lots of them. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/productcandidates.jpg} \end{figure} \noindent For instance, let's pick, as our constituents, two Haskell types, \code{Int} and \code{Bool}, and get a sampling of candidates for their product. Here's one: \code{Int}. Can \code{Int} be considered a candidate for the product of \code{Int} and \code{Bool}? Yes, it can --- and here are its projections: \src{snippet10} That's pretty lame, but it matches the criteria. Here's another one: \code{(Int, Int, Bool)}. It's a tuple of three elements, or a triple. Here are two morphisms that make it a legitimate candidate (we are using pattern matching on triples): \src{snippet11} You may have noticed that while our first candidate was too small --- it only covered the \code{Int} dimension of the product; the second was too big --- it spuriously duplicated the \code{Int} dimension. But we haven't explored yet the other part of the universal construction: the ranking. We want to be able to compare two instances of our pattern. We want to compare one candidate object $c$ and its two projections $p$ and $q$ with another candidate object $c'$ and its two projections $p'$ and $q'$. We would like to say that $c$ is ``better'' than $c'$ if there is a morphism $m$ from $c'$ to $c$ --- but that's too weak. We also want its projections to be ``better,'' or ``more universal,'' than the projections of $c'$. What it means is that the projections $p'$ and $q'$ can be reconstructed from $p$ and $q$ using $m$: \src{snippet12} \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/productranking.jpg} \end{figure} \noindent Another way of looking at these equations is that $m$ \emph{factorizes} $p'$ and $q'$. Just pretend that these equations are in natural numbers, and the dot is multiplication: $m$ is a common factor shared by $p'$ and $q'$. Just to build some intuitions, let me show you that the pair \code{(Int, Bool)} with the two canonical projections, \code{fst} and \code{snd} is indeed \emph{better} than the two candidates I presented before. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/not-a-product.jpg} \end{figure} \noindent The mapping \code{m} for the first candidate is: \src{snippet13} Indeed, the two projections, \code{p} and \code{q} can be reconstructed as: \src{snippet14} The \code{m} for the second example is similarly uniquely determined: \src{snippet15} We were able to show that \code{(Int, Bool)} is better than either of the two candidates. Let's see why the opposite is not true. Could we find some \code{m'} that would help us reconstruct \code{fst} and \code{snd} from \code{p} and \code{q}? \src{snippet16} In our first example, \code{q} always returned \code{True} and we know that there are pairs whose second component is \code{False}. We can't reconstruct \code{snd} from \code{q}. The second example is different: we retain enough information after running either \code{p} or \code{q}, but there is more than one way to factorize \code{fst} and \code{snd}. Because both \code{p} and \code{q} ignore the second component of the triple, our \code{m'} can put anything in it. We can have: \src{snippet17} or \src{snippet18} and so on. Putting it all together, given any type \code{c} with two projections \code{p} and \code{q}, there is a unique \code{m} from \code{c} to the Cartesian product \code{(a, b)} that factorizes them. In fact, it just combines \code{p} and \code{q} into a pair. \src{snippet19} That makes the Cartesian product \code{(a, b)} our best match, which means that this universal construction works in the category of sets. It picks the product of any two sets. Now let's forget about sets and define a product of two objects in any category using the same universal construction. Such a product doesn't always exist, but when it does, it is unique up to a unique isomorphism. \begin{quote} A \textbf{product} of two objects $a$ and $b$ is the object $c$ equipped with two projections such that for any other object $c'$ equipped with two projections there is a unique morphism $m$ from $c'$ to $c$ that factorizes those projections. \end{quote} \noindent A (higher order) function that produces the factorizing function \code{m} from two candidates is sometimes called the \newterm{factorizer}. In our case, it would be the function: \src{snippet20} \section{Coproduct} Like every construction in category theory, the product has a dual, which is called the coproduct. When we reverse the arrows in the product pattern, we end up with an object $c$ equipped with two \emph{injections}, \code{i} and \code{j}: morphisms from $a$ and $b$ to $c$. \src{snippet21} \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/coproductpattern.jpg} \end{figure} \noindent The ranking is also inverted: object $c$ is ``better'' than object $c'$ that is equipped with the injections $i'$ and $j'$ if there is a morphism $m$ from $c$ to $c'$ that factorizes the injections: \src{snippet22} \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/coproductranking.jpg} \end{figure} \noindent The ``best'' such object, one with a unique morphism connecting it to any other pattern, is called a coproduct and, if it exists, is unique up to unique isomorphism. \begin{quote} A \textbf{coproduct} of two objects $a$ and $b$ is the object $c$ equipped with two injections such that for any other object $c'$ equipped with two injections there is a unique morphism $m$ from $c$ to $c'$ that factorizes those injections. \end{quote} \noindent In the category of sets, the coproduct is the \emph{disjoint union} of two sets. An element of the disjoint union of $a$ and $b$ is either an element of $a$ or an element of $b$. If the two sets overlap, the disjoint union contains two copies of the common part. You can think of an element of a disjoint union as being tagged with an identifier that specifies its origin. For a programmer, it's easier to understand a coproduct in terms of types: it's a tagged union of two types. C++ supports unions, but they are not tagged. It means that in your program you have to somehow keep track which member of the union is valid. To create a tagged union, you have to define a tag --- an enumeration --- and combine it with the union. For instance, a tagged union of an \code{int} and a \code{char const *} could be implemented as: \begin{snip}{cpp} struct Contact { enum { isPhone, isEmail } tag; union { int phoneNum; char const * emailAddr; }; }; \end{snip} The two injections can either be implemented as constructors or as functions. For instance, here's the first injection as a function \code{PhoneNum}: \begin{snip}{cpp} Contact PhoneNum(int n) { Contact c; c.tag = isPhone; c.phoneNum = n; return c; } \end{snip} It injects an integer into \code{Contact}. A tagged union is also called a \newterm{variant}, and there is a very general implementation of a variant in the C++17 standard, \code{std::variant}. In Haskell, you can combine any data types into a tagged union by separating data constructors with a vertical bar. The \code{Contact} example translates into the declaration: \src{snippet23} Here, \code{PhoneNum} and \code{EmailAddr} serve both as constructors (injections), and as tags for pattern matching (more about this later). For instance, this is how you would construct a contact using a phone number: \src{snippet24} Unlike the canonical implementation of the product that is built into Haskell as the primitive pair, the canonical implementation of the coproduct is a data type called \code{Either}, which is defined in the standard Prelude as: \src{snippet25} It is parameterized by two types, \code{a} and \code{b} and has two constructors: \code{Left} that takes a value of type \code{a}, and \code{Right} that takes a value of type \code{b}. Just as we've defined the factorizer for a product, we can define one for the coproduct. Given a candidate type \code{c} and two candidate injections \code{i} and \code{j}, the factorizer for \code{Either} produces the factoring function: \src{snippet26} \section{Asymmetry} We've seen two sets of dual definitions: The definition of a terminal object can be obtained from the definition of the initial object by reversing the direction of arrows; in a similar way, the definition of the coproduct can be obtained from that of the product. Yet in the category of sets the initial object is very different from the final object, and coproduct is very different from product. We'll see later that product behaves like multiplication, with the terminal object playing the role of one; whereas coproduct behaves more like the sum, with the initial object playing the role of zero. In particular, for finite sets, the size of the product is the product of the sizes of individual sets, and the size of the coproduct is the sum of the sizes. This shows that the category of sets is not symmetric with respect to the inversion of arrows. Notice that while the empty set has a unique morphism to any set (the \code{absurd} function), it has no morphisms coming back. The singleton set has a unique morphism coming to it from any set, but it \emph{also} has outgoing morphisms to every set (except for the empty one). As we've seen before, these outgoing morphisms from the terminal object play a very important role of picking elements of other sets (the empty set has no elements, so there's nothing to pick). It's the relationship of the singleton set to the product that sets it apart from the coproduct. Consider using the singleton set, represented by the unit type \code{()}, as yet another --- vastly inferior --- candidate for the product pattern. Equip it with two projections \code{p} and \code{q}: functions from the singleton to each of the constituent sets. Each selects a concrete element from either set. Because the product is universal, there is also a (unique) morphism \code{m} from our candidate, the singleton, to the product. This morphism selects an element from the product set --- it selects a concrete pair. It also factorizes the two projections: \src{snippet27} When acting on the singleton value \code{()}, the only element of the singleton set, these two equations become: \src{snippet28} Since \code{m ()} is the element of the product picked by \code{m}, these equations tell us that the element picked by \code{p} from the first set, \code{p ()}, is the first component of the pair picked by \code{m}. Similarly, \code{q ()} is equal to the second component. This is in total agreement with our understanding that elements of the product are pairs of elements from the constituent sets. There is no such simple interpretation of the coproduct. We could try the singleton set as a candidate for a coproduct, in an attempt to extract the elements from it, but there we would have two injections going into it rather than two projections coming out of it. They'd tell us nothing about their sources (in fact, we've seen that they ignore the input parameter). Neither would the unique morphism from the coproduct to our singleton. The category of sets just looks very different when seen from the direction of the initial object than it does when seen from the terminal end. This is not an intrinsic property of sets, it's a property of functions, which we use as morphisms in $\Set$. Functions are, in general, asymmetric. Let me explain. A function must be defined for every element of its domain set (in programming, we call it a \newterm{total} function), but it doesn't have to cover the whole codomain. We've seen some extreme cases of it: functions from a singleton set --- functions that select just a single element in the codomain. (Actually, functions from an empty set are the real extremes.) When the size of the domain is much smaller than the size of the codomain, we often think of such functions as embedding the domain in the codomain. For instance, we can think of a function from a singleton set as embedding its single element in the codomain. I call them \newterm{embedding} functions, but mathematicians prefer to give a name to the opposite: functions that tightly fill their codomains are called \newterm{surjective} or \newterm{onto}. The other source of asymmetry is that functions are allowed to map many elements of the domain set into one element of the codomain. They can collapse them. The extreme case are functions that map whole sets into a singleton. You've seen the polymorphic \code{unit} function that does just that. The collapsing can only be compounded by composition. A composition of two collapsing functions is even more collapsing than the individual functions. Mathematicians have a name for non-collapsing functions: they call them \newterm{injective} or \newterm{one-to-one}. Of course there are some functions that are neither embedding nor collapsing. They are called \newterm{bijections} and they are truly symmetric, because they are invertible. In the category of sets, an isomorphism is the same as a bijection. \section{Challenges} \begin{enumerate} \tightlist \item Show that the terminal object is unique up to unique isomorphism. \item What is a product of two objects in a poset? Hint: Use the universal construction. \item What is a coproduct of two objects in a poset? \item Implement the equivalent of Haskell \code{Either} as a generic type in your favorite language (other than Haskell). \item Show that \code{Either} is a ``better'' coproduct than \code{int} equipped with two injections: \begin{snip}{cpp} int i(int n) { return n; } int j(bool b) { return b ? 0: 1; } \end{snip} Hint: Define a function \begin{snip}{cpp} int m(Either const & e); \end{snip} that factorizes \code{i} and \code{j}. \item Continuing the previous problem: How would you argue that \code{int} with the two injections \code{i} and \code{j} cannot be ``better'' than \code{Either}? \item Still continuing: What about these injections? \begin{snip}{cpp} int i(int n) { if (n < 0) return n; return n + 2; } int j(bool b) { return b ? 0: 1; } \end{snip} \item Come up with an inferior candidate for a coproduct of \code{int} and \code{bool} that cannot be better than \code{Either} because it allows multiple acceptable morphisms from it to \code{Either}. \end{enumerate} \section{Bibliography} \begin{enumerate} \tightlist \item The Catsters, \urlref{https://www.youtube.com/watch?v=upCSDIO9pjc}{Products and Coproducts} video. \end{enumerate} ================================================ FILE: src/content/1.6/code/haskell/snippet01.hs ================================================ swap :: (a, b) -> (b, a) swap (x, y) = (y, x) ================================================ FILE: src/content/1.6/code/haskell/snippet02.hs ================================================ ((a, b), c) ================================================ FILE: src/content/1.6/code/haskell/snippet03.hs ================================================ (a, (b, c)) ================================================ FILE: src/content/1.6/code/haskell/snippet04.hs ================================================ alpha :: ((a, b), c) -> (a, (b, c)) alpha ((x, y), z) = (x, (y, z)) ================================================ FILE: src/content/1.6/code/haskell/snippet05.hs ================================================ alpha_inv :: (a, (b, c)) -> ((a, b), c) alpha_inv (x, (y, z)) = ((x, y), z) ================================================ FILE: src/content/1.6/code/haskell/snippet06.hs ================================================ (a, ()) ================================================ FILE: src/content/1.6/code/haskell/snippet07.hs ================================================ rho :: (a, ()) -> a rho (x, ()) = x ================================================ FILE: src/content/1.6/code/haskell/snippet08.hs ================================================ rho_inv :: a -> (a, ()) rho_inv x = (x, ()) ================================================ FILE: src/content/1.6/code/haskell/snippet09.hs ================================================ data Pair a b = P a b ================================================ FILE: src/content/1.6/code/haskell/snippet10.hs ================================================ stmt :: Pair String Bool stmt = P "This statement is" False ================================================ FILE: src/content/1.6/code/haskell/snippet11.hs ================================================ data Pair a b = Pair a b ================================================ FILE: src/content/1.6/code/haskell/snippet12.hs ================================================ stmt = (,) "This statement is" False ================================================ FILE: src/content/1.6/code/haskell/snippet13.hs ================================================ data Stmt = Stmt String Bool ================================================ FILE: src/content/1.6/code/haskell/snippet14.hs ================================================ startsWithSymbol :: (String, String, Int) -> Bool startsWithSymbol (name, symbol, _) = isPrefixOf symbol name ================================================ FILE: src/content/1.6/code/haskell/snippet15.hs ================================================ data Element = Element { name :: String , symbol :: String , atomicNumber :: Int } ================================================ FILE: src/content/1.6/code/haskell/snippet16.hs ================================================ tupleToElem :: (String, String, Int) -> Element tupleToElem (n, s, a) = Element { name = n , symbol = s , atomicNumber = a } ================================================ FILE: src/content/1.6/code/haskell/snippet17.hs ================================================ elemToTuple :: Element -> (String, String, Int) elemToTuple e = (name e, symbol e, atomicNumber e) ================================================ FILE: src/content/1.6/code/haskell/snippet18.hs ================================================ atomicNumber :: Element -> Int ================================================ FILE: src/content/1.6/code/haskell/snippet19.hs ================================================ startsWithSymbol :: Element -> Bool startsWithSymbol e = isPrefixOf (symbol e) (name e) ================================================ FILE: src/content/1.6/code/haskell/snippet20.hs ================================================ startsWithSymbol e = symbol e `isPrefixOf` name e ================================================ FILE: src/content/1.6/code/haskell/snippet21.hs ================================================ data Either a b = Left a | Right b ================================================ FILE: src/content/1.6/code/haskell/snippet22.hs ================================================ data OneOfThree a b c = Sinistral a | Medial b | Dextral c ================================================ FILE: src/content/1.6/code/haskell/snippet23.hs ================================================ Either a Void ================================================ FILE: src/content/1.6/code/haskell/snippet24.hs ================================================ data Color = Red | Green | Blue ================================================ FILE: src/content/1.6/code/haskell/snippet25.hs ================================================ data Bool = True | False ================================================ FILE: src/content/1.6/code/haskell/snippet26.hs ================================================ data Maybe a = Nothing | Just a ================================================ FILE: src/content/1.6/code/haskell/snippet27.hs ================================================ data NothingType = Nothing ================================================ FILE: src/content/1.6/code/haskell/snippet28.hs ================================================ data JustType a = Just a ================================================ FILE: src/content/1.6/code/haskell/snippet29.hs ================================================ type Maybe a = Either () a ================================================ FILE: src/content/1.6/code/haskell/snippet30.hs ================================================ data List a = Nil | Cons a (List a) ================================================ FILE: src/content/1.6/code/haskell/snippet31.hs ================================================ maybeTail :: List a -> Maybe (List a) maybeTail Nil = Nothing maybeTail (Cons _ t) = Just t ================================================ FILE: src/content/1.6/code/haskell/snippet32.hs ================================================ (a, Either b c) ================================================ FILE: src/content/1.6/code/haskell/snippet33.hs ================================================ Either (a, b) (a, c) ================================================ FILE: src/content/1.6/code/haskell/snippet34.hs ================================================ prodToSum :: (a, Either b c) -> Either (a, b) (a, c) prodToSum (x, e) = case e of Left y -> Left (x, y) Right z -> Right (x, z) ================================================ FILE: src/content/1.6/code/haskell/snippet35.hs ================================================ sumToProd :: Either (a, b) (a, c) -> (a, Either b c) sumToProd e = case e of Left (x, y) -> (x, Left y) Right (x, z) -> (x, Right z) ================================================ FILE: src/content/1.6/code/haskell/snippet36.hs ================================================ prod1 :: (Int, Either String Float) prod1 = (2, Left "Hi!") ================================================ FILE: src/content/1.6/code/haskell/snippet37.hs ================================================ data List a = Nil | Cons a (List a) ================================================ FILE: src/content/1.6/code/ocaml/snippet01.ml ================================================ let swap (a, b) = b, a ================================================ FILE: src/content/1.6/code/ocaml/snippet02.ml ================================================ (('a * 'b) * 'c) ================================================ FILE: src/content/1.6/code/ocaml/snippet03.ml ================================================ ('a * ('b * 'c)) ================================================ FILE: src/content/1.6/code/ocaml/snippet04.ml ================================================ let alpha ((a, b), c) = a, (b, c) ================================================ FILE: src/content/1.6/code/ocaml/snippet05.ml ================================================ let alpha_inv (a, (b, c)) = (a, b), c ================================================ FILE: src/content/1.6/code/ocaml/snippet06.ml ================================================ 'a * unit ================================================ FILE: src/content/1.6/code/ocaml/snippet07.ml ================================================ let rho (a, ()) = a ================================================ FILE: src/content/1.6/code/ocaml/snippet08.ml ================================================ let rho_inv a = a, () ================================================ FILE: src/content/1.6/code/ocaml/snippet09.ml ================================================ type ('a, 'b) pair = P of 'a * 'b ================================================ FILE: src/content/1.6/code/ocaml/snippet10.ml ================================================ let stmt = P ("This statement is", false) ================================================ FILE: src/content/1.6/code/ocaml/snippet11.ml ================================================ type ('a, 'b) pair = Pair of ('a * 'b) ================================================ FILE: src/content/1.6/code/ocaml/snippet12.ml ================================================ let stmt = "This statement is", false ================================================ FILE: src/content/1.6/code/ocaml/snippet13.ml ================================================ type stmt = Stmt of string * int ================================================ FILE: src/content/1.6/code/ocaml/snippet14.ml ================================================ let starts_with_symbol (name, symbol, _) = String.is_prefix name ~prefix:symbol ;; ================================================ FILE: src/content/1.6/code/ocaml/snippet15.ml ================================================ type element = { name : string ; symbol : string ; atomic_number : int } ================================================ FILE: src/content/1.6/code/ocaml/snippet16.ml ================================================ let tuple_to_elem (name, symbol, atomic_number) = { name; symbol; atomic_number } ;; ================================================ FILE: src/content/1.6/code/ocaml/snippet17.ml ================================================ let elem_to_tuple { name; symbol; atomic_number } = name, symbol, atomic_number ;; ================================================ FILE: src/content/1.6/code/ocaml/snippet18.ml ================================================ let atomic_number { atomic_number } = atomic_number ================================================ FILE: src/content/1.6/code/ocaml/snippet19.ml ================================================ let starts_with_symbol { name; symbol; _ } = String.is_prefix name ~prefix:symbol ;; ================================================ FILE: src/content/1.6/code/ocaml/snippet20.ml ================================================ (* OCaml only allows special characters in the infix operator. So, the above function name cannot be applied be infix. *) ================================================ FILE: src/content/1.6/code/ocaml/snippet21.ml ================================================ type ('a, 'b) either = | Left of 'a | Right of 'b ================================================ FILE: src/content/1.6/code/ocaml/snippet22.ml ================================================ type ('a, 'b, 'c) one_of_three = | Sinistrial of 'a | Medial of 'b | Dextral of 'c ================================================ FILE: src/content/1.6/code/ocaml/snippet23.ml ================================================ 'a void either ================================================ FILE: src/content/1.6/code/ocaml/snippet24.ml ================================================ type color = | Red | Green | Blue ================================================ FILE: src/content/1.6/code/ocaml/snippet25.ml ================================================ type bool = | True | False ================================================ FILE: src/content/1.6/code/ocaml/snippet26.ml ================================================ type 'a maybe = | Nothing | Just of 'a ================================================ FILE: src/content/1.6/code/ocaml/snippet27.ml ================================================ type nothing_type = Nothing ================================================ FILE: src/content/1.6/code/ocaml/snippet28.ml ================================================ type 'a just_type = Just of 'a ================================================ FILE: src/content/1.6/code/ocaml/snippet29.ml ================================================ type 'a maybe = (unit, 'a) either ================================================ FILE: src/content/1.6/code/ocaml/snippet30.ml ================================================ type 'a list = | Nil | Cons of 'a * 'a list ================================================ FILE: src/content/1.6/code/ocaml/snippet31.ml ================================================ type 'a maybe = | Nothing | Just of 'a let maybe_tail = function | Nil -> Nothing | Cons (_, xs) -> Just xs ;; ================================================ FILE: src/content/1.6/code/ocaml/snippet32.ml ================================================ 'a * ('b, 'c) either ================================================ FILE: src/content/1.6/code/ocaml/snippet33.ml ================================================ ('a * 'b, 'c * 'd) either ================================================ FILE: src/content/1.6/code/ocaml/snippet34.ml ================================================ let prod_to_sum (x, e) = match e with | Left y -> Left (x, y) | Right z -> Right (x, z) ;; ================================================ FILE: src/content/1.6/code/ocaml/snippet35.ml ================================================ let sum_to_prod = function | Left (x, y) -> x, Left y | Right (x, z) -> x, Right z ;; ================================================ FILE: src/content/1.6/code/ocaml/snippet36.ml ================================================ let prod1 = 2, Left "Hi!" ================================================ FILE: src/content/1.6/code/ocaml/snippet37.ml ================================================ type 'a list = | Nil | Cons of 'a * 'a list ================================================ FILE: src/content/1.6/code/reason/snippet01.re ================================================ let swap = ((a, b)) => (b, a); ================================================ FILE: src/content/1.6/code/reason/snippet02.re ================================================ (('a, 'b), 'c) ================================================ FILE: src/content/1.6/code/reason/snippet03.re ================================================ ('a, ('b, 'c)) ================================================ FILE: src/content/1.6/code/reason/snippet04.re ================================================ let alpha = (((a, b), c)) => (a, (b, c)); ================================================ FILE: src/content/1.6/code/reason/snippet05.re ================================================ let alpha_inv = ((a, (b, c))) => ((a, b), c); ================================================ FILE: src/content/1.6/code/reason/snippet06.re ================================================ ('a, unit) ================================================ FILE: src/content/1.6/code/reason/snippet07.re ================================================ let rho = ((a, ())) => a; ================================================ FILE: src/content/1.6/code/reason/snippet08.re ================================================ let rho_inv = a => (a, ()); ================================================ FILE: src/content/1.6/code/reason/snippet09.re ================================================ type pair('a, 'b) = | P('a, 'b); ================================================ FILE: src/content/1.6/code/reason/snippet10.re ================================================ let stmt = P("This statement is", false); ================================================ FILE: src/content/1.6/code/reason/snippet11.re ================================================ type pair('a, 'b) = | Pair(('a, 'b)); ================================================ FILE: src/content/1.6/code/reason/snippet12.re ================================================ let stmt = ("This statement is", false); ================================================ FILE: src/content/1.6/code/reason/snippet13.re ================================================ type stmt = | Stmt(string, int); ================================================ FILE: src/content/1.6/code/reason/snippet14.re ================================================ let starts_with_symbol = ((name, symbol, _)) => String.is_prefix(name, ~prefix=symbol); ================================================ FILE: src/content/1.6/code/reason/snippet15.re ================================================ type element = { name: string, symbol: string, atomic_number: int, }; ================================================ FILE: src/content/1.6/code/reason/snippet16.re ================================================ let tuple_to_elem = ((name, symbol, atomic_number)) => { name, symbol, atomic_number, }; ================================================ FILE: src/content/1.6/code/reason/snippet17.re ================================================ let elem_to_tuple = ({name, symbol, atomic_number}) => ( name, symbol, atomic_number, ); ================================================ FILE: src/content/1.6/code/reason/snippet18.re ================================================ let atomic_number = ({atomic_number}) => atomic_number; ================================================ FILE: src/content/1.6/code/reason/snippet19.re ================================================ let starts_with_symbol = ({name, symbol, _}) => String.is_prefix(name, ~prefix=symbol); ================================================ FILE: src/content/1.6/code/reason/snippet20.re ================================================ // ReasonML only allows special characters in the infix operator. // So, the above function name cannot be applied as infix. ================================================ FILE: src/content/1.6/code/reason/snippet21.re ================================================ type either('a, 'b) = | Left('a) | Right('b); ================================================ FILE: src/content/1.6/code/reason/snippet22.re ================================================ type one_of_three('a, 'b, 'c) = | Sinistrial('a) | Medial('b) | Dextral('c); ================================================ FILE: src/content/1.6/code/reason/snippet23.re ================================================ either('a, void) ================================================ FILE: src/content/1.6/code/reason/snippet24.re ================================================ type color = | Red | Green | Blue; ================================================ FILE: src/content/1.6/code/reason/snippet25.re ================================================ type bool = | True | False; ================================================ FILE: src/content/1.6/code/reason/snippet26.re ================================================ type maybe('a) = | Nothing | Just('a); ================================================ FILE: src/content/1.6/code/reason/snippet27.re ================================================ type nothing_type = | Nothing; ================================================ FILE: src/content/1.6/code/reason/snippet28.re ================================================ type just_type('a) = | Just('a); ================================================ FILE: src/content/1.6/code/reason/snippet29.re ================================================ type maybe('a) = either(unit, 'a); ================================================ FILE: src/content/1.6/code/reason/snippet30.re ================================================ type list('a) = | Nil | Cons('a, list('a)); ================================================ FILE: src/content/1.6/code/reason/snippet31.re ================================================ type maybe('a) = | Nothing | Just('a); let maybe_tail = fun | Nil => Nothing | Cons(_, xs) => Just(xs); ================================================ FILE: src/content/1.6/code/reason/snippet32.re ================================================ ('a, either('b, 'c)) ================================================ FILE: src/content/1.6/code/reason/snippet33.re ================================================ either(('a, 'b), ('c, 'd)) ================================================ FILE: src/content/1.6/code/reason/snippet34.re ================================================ let prod_to_sum = ((x, e)) => switch (e) { | Left(y) => Left(x, y) | Right(z) => Right(x, z) }; ================================================ FILE: src/content/1.6/code/reason/snippet35.re ================================================ let sum_to_prod = fun | Left(x, y) => (x, Left(y)) | Right(x, z) => (x, Right(z)); ================================================ FILE: src/content/1.6/code/reason/snippet36.re ================================================ let prod1 = (2, Left("Hi!")); ================================================ FILE: src/content/1.6/code/reason/snippet37.re ================================================ type list('a) = | Nil | Cons('a, list('a)); ================================================ FILE: src/content/1.6/code/scala/snippet01.scala ================================================ def swap[A, B]: ((A, B)) => (B, A) = { case (x, y) => (y, x) } ================================================ FILE: src/content/1.6/code/scala/snippet02.scala ================================================ ((A, B), C) ================================================ FILE: src/content/1.6/code/scala/snippet03.scala ================================================ (A, (B, C)) ================================================ FILE: src/content/1.6/code/scala/snippet04.scala ================================================ def alpha[A, B, C]: (((A, B), C)) => ((A, (B, C))) = { case ((x, y), z) => (x, (y, z)) } ================================================ FILE: src/content/1.6/code/scala/snippet05.scala ================================================ def alphaInv[A, B, C]: ((A, (B, C))) => (((A, B), C)) = { case (x, (y, z)) => ((x, y), z) } ================================================ FILE: src/content/1.6/code/scala/snippet06.scala ================================================ (A, Unit) ================================================ FILE: src/content/1.6/code/scala/snippet07.scala ================================================ def rho[A]: ((A, Unit)) => A = { case (x, ()) => x } ================================================ FILE: src/content/1.6/code/scala/snippet08.scala ================================================ def rhoInv[A]: A => (A, Unit) = x => (x, ()) ================================================ FILE: src/content/1.6/code/scala/snippet09.scala ================================================ sealed trait Pair[A, B] case class P[A, B](a: A, b: B) extends Pair[A, B] ================================================ FILE: src/content/1.6/code/scala/snippet10.scala ================================================ val stmt: Pair[String, Boolean] = P("This statement is", false) ================================================ FILE: src/content/1.6/code/scala/snippet11.scala ================================================ case class Pair[A, B](a: A, b: B) ================================================ FILE: src/content/1.6/code/scala/snippet12.scala ================================================ val stmt = ("This statement is", false) ================================================ FILE: src/content/1.6/code/scala/snippet13.scala ================================================ case class Stmt(s: String, b: Boolean) ================================================ FILE: src/content/1.6/code/scala/snippet14.scala ================================================ val startsWithSymbol: ((String, String, Int)) => Boolean = { case (name, symbol, _) => name.startsWith(symbol) } ================================================ FILE: src/content/1.6/code/scala/snippet15.scala ================================================ case class Element( name: String, symbol: String, atomicNumber: Int ) ================================================ FILE: src/content/1.6/code/scala/snippet16.scala ================================================ val tupleToElem: ((String, String, Int)) => Element = { case (n, s, a) => Element(n, s, a) } ================================================ FILE: src/content/1.6/code/scala/snippet17.scala ================================================ val elemToTuple: Element => (String, String, Int) = e => (e.name, e.symbol, e.atomicNumber) ================================================ FILE: src/content/1.6/code/scala/snippet18.scala ================================================ val atomicNumber: Element => Int ================================================ FILE: src/content/1.6/code/scala/snippet19.scala ================================================ val startsWithSymbol: Element => Boolean = e => e.name startsWith e.symbol ================================================ FILE: src/content/1.6/code/scala/snippet20.scala ================================================ val startsWithSymbol: Element => Boolean = e => e.name startsWith e.symbol ================================================ FILE: src/content/1.6/code/scala/snippet21.scala ================================================ sealed trait Either[+A, +B] case class Left[A](v: A) extends Either[A, Nothing] case class Right[B](v: B) extends Either[Nothing, B] ================================================ FILE: src/content/1.6/code/scala/snippet22.scala ================================================ sealed trait OneOfThree[+A, +B, +C] case class Sinistral[A](v: A) extends OneOfThree[A, Nothing, Nothing] case class Medial[B](v: B) extends OneOfThree[Nothing, B, Nothing] case class Dextral[C](v: C) extends OneOfThree[Nothing, Nothing, C] ================================================ FILE: src/content/1.6/code/scala/snippet23.scala ================================================ Either[A, Nothing] ================================================ FILE: src/content/1.6/code/scala/snippet24.scala ================================================ sealed trait Color case object Red extends Color case object Green extends Color case object Blue extends Color ================================================ FILE: src/content/1.6/code/scala/snippet25.scala ================================================ sealed trait Bool case object True extends Bool case object False extends Bool ================================================ FILE: src/content/1.6/code/scala/snippet26.scala ================================================ sealed trait Option[+A] case object None extends Option[Nothing] case class Some[A](a: A) extends Option[A] ================================================ FILE: src/content/1.6/code/scala/snippet27.scala ================================================ case object NoneType ================================================ FILE: src/content/1.6/code/scala/snippet28.scala ================================================ case class SomeType[A](a: A) ================================================ FILE: src/content/1.6/code/scala/snippet29.scala ================================================ type Option[A] = Either[Unit, A] ================================================ FILE: src/content/1.6/code/scala/snippet30.scala ================================================ sealed trait List[+A] case object Nil extends List[Nothing] case class Cons[A](h: A, t: List[A]) extends List[A] ================================================ FILE: src/content/1.6/code/scala/snippet31.scala ================================================ def maybeTail[A]: List[A] => Option[List[A]] = { case Nil => None case Cons(_, t) => Some(t) } ================================================ FILE: src/content/1.6/code/scala/snippet32.scala ================================================ (A, Either[B, C]) ================================================ FILE: src/content/1.6/code/scala/snippet33.scala ================================================ Either[(A, B), (A, C)] ================================================ FILE: src/content/1.6/code/scala/snippet34.scala ================================================ def prodToSum[A, B, C]: ((A, Either[B, C])) => Either[(A, B), (A, C)] = { case (x, e) => e match { case Left(y) => Left((x, y)) case Right(z) => Right((x, z)) } } ================================================ FILE: src/content/1.6/code/scala/snippet35.scala ================================================ def sumToProd[A, B, C]: Either[(A, B), (A, C)] => (A, Either[B, C]) = { case Left((x, y)) => (x, Left(y)) case Right((x, z)) => (x, Right(z)) } ================================================ FILE: src/content/1.6/code/scala/snippet36.scala ================================================ val prod1: (Int, Either[String, Float]) = (2, Left("Hi!")) ================================================ FILE: src/content/1.6/code/scala/snippet37.scala ================================================ sealed trait List[+A] case object Nil extends List[Nothing] case class Cons[A](h: A, t: List[A]) extends List[A] ================================================ FILE: src/content/1.6/simple-algebraic-data-types.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{W}{e've seen two basic} ways of combining types: using a product and a coproduct. It turns out that a lot of data structures in everyday programming can be built using just these two mechanisms. This fact has important practical consequences. Many properties of data structures are composable. For instance, if you know how to compare values of basic types for equality, and you know how to generalize these comparisons to product and coproduct types, you can automate the derivation of equality operators for composite types. In Haskell you can automatically derive equality, comparison, conversion to and from string, and more, for a large subset of composite types. Let's have a closer look at product and sum types as they appear in programming. \section{Product Types} The canonical implementation of a product of two types in a programming language is a pair. In Haskell, a pair is a primitive type constructor; in C++ it's a relatively complex template defined in the Standard Library. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/pair.jpg} \end{figure} \noindent Pairs are not strictly commutative: a pair \code{(Int, Bool)} cannot be substituted for a pair \code{(Bool, Int)}, even though they carry the same information. They are, however, commutative up to isomorphism --- the isomorphism being given by the \code{swap} function (which is its own inverse): \src{snippet01} You can think of the two pairs as simply using a different format for storing the same data. It's just like big endian vs. little endian. You can combine an arbitrary number of types into a product by nesting pairs inside pairs, but there is an easier way: nested pairs are equivalent to tuples. It's the consequence of the fact that different ways of nesting pairs are isomorphic. If you want to combine three types in a product, \code{a}, \code{b}, and \code{c}, in this order, you can do it in two ways: \src{snippet02} or \src{snippet03} These types are different --- you can't pass one to a function that expects the other --- but their elements are in one-to-one correspondence. There is a function that maps one to another: \src{snippet04} and this function is invertible: \src{snippet05} so it's an isomorphism. These are just different ways of repackaging the same data. You can interpret the creation of a product type as a binary operation on types. From that perspective, the above isomorphism looks very much like the associativity law we've seen in monoids: \[(a * b) * c = a * (b * c)\] Except that, in the monoid case, the two ways of composing products were equal, whereas here they are only equal ``up to isomorphism.'' If we can live with isomorphisms, and don't insist on strict equality, we can go even further and show that the unit type, \code{()}, is the unit of the product the same way 1 is the unit of multiplication. Indeed, the pairing of a value of some type \code{a} with a unit doesn't add any information. The type: \src{snippet06} is isomorphic to \code{a}. Here's the isomorphism: \src{snippet07} \src{snippet08} These observations can be formalized by saying that $\Set$ (the category of sets) is a \newterm{monoidal category}. It's a category that's also a monoid, in the sense that you can multiply objects (here, take their Cartesian product). I'll talk more about monoidal categories, and give the full definition in the future. There is a more general way of defining product types in Haskell --- especially, as we'll see soon, when they are combined with sum types. It uses named constructors with multiple arguments. A pair, for instance, can be defined alternatively as: \src{snippet09} Here, \code{Pair a b} is the name of the type parameterized by two other types, \code{a} and \code{b}; and \code{P} is the name of the data constructor. You define a pair type by passing two types to the \code{Pair} type constructor. You construct a pair value by passing two values of appropriate types to the constructor \code{P}. For instance, let's define a value \code{stmt} as a pair of \code{String} and \code{Bool}: \src{snippet10} The first line is the type signature. It uses the type constructor \code{Pair}, with \code{String} and \code{Bool} replacing \code{a} and the \code{b} in the generic definition of \code{Pair}. The second line defines the actual value by passing a concrete string and a concrete Boolean to the data constructor \code{P}. Type constructors are used to construct types; data constructors, to construct values. Since the name spaces for type and data constructors are separate in Haskell, you will often see the same name used for both, as in: \src{snippet11} And if you squint hard enough, you may even view the built-in pair type as a variation on this kind of declaration, where the name \code{Pair} is replaced with the binary operator \code{(,)}. In fact you can use \code{(,)} just like any other named constructor and create pairs using prefix notation: \src{snippet12} Similarly, you can use \code{(,,)} to create triples, and so on. Instead of using generic pairs or tuples, you can also define specific named product types, as in: \src{snippet13} which is just a product of \code{String} and \code{Bool}, but it's given its own name and constructor. The advantage of this style of declaration is that you may define many types that have the same content but different meaning and functionality, and which cannot be substituted for each other. Programming with tuples and multi-argument constructors can get messy and error prone --- keeping track of which component represents what. It's often preferable to give names to components. A product type with named fields is called a \newterm{record} in Haskell, and a \code{struct} in C. \section{Records} Let's have a look at a simple example. We want to describe chemical elements by combining two strings, name and symbol; and an integer, the atomic number; into one data structure. We can use a tuple \code{(String, String, Int)} and remember which component represents what. We would extract components by pattern matching, as in this function that checks if the symbol of the element is the prefix of its name (as in \textbf{He} being the prefix of \textbf{Helium}): \src{snippet14} This code is error prone, and is hard to read and maintain. It's much better to define a record: \src{snippet15} The two representations are isomorphic, as witnessed by these two conversion functions, which are the inverse of each other: \src{snippet16} \src{snippet17} Notice that the names of record fields also serve as functions to access these fields. For instance, \code{atomicNumber e} retrieves the \code{atomicNumber} field from \code{e}. We use \code{atomicNumber} as a function of the type: \src{snippet18} With the record syntax for \code{Element}, our function \code{startsWithSymbol} becomes more readable: \src{snippet19} We could even use the Haskell trick of turning the function \code{isPrefixOf} into an infix operator by surrounding it with backquotes, and make it read almost like a sentence: \src{snippet20} The parentheses could be omitted in this case, because an infix operator has lower precedence than a function call. \section{Sum Types} Just as the product in the category of sets gives rise to product types, the coproduct gives rise to sum types. The canonical implementation of a sum type in Haskell is: \src{snippet21} And like pairs, \code{Either}s are commutative (up to isomorphism), can be nested, and the nesting order is irrelevant (up to isomorphism). So we can, for instance, define a sum equivalent of a triple: \src{snippet22} and so on. It turns out that $\Set$ is also a (symmetric) monoidal category with respect to coproduct. The role of the binary operation is played by the disjoint sum, and the role of the unit element is played by the initial object. In terms of types, we have \code{Either} as the monoidal operator and \code{Void}, the uninhabited type, as its neutral element. You can think of \code{Either} as plus, and \code{Void} as zero. Indeed, adding \code{Void} to a sum type doesn't change its content. For instance: \src{snippet23} is isomorphic to \code{a}. That's because there is no way to construct a \code{Right} version of this type --- there isn't a value of type \code{Void}. The only inhabitants of \code{Either a Void} are constructed using the \code{Left} constructors and they simply encapsulate a value of type \code{a}. So, symbolically, $a + 0 = a$. Sum types are pretty common in Haskell, but their C++ equivalents, unions or variants, are much less common. There are several reasons for that. First of all, the simplest sum types are just enumerations and are implemented using \code{enum} in C++. The equivalent of the Haskell sum type: \src{snippet24} is the C++: \begin{snip}{cpp} enum { Red, Green, Blue }; \end{snip} An even simpler sum type: \src{snippet25} is the primitive \code{bool} in C++. Simple sum types that encode the presence or absence of a value are variously implemented in C++ using special tricks and ``impossible'' values, like empty strings, negative numbers, null pointers, etc. This kind of optionality, if deliberate, is expressed in Haskell using the \code{Maybe} type: \src{snippet26} The \code{Maybe} type is a sum of two types. You can see this if you separate the two constructors into individual types. The first one would look like this: \src{snippet27} It's an enumeration with one value called \code{Nothing}. In other words, it's a singleton, which is equivalent to the unit type \code{()}. The second part: \src{snippet28} is just an encapsulation of the type \code{a}. We could have encoded \code{Maybe} as: \src{snippet29} More complex sum types are often faked in C++ using pointers. A pointer can be either null, or point to a value of specific type. For instance, a Haskell list type, which can be defined as a (recursive) sum type: \src{snippet30} can be translated to C++ using the null pointer trick to implement the empty list: \begin{snip}{cpp} template class List { Node * _head; public: List() : _head(nullptr) {} // Nil List(A a, List l) // Cons : _head(new Node(a, l)) {} }; \end{snip} Notice that the two Haskell constructors \code{Nil} and \code{Cons} are translated into two overloaded \code{List} constructors with analogous arguments (none, for \code{Nil}; and a value and a list for \code{Cons}). The \code{List} class doesn't need a tag to distinguish between the two components of the sum type. Instead it uses the special \code{nullptr} value for \code{\_head} to encode \code{Nil}. The main difference, though, between Haskell and C++ types is that Haskell data structures are immutable. If you create an object using one particular constructor, the object will forever remember which constructor was used and what arguments were passed to it. So a \code{Maybe} object that was created as \code{Just "energy"} will never turn into \code{Nothing}. Similarly, an empty list will forever be empty, and a list of three elements will always have the same three elements. It's this immutability that makes construction reversible. Given an object, you can always disassemble it down to parts that were used in its construction. This deconstruction is done with pattern matching and it reuses constructors as patterns. Constructor arguments, if any, are replaced with variables (or other patterns). The \code{List} data type has two constructors, so the deconstruction of an arbitrary \code{List} uses two patterns corresponding to those constructors. One matches the empty \code{Nil} list, and the other a \code{Cons}-constructed list. For instance, here's the definition of a simple function on \code{List}s: \src{snippet31} The first part of the definition of \code{maybeTail} uses the \code{Nil} constructor as pattern and returns \code{Nothing}. The second part uses the \code{Cons} constructor as pattern. It replaces the first constructor argument with a wildcard, because we are not interested in it. The second argument to \code{Cons} is bound to the variable \code{t} (I will call these things variables even though, strictly speaking, they never vary: once bound to an expression, a variable never changes). The return value is \code{Just t}. Now, depending on how your \code{List} was created, it will match one of the clauses. If it was created using \code{Cons}, the two arguments that were passed to it will be retrieved (and the first discarded). Even more elaborate sum types are implemented in C++ using polymorphic class hierarchies. A family of classes with a common ancestor may be understood as one variant type, in which the vtable serves as a hidden tag. What in Haskell would be done by pattern matching on the constructor, and by calling specialized code, in C++ is accomplished by dispatching a call to a virtual function based on the vtable pointer. You will rarely see \code{union} used as a sum type in C++ because of severe limitations on what can go into a union. You can't even put a \code{std::string} into a union because it has a copy constructor. \section{Algebra of Types} Taken separately, product and sum types can be used to define a variety of useful data structures, but the real strength comes from combining the two. Once again we are invoking the power of composition. Let's summarize what we've discovered so far. We've seen two commutative monoidal structures underlying the type system: We have the sum types with \code{Void} as the neutral element, and the product types with the unit type, \code{()}, as the neutral element. We'd like to think of them as analogous to addition and multiplication. In this analogy, \code{Void} would correspond to zero, and unit, \code{()}, to one. Let's see how far we can stretch this analogy. For instance, does multiplication by zero give zero? In other words, is a product type with one component being \code{Void} isomorphic to \code{Void}? For example, is it possible to create a pair of, say \code{Int} and \code{Void}? To create a pair you need two values. Although you can easily come up with an integer, there is no value of type \code{Void}. Therefore, for any type \code{a}, the type \code{(a, Void)} is uninhabited --- has no values --- and is therefore equivalent to \code{Void}. In other words, $a \times 0 = 0$. Another thing that links addition and multiplication is the distributive property: \[a \times (b + c) = a \times b + a \times c\] Does it also hold for product and sum types? Yes, it does --- up to isomorphisms, as usual. The left hand side corresponds to the type: \src{snippet32} and the right hand side corresponds to the type: \src{snippet33} Here's the function that converts them one way: \src{snippet34} and here's one that goes the other way: \src{snippet35} The \code{case of} clause is used for pattern matching inside functions. Each pattern is followed by an arrow and the expression to be evaluated when the pattern matches. For instance, if you call \code{prodToSum} with the value: \src{snippet36} the \code{e} in \code{case e of} will be equal to \code{Left "Hi!"}. It will match the pattern \code{Left y}, substituting \code{"Hi!"} for \code{y}. Since the \code{x} has already been matched to \code{2}, the result of the \code{case of} clause, and the whole function, will be \code{Left (2, "Hi!")}, as expected. I'm not going to prove that these two functions are the inverse of each other, but if you think about it, they must be! They are just trivially re-packing the contents of the two data structures. It's the same data, only different format. Mathematicians have a name for two such intertwined monoids: it's called a \newterm{semiring}. It's not a full \newterm{ring}, because we can't define subtraction of types. That's why a semiring is sometimes called a \newterm{rig}, which is a pun on ``ring without an \emph{n}'' (negative). But barring that, we can get a lot of mileage from translating statements about, say, natural numbers, which form a rig, to statements about types. Here's a translation table with some entries of interest: \begin{longtable}[]{@{}ll@{}} \toprule Numbers & Types\tabularnewline \midrule \endhead $0$ & \code{Void}\tabularnewline $1$ & \code{()}\tabularnewline $a + b$ & \code{data Either a b = Left a | Right b}\tabularnewline $a \times b$ & \code{(a, b)} or \code{data Pair a b = Pair a b}\tabularnewline $2 = 1 + 1$ & \code{data Bool = True | False}\tabularnewline $1 + a$ & \code{data Maybe a = Nothing | Just a}\tabularnewline \bottomrule \end{longtable} \noindent The list type is quite interesting, because it's defined as a solution to an equation. The type we are defining appears on both sides of the equation: \src{snippet37} If we do our usual substitutions, and also replace \code{List a} with \code{x}, we get the equation: \begin{Verbatim} x = 1 + a * x \end{Verbatim} We can't solve it using traditional algebraic methods because we can't subtract or divide types. But we can try a series of substitutions, where we keep replacing \code{x} on the right hand side with \code{(1 + a*x)}, and use the distributive property. This leads to the following series: \begin{Verbatim} x = 1 + a*x x = 1 + a*(1 + a*x) = 1 + a + a*a*x x = 1 + a + a*a*(1 + a*x) = 1 + a + a*a + a*a*a*x ... x = 1 + a + a*a + a*a*a + a*a*a*a... \end{Verbatim} We end up with an infinite sum of products (tuples), which can be interpreted as: A list is either empty, \code{1}; or a singleton, \code{a}; or a pair, \code{a*a}; or a triple, \code{a*a*a}; etc\ldots{} Well, that's exactly what a list is --- a string of \code{a}s! There's much more to lists than that, and we'll come back to them and other recursive data structures after we learn about functors and fixed points. Solving equations with symbolic variables --- that's algebra! It's what gives these types their name: algebraic data types. Finally, I should mention one very important interpretation of the algebra of types. Notice that a product of two types \code{a} and \code{b} must contain both a value of type \code{a} \emph{and} a value of type \code{b}, which means both types must be inhabited. A sum of two types, on the other hand, contains either a value of type \code{a} \emph{or} a value of type \code{b}, so it's enough if one of them is inhabited. Logical \emph{and} and \emph{or} also form a semiring, and it too can be mapped into type theory: \begin{longtable}[]{@{}ll@{}} \toprule Logic & Types\tabularnewline \midrule \endhead $\mathit{false}$ & \code{Void}\tabularnewline $\mathit{true}$ & \code{()}\tabularnewline $a \mathbin{||} b$ & \code{data Either a b = Left a | Right b}\tabularnewline $a \mathbin{\&\&} b$ & \code{(a, b)}\tabularnewline \bottomrule \end{longtable} \noindent This analogy goes deeper, and is the basis of the Curry-Howard isomorphism between logic and type theory. We'll come back to it when we talk about function types. \section{Challenges} \begin{enumerate} \tightlist \item Show the isomorphism between \code{Maybe a} and \code{Either () a}. \item Here's a sum type defined in Haskell: \begin{snip}{haskell} data Shape = Circle Float | Rect Float Float \end{snip} When we want to define a function like \code{area} that acts on a \code{Shape}, we do it by pattern matching on the two constructors: \begin{snip}{haskell} area :: Shape -> Float area (Circle r) = pi * r * r area (Rect d h) = d * h \end{snip} Implement \code{Shape} in C++ or Java as an interface and create two classes: \code{Circle} and \code{Rect}. Implement \code{area} as a virtual function. \item Continuing with the previous example: We can easily add a new function \code{circ} that calculates the circumference of a \code{Shape}. We can do it without touching the definition of \code{Shape}: \begin{snip}{haskell} circ :: Shape -> Float circ (Circle r) = 2.0 * pi * r circ (Rect d h) = 2.0 * (d + h) \end{snip} Add \code{circ} to your C++ or Java implementation. What parts of the original code did you have to touch? \item Continuing further: Add a new shape, \code{Square}, to \code{Shape} and make all the necessary updates. What code did you have to touch in Haskell vs. C++ or Java? (Even if you're not a Haskell programmer, the modifications should be pretty obvious.) \item Show that $a + a = 2 \times a$ holds for types (up to isomorphism). Remember that $2$ corresponds to \code{Bool}, according to our translation table. \end{enumerate} ================================================ FILE: src/content/1.7/code/haskell/snippet01.hs ================================================ data Maybe a = Nothing | Just a ================================================ FILE: src/content/1.7/code/haskell/snippet02.hs ================================================ f :: a -> b ================================================ FILE: src/content/1.7/code/haskell/snippet03.hs ================================================ f' :: Maybe a -> Maybe b f' Nothing = Nothing f' (Just x) = Just (f x) ================================================ FILE: src/content/1.7/code/haskell/snippet04.hs ================================================ fmap :: (a -> b) -> (Maybe a -> Maybe b) ================================================ FILE: src/content/1.7/code/haskell/snippet05.hs ================================================ fmap :: (a -> b) -> Maybe a -> Maybe b ================================================ FILE: src/content/1.7/code/haskell/snippet06.hs ================================================ fmap _ Nothing = Nothing fmap f (Just x) = Just (f x) ================================================ FILE: src/content/1.7/code/haskell/snippet07.hs ================================================ id x = x ================================================ FILE: src/content/1.7/code/haskell/snippet08.hs ================================================ fmap id = id ================================================ FILE: src/content/1.7/code/haskell/snippet09.hs ================================================ fmap (g . f) = fmap g . fmap f ================================================ FILE: src/content/1.7/code/haskell/snippet10.hs ================================================ class Eq a where (==) :: a -> a -> Bool ================================================ FILE: src/content/1.7/code/haskell/snippet11.hs ================================================ data Point = Pt Float Float ================================================ FILE: src/content/1.7/code/haskell/snippet12.hs ================================================ instance Eq Point where (Pt x y) == (Pt x' y') = x == x' && y == y' ================================================ FILE: src/content/1.7/code/haskell/snippet13.hs ================================================ class Functor f where fmap :: (a -> b) -> f a -> f b ================================================ FILE: src/content/1.7/code/haskell/snippet14.hs ================================================ instance Functor Maybe where fmap _ Nothing = Nothing fmap f (Just x) = Just (f x) ================================================ FILE: src/content/1.7/code/haskell/snippet15.hs ================================================ data List a = Nil | Cons a (List a) ================================================ FILE: src/content/1.7/code/haskell/snippet16.hs ================================================ fmap :: (a -> b) -> (List a -> List b) ================================================ FILE: src/content/1.7/code/haskell/snippet17.hs ================================================ fmap f (Cons x t) = Cons (f x) (fmap f t) ================================================ FILE: src/content/1.7/code/haskell/snippet18.hs ================================================ instance Functor List where fmap _ Nil = Nil fmap f (Cons x t) = Cons (f x) (fmap f t) ================================================ FILE: src/content/1.7/code/haskell/snippet19.hs ================================================ (->) a b ================================================ FILE: src/content/1.7/code/haskell/snippet20.hs ================================================ (->) a ================================================ FILE: src/content/1.7/code/haskell/snippet21.hs ================================================ fmap :: (a -> b) -> (r -> a) -> (r -> b) ================================================ FILE: src/content/1.7/code/haskell/snippet22.hs ================================================ instance Functor ((->) r) where fmap f g = f . g ================================================ FILE: src/content/1.7/code/haskell/snippet23.hs ================================================ fmap f g = (.) f g ================================================ FILE: src/content/1.7/code/haskell/snippet24.hs ================================================ fmap = (.) ================================================ FILE: src/content/1.7/code/haskell/snippet25.hs ================================================ nats :: [Integer] nats = [1..] ================================================ FILE: src/content/1.7/code/haskell/snippet26.hs ================================================ data Const c a = Const c ================================================ FILE: src/content/1.7/code/haskell/snippet27.hs ================================================ fmap :: (a -> b) -> Const c a -> Const c b ================================================ FILE: src/content/1.7/code/haskell/snippet28.hs ================================================ instance Functor (Const c) where fmap _ (Const v) = Const v ================================================ FILE: src/content/1.7/code/haskell/snippet29.hs ================================================ maybeTail :: [a] -> Maybe [a] maybeTail [] = Nothing maybeTail (x:xs) = Just xs ================================================ FILE: src/content/1.7/code/haskell/snippet30.hs ================================================ square x = x * x mis :: Maybe [Int] mis = Just [1, 2, 3] mis2 = fmap (fmap square) mis ================================================ FILE: src/content/1.7/code/haskell/snippet31.hs ================================================ mis2 = (fmap . fmap) square mis ================================================ FILE: src/content/1.7/code/haskell/snippet32.hs ================================================ fmap :: (a -> b) -> (f a -> f b) ================================================ FILE: src/content/1.7/code/haskell/snippet33.hs ================================================ square :: Int -> Int ================================================ FILE: src/content/1.7/code/haskell/snippet34.hs ================================================ [Int] -> [Int] ================================================ FILE: src/content/1.7/code/haskell/snippet35.hs ================================================ Maybe [Int] -> Maybe [Int] ================================================ FILE: src/content/1.7/code/ocaml/snippet01.ml ================================================ type 'a option = | None | Some of 'a ================================================ FILE: src/content/1.7/code/ocaml/snippet02.ml ================================================ module type AtoB = sig type a type b val f : a -> b end ================================================ FILE: src/content/1.7/code/ocaml/snippet03.ml ================================================ let f' f = function | None -> None | Some x -> Some (f x) ;; ================================================ FILE: src/content/1.7/code/ocaml/snippet04.ml ================================================ module type Maybe_Functor = sig type a type b val fmap : (a -> b) -> a option -> b option end ================================================ FILE: src/content/1.7/code/ocaml/snippet05.ml ================================================ module type Maybe_Functor = sig type a type b val fmap : (a -> b) -> a option -> b option end ================================================ FILE: src/content/1.7/code/ocaml/snippet06.ml ================================================ let fmap f = function | None -> None | Some x -> Some (f x) ;; ================================================ FILE: src/content/1.7/code/ocaml/snippet07.ml ================================================ let id x = x ================================================ FILE: src/content/1.7/code/ocaml/snippet08.ml ================================================ module Test_Functor_Id (F : Functor) = struct open F let test_id x = assert (fmap id x = x) end ================================================ FILE: src/content/1.7/code/ocaml/snippet09.ml ================================================ module Test_Functor_Compose (F : Functor) = struct open F (* Compose *) let ( <.> ) f g x = f (g x) let test_compose f g x = assert (fmap (f <.> g) x = fmap f (fmap g x)) ;; end ================================================ FILE: src/content/1.7/code/ocaml/snippet10.ml ================================================ module type Eq = sig type a val ( == ) : a -> a -> bool end ================================================ FILE: src/content/1.7/code/ocaml/snippet11.ml ================================================ type point = Pt of float * float ================================================ FILE: src/content/1.7/code/ocaml/snippet12.ml ================================================ module Point_Eq (E : Eq with type a = float) = struct type a = point let ( == ) (Pt (p1x, p1y)) (Pt (p2x, p2y)) = E.(p1x == p2x) && E.(p2x == p2y) ;; end ================================================ FILE: src/content/1.7/code/ocaml/snippet13.ml ================================================ module type Functor = sig type 'a t val fmap : ('a -> 'b) -> 'a t -> 'b t end ================================================ FILE: src/content/1.7/code/ocaml/snippet14.ml ================================================ module Option_Functor : Functor with type 'a t = 'a option = struct type 'a t = 'a option let fmap f = function | None -> None | Some x -> Some (f x) ;; end ================================================ FILE: src/content/1.7/code/ocaml/snippet15.ml ================================================ type 'a list = | Nil | Cons of 'a * 'a list ================================================ FILE: src/content/1.7/code/ocaml/snippet16.ml ================================================ module type List_Functor_Type = sig type 'a t = 'a list val fmap : ('a -> 'b) -> 'a list -> 'b list end ================================================ FILE: src/content/1.7/code/ocaml/snippet17.ml ================================================ let rec fmap f = function | Nil -> Nil | Cons (x, xs) -> Cons (f x, fmap f xs) ;; ================================================ FILE: src/content/1.7/code/ocaml/snippet18.ml ================================================ module List_Functor : Functor with type 'a t = 'a list = struct type 'a t = 'a list let rec fmap f = function | Nil -> Nil | Cons (x, xs) -> Cons (f x, fmap f xs) ;; end ================================================ FILE: src/content/1.7/code/ocaml/snippet19.ml ================================================ type ('a, 'b) t = 'a -> 'b ================================================ FILE: src/content/1.7/code/ocaml/snippet20.ml ================================================ module type T = sig type t end module Partially_Applied_FunctionType (T : T) = struct type 'b t = T.t -> 'b end ================================================ FILE: src/content/1.7/code/ocaml/snippet21.ml ================================================ module type Reader_Fmap_Example = sig val fmap : ('a -> 'b) -> ('r -> 'a) -> 'r -> 'b end ================================================ FILE: src/content/1.7/code/ocaml/snippet22.ml ================================================ module Reader_Functor (T : T) : Functor = struct type 'a t = T.t -> 'a let fmap f ra r = f (ra r) end ================================================ FILE: src/content/1.7/code/ocaml/snippet23.ml ================================================ let fmap f g = compose f g ================================================ FILE: src/content/1.7/code/ocaml/snippet24.ml ================================================ let fmap : ('a -> 'b) -> ('r -> 'a) -> 'r -> 'b = compose ================================================ FILE: src/content/1.7/code/ocaml/snippet25.ml ================================================ let nats = Caml.Stream.from (fun i -> Some (i + 1)) ================================================ FILE: src/content/1.7/code/ocaml/snippet26.ml ================================================ type ('c, 'a) const = Const of 'c ================================================ FILE: src/content/1.7/code/ocaml/snippet27.ml ================================================ module type Const_Functor_Example = sig val fmap : ('a -> 'b) -> ('c, 'a) const -> ('c, 'b) const end ================================================ FILE: src/content/1.7/code/ocaml/snippet28.ml ================================================ module Const_Functor (T : T) : Functor = struct type 'a t = (T.t, 'a) const let fmap f (Const c) = Const c (* or even let fmap _ c = c *) end ================================================ FILE: src/content/1.7/code/ocaml/snippet29.ml ================================================ let maybe_tail = function | [] -> None | _ :: xs -> Some xs ;; ================================================ FILE: src/content/1.7/code/ocaml/snippet30.ml ================================================ let square x = x * x let mis = Some (Cons (1, Cons (2, Cons (3, Nil)))) let mis2 = Option_Functor.fmap (List_Functor.fmap square) mis ================================================ FILE: src/content/1.7/code/ocaml/snippet31.ml ================================================ let fmapO = Option_Functor.fmap let fmapL = List_Functor.fmap let fmapC f l = (compose fmapO fmapL) f l let mis2 = fmapC square mis ================================================ FILE: src/content/1.7/code/ocaml/snippet32.ml ================================================ module type Fmap_Alt_Sig_Example = sig type 'a t val fmap : ('a -> 'b) -> 'a t -> 'b t end ================================================ FILE: src/content/1.7/code/ocaml/snippet33.ml ================================================ module type Square_Signature = sig val square : int -> int end ================================================ FILE: src/content/1.7/code/ocaml/snippet34.ml ================================================ int list -> int list ================================================ FILE: src/content/1.7/code/ocaml/snippet35.ml ================================================ int list option -> int list option ================================================ FILE: src/content/1.7/code/reason/snippet01.re ================================================ let compose = (f, g, x) => f(g(x)); ================================================ FILE: src/content/1.7/code/reason/snippet02.re ================================================ type option('a) = | None | Some('a); ================================================ FILE: src/content/1.7/code/reason/snippet03.re ================================================ module type AtoB = { type a; type b; let f: a => b; }; ================================================ FILE: src/content/1.7/code/reason/snippet04.re ================================================ let f' = f => fun | None => None | Some(x) => Some(f(x)); ================================================ FILE: src/content/1.7/code/reason/snippet05.re ================================================ module type Maybe_Functor = { type a; type b; let fmap: (a => b, option(a)) => option(b); }; ================================================ FILE: src/content/1.7/code/reason/snippet06.re ================================================ module type Maybe_Functor = { type a; type b; let fmap: (a => b, option(a)) => option(b); }; ================================================ FILE: src/content/1.7/code/reason/snippet07.re ================================================ let fmap = f => fun | None => None | Some(x) => Some(f(x)); ================================================ FILE: src/content/1.7/code/reason/snippet08.re ================================================ let id = x => x; ================================================ FILE: src/content/1.7/code/reason/snippet09.re ================================================ module Test_Functor_Id = (F: Functor) => { open F; let test_id = x => assert(fmap(id, x) == x); }; ================================================ FILE: src/content/1.7/code/reason/snippet10.re ================================================ module Test_Functor_Compose = (F: Functor) => { open F; /* Compose */ let (<.>) = (f, g, x) => f(g(x)); let test_compose = (f, g, x) => assert(fmap(f <.> g, x) == fmap(f, fmap(g, x))); }; ================================================ FILE: src/content/1.7/code/reason/snippet11.re ================================================ module type Eq = { type a; let (===): (a, a) => bool; }; ================================================ FILE: src/content/1.7/code/reason/snippet12.re ================================================ type point = | Pt(float, float); ================================================ FILE: src/content/1.7/code/reason/snippet13.re ================================================ module Point_Eq = (E: Eq with type a = float) => { type a = point; let (===) = (Pt(p1x, p1y), Pt(p2x, p2y)) => E.(p1x === p2x) && E.(p2x === p2y); }; ================================================ FILE: src/content/1.7/code/reason/snippet14.re ================================================ module type Functor = { type t('a); let fmap: ('a => 'b, t('a)) => t('b); }; ================================================ FILE: src/content/1.7/code/reason/snippet15.re ================================================ module Option_Functor: Functor with type t('a) = option('a) = { type t('a) = option('a); let fmap = f => fun | None => None | Some(x) => Some(f(x)); }; ================================================ FILE: src/content/1.7/code/reason/snippet16.re ================================================ type list('a) = | Nil | Cons('a, list('a)); ================================================ FILE: src/content/1.7/code/reason/snippet17.re ================================================ module type List_Functor_Type = { type t('a) = list('a); let fmap: ('a => 'b, list('a)) => list('b); }; ================================================ FILE: src/content/1.7/code/reason/snippet18.re ================================================ let rec fmap = f => fun | Nil => Nil | Cons(x, xs) => Cons(f(x), fmap(f, xs)); ================================================ FILE: src/content/1.7/code/reason/snippet19.re ================================================ module List_Functor: Functor with type t('a) = list('a) = { type t('a) = list('a); let rec fmap = f => fun | Nil => Nil | Cons(x, xs) => Cons(f(x), fmap(f, xs)); }; ================================================ FILE: src/content/1.7/code/reason/snippet20.re ================================================ type t('a, 'b) = 'a => 'b; ================================================ FILE: src/content/1.7/code/reason/snippet21.re ================================================ module type T = {type t;}; module Partially_Applied_FunctionType = (T: T) => { type t('b) = T.t => 'b; }; ================================================ FILE: src/content/1.7/code/reason/snippet22.re ================================================ module type Reader_Fmap_Example = { let fmap: ('a => 'b, 'r => 'a, 'r) => 'b; }; ================================================ FILE: src/content/1.7/code/reason/snippet23.re ================================================ module Reader_Functor = (T: T) : Functor => { type t('a) = T.t => 'a; let fmap = (f, ra, r) => f(ra(r)); }; ================================================ FILE: src/content/1.7/code/reason/snippet24.re ================================================ let fmap: ('a => 'b, 'r => 'a, 'r) => 'b = ( compose: ('a => 'b, 'r => 'a, 'r) => 'b ); ================================================ FILE: src/content/1.7/code/reason/snippet25.re ================================================ let nats = Caml.Stream.from(i => Some(i + 1)); ================================================ FILE: src/content/1.7/code/reason/snippet26.re ================================================ type const('c, 'a) = | Const('c); ================================================ FILE: src/content/1.7/code/reason/snippet27.re ================================================ module type Const_Functor_Example = { let fmap: ('a => 'b, const('c, 'a)) => const('c, 'b); }; ================================================ FILE: src/content/1.7/code/reason/snippet28.re ================================================ module Const_Functor = (T: T) : Functor => { type t('a) = const(T.t, 'a); // or even let fmap = (_, c) => c; let fmap = (f, Const(c)) => Const(c); }; ================================================ FILE: src/content/1.7/code/reason/snippet29.re ================================================ let maybe_tail = fun | [] => None | [_, ...xs] => Some(xs); ================================================ FILE: src/content/1.7/code/reason/snippet30.re ================================================ let square = x => x * x; let mis = Some(Cons(1, Cons(2, Cons(3, Nil)))); let mis2 = Option_Functor.fmap(List_Functor.fmap(square), mis); ================================================ FILE: src/content/1.7/code/reason/snippet31.re ================================================ let fmapO = Option_Functor.fmap; let fmapL = List_Functor.fmap; let fmapC = (f, l) => (compose(fmapO, fmapL))(f, l); let mis2 = fmapC(square, mis); ================================================ FILE: src/content/1.7/code/reason/snippet32.re ================================================ module type Fmap_Alt_Sig_Example = { type t('a); let fmap: ('a => 'b, t('a)) => t('b); }; ================================================ FILE: src/content/1.7/code/reason/snippet33.re ================================================ module type Square_Signature = {let square: int => int;}; ================================================ FILE: src/content/1.7/code/reason/snippet34.re ================================================ list(int) => list(int) ================================================ FILE: src/content/1.7/code/reason/snippet35.re ================================================ option(list(int)) => option(list(int)) ================================================ FILE: src/content/1.7/code/scala/snippet01.scala ================================================ sealed trait Option[+A] case object None extends Option[Nothing] case class Some[A](a: A) extends Option[A] ================================================ FILE: src/content/1.7/code/scala/snippet02.scala ================================================ val f: A => B ================================================ FILE: src/content/1.7/code/scala/snippet03.scala ================================================ def f1[A, B]: Option[A] => Option[B] = { case None => None case Some(x) => Some(f(x)) } ================================================ FILE: src/content/1.7/code/scala/snippet04.scala ================================================ def fmap[A, B](f: A => B): (Option[A] => Option[B]) ================================================ FILE: src/content/1.7/code/scala/snippet05.scala ================================================ def fmap[A, B](f: A => B)(fa: Option[A]): Option[B] ================================================ FILE: src/content/1.7/code/scala/snippet06.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.7/code/scala/snippet07.scala ================================================ def identity[A](x: A) = x ================================================ FILE: src/content/1.7/code/scala/snippet08.scala ================================================ fmap(identity) == identity ================================================ FILE: src/content/1.7/code/scala/snippet09.scala ================================================ fmap(g compose f) == fmap(g) compose fmap(f) ================================================ FILE: src/content/1.7/code/scala/snippet10.scala ================================================ trait Eq[A]{ def ===(x: A, y: A): Boolean } ================================================ FILE: src/content/1.7/code/scala/snippet11.scala ================================================ case class Point(x: Float, y: Float) ================================================ FILE: src/content/1.7/code/scala/snippet12.scala ================================================ implicit val pointEq = new Eq[Point] { def ===(a1: Point, a2: Point): Boolean = a1.x == a2.x && a1.y == a2.y } ================================================ FILE: src/content/1.7/code/scala/snippet13.scala ================================================ trait Functor[F[_]] { def fmap[A, B](f: A => B)(fa: F[A]): F[B] } ================================================ FILE: src/content/1.7/code/scala/snippet14.scala ================================================ implicit val optionFunctor = new Functor[Option] { def fmap[A, B](f: A => B)(fa: Option[A]): Option[B] = fa match { case None => None case Some(x) => Some(f(x)) } } ================================================ FILE: src/content/1.7/code/scala/snippet15.scala ================================================ sealed trait List[+E] case object Nil extends List[Nothing] case class Cons[E](h: E, t: List[E]) extends List[E] ================================================ FILE: src/content/1.7/code/scala/snippet16.scala ================================================ def fmap[A, B](f: A => B): (List[A] => List[B]) ================================================ FILE: src/content/1.7/code/scala/snippet17.scala ================================================ def fmap[A, B](f: A => B)(fa: List[A]): List[B] = fa match { case Cons(x, t) => Cons(f(x), fmap(f)(t)) } ================================================ FILE: src/content/1.7/code/scala/snippet18.scala ================================================ implicit val listFunctor = new Functor[List] { def fmap[A, B](f: A => B)(fa: List[A]): List[B] = fa match { case Nil => Nil case Cons(x, t) => Cons(f(x), fmap(f)(t)) } } ================================================ FILE: src/content/1.7/code/scala/snippet19.scala ================================================ Function1[A, B] // or A => B ================================================ FILE: src/content/1.7/code/scala/snippet20.scala ================================================ // with the Kind Projector plugin Function1[A, ?] or A => ? ================================================ FILE: src/content/1.7/code/scala/snippet21.scala ================================================ def fmap[A, B](f: A => B)(g: R => A): (R => B) ================================================ FILE: src/content/1.7/code/scala/snippet22.scala ================================================ // with the Kind Projector plugin: implicit def function1Functor[R] = new Functor[R => ?] { def fmap[A, B](f: A => B)(g: R => A): (R => B) = f compose g } ================================================ FILE: src/content/1.7/code/scala/snippet23.scala ================================================ def fmap[A, B]: (A => B) => (R => A) => (R => B) = f => g => f compose g ================================================ FILE: src/content/1.7/code/scala/snippet24.scala ================================================ def fmap[A, B]: (A => B) => (R => A) => (R => B) = _ compose ================================================ FILE: src/content/1.7/code/scala/snippet25.scala ================================================ // LazyLists are supported as of Scala 2.13 def nats: LazyList[Int] = LazyList.from(1) ================================================ FILE: src/content/1.7/code/scala/snippet26.scala ================================================ case class Const[C, A](v: C) ================================================ FILE: src/content/1.7/code/scala/snippet27.scala ================================================ def fmap[A, B](f: A => B)(ca: Const[C, A]): Const[C, B] ================================================ FILE: src/content/1.7/code/scala/snippet28.scala ================================================ implicit def constFunctor[C] = new Functor[Const[C, ?]] { def fmap[A, B](f: A => B)(ca: Const[C, A]): Const[C, B] = Const(ca.v) } ================================================ FILE: src/content/1.7/code/scala/snippet29.scala ================================================ def maybeTail[A]: List[A] => Option[List[A]] = { case Nil => None case Cons(x, xs) => Some(xs) } ================================================ FILE: src/content/1.7/code/scala/snippet30.scala ================================================ def square: Int => Int = x => x * x val mis: Option[List[Int]] = Some(Cons(1, Cons(2, Cons(3, Nil)))) val mis2 = optionFunctor. fmap(listFunctor.fmap(square))(mis) ================================================ FILE: src/content/1.7/code/scala/snippet31.scala ================================================ def fmapO[A, B]: (A => B) => Option[A] => Option[B] = optionFunctor.fmap def fmapL[A, B]: (A => B) => List[A] => List[B] = listFunctor.fmap def fmapC[A, B]: (A => B) => Option[List[A]] => Option[List[B]] = fmapO.compose(fmapL) val mis2 = fmapC(square)(mis) ================================================ FILE: src/content/1.7/code/scala/snippet32.scala ================================================ def fmap[F[_], A, B]: (A => B) => (F[A] => F[B]) ================================================ FILE: src/content/1.7/code/scala/snippet33.scala ================================================ def square: Int => Int ================================================ FILE: src/content/1.7/code/scala/snippet34.scala ================================================ List[Int] => List[Int] ================================================ FILE: src/content/1.7/code/scala/snippet35.scala ================================================ Option[List[Int]] => Option[List[Int]] ================================================ FILE: src/content/1.7/functors.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{A}{t the risk of sounding} like a broken record, I will say this about functors: A functor is a very simple but powerful idea. Category theory is just full of those simple but powerful ideas. A functor is a mapping between categories. Given two categories, $\cat{C}$ and $\cat{D}$, a functor $F$ maps objects in $\cat{C}$ to objects in $\cat{D}$ --- it's a function on objects. If $a$ is an object in $\cat{C}$, we'll write its image in $\cat{D}$ as $F a$ (no parentheses). But a category is not just objects --- it's objects and morphisms that connect them. A functor also maps morphisms --- it's a function on morphisms. But it doesn't map morphisms willy-nilly --- it preserves connections. So if a morphism $f$ in $\cat{C}$ connects object $a$ to object $b$, \[f \Colon a \to b\] the image of $f$ in $\cat{D}$, $F f$, will connect the image of $a$ to the image of $b$: \[F f \Colon F a \to F b\] (This is a mixture of mathematical and Haskell notation that hopefully makes sense by now. I won't use parentheses when applying functors to objects or morphisms.) \begin{figure}[H] \centering\includegraphics[width=0.3\textwidth]{images/functor.jpg} \end{figure} \noindent As you can see, a functor preserves the structure of a category: what's connected in one category will be connected in the other category. But there's something more to the structure of a category: there's also the composition of morphisms. If $h$ is a composition of $f$ and $g$: \[h = g \circ f\] we want its image under $F$ to be a composition of the images of $f$ and $g$: \[F h = F g \circ F f\] \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/functorcompos.jpg} \end{figure} \noindent Finally, we want all identity morphisms in $\cat{C}$ to be mapped to identity morphisms in $\cat{D}$: \[F \idarrow[a] = \idarrow[F a]\] \noindent Here, $\idarrow[a]$ is the identity at the object $a$, and $\idarrow[F a]$ the identity at $F a$. \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/functorid.jpg} \end{figure} \noindent Note that these conditions make functors much more restrictive than regular functions. Functors must preserve the structure of a category. If you picture a category as a collection of objects held together by a network of morphisms, a functor is not allowed to introduce any tears into this fabric. It may smash objects together, it may glue multiple morphisms into one, but it may never break things apart. This no-tearing constraint is similar to the continuity condition you might know from calculus. In this sense functors are ``continuous'' (although there exists an even more restrictive notion of continuity for functors). Just like functions, functors may do both collapsing and embedding. The embedding aspect is more prominent when the source category is much smaller than the target category. In the extreme, the source can be the trivial singleton category --- a category with one object and one morphism (the identity). A functor from the singleton category to any other category simply selects an object in that category. This is fully analogous to the property of morphisms from singleton sets selecting elements in target sets. The maximally collapsing functor is called the constant functor $\Delta_c$. It maps every object in the source category to one selected object $c$ in the target category. It also maps every morphism in the source category to the identity morphism $\idarrow[c]$. It acts like a black hole, compacting everything into one singularity. We'll see more of this functor when we discuss limits and colimits. \section{Functors in Programming} Let's get down to earth and talk about programming. We have our category of types and functions. We can talk about functors that map this category into itself --- such functors are called endofunctors. So what's an endofunctor in the category of types? First of all, it maps types to types. We've seen examples of such mappings, maybe without realizing that they were just that. I'm talking about definitions of types that were parameterized by other types. Let's see a few examples. \subsection{The Maybe Functor} The definition of \code{Maybe} is a mapping from type \code{a} to type \code{Maybe a}: \src{snippet01} Here's an important subtlety: \code{Maybe} itself is not a type, it's a \emph{type constructor}. You have to give it a type argument, like \code{Int} or \code{Bool}, in order to turn it into a type. \code{Maybe} without any argument represents a function on types. But can we turn \code{Maybe} into a functor? (From now on, when I speak of functors in the context of programming, I will almost always mean endofunctors.) A functor is not only a mapping of objects (here, types) but also a mapping of morphisms (here, functions). For any function from \code{a} to \code{b}: \src{snippet02} we would like to produce a function from \code{Maybe a} to \code{Maybe b}. To define such a function, we'll have two cases to consider, corresponding to the two constructors of \code{Maybe}. The \code{Nothing} case is simple: we'll just return \code{Nothing} back. And if the argument is \code{Just}, we'll apply the function \code{f} to its contents. So the image of \code{f} under \code{Maybe} is the function: \src{snippet03} (By the way, in Haskell you can use apostrophes in variables names, which is very handy in cases like these.) In Haskell, we implement the morphism-mapping part of a functor as a higher order function called \code{fmap}. In the case of \code{Maybe}, it has the following signature: \src{snippet04} \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/functormaybe.jpg} \end{figure} \noindent We often say that \code{fmap} \emph{lifts} a function. The lifted function acts on \code{Maybe} values. As usual, because of currying, this signature may be interpreted in two ways: as a function of one argument --- which itself is a function \code{(a -> b)} --- returning a function \code{(Maybe a -> Maybe b)}; or as a function of two arguments returning \code{Maybe b}: \src{snippet05} Based on our previous discussion, this is how we implement \code{fmap} for \code{Maybe}: \src{snippet06} To show that the type constructor \code{Maybe} together with the function \code{fmap} form a functor, we have to prove that \code{fmap} preserves identity and composition. These are called ``the functor laws,'' but they simply ensure the preservation of the structure of the category. \subsection{Equational Reasoning} To prove the functor laws, I will use \newterm{equational reasoning}, which is a common proof technique in Haskell. It takes advantage of the fact that Haskell functions are defined as equalities: the left hand side equals the right hand side. You can always substitute one for another, possibly renaming variables to avoid name conflicts. Think of this as either inlining a function, or the other way around, refactoring an expression into a function. Let's take the identity function as an example: \src{snippet07} If you see, for instance, \code{id y} in some expression, you can replace it with \code{y} (inlining). Further, if you see \code{id} applied to an expression, say \code{id (y + 2)}, you can replace it with the expression itself \code{(y + 2)}. And this substitution works both ways: you can replace any expression \code{e} with \code{id e} (refactoring). If a function is defined by pattern matching, you can use each sub-definition independently. For instance, given the above definition of \code{fmap} you can replace \code{fmap f Nothing} with \code{Nothing}, or the other way around. Let's see how this works in practice. Let's start with the preservation of identity: \src{snippet08} There are two cases to consider: \code{Nothing} and \code{Just}. Here's the first case (I'm using Haskell pseudo-code to transform the left hand side to the right hand side): \begin{snip}{haskell} fmap id Nothing = { definition of fmap } Nothing = { definition of id } id Nothing \end{snip} Notice that in the last step I used the definition of \code{id} backwards. I replaced the expression \code{Nothing} with \code{id\ Nothing}. In practice, you carry out such proofs by ``burning the candle at both ends,'' until you hit the same expression in the middle --- here it was \code{Nothing}. The second case is also easy: \begin{snip}{haskell} fmap id (Just x) = { definition of fmap } Just (id x) = { definition of id } Just x = { definition of id } id (Just x) \end{snip} Now, let's show that \code{fmap} preserves composition: \src{snippet09} First the \code{Nothing} case: \begin{snip}{haskell} fmap (g . f) Nothing = { definition of fmap } Nothing = { definition of fmap } fmap g Nothing = { definition of fmap } fmap g (fmap f Nothing) \end{snip} And then the \code{Just} case: \begin{snip}{haskell} fmap (g . f) (Just x) = { definition of fmap } Just ((g . f) x) = { definition of composition } Just (g (f x)) = { definition of fmap } fmap g (Just (f x)) = { definition of fmap } fmap g (fmap f (Just x)) = { definition of composition } (fmap g . fmap f) (Just x) \end{snip} It's worth stressing that equational reasoning doesn't work for C++ style ``functions'' with side effects. Consider this code: \begin{snip}{cpp} int square(int x) { return x * x; } int counter() { static int c = 0; return c++; } double y = square(counter()); \end{snip} Using equational reasoning, you would be able to inline \code{square} to get: \begin{snip}{cpp} double y = counter() * counter(); \end{snip} This is definitely not a valid transformation, and it will not produce the same result. Despite that, the C++ compiler will try to use equational reasoning if you implement \code{square} as a macro, with disastrous results. \subsection{Optional} Functors are easily expressed in Haskell, but they can be defined in any language that supports generic programming and higher-order functions. Let's consider the C++ analog of \code{Maybe}, the template type \code{optional}. Here's a sketch of the implementation (the actual implementation is much more complex, dealing with various ways the argument may be passed, with copy semantics, and with the resource management issues characteristic of C++): \begin{snip}{cpp} template class optional { bool _isValid; // the tag T _v; public: optional() : _isValid(false) {} // Nothing optional(T x) : _isValid(true) , _v(x) {} // Just bool isValid() const { return _isValid; } T val() const { return _v; } }; \end{snip} This template provides one part of the definition of a functor: the mapping of types. It maps any type \code{T} to a new type \code{optional}. Let's define its action on functions: \begin{snip}{cpp} template std::function(optional)> fmap(std::function f) { return [f](optional opt) { if (!opt.isValid()) return optional{}; else return optional{ f(opt.val()) }; }; } \end{snip} This is a higher order function, taking a function as an argument and returning a function. Here's the uncurried version of it: \begin{snip}{cpp} template optional fmap(std::function f, optional opt) { if (!opt.isValid()) return optional{}; else return optional{ f(opt.val()) }; } \end{snip} There is also an option of making \code{fmap} a template method of \code{optional}. This embarrassment of choices makes abstracting the functor pattern in C++ a problem. Should functor be an interface to inherit from (unfortunately, you can't have template virtual functions)? Should it be a curried or an uncurried free template function? Can the C++ compiler correctly infer the missing types, or should they be specified explicitly? Consider a situation where the input function \code{f} takes an \code{int} to a \code{bool}. How will the compiler figure out the type of \code{g}: \begin{snip}{cpp} auto g = fmap(f); \end{snip} especially if, in the future, there are multiple functors overloading \code{fmap}? (We'll see more functors soon.) \subsection{Typeclasses} So how does Haskell deal with abstracting the functor? It uses the typeclass mechanism. A typeclass defines a family of types that support a common interface. For instance, the class of objects that support equality is defined as follows: \src{snippet10} This definition states that type \code{a} is of the class \code{Eq} if it supports the operator \code{(==)} that takes two arguments of type \code{a} and returns a \code{Bool}. If you want to tell Haskell that a particular type is \code{Eq}, you have to declare it an \newterm{instance} of this class and provide the implementation of \code{(==)}. For example, given the definition of a 2D \code{Point} (a product type of two \code{Float}s): \src{snippet11} you can define the equality of points: \src{snippet12} Here I used the operator \code{(==)} (the one I'm defining) in the infix position between the two patterns \code{(Pt x y)} and \code{(Pt x' y')}. The body of the function follows the single equal sign. Once \code{Point} is declared an instance of \code{Eq}, you can directly compare points for equality. Notice that, unlike in C++ or Java, you don't have to specify the \code{Eq} class (or interface) when defining \code{Point} --- you can do it later in client code. Typeclasses are also Haskell's only mechanism for overloading functions (and operators). We will need that for overloading \code{fmap} for different functors. There is one complication, though: a functor is not defined as a type but as a mapping of types, a type constructor. We need a typeclass that's not a family of types, as was the case with \code{Eq}, but a family of type constructors. Fortunately a Haskell typeclass works with type constructors as well as with types. So here's the definition of the \code{Functor} class: \src{snippet13} It stipulates that \code{f} is a \code{Functor} if there exists a function \code{fmap} with the specified type signature. The lowercase \code{f} is a type variable, similar to type variables \code{a} and \code{b}. The compiler, however, is able to deduce that it represents a type constructor rather than a type by looking at its usage: acting on other types, as in \code{f a} and \code{f b}. Accordingly, when declaring an instance of \code{Functor}, you have to give it a type constructor, as is the case with \code{Maybe}: \src{snippet14} By the way, the \code{Functor} class, as well as its instance definitions for a lot of simple data types, including \code{Maybe}, are part of the standard Prelude library. \subsection{Functor in C++} Can we try the same approach in C++? A type constructor corresponds to a template class, like \code{optional}, so by analogy, we would parameterize \code{fmap} with a \newterm{template template parameter} \code{F}. This is the syntax for it: \begin{snip}{cpp} template F, typename A, typename B> F fmap(std::function, F); \end{snip} We would like to be able to specialize this template for different functors. Unfortunately, there is a prohibition against partial specialization of template functions in C++. You can't write: \begin{snip}{cpp} template optional fmap(std::function f, optional opt) \end{snip} Instead, we have to fall back on function overloading, which brings us back to the original definition of the uncurried \code{fmap}: \begin{snip}{cpp} template optional fmap(std::function f, optional opt) { if (!opt.isValid()) return optional{}; else return optional{ f(opt.val()) }; } \end{snip} This definition works, but only because the second argument of \code{fmap} selects the overload. It totally ignores the more generic definition of \code{fmap}. \subsection{The List Functor} To get some intuition as to the role of functors in programming, we need to look at more examples. Any type that is parameterized by another type is a candidate for a functor. Generic containers are parameterized by the type of the elements they store, so let's look at a very simple container, the list: \src{snippet15} We have the type constructor \code{List}, which is a mapping from any type \code{a} to the type \code{List a}. To show that \code{List} is a functor we have to define the lifting of functions: Given a function \code{a -> b} define a function \code{List a -> List b}: \src{snippet16} A function acting on \code{List a} must consider two cases corresponding to the two list constructors. The \code{Nil} case is trivial --- just return \code{Nil} --- there isn't much you can do with an empty list. The \code{Cons} case is a bit tricky, because it involves recursion. So let's step back for a moment and consider what we are trying to do. We have a list of \code{a}, a function \code{f} that turns \code{a} to \code{b}, and we want to generate a list of \code{b}. The obvious thing is to use \code{f} to turn each element of the list from \code{a} to \code{b}. How do we do this in practice, given that a (non-empty) list is defined as the \code{Cons} of a head and a tail? We apply \code{f} to the head and apply the lifted (\code{fmap}ped) \code{f} to the tail. This is a recursive definition, because we are defining lifted \code{f} in terms of lifted \code{f}: \src{snippet17} Notice that, on the right hand side, \code{fmap f} is applied to a list that's shorter than the list for which we are defining it --- it's applied to its tail. We recurse towards shorter and shorter lists, so we are bound to eventually reach the empty list, or \code{Nil}. But as we've decided earlier, \code{fmap f} acting on \code{Nil} returns \code{Nil}, thus terminating the recursion. To get the final result, we combine the new head \code{(f x)} with the new tail \code{(fmap f t)} using the \code{Cons} constructor. Putting it all together, here's the instance declaration for the list functor: \src{snippet18} If you are more comfortable with C++, consider the case of a \code{std::vector}, which could be considered the most generic C++ container. The implementation of \code{fmap} for \code{std::vector} is just a thin encapsulation of \code{std::transform}: \begin{snip}{cpp} template std::vector fmap(std::function f, std::vector v) { std::vector w; std::transform( std::begin(v) , std::end(v) , std::back_inserter(w) , f); return w; } \end{snip} We can use it, for instance, to square the elements of a sequence of numbers: \begin{snip}{cpp} std::vector v{ 1, 2, 3, 4 }; auto w = fmap([](int i) { return i*i; }, v); std::copy( std::begin(w) , std::end(w) , std::ostream_iterator(std::cout, ", ")); \end{snip} Most C++ containers are functors by virtue of implementing iterators that can be passed to \code{std::transform}, which is the more primitive cousin of \code{fmap}. Unfortunately, the simplicity of a functor is lost under the usual clutter of iterators and temporaries (see the implementation of \code{fmap} above). I'm happy to say that the new proposed C++ range library makes the functorial nature of ranges much more pronounced. \subsection{The Reader Functor} Now that you might have developed some intuitions --- for instance, functors being some kind of containers --- let me show you an example which at first sight looks very different. Consider a mapping of type \code{a} to the type of a function returning \code{a}. We haven't really talked about function types in depth --- the full categorical treatment is coming --- but we have some understanding of those as programmers. In Haskell, a function type is constructed using the arrow type constructor \code{(->)} which takes two types: the argument type and the result type. You've already seen it in infix form, \code{a -> b}, but it can equally well be used in prefix form, when parenthesized: \src{snippet19} Just like with regular functions, type functions of more than one argument can be partially applied. So when we provide just one type argument to the arrow, it still expects another one. That's why: \src{snippet20} is a type constructor. It needs one more type \code{b} to produce a complete type \code{a -> b}. As it stands, it defines a whole family of type constructors parameterized by \code{a}. Let's see if this is also a family of functors. Dealing with two type parameters can get a bit confusing, so let's do some renaming. Let's call the argument type \code{r} and the result type \code{a}, in line with our previous functor definitions. So our type constructor takes any type \code{a} and maps it into the type \code{r -> a}. To show that it's a functor, we want to lift a function \code{a -> b} to a function that takes \code{r -> a} and returns \code{r -> b}. These are the types that are formed using the type constructor \code{(->) r} acting on, respectively, \code{a} and \code{b}. Here's the type signature of \code{fmap} applied to this case: \src{snippet21} We have to solve the following puzzle: given a function \code{f :: a -> b} and a function \code{g :: r -> a}, create a function \code{r -> b}. There is only one way we can compose the two functions, and the result is exactly what we need. So here's the implementation of our \code{fmap}: \src{snippet22} It just works! If you like terse notation, this definition can be reduced further by noticing that composition can be rewritten in prefix form: \src{snippet23} and the arguments can be omitted to yield a direct equality of two functions: \src{snippet24} This combination of the type constructor \code{(->) r} with the above implementation of \code{fmap} is called the reader functor. \section{Functors as Containers} We've seen some examples of functors in programming languages that define general-purpose containers, or at least objects that contain some value of the type they are parameterized over. The reader functor seems to be an outlier, because we don't think of functions as data. But we've seen that pure functions can be memoized, and function execution can be turned into table lookup. Tables are data. Conversely, because of Haskell's laziness, a traditional container, like a list, may actually be implemented as a function. Consider, for instance, an infinite list of natural numbers, which can be compactly defined as: \src{snippet25} In the first line, a pair of square brackets is Haskell's built-in type constructor for lists. In the second line, square brackets are used to create a list literal. Obviously, an infinite list like this cannot be stored in memory. The compiler implements it as a function that generates \code{Integer}s on demand. Haskell effectively blurs the distinction between data and code. A list could be considered a function, and a function could be considered a table that maps arguments to results. The latter can even be practical if the domain of the function is finite and not too large. It would not be practical, however, to implement \code{strlen} as table lookup, because there are infinitely many different strings. As programmers, we don't like infinities, but in category theory you learn to eat infinities for breakfast. Whether it's a set of all strings or a collection of all possible states of the Universe, past, present, and future --- we can deal with it! So I like to think of the functor object (an object of the type generated by an endofunctor) as containing a value or values of the type over which it is parameterized, even if these values are not physically present there. One example of a functor is a C++ \code{std::future}, which may at some point contain a value, but it's not guaranteed it will; and if you want to access it, you may block waiting for another thread to finish execution. Another example is a Haskell \code{IO} object, which may contain user input, or the future versions of our Universe with ``Hello World!'' displayed on the monitor. According to this interpretation, a functor object is something that may contain a value or values of the type it's parameterized upon. Or it may contain a recipe for generating those values. We are not at all concerned about being able to access the values --- that's totally optional, and outside of the scope of the functor. All we are interested in is to be able to manipulate those values using functions. If the values can be accessed, then we should be able to see the results of this manipulation. If they can't, then all we care about is that the manipulations compose correctly and that the manipulation with an identity function doesn't change anything. Just to show you how much we don't care about being able to access the values inside a functor object, here's a type constructor that ignores completely its argument \code{a}: \src{snippet26} The \code{Const} type constructor takes two types, \code{c} and \code{a}. Just like we did with the arrow constructor, we are going to partially apply it to create a functor. The data constructor (also called \code{Const}) takes just one value of type \code{c}. It has no dependence on \code{a}. The type of \code{fmap} for this type constructor is: \src{snippet27} Because the functor ignores its type argument, the implementation of \code{fmap} is free to ignore its function argument --- the function has nothing to act upon: \src{snippet28} This might be a little clearer in C++ (I never thought I would utter those words!), where there is a stronger distinction between type arguments --- which are compile-time --- and values, which are run-time: \begin{snip}{cpp} template struct Const { Const(C v) : _v(v) {} C _v; }; \end{snip} The C++ implementation of \code{fmap} also ignores the function argument and essentially re-casts the \code{Const} argument without changing its value: \begin{snip}{cpp} template Const fmap(std::function f, Const c) { return Const{c._v}; } \end{snip} Despite its weirdness, the \code{Const} functor plays an important role in many constructions. In category theory, it's a special case of the $\Delta_c$ functor I mentioned earlier --- the endo-functor case of a black hole. We'll be seeing more of it in the future. \section{Functor Composition} It's not hard to convince yourself that functors between categories compose, just like functions between sets compose. A composition of two functors, when acting on objects, is just the composition of their respective object mappings; and similarly when acting on morphisms. After jumping through two functors, identity morphisms end up as identity morphisms, and compositions of morphisms finish up as compositions of morphisms. There's really nothing much to it. In particular, it's easy to compose endofunctors. Remember the function \code{maybeTail}? I'll rewrite it using Haskell's built in implementation of lists: \src{snippet29} (The empty list constructor that we used to call \code{Nil} is replaced with the empty pair of square brackets \code{{[}{]}}. The \code{Cons} constructor is replaced with the infix operator \code{:} (colon).) The result of \code{maybeTail} is of a type that's a composition of two functors, \code{Maybe} and \code{{[}{]}}, acting on \code{a}. Each of these functors is equipped with its own version of \code{fmap}, but what if we want to apply some function \code{f} to the contents of the composite: a \code{Maybe} list? We have to break through two layers of functors. We can use \code{fmap} to break through the outer \code{Maybe}. But we can't just send \code{f} inside \code{Maybe} because \code{f} doesn't work on lists. We have to send \code{(fmap f)} to operate on the inner list. For instance, let's see how we can square the elements of a \code{Maybe} list of integers: \src{snippet30} The compiler, after analyzing the types, will figure out that, for the outer \code{fmap}, it should use the implementation from the \code{Maybe} instance, and for the inner one, the list functor implementation. It may not be immediately obvious that the above code may be rewritten as: \src{snippet31} But remember that \code{fmap} may be considered a function of just one argument: \src{snippet32} In our case, the second \code{fmap} in \code{(fmap . fmap)} takes as its argument: \src{snippet33} and returns a function of the type: \src{snippet34} The first \code{fmap} then takes that function and returns a function: \src{snippet35} Finally, that function is applied to \code{mis}. So the composition of two functors is a functor whose \code{fmap} is the composition of the corresponding \code{fmap}s. Going back to category theory: It's pretty obvious that functor composition is associative (the mapping of objects is associative, and the mapping of morphisms is associative). And there is also a trivial identity functor in every category: it maps every object to itself, and every morphism to itself. So functors have all the same properties as morphisms in some category. But what category would that be? It would have to be a category in which objects are categories and morphisms are functors. It's a category of categories. But a category of \emph{all} categories would have to include itself, and we would get into the same kinds of paradoxes that made the set of all sets impossible. There is, however, a category of all \emph{small} categories called $\Cat$ (which is big, so it can't be a member of itself). A small category is one in which objects form a set, as opposed to something larger than a set. Mind you, in category theory, even an infinite uncountable set is considered ``small.'' I thought I'd mention these things because I find it pretty amazing that we can recognize the same structures repeating themselves at many levels of abstraction. We'll see later that functors form categories as well. \section{Challenges} \begin{enumerate} \tightlist \item Can we turn the \code{Maybe} type constructor into a functor by defining: \begin{snip}{haskell} fmap _ _ = Nothing \end{snip} which ignores both of its arguments? (Hint: Check the functor laws.) \item Prove functor laws for the reader functor. Hint: it's really simple. \item Implement the reader functor in your second favorite language (the first being Haskell, of course). \item Prove the functor laws for the list functor. Assume that the laws are true for the tail part of the list you're applying it to (in other words, use \emph{induction}). \end{enumerate} ================================================ FILE: src/content/1.8/code/haskell/snippet01.hs ================================================ class Bifunctor f where bimap :: (a -> c) -> (b -> d) -> f a b -> f c d bimap g h = first g . second h first :: (a -> c) -> f a b -> f c b first g = bimap g id second :: (b -> d) -> f a b -> f a d second = bimap id ================================================ FILE: src/content/1.8/code/haskell/snippet02.hs ================================================ instance Bifunctor (,) where bimap f g (x, y) = (f x, g y) ================================================ FILE: src/content/1.8/code/haskell/snippet03.hs ================================================ bimap :: (a -> c) -> (b -> d) -> (a, b) -> (c, d) ================================================ FILE: src/content/1.8/code/haskell/snippet04.hs ================================================ instance Bifunctor Either where bimap f _ (Left x) = Left (f x) bimap _ g (Right y) = Right (g y) ================================================ FILE: src/content/1.8/code/haskell/snippet05.hs ================================================ data Identity a = Identity a ================================================ FILE: src/content/1.8/code/haskell/snippet06.hs ================================================ instance Functor Identity where fmap f (Identity x) = Identity (f x) ================================================ FILE: src/content/1.8/code/haskell/snippet07.hs ================================================ data Maybe a = Nothing | Just a ================================================ FILE: src/content/1.8/code/haskell/snippet08.hs ================================================ type Maybe a = Either (Const () a) (Identity a) ================================================ FILE: src/content/1.8/code/haskell/snippet09.hs ================================================ newtype BiComp bf fu gu a b = BiComp (bf (fu a) (gu b)) ================================================ FILE: src/content/1.8/code/haskell/snippet10.hs ================================================ instance (Bifunctor bf, Functor fu, Functor gu) => Bifunctor (BiComp bf fu gu) where bimap f1 f2 (BiComp x) = BiComp (bimap (fmap f1) (fmap f2) x) ================================================ FILE: src/content/1.8/code/haskell/snippet11.hs ================================================ bf (fu a) (gu b) ================================================ FILE: src/content/1.8/code/haskell/snippet12.hs ================================================ f1 :: a -> a' f2 :: b -> b' ================================================ FILE: src/content/1.8/code/haskell/snippet13.hs ================================================ bimap :: (fu a -> fu a') -> (gu b -> gu b') -> bf (fu a) (gu b) -> bf (fu a') (gu b') ================================================ FILE: src/content/1.8/code/haskell/snippet14.hs ================================================ data Tree a = Leaf a | Node (Tree a) (Tree a) deriving Functor ================================================ FILE: src/content/1.8/code/haskell/snippet15.hs ================================================ instance Functor Tree where fmap f (Leaf a) = Leaf (f a) fmap f (Node t t') = Node (fmap f t) (fmap f t') ================================================ FILE: src/content/1.8/code/haskell/snippet16.hs ================================================ type Writer a = (a, String) ================================================ FILE: src/content/1.8/code/haskell/snippet17.hs ================================================ (>=>) :: (a -> Writer b) -> (b -> Writer c) -> (a -> Writer c) m1 >=> m2 = \x -> let (y, s1) = m1 x (z, s2) = m2 y in (z, s1 ++ s2) ================================================ FILE: src/content/1.8/code/haskell/snippet18.hs ================================================ return :: a -> Writer a return x = (x, "") ================================================ FILE: src/content/1.8/code/haskell/snippet19.hs ================================================ fmap f = id >=> (\x -> return (f x)) ================================================ FILE: src/content/1.8/code/haskell/snippet20.hs ================================================ (->) r ================================================ FILE: src/content/1.8/code/haskell/snippet21.hs ================================================ type Reader r a = r -> a ================================================ FILE: src/content/1.8/code/haskell/snippet22.hs ================================================ instance Functor (Reader r) where fmap f g = f . g ================================================ FILE: src/content/1.8/code/haskell/snippet23.hs ================================================ type Op r a = a -> r ================================================ FILE: src/content/1.8/code/haskell/snippet24.hs ================================================ fmap :: (a -> b) -> (a -> r) -> (b -> r) ================================================ FILE: src/content/1.8/code/haskell/snippet25.hs ================================================ class Contravariant f where contramap :: (b -> a) -> (f a -> f b) ================================================ FILE: src/content/1.8/code/haskell/snippet26.hs ================================================ instance Contravariant (Op r) where -- (b -> a) -> Op r a -> Op r b contramap f g = g . f ================================================ FILE: src/content/1.8/code/haskell/snippet27.hs ================================================ flip :: (a -> b -> c) -> (b -> a -> c) flip f y x = f x y ================================================ FILE: src/content/1.8/code/haskell/snippet28.hs ================================================ contramap = flip (.) ================================================ FILE: src/content/1.8/code/haskell/snippet29.hs ================================================ class Profunctor p where dimap :: (a -> b) -> (c -> d) -> p b c -> p a d dimap f g = lmap f . rmap g lmap :: (a -> b) -> p b c -> p a c lmap f = dimap f id rmap :: (b -> c) -> p a b -> p a c rmap = dimap id ================================================ FILE: src/content/1.8/code/haskell/snippet30.hs ================================================ instance Profunctor (->) where dimap ab cd bc = cd . bc . ab lmap = flip (.) rmap = (.) ================================================ FILE: src/content/1.8/code/ocaml/snippet01.ml ================================================ (** You can represent bifunctor defintion in two forms and implement just and derive the other from it. *) module type BifunctorCore = sig type ('a, 'b) t val bimap : ('a -> 'c) -> ('b -> 'd) -> ('a, 'b) t -> ('c, 'd) t end module type BifunctorExt = sig type ('a, 'b) t val first : ('a -> 'c) -> ('a, 'b) t -> ('c, 'b) t val second : ('b -> 'd) -> ('a, 'b) t -> ('a, 'd) t end module BifunctorCore_Using_Ext (M : BifunctorExt) : BifunctorCore = struct type ('a, 'b) t = ('a, 'b) M.t let bimap g h x = M.first g (M.second h x) end module BifunctorExt_Using_Core (M : BifunctorCore) : BifunctorExt = struct type ('a, 'b) t = ('a, 'b) M.t let first g x = M.bimap g id x let second h x = M.bimap id h x end ================================================ FILE: src/content/1.8/code/ocaml/snippet02.ml ================================================ module Bifunctor_Product : BifunctorCore = struct type ('a, 'b) t = 'a * 'b let bimap f g (l, r) = f l, g r end ================================================ FILE: src/content/1.8/code/ocaml/snippet03.ml ================================================ val bimap : ('a -> 'c) -> ('b -> 'd) -> ('a, 'b) Bifunctor_Product.t -> ('c, 'd) Bifunctor_Product.t ================================================ FILE: src/content/1.8/code/ocaml/snippet04.ml ================================================ type ('a, 'b) either = | Left of 'a | Right of 'b module Bifunctor_Either : BifunctorCore = struct type ('a, 'b) t = ('a, 'b) either let bimap f g = function | Left a -> Left (f a) | Right b -> Right (g b) ;; end ================================================ FILE: src/content/1.8/code/ocaml/snippet05.ml ================================================ type 'a id = Id of 'a ================================================ FILE: src/content/1.8/code/ocaml/snippet06.ml ================================================ module Identity_Functor : Functor = struct type 'a t = 'a id let fmap f (Id a) = Id (f a) end ================================================ FILE: src/content/1.8/code/ocaml/snippet07.ml ================================================ type 'a option = | None | Some of 'a ================================================ FILE: src/content/1.8/code/ocaml/snippet08.ml ================================================ (** OCaml doesn't have a built in Const type *) type ('a, 'b) const = Const of 'a (** OCaml doesn't have a built in either type *) type ('a, 'b) either = | Left of 'a | Right of 'b (** Either type *) type 'a option = ((unit, 'a) const, 'a id) either ================================================ FILE: src/content/1.8/code/ocaml/snippet09.ml ================================================ (** OCaml doesn't support higher kinded types. So, we have to use module functors to emulate the behavior higher kinded types. There's less verbose options using type defunctionalization but it's more advanced and obscures the flow of this book *) module type BiComp = functor (BF : sig type ('a, 'b) t end) (FU : sig type 'a t end) (GU : sig type 'b t end) -> sig type ('a, 'b) bicomp = BiComp of ('a FU.t, 'b GU.t) BF.t end ================================================ FILE: src/content/1.8/code/ocaml/snippet10.ml ================================================ module BiCompBifunctor (BF : BifunctorCore) (FU : Functor) (GU : Functor) : BifunctorCore = struct type ('a, 'b) t = BiComp of ('a FU.t, 'b GU.t) BF.t let bimap f g (BiComp x) = BiComp (BF.bimap (FU.fmap f) (GU.fmap g) x) ;; end ================================================ FILE: src/content/1.8/code/ocaml/snippet11.ml ================================================ type ('a FU.t, 'b GU.t) BF.t ================================================ FILE: src/content/1.8/code/ocaml/snippet12.ml ================================================ val f1 : a -> a' val f2 : b -> b' ================================================ FILE: src/content/1.8/code/ocaml/snippet13.ml ================================================ val bimap : (a FU.t -> a' FU.t) -> (b GU.t -> b' GU.t) -> (a FU.t, b GU.t) t -> (a' FU.t, b' GU.t) t ================================================ FILE: src/content/1.8/code/ocaml/snippet14.ml ================================================ (** Deriving a functor in OCaml is not available as a language extension. You could try experimental library like ocsigen to derive functors.*) type 'a tree = | Leaf of 'a | Node of 'a tree * 'a tree ================================================ FILE: src/content/1.8/code/ocaml/snippet15.ml ================================================ module TreeFunctor : Functor = struct type 'a t = 'a tree let rec fmap f = function | Leaf a -> Leaf (f a) | Node (l, r) -> Node (fmap f l, fmap f r) ;; end ================================================ FILE: src/content/1.8/code/ocaml/snippet16.ml ================================================ type 'a writer = 'a * string ================================================ FILE: src/content/1.8/code/ocaml/snippet17.ml ================================================ module KleisliComposition = struct let ( >=> ) : ('a -> 'b writer) -> ('b -> 'c writer) -> 'a -> 'c writer = fun m1 m2 x -> let y, s1 = m1 x in let z, s2 = m2 y in z, StringLabels.concat ~sep:"" [ s1; s2 ] ;; end ================================================ FILE: src/content/1.8/code/ocaml/snippet18.ml ================================================ module KleisliIdentity = struct let return : 'a -> 'a writer = fun a -> a, "" end ================================================ FILE: src/content/1.8/code/ocaml/snippet19.ml ================================================ module KleisliFunctor : Functor = struct type 'a t = 'a writer let fmap f = KleisliComposition.( >=> ) id (fun x -> KleisliIdentity.return (f x)) ;; end ================================================ FILE: src/content/1.8/code/ocaml/snippet20.ml ================================================ module PartialArrow (T : sig type r end) = struct type 'a t = T.r -> 'a end ================================================ FILE: src/content/1.8/code/ocaml/snippet21.ml ================================================ type ('r, 'a) reader = 'r -> 'a ================================================ FILE: src/content/1.8/code/ocaml/snippet22.ml ================================================ module ReaderFunctor (In : sig type r end) : Functor = struct type 'a t = (In.r, 'a) reader let fmap f g = compose f g end ================================================ FILE: src/content/1.8/code/ocaml/snippet23.ml ================================================ type ('r, 'a) op = 'a -> 'r ================================================ FILE: src/content/1.8/code/ocaml/snippet24.ml ================================================ val fmap : 'a 'b. ('a -> 'b) -> ('a -> 'r) -> ('b -> 'r) ================================================ FILE: src/content/1.8/code/ocaml/snippet25.ml ================================================ module type Contravariant = sig type 'a t val contramap : ('b -> 'a) -> 'a t -> 'b t end ================================================ FILE: src/content/1.8/code/ocaml/snippet26.ml ================================================ module OpContravariant (In : sig type r end) : Contravariant = struct type 'a t = (In.r, 'a) op let contramap f g = compose g f end ================================================ FILE: src/content/1.8/code/ocaml/snippet27.ml ================================================ let flip f b a = f a b ================================================ FILE: src/content/1.8/code/ocaml/snippet28.ml ================================================ let contramap : ('b -> 'a) -> ('r, 'a) op -> ('r, 'b) op = fun f g -> flip compose f g ;; ================================================ FILE: src/content/1.8/code/ocaml/snippet29.ml ================================================ (* Profunctor definition *) module type Profunctor = sig type ('a, 'b) p val dimap : ('a -> 'b) -> ('c -> 'd) -> ('b, 'c) p -> ('a, 'd) p end (* Profunctor alternate definition *) module type ProfunctorExt = sig type ('a, 'b) p val lmap : ('a -> 'b) -> ('b, 'c) p -> ('a, 'c) p val rmap : ('b -> 'c) -> ('a, 'b) p -> ('a, 'c) p end (* Profunctor dimap defined using lmap and rmap *) module Profunctor_Using_Ext (PF : ProfunctorExt) : Profunctor = struct type ('a, 'b) p = ('a, 'b) PF.p let dimap f g = compose (PF.lmap f) (PF.rmap g) end (** Profunctor lmap and rmap defined using dimap *) module ProfunctorExt_Using_Dimap (PF : Profunctor) : ProfunctorExt = struct type ('a, 'b) p = ('a, 'b) PF.p let lmap f = PF.dimap f id let rmap g = PF.dimap id g end ================================================ FILE: src/content/1.8/code/ocaml/snippet30.ml ================================================ module ProfunctorArrow : Profunctor = struct type ('a, 'b) p = 'a -> 'b let dimap f g p = compose g (compose p f) end module ProfunctorExtArrow : ProfunctorExt = struct type ('a, 'b) p = 'a -> 'b let lmap f p = (flip compose) f p let rmap = compose end ================================================ FILE: src/content/1.8/code/reason/snippet01.re ================================================ /** You can represent bifunctor defintion in two forms * and implement just and derive the other from it. */ module type BifunctorCore = { type t('a, 'b); let bimap: ('a => 'c, 'b => 'd, t('a, 'b)) => t('c, 'd); }; module type BifunctorExt = { type t('a, 'b); let first: ('a => 'c, t('a, 'b)) => t('c, 'b); let second: ('b => 'd, t('a, 'b)) => t('a, 'd); }; module BifunctorCore_Using_Ext = (M: BifunctorExt) : BifunctorCore => { type t('a, 'b) = M.t('a, 'b); let bimap = (g, h, x) => M.first(g, M.second(h, x)); }; module BifunctorExt_Using_Core = (M: BifunctorCore) : BifunctorExt => { type t('a, 'b) = M.t('a, 'b); let first = (g, x) => M.bimap(g, id, x); let second = (h, x) => M.bimap(id, h, x); }; ================================================ FILE: src/content/1.8/code/reason/snippet02.re ================================================ module Bifunctor_Product: BifunctorCore = { type t('a, 'b) = ('a, 'b); let bimap = (f, g, (l, r)) => (f(l), g(r)); }; ================================================ FILE: src/content/1.8/code/reason/snippet03.re ================================================ let bimap: ('a => 'c, 'b => 'd, Bifunctor_Product.t('a, 'b)) => Bifunctor_Product.t('c, 'd) ================================================ FILE: src/content/1.8/code/reason/snippet04.re ================================================ type either('a, 'b) = | Left('a) | Right('b); module Bifunctor_Either: BifunctorCore = { type t('a, 'b) = either('a, 'b); let bimap = (f, g) => fun | Left(a) => Left(f(a)) | Right(b) => Right(g(b)); }; ================================================ FILE: src/content/1.8/code/reason/snippet05.re ================================================ type id('a) = | Id('a); ================================================ FILE: src/content/1.8/code/reason/snippet06.re ================================================ module Identity_Functor: Functor = { type t('a) = id('a); let fmap = (f, Id(a)) => Id(f(a)); }; ================================================ FILE: src/content/1.8/code/reason/snippet07.re ================================================ type option('a) = | None | Some('a); ================================================ FILE: src/content/1.8/code/reason/snippet08.re ================================================ /* ReasonML doesn't have a built in Const type */ type const('a, 'b) = | Const('a); /* ReasonML doesn't have a built in either type */ type either('a, 'b) = | Left('a) | Right('b); /* Either type */ type option('a) = either(const(unit, 'a), id('a)); ================================================ FILE: src/content/1.8/code/reason/snippet09.re ================================================ /** ReasonML doesn't support higher kinded types. * So, we have to use module functors to emulate the behavior higher kinded types. * There's less verbose options using type defunctionalization * but it's more advanced and obscures the flow of this book */ module type BiComp = ( BF: {type t('a, 'b);}, FU: {type t('a);}, GU: {type t('b);} ) => { type bicomp('a, 'b) = | BiComp(BF.t(FU.t('a), GU.t('b))); }; ================================================ FILE: src/content/1.8/code/reason/snippet10.re ================================================ module BiCompBifunctor = ( BF: BifunctorCore, FU: Functor, GU: Functor ): BifunctorCore => { type t('a, 'b) = | BiComp(BF.t(FU.t('a), GU.t('b))); let bimap = (f, g, BiComp(x)) => BiComp(BF.bimap(FU.fmap(f), GU.fmap(g), x)); }; ================================================ FILE: src/content/1.8/code/reason/snippet11.re ================================================ BF.t(FU.t('a), GU.t('b)); ================================================ FILE: src/content/1.8/code/reason/snippet12.re ================================================ let f1: a => a' let f2: b => b' ================================================ FILE: src/content/1.8/code/reason/snippet13.re ================================================ let bimap: (FU.t(a) => FU.t(a')) => (GU.t(b) => GU.t(b')) => (FU.t(a), GU.t(b)) => (FU.t(a'), GU.t(b')) ================================================ FILE: src/content/1.8/code/reason/snippet14.re ================================================ /** Deriving a functor in ReasonML is not available as a * language extension. You could try experimental library * like ocsigen to derive functors.*/ type tree('a) = | Leaf('a) | Node(tree('a), tree('a)); ================================================ FILE: src/content/1.8/code/reason/snippet15.re ================================================ module TreeFunctor: Functor = { type t('a) = tree('a); let rec fmap = f => fun | Leaf(a) => Leaf(f(a)) | Node(l, r) => Node(fmap(f, l), fmap(f, r)); }; ================================================ FILE: src/content/1.8/code/reason/snippet16.re ================================================ type writer('a) = ('a, string); ================================================ FILE: src/content/1.8/code/reason/snippet17.re ================================================ module KleisliComposition = { let (>=>): ('a => writer('b), 'b => writer('c), 'a) => writer('c) = (m1, m2, x) => { let (y, s1) = m1(x); let (z, s2) = m2(y); (z, StringLabels.concat(~sep="", [s1, s2])); }; }; ================================================ FILE: src/content/1.8/code/reason/snippet18.re ================================================ module KleisliIdentity = { let return: 'a => writer('a) = a => (a, ""); }; ================================================ FILE: src/content/1.8/code/reason/snippet19.re ================================================ module KleisliFunctor: Functor = { type t('a) = writer('a); let fmap = f => KleisliComposition.(>=>)(id, x => KleisliIdentity.return(f(x))); }; ================================================ FILE: src/content/1.8/code/reason/snippet20.re ================================================ module PartialArrow = (T: {type r;}) => { type t('a) = T.r => 'a; }; ================================================ FILE: src/content/1.8/code/reason/snippet21.re ================================================ type reader('r, 'a) = 'r => 'a; ================================================ FILE: src/content/1.8/code/reason/snippet22.re ================================================ module ReaderFunctor = (In: {type r;}) : Functor => { type t('a) = reader(In.r, 'a); let fmap = (f, g) => compose(f, g); }; ================================================ FILE: src/content/1.8/code/reason/snippet23.re ================================================ type op('r, 'a) = 'a => 'r; ================================================ FILE: src/content/1.8/code/reason/snippet24.re ================================================ let fmap: 'a, 'b . ('a => 'b) => ('a => 'r) => ('b => 'r) ================================================ FILE: src/content/1.8/code/reason/snippet25.re ================================================ module type Contravariant = { type t('a); let contramap: ('b => 'a, t('a)) => t('b); }; ================================================ FILE: src/content/1.8/code/reason/snippet26.re ================================================ module OpContravariant = (In: {type r;}) : Contravariant => { type t('a) = op(In.r, 'a); let contramap = (f, g) => compose(g, f); }; ================================================ FILE: src/content/1.8/code/reason/snippet27.re ================================================ let flip = (f, b, a) => f(a, b); ================================================ FILE: src/content/1.8/code/reason/snippet28.re ================================================ let contramap: ('b => 'a, op('r, 'a)) => op('r, 'b) = ( (f, g) => flip(compose, f, g): ('b => 'a, op('r, 'a)) => op('r, 'b) ); ================================================ FILE: src/content/1.8/code/reason/snippet29.re ================================================ /* Profunctor definition */ module type Profunctor = { type p('a, 'b); let dimap: ('a => 'b, 'c => 'd, p('b, 'c)) => p('a, 'd); }; /* Profunctor alternate definition */ module type ProfunctorExt = { type p('a, 'b); let lmap: ('a => 'b, p('b, 'c)) => p('a, 'c); let rmap: ('b => 'c, p('a, 'b)) => p('a, 'c); }; /* Profunctor dimap defined using lmap and rmap */ module Profunctor_Using_Ext = (PF: ProfunctorExt) : Profunctor => { type p('a, 'b) = PF.p('a, 'b); let dimap = (f, g) => compose(PF.lmap(f), PF.rmap(g)); }; /** Profunctor lmap and rmap defined using dimap */ module ProfunctorExt_Using_Dimap = (PF: Profunctor) : ProfunctorExt => { type p('a, 'b) = PF.p('a, 'b); let lmap = f => PF.dimap(f, id); let rmap = g => PF.dimap(id, g); }; ================================================ FILE: src/content/1.8/code/reason/snippet30.re ================================================ module ProfunctorArrow: Profunctor = { type p('a, 'b) = 'a => 'b; let dimap = (f, g, p) => compose(g, compose(p, f)); }; module ProfunctorExtArrow: ProfunctorExt = { type p('a, 'b) = 'a => 'b; let lmap = (f, p) => (flip(compose))(f, p); let rmap = compose; }; ================================================ FILE: src/content/1.8/code/scala/snippet01.scala ================================================ trait Bifunctor[F[_, _]] { def bimap[A, B, C, D](g: A => C)(h: B => D): F[A, B] => F[C, D] = first(g) compose second(h) def first[A, B, C](g: A => C): F[A, B] => F[C, B] = bimap(g)(identity[B]) def second[A, B, D](h: B => D): F[A, B] => F[A, D] = bimap(identity[A])(h) } ================================================ FILE: src/content/1.8/code/scala/snippet02.scala ================================================ implicit val tuple2Bifunctor = new Bifunctor[Tuple2] { override def bimap[A, B, C, D](f: A => C)(g: B => D): ((A, B)) => (C, D) = { case (x, y) => (f(x), g(y)) } } ================================================ FILE: src/content/1.8/code/scala/snippet03.scala ================================================ def bimap[A, B, C, D](f: A => C)(g: B => D): ((A, B)) => (C, D) ================================================ FILE: src/content/1.8/code/scala/snippet04.scala ================================================ implicit val eitherBifunctor = new Bifunctor[Either] { override def bimap[A, B, C, D](f: A => C)(g: B => D): Either[A, B] => Either[C, D] = { case Left(x) => Left(f(x)) case Right(y) => Right(g(y)) } } ================================================ FILE: src/content/1.8/code/scala/snippet05.scala ================================================ type Id[A] = A ================================================ FILE: src/content/1.8/code/scala/snippet06.scala ================================================ implicit val identityFunctor = new Functor[Id] { def fmap[A, B](f: A => B)(x: Id[A]): Id[B] = f(x) } ================================================ FILE: src/content/1.8/code/scala/snippet07.scala ================================================ sealed trait Option[+A] case object None extends Option[Nothing] case class Some[A](a: A) extends Option[A] ================================================ FILE: src/content/1.8/code/scala/snippet08.scala ================================================ type Option[A] = Either[Const[Unit, A], Id[A]] ================================================ FILE: src/content/1.8/code/scala/snippet09.scala ================================================ case class BiComp[BF[_, _], FU[_], GU[_], A, B](v: BF[FU[A], GU[B]]) ================================================ FILE: src/content/1.8/code/scala/snippet10.scala ================================================ implicit def bicompBiFunctor[ BF[_, _], FU[_], GU[_]]( implicit BF: Bifunctor[BF], FU: Functor[FU], GU: Functor[GU]) = { // partially-applied type BiComp type BiCompAB[A, B] = BiComp[BF, FU, GU, A, B] new Bifunctor[BiCompAB] { override def bimap[A, B, C, D](f1: A => C)(f2: B => D): BiCompAB[A, B] => BiCompAB[C, D] = { case BiComp(x) => BiComp( BF.bimap(FU.fmap(f1))(GU.fmap(f2))(x) ) } } } ================================================ FILE: src/content/1.8/code/scala/snippet11.scala ================================================ BF[FU[A], GU[B]] ================================================ FILE: src/content/1.8/code/scala/snippet12.scala ================================================ def f1: A => A1 def f2: B => B1 ================================================ FILE: src/content/1.8/code/scala/snippet13.scala ================================================ def bimap: (FU[A] => FU[A1]) => (GU[B] => GU[B1]) => BiComp[BF, FU, GU, A, B] => BiComp[BF, FU, GU, A1, B1] ================================================ FILE: src/content/1.8/code/scala/snippet14.scala ================================================ sealed trait Tree[+A] case class Leaf[A](a: A) extends Tree[A] case class Node[A](l: Tree[A], r: Tree[A]) extends Tree[A] // implicit def treeFunctor = ??? ================================================ FILE: src/content/1.8/code/scala/snippet15.scala ================================================ implicit val treeFunctor = new Functor[Tree] { def fmap[A, B](f: A => B)(fa: Tree[A]): Tree[B] = fa match { case Leaf(a) => Leaf(f(a)) case Node(t, t1) => Node(fmap(f)(t), fmap(f)(t1)) } } ================================================ FILE: src/content/1.8/code/scala/snippet16.scala ================================================ type Writer[A] = (A, String) ================================================ FILE: src/content/1.8/code/scala/snippet17.scala ================================================ object kleisli { //allows us to use >=> as an infix operator implicit class KleisliOps[A, B](m1: A => Writer[B]) { def >=>[C](m2: B => Writer[C]): A => Writer[C] = x => { val (y, s1) = m1(x) val (z, s2) = m2(y) (z, s1 + s2) } } } ================================================ FILE: src/content/1.8/code/scala/snippet18.scala ================================================ // return is a keyword in Scala def pure[A](x: A): Writer[A] = (x, "") ================================================ FILE: src/content/1.8/code/scala/snippet19.scala ================================================ import kleisli._ def fmap[A, B](f: A => B): Writer[A] => Writer[B] = identity[Writer[A]] _ >=> (x => pure(f(x))) ================================================ FILE: src/content/1.8/code/scala/snippet20.scala ================================================ // with Kind Projector plugin Function1[R, ?] or R => ? ================================================ FILE: src/content/1.8/code/scala/snippet21.scala ================================================ type Reader[R, A] = R => A ================================================ FILE: src/content/1.8/code/scala/snippet22.scala ================================================ implicit def readerFunctor[R] = new Functor[Reader[R, ?]] { def fmap[A, B](f: A => B)(g: Reader[R, A]): Reader[R, B] = f compose g } ================================================ FILE: src/content/1.8/code/scala/snippet23.scala ================================================ type Op[R, A] = A => R ================================================ FILE: src/content/1.8/code/scala/snippet24.scala ================================================ def fmap[A, B]: (A => B) => (A => R) => (B => R) ================================================ FILE: src/content/1.8/code/scala/snippet25.scala ================================================ trait Contravariant[F[_]] { def contramap[A, B](f: B => A)(fa: F[A]): F[B] } ================================================ FILE: src/content/1.8/code/scala/snippet26.scala ================================================ implicit def opContravariant[R] = new Contravariant[R, ?] { def contramap[A, B](f: B => A)(g: Op[R, A]): Op[R, B] = g compose f } ================================================ FILE: src/content/1.8/code/scala/snippet27.scala ================================================ def flip[A, B, C]: (A => B => C) => (B => A => C) = f => y => x => f(x)(y) ================================================ FILE: src/content/1.8/code/scala/snippet28.scala ================================================ def compose[A, B, C]: (A => B) => (C => A) => C => B = f => g => c => f(g(c)) def contramap[A, B, R](f: B => A)(g: Op[R, A]): Op[R, B] = flip(compose[A, R, B])(f)(g) // or just: (f andThen g) ================================================ FILE: src/content/1.8/code/scala/snippet29.scala ================================================ trait Profunctor[F[_, _]] { def dimap[A, B, C, D]: (A => B) => (C => D) => F[B, C] => F[A, D] = f => g => lmap(f) compose rmap[B, C, D](g) def lmap[A, B, C]: (A => B) => F[B, C] => F[A, C] = f => dimap(f)(identity[C]) def rmap[A, B, C]: (B => C) => F[A, B] => F[A, C] = dimap[A, A, B, C](identity[A]) } ================================================ FILE: src/content/1.8/code/scala/snippet30.scala ================================================ implicit val function1Profunctor = new Profunctor[Function1] { override def bimap[A, B, C, D]: (A => B) => (C => D) => (B => C) => (A => D) = ab => cd => bc => cd compose bc compose ab override def lmap[A, B, C]: (A => B) => (B => C) => (A => C) = flip(compose) override def rmap[A, B, C]: (B => C) => (A => B) => (A => C) = compose } ================================================ FILE: src/content/1.8/functoriality.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{N}{ow that you know} what a functor is, and have seen a few examples, let's see how we can build larger functors from smaller ones. In particular it's interesting to see which type constructors (which correspond to mappings between objects in a category) can be extended to functors (which include mappings between morphisms). \section{Bifunctors} Since functors are morphisms in $\Cat$ (the category of categories), a lot of intuitions about morphisms --- and functions in particular --- apply to functors as well. For instance, just like you can have a function of two arguments, you can have a functor of two arguments, or a \newterm{bifunctor}. On objects, a bifunctor maps every pair of objects, one from category $\cat{C}$, and one from category $\cat{D}$, to an object in category $\cat{E}$. Notice that this is just saying that it's a mapping from a \newterm{Cartesian product} of categories $\cat{C}\times{}\cat{D}$ to $\cat{E}$. \begin{figure}[H] \centering\includegraphics[width=0.3\textwidth]{images/bifunctor.jpg} \end{figure} \noindent That's pretty straightforward. But functoriality means that a bifunctor has to map morphisms as well. This time, though, it must map a pair of morphisms, one from $\cat{C}$ and one from $\cat{D}$, to a morphism in $\cat{E}$. Again, a pair of morphisms is just a single morphism in the product category $\cat{C}\times{}\cat{D}$. We define a morphism in a Cartesian product of categories as a pair of morphisms which goes from one pair of objects to another pair of objects. These pairs of morphisms can be composed in the obvious way: \[(f, g) \circ (f', g') = (f \circ f', g \circ g')\] The composition is associative and it has an identity --- a pair of identity morphisms $(\id, \id)$. So a Cartesian product of categories is indeed a category. An easier way to think about bifunctors would be to consider them functors in each argument separately. So instead of translating functorial laws --- associativity and identity preservation --- from functors to bifunctors, it would be enough to check them separately for each argument. However, in general, separate functoriality is not enough to prove joint functoriality. Categories in which joint functoriality fails are called \newterm{premonoidal}. Let's define a bifunctor in Haskell. In this case all three categories are the same: the category of Haskell types. A bifunctor is a type constructor that takes two type arguments. Here's the definition of the \code{Bifunctor} typeclass taken directly from the library \code{Control.Bifunctor}: \src{snippet01} \begin{figure}[H] \centering\includegraphics[width=0.3\textwidth]{images/bimap.jpg} \caption{bimap} \end{figure} The type variable \code{f} represents the bifunctor. You can see that in all type signatures it's always applied to two type arguments. The first type signature defines \code{bimap}: a mapping of two functions at once. The result is a lifted function, \code{(f a b -> f c d)}, operating on types generated by the bifunctor's type constructor. There is a default implementation of \code{bimap} in terms of \code{first} and \code{second}. (As mentioned before, this doesn't always work, because the two maps may not commute, that is \code{first g . second h} may not be the same as \code{second h . first g}.) \noindent The two other type signatures, \code{first} and \code{second}, are the two \code{fmap}s witnessing the functoriality of \code{f} in the first and the second argument, respectively. \begin{figure}[H] \centering \begin{minipage}{0.45\textwidth} \centering \includegraphics[width=0.65\textwidth]{images/first.jpg} % first figure itself \caption{first} \end{minipage}\hfill \begin{minipage}{0.45\textwidth} \centering \includegraphics[width=0.6\textwidth]{images/second.jpg} % second figure itself \caption{second} \end{minipage} \end{figure} \noindent The typeclass definition provides default implementations for both of them in terms of \code{bimap}. When declaring an instance of \code{Bifunctor}, you have a choice of either implementing \code{bimap} and accepting the defaults for \code{first} and \code{second}, or implementing both \code{first} and \code{second} and accepting the default for \code{bimap} (of course, you may implement all three of them, but then it's up to you to make sure they are related to each other in this manner). \section{Product and Coproduct Bifunctors} An important example of a bifunctor is the categorical product --- a product of two objects that is defined by a \hyperref[products-and-coproducts]{universal construction}. If the product exists for any pair of objects, the mapping from those objects to the product is bifunctorial. This is true in general, and in Haskell in particular. Here's the \code{Bifunctor} instance for a pair constructor --- the simplest product type: \src{snippet02}[b] There isn't much choice: \code{bimap} simply applies the first function to the first component, and the second function to the second component of a pair. The code pretty much writes itself, given the types: \src{snippet03} The action of the bifunctor here is to make pairs of types, for instance: \begin{snip}{haskell} (,) a b = (a, b) \end{snip} By duality, a coproduct, if it's defined for every pair of objects in a category, is also a bifunctor. In Haskell, this is exemplified by the \code{Either} type constructor being an instance of \code{Bifunctor}: \src{snippet04}[b] This code also writes itself. Now, remember when we talked about monoidal categories? A monoidal category defines a binary operator acting on objects, together with a unit object. I mentioned that $\Set$ is a monoidal category with respect to Cartesian product, with the singleton set as a unit. And it's also a monoidal category with respect to disjoint union, with the empty set as a unit. What I haven't mentioned is that one of the requirements for a monoidal category is that the binary operator be a bifunctor. This is a very important requirement --- we want the monoidal product to be compatible with the structure of the category, which is defined by morphisms. We are now one step closer to the full definition of a monoidal category (we still need to learn about naturality, before we can get there). \section{Functorial Algebraic Data Types} We've seen several examples of parameterized data types that turned out to be functors --- we were able to define \code{fmap} for them. Complex data types are constructed from simpler data types. In particular, algebraic data types (\acronym{ADT}s) are created using sums and products. We have just seen that sums and products are functorial. We also know that functors compose. So if we can show that the basic building blocks of \acronym{ADT}s are functorial, we'll know that parameterized \acronym{ADT}s are functorial too. So what are the building blocks of parameterized algebraic data types? First, there are the items that have no dependency on the type parameter of the functor, like \code{Nothing} in \code{Maybe}, or \code{Nil} in \code{List}. They are equivalent to the \code{Const} functor. Remember, the \code{Const} functor ignores its type parameter (really, the \emph{second} type parameter, which is the one of interest to us, the first one being kept constant). Then there are the elements that simply encapsulate the type parameter itself, like \code{Just} in \code{Maybe}. They are equivalent to the identity functor. I mentioned the identity functor previously, as the identity morphism in \emph{Cat}, but didn't give its definition in Haskell. Here it is: \src{snippet05} \src{snippet06} You can think of \code{Identity} as the simplest possible container that always stores just one (immutable) value of type \code{a}. Everything else in algebraic data structures is constructed from these two primitives using products and sums. With this new knowledge, let's have a fresh look at the \code{Maybe} type constructor: \src{snippet07} It's a sum of two types, and we now know that the sum is functorial. The first part, \code{Nothing} can be represented as a \code{Const ()} acting on \code{a} (the first type parameter of \code{Const} is set to unit --- later we'll see more interesting uses of \code{Const}). The second part is just a different name for the identity functor. We could have defined \code{Maybe}, up to isomorphism, as: \src{snippet08} So \code{Maybe} is the composition of the bifunctor \code{Either} with two functors, \code{Const ()} and \code{Identity}. (\code{Const} is really a bifunctor, but here we always use it partially applied.) We've already seen that a composition of functors is a functor --- we can easily convince ourselves that the same is true of bifunctors. All we need is to figure out how a composition of a bifunctor with two functors works on morphisms. Given two morphisms, we simply lift one with one functor and the other with the other functor. We then lift the resulting pair of lifted morphisms with the bifunctor. We can express this composition in Haskell. Let's define a data type that is parameterized by a bifunctor \code{bf} (it's a type variable that is a type constructor that takes two types as arguments), two functors \code{fu} and \code{gu} (type constructors that take one type variable each), and two regular types \code{a} and \code{b}. We apply \code{fu} to \code{a} and \code{gu} to \code{b}, and then apply \code{bf} to the resulting two types: \src{snippet09} That's the composition on objects, or types. Notice how in Haskell we apply type constructors to types, just like we apply functions to arguments. The syntax is the same. If you're getting a little lost, try applying \code{BiComp} to \code{Either}, \code{Const ()}, \code{Identity}, \code{a}, and \code{b}, in this order. You will recover our bare-bone version of \code{Maybe b} (\code{a} is ignored). The new data type \code{BiComp} is a bifunctor in \code{a} and \code{b}, but only if \code{bf} is itself a \code{Bifunctor} and \code{fu} and \code{gu} are \code{Functor}s. The compiler must know that there will be a definition of \code{bimap} available for \code{bf}, and definitions of \code{fmap} for \code{fu} and \code{gu}. In Haskell, this is expressed as a precondition in the instance declaration: a set of class constraints followed by a double arrow: \src{snippet10}[b] The implementation of \code{bimap} for \code{BiComp} is given in terms of \code{bimap} for \code{bf} and the two \code{fmap}s for \code{fu} and \code{gu}. The compiler automatically infers all the types and picks the correct overloaded functions whenever \code{BiComp} is used. The \code{x} in the definition of \code{bimap} has the type: \src{snippet11} which is quite a mouthful. The outer \code{bimap} breaks through the outer \code{bf} layer, and the two \code{fmap}s dig under \code{fu} and \code{gu}, respectively. If the types of \code{f1} and \code{f2} are: \src{snippet12} then the final result is of the type \code{bf (fu a') (gu b')}: \src{snippet13}[b] If you like jigsaw puzzles, these kinds of type manipulations can provide hours of entertainment. So it turns out that we didn't have to prove that \code{Maybe} was a functor --- this fact followed from the way it was constructed as a sum of two functorial primitives. A perceptive reader might ask the question: If the derivation of the \code{Functor} instance for algebraic data types is so mechanical, can't it be automated and performed by the compiler? Indeed, it can, and it is. You need to enable a particular Haskell extension by including this line at the top of your source file: \begin{snip}{haskell} {-# LANGUAGE DeriveFunctor #-} \end{snip} and then add \code{deriving Functor} to your data structure: \begin{snip}{haskell} data Maybe a = Nothing | Just a deriving Functor \end{snip} and the corresponding \code{fmap} will be implemented for you. The regularity of algebraic data structures makes it possible to derive instances not only of \code{Functor} but of several other type classes, including the \code{Eq} type class I mentioned before. There is also the option of teaching the compiler to derive instances of your own typeclasses, but that's a bit more advanced. The idea though is the same: You provide the behavior for the basic building blocks and sums and products, and let the compiler figure out the rest. \section{Functors in C++} If you are a C++ programmer, you obviously are on your own as far as implementing functors goes. However, you should be able to recognize some types of algebraic data structures in C++. If such a data structure is made into a generic template, you should be able to quickly implement \code{fmap} for it. Let's have a look at a tree data structure, which we would define in Haskell as a recursive sum type: \src{snippet14} As I mentioned before, one way of implementing sum types in C++ is through class hierarchies. It would be natural, in an object-oriented language, to implement \code{fmap} as a virtual function of the base class \code{Functor} and then override it in all subclasses. Unfortunately this is impossible because \code{fmap} is a template, parameterized not only by the type of the object it's acting upon (the \code{this} pointer) but also by the return type of the function that's been applied to it. Virtual functions cannot be templatized in C++. We'll implement \code{fmap} as a generic free function, and we'll replace pattern matching with \code{dynamic\_cast}. The base class must define at least one virtual function in order to support dynamic casting, so we'll make the destructor virtual (which is a good idea in any case): \begin{snip}{cpp} template struct Tree { virtual ~Tree() {} }; \end{snip} The \code{Leaf} is just an \code{Identity} functor in disguise: \begin{snip}{cpp} template struct Leaf : public Tree { T _label; Leaf(T l) : _label(l) {} }; \end{snip} The \code{Node} is a product type: \begin{snip}{cpp} template struct Node : public Tree { Tree * _left; Tree * _right; Node(Tree * l, Tree * r) : _left(l), _right(r) {} }; \end{snip} When implementing \code{fmap} we take advantage of dynamic dispatching on the type of the \code{Tree}. The \code{Leaf} case applies the \code{Identity} version of \code{fmap}, and the \code{Node} case is treated like a bifunctor composed with two copies of the \code{Tree} functor. As a C++ programmer, you're probably not used to analyzing code in these terms, but it's a good exercise in categorical thinking. \begin{snip}{cpp} template Tree * fmap(std::function f, Tree * t) { Leaf * pl = dynamic_cast *>(t); if (pl) return new Leaf(f (pl->_label)); Node * pn = dynamic_cast*>(t); if (pn) return new Node( fmap(f, pn->_left) , fmap(f, pn->_right)); return nullptr; } \end{snip} For simplicity, I decided to ignore memory and resource management issues, but in production code you would probably use smart pointers (unique or shared, depending on your policy). Compare it with the Haskell implementation of \code{fmap}: \src{snippet15} This implementation can also be automatically derived by the compiler. \section{The Writer Functor} I promised that I would come back to the \hyperref[kleisli-categories]{Kleisli category} I described earlier. Morphisms in that category were represented as ``embellished'' functions returning the \code{Writer} data structure. \src{snippet16} I said that the embellishment was somehow related to endofunctors. And, indeed, the \code{Writer} type constructor is functorial in \code{a}. We don't even have to implement \code{fmap} for it, because it's just a simple product type. But what's the relation between a Kleisli category and a functor --- in general? A Kleisli category, being a category, defines composition and identity. Let me remind you that the composition is given by the fish operator: \src{snippet17} and the identity morphism by a function called \code{return}: \src{snippet18} It turns out that, if you look at the types of these two functions long enough (and I mean, \emph{long} enough), you can find a way to combine them to produce a function with the right type signature to serve as \code{fmap}. Like this: \src{snippet19} Here, the fish operator combines two functions: one of them is the familiar \code{id}, and the other is a lambda that applies \code{return} to the result of acting with \code{f} on the lambda's argument. The hardest part to wrap your brain around is probably the use of \code{id}. Isn't the argument to the fish operator supposed to be a function that takes a ``normal'' type and returns an embellished type? Well, not really. Nobody says that \code{a} in \code{a -> Writer b} must be a ``normal'' type. It's a type variable, so it can be anything, in particular it can be an embellished type, like \code{Writer b}. So \code{id} will take \code{Writer a} and turn it into \code{Writer a}. The fish operator will fish out the value of \code{a} and pass it as \code{x} to the lambda. There, \code{f} will turn it into a \code{b} and \code{return} will embellish it, making it \code{Writer b}. Putting it all together, we end up with a function that takes \code{Writer a} and returns \code{Writer b}, exactly what \code{fmap} is supposed to produce. Notice that this argument is very general: you can replace \code{Writer} with any type constructor. As long as it supports a fish operator and \code{return}, you can define \code{fmap} as well. So the embellishment in the Kleisli category is always a functor. (Not every functor, though, gives rise to a Kleisli category.) You might wonder if the \code{fmap} we have just defined is the same \code{fmap} the compiler would have derived for us with \code{deriving Functor}. Interestingly enough, it is. This is due to the way Haskell implements polymorphic functions. It's called \newterm{parametric polymorphism}, and it's a source of so called \newterm{theorems for free}. One of those theorems says that, if there is an implementation of \code{fmap} for a given type constructor, one that preserves identity, then it must be unique. \section{Covariant and Contravariant Functors} Now that we've reviewed the writer functor, let's go back to the reader functor. It was based on the partially applied function-arrow type constructor: \src{snippet20} We can rewrite it as a type synonym: \src{snippet21} for which the \code{Functor} instance, as we've seen before, reads: \src{snippet22} But just like the pair type constructor, or the \code{Either} type constructor, the function type constructor takes two type arguments. The pair and \code{Either} were functorial in both arguments --- they were bifunctors. Is the function constructor a bifunctor too? Let's try to make it functorial in the first argument. We'll start with a type synonym --- it's just like the \code{Reader} but with the arguments flipped: \src{snippet23} This time we fix the return type, \code{r}, and vary the argument type, \code{a}. Let's see if we can somehow match the types in order to implement \code{fmap}, which would have the following type signature: \src{snippet24} With just two functions taking \code{a} and returning, respectively, \code{b} and \code{r}, there is simply no way to build a function taking \code{b} and returning \code{r}! It would be different if we could somehow invert the first function, so that it took \code{b} and returned \code{a} instead. We can't invert an arbitrary function, but we can go to the opposite category. A short recap: For every category $\cat{C}$ there is a dual category $\cat{C}^\mathit{op}$. It's a category with the same objects as $\cat{C}$, but with all the arrows reversed. Consider a functor that goes between $\cat{C}^\mathit{op}$ and some other category $\cat{D}$: \[F \Colon \cat{C}^\mathit{op} \to \cat{D}\] Such a functor maps a morphism $f^\mathit{op} \Colon a \to b$ in $\cat{C}^\mathit{op}$ to the morphism $F f^\mathit{op} \Colon F a \to F b$ in $\cat{D}$. But the morphism $f^\mathit{op}$ secretly corresponds to some morphism $f \Colon b \to a$ in the original category $\cat{C}$. Notice the inversion. Now, $F$ is a regular functor, but there is another mapping we can define based on $F$, which is not a functor --- let's call it $G$. It's a mapping from $\cat{C}$ to $\cat{D}$. It maps objects the same way $F$ does, but when it comes to mapping morphisms, it reverses them. It takes a morphism $f \Colon b \to a$ in $\cat{C}$, maps it first to the opposite morphism $f^\mathit{op} \Colon a \to b$ and then uses the functor $F$ on it, to get $F f^\mathit{op} \Colon F a \to F b$. Considering that $F a$ is the same as $G a$ and $F b$ is the same as $G b$, the whole trip can be described as: $G f \Colon (b \to a) \to (G a \to G b)$ It's a ``functor with a twist.'' A mapping of categories that inverts the direction of morphisms in this manner is called a \emph{contravariant functor}. Notice that a contravariant functor is just a regular functor from the opposite category. The regular functors, by the way --- the kind we've been studying thus far --- are called \emph{covariant} functors. \begin{figure}[H] \centering \includegraphics[width=40mm]{images/contravariant.jpg} \end{figure} \noindent Here's the typeclass defining a contravariant functor (really, a contravariant \emph{endo}functor) in Haskell: \src{snippet25} Our type constructor \code{Op} is an instance of it: \src{snippet26} Notice that the function \code{f} inserts itself \emph{before} (that is, to the right of) the contents of \code{Op} --- the function \code{g}. The definition of \code{contramap} for \code{Op} may be made even terser, if you notice that it's just the function composition operator with the arguments flipped. There is a special function for flipping arguments, called \code{flip}: \src{snippet27} With it, we get: \src{snippet28} \section{Profunctors} We've seen that the function-arrow operator is contravariant in its first argument and covariant in the second. Is there a name for such a beast? It turns out that, if the target category is $\Set$, such a beast is called a \newterm{profunctor}. Because a contravariant functor is equivalent to a covariant functor from the opposite category, a profunctor is defined as: \[\cat{C}^\mathit{op} \times \cat{D} \to \Set\] Since, to first approximation, Haskell types are sets, we apply the name \code{Profunctor} to a type constructor \code{p} of two arguments, which is contra-functorial in the first argument and functorial in the second. Here's the appropriate typeclass taken from the \code{Data.Profunctor} library: \src{snippet29}[b] All three functions come with default implementations. Just like with \code{Bifunctor}, when declaring an instance of \code{Profunctor}, you have a choice of either implementing \code{dimap} and accepting the defaults for \code{lmap} and \code{rmap}, or implementing both \code{lmap} and \code{rmap} and accepting the default for \code{dimap}. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/dimap.jpg} \caption{dimap} \end{figure} \noindent Now we can assert that the function-arrow operator is an instance of a \code{Profunctor}: \src{snippet30}[b] Profunctors have their application in the Haskell lens library. We'll see them again when we talk about ends and coends. \section{The Hom-Functor} The above examples are the reflection of a more general statement that the mapping that takes a pair of objects $a$ and $b$ and assigns to it the set of morphisms between them, the hom-set $\cat{C}(a, b)$, is a functor. It is a functor from the product category $\cat{C}^\mathit{op}\times{}\cat{C}$ to the category of sets, $\Set$. Let's define its action on morphisms. A morphism in $\cat{C}^\mathit{op}\times{}\cat{C}$ is a pair of morphisms from $\cat{C}$: \begin{gather*} f \Colon a' \to a \\ g \Colon b \to b' \end{gather*} The lifting of this pair must be a morphism (a function) from the set $\cat{C}(a, b)$ to the set $\cat{C}(a', b')$. Just pick any element $h$ of $\cat{C}(a, b)$ (it's a morphism from $a$ to $b$) and assign to it: \[g \circ h \circ f\] which is an element of $\cat{C}(a', b')$. As you can see, the hom-functor is a special case of a profunctor. \section{Challenges} \begin{enumerate} \tightlist \item Show that the data type: \begin{snip}{haskell} data Pair a b = Pair a b \end{snip} is a bifunctor. For additional credit implement all three methods of \code{Bifunctor} and use equational reasoning to show that these definitions are compatible with the default implementations whenever they can be applied. \item Show the isomorphism between the standard definition of \code{Maybe} and this desugaring: \begin{snip}{haskell} type Maybe' a = Either (Const () a) (Identity a) \end{snip} Hint: Define two mappings between the two implementations. For additional credit, show that they are the inverse of each other using equational reasoning. \item Let's try another data structure. I call it a \code{PreList} because it's a precursor to a \code{List}. It replaces recursion with a type parameter \code{b}. \begin{snip}{haskell} data PreList a b = Nil | Cons a b \end{snip} You could recover our earlier definition of a \code{List} by recursively applying \code{PreList} to itself (we'll see how it's done when we talk about fixed points). Show that \code{PreList} is an instance of \code{Bifunctor}. \item Show that the following data types define bifunctors in \code{a} and \code{b}: \begin{snip}{haskell} data K2 c a b = K2 c data Fst a b = Fst a data Snd a b = Snd b \end{snip} For additional credit, check your solutions against Conor McBride's paper \urlref{http://strictlypositive.org/CJ.pdf}{Clowns to the Left of me, Jokers to the Right}. \item Define a bifunctor in a language other than Haskell. Implement \code{bimap} for a generic pair in that language. \item Should \code{std::map} be considered a bifunctor or a profunctor in the two template arguments \code{Key} and \code{T}? How would you redesign this data type to make it so? \end{enumerate} ================================================ FILE: src/content/1.9/code/haskell/snippet01.hs ================================================ a -> (b -> c) ================================================ FILE: src/content/1.9/code/haskell/snippet02.hs ================================================ a -> b -> c ================================================ FILE: src/content/1.9/code/haskell/snippet03.hs ================================================ catstr :: String -> String -> String catstr s s' = s ++ s' ================================================ FILE: src/content/1.9/code/haskell/snippet04.hs ================================================ catstr' s = \s' -> s ++ s' ================================================ FILE: src/content/1.9/code/haskell/snippet05.hs ================================================ greet :: String -> String greet = catstr "Hello " ================================================ FILE: src/content/1.9/code/haskell/snippet06.hs ================================================ (a, b) -> c ================================================ FILE: src/content/1.9/code/haskell/snippet07.hs ================================================ curry :: ((a, b) -> c) -> (a -> b -> c) curry f a b = f (a, b) ================================================ FILE: src/content/1.9/code/haskell/snippet08.hs ================================================ uncurry :: (a -> b -> c) -> ((a, b) -> c) uncurry f (a, b) = f a b ================================================ FILE: src/content/1.9/code/haskell/snippet09.hs ================================================ factorizer :: ((a, b) -> c) -> (a -> (b -> c)) factorizer g = \a -> (\b -> g (a, b)) ================================================ FILE: src/content/1.9/code/haskell/snippet10.hs ================================================ f :: Either Int Double -> String ================================================ FILE: src/content/1.9/code/haskell/snippet11.hs ================================================ f (Left n) = if n < 0 then "Negative int" else "Positive int" f (Right x) = if x < 0.0 then "Negative double" else "Positive double" ================================================ FILE: src/content/1.9/code/haskell/snippet12.hs ================================================ eval :: ((a -> b), a) -> b ================================================ FILE: src/content/1.9/code/haskell/snippet13.hs ================================================ eval :: ((a -> b), a) -> b eval (f, x) = f x ================================================ FILE: src/content/1.9/code/haskell/snippet14.hs ================================================ Either a b -> a ================================================ FILE: src/content/1.9/code/haskell/snippet15.hs ================================================ absurd :: Void -> a ================================================ FILE: src/content/1.9/code/ocaml/snippet01.ml ================================================ 'a -> ('b -> 'c) ================================================ FILE: src/content/1.9/code/ocaml/snippet02.ml ================================================ 'a -> 'b -> 'c ================================================ FILE: src/content/1.9/code/ocaml/snippet03.ml ================================================ let catstr s s' = String.concat ~sep:"" [ s; s' ] ================================================ FILE: src/content/1.9/code/ocaml/snippet04.ml ================================================ let catstr s s' = String.concat ~sep:"" [ s; s' ] ================================================ FILE: src/content/1.9/code/ocaml/snippet05.ml ================================================ let greet = catstr "Hello" ================================================ FILE: src/content/1.9/code/ocaml/snippet06.ml ================================================ 'a * 'b -> 'a ================================================ FILE: src/content/1.9/code/ocaml/snippet07.ml ================================================ let curry f a b = f (a, b) ================================================ FILE: src/content/1.9/code/ocaml/snippet08.ml ================================================ let uncurry f p = f (fst p) (snd p) ================================================ FILE: src/content/1.9/code/ocaml/snippet09.ml ================================================ let factorizer g a b = g (a, b) ================================================ FILE: src/content/1.9/code/ocaml/snippet10.ml ================================================ module type Exponential_Of_Sums_Example = sig val f : (int, float) either -> string end ================================================ FILE: src/content/1.9/code/ocaml/snippet11.ml ================================================ module Exp_Sum_Impl : Exponential_Of_Sums_Example = struct let f = function | Left n -> if n < 0 then "Negative int" else "Positive int" | Right x -> if Float.compare x 0.4 < 0 then "Negative double" else "Positive double" ;; end ================================================ FILE: src/content/1.9/code/ocaml/snippet12.ml ================================================ val eval : (('a -> 'b), 'a) -> 'b ================================================ FILE: src/content/1.9/code/ocaml/snippet13.ml ================================================ let eval (f, a) = f a ================================================ FILE: src/content/1.9/code/ocaml/snippet14.ml ================================================ ('a, 'b) either -> 'a ================================================ FILE: src/content/1.9/code/ocaml/snippet15.ml ================================================ val absurd : void -> 'a ================================================ FILE: src/content/1.9/code/reason/snippet01.re ================================================ 'a => ('b => 'c) ================================================ FILE: src/content/1.9/code/reason/snippet02.re ================================================ 'a => 'b => 'c ================================================ FILE: src/content/1.9/code/reason/snippet03.re ================================================ let catstr = (s, s') => String.concat(~sep="", [s, s']); ================================================ FILE: src/content/1.9/code/reason/snippet04.re ================================================ let catstr = (s, s') => String.concat(~sep="", [s, s']); ================================================ FILE: src/content/1.9/code/reason/snippet05.re ================================================ let greet = catstr("Hello"); ================================================ FILE: src/content/1.9/code/reason/snippet06.re ================================================ ('a, 'b) => 'a ================================================ FILE: src/content/1.9/code/reason/snippet07.re ================================================ let curry = (f, a, b) => f((a, b)); ================================================ FILE: src/content/1.9/code/reason/snippet08.re ================================================ let uncurry = (f, p) => f(fst(p), snd(p)); ================================================ FILE: src/content/1.9/code/reason/snippet09.re ================================================ let factorizer = (g, a, b) => g((a, b)); ================================================ FILE: src/content/1.9/code/reason/snippet10.re ================================================ module type Exponential_Of_Sums_Example = { let f: either(int, float) => string; }; ================================================ FILE: src/content/1.9/code/reason/snippet11.re ================================================ module Exp_Sum_Impl: Exponential_Of_Sums_Example = { let f = fun | Left(n) => n < 0 ? "Negative int" : "Positive int" | Right(x) => x < 0.0 ? "Negative double" : "Positive double" }; ================================================ FILE: src/content/1.9/code/reason/snippet12.re ================================================ let eval: (('a => 'b), 'a) => 'b ================================================ FILE: src/content/1.9/code/reason/snippet13.re ================================================ let eval = ((f, a)) => f(a); ================================================ FILE: src/content/1.9/code/reason/snippet14.re ================================================ either('a, 'b) => 'a ================================================ FILE: src/content/1.9/code/reason/snippet15.re ================================================ let absurd: void => 'a ================================================ FILE: src/content/1.9/code/scala/snippet01.scala ================================================ A => (B => C) ================================================ FILE: src/content/1.9/code/scala/snippet02.scala ================================================ A => B => C ================================================ FILE: src/content/1.9/code/scala/snippet03.scala ================================================ val catstr: (String, String) => String = (s, s1) => s ++ s1 ================================================ FILE: src/content/1.9/code/scala/snippet04.scala ================================================ val catstrᛌ: String => String => String = s => s1 => s ++ s1 ================================================ FILE: src/content/1.9/code/scala/snippet05.scala ================================================ val greet: String => String = catstr("Hello ", _) ================================================ FILE: src/content/1.9/code/scala/snippet06.scala ================================================ (A, B) => C ================================================ FILE: src/content/1.9/code/scala/snippet07.scala ================================================ def curry[A, B, C](f: (A, B) => C): A => B => C = a => b => f(a, b) ================================================ FILE: src/content/1.9/code/scala/snippet08.scala ================================================ def uncurry[A, B, C](f: A => B => C): (A, B) => C = (a, b) => f(a)(b) ================================================ FILE: src/content/1.9/code/scala/snippet09.scala ================================================ def factorizer[A, B, C](g: (A, B) => C): A => (B => C) = a => (b => g(a, b)) ================================================ FILE: src/content/1.9/code/scala/snippet10.scala ================================================ val f: Either[Int, Double] => String ================================================ FILE: src/content/1.9/code/scala/snippet11.scala ================================================ val f: Either[Int, Double] => String = { case Left(n) => if (n < 0) "Negative int" else "Positive int" case Right(x) => if (x < 0.0) "Negative double" else "Positive double" } ================================================ FILE: src/content/1.9/code/scala/snippet12.scala ================================================ def eval[A, B]: ((A => B), A) => B ================================================ FILE: src/content/1.9/code/scala/snippet13.scala ================================================ def eval[A, B]: ((A => B), A) => B = (f, x) => f(x) ================================================ FILE: src/content/1.9/code/scala/snippet14.scala ================================================ Either[A, B] => A ================================================ FILE: src/content/1.9/code/scala/snippet15.scala ================================================ def absurd[A]: Nothing => A ================================================ FILE: src/content/1.9/function-types.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{S}{o far} I've been glossing over the meaning of function types. A function type is different from other types. Take \code{Integer}, for instance: It's just a set of integers. \code{Bool} is a two element set. But a function type $a\to b$ is more than that: it's a set of morphisms between objects $a$ and $b$. A set of morphisms between two objects in any category is called a hom-set. It just so happens that in the category $\Set$ every hom-set is itself an object in the same category ---because it is, after all, a \emph{set}. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/set-hom-set.jpg} \caption{Hom-set in Set is just a set} \end{figure} \noindent The same is not true of other categories where hom-sets are external to a category. They are even called \emph{external} hom-sets. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/hom-set.jpg} \caption{Hom-set in category C is an external set} \end{figure} \noindent It's the self-referential nature of the category $\Set$ that makes function types special. But there is a way, at least in some categories, to construct objects that represent hom-sets. Such objects are called \newterm{internal} hom-sets. \section{Universal Construction} Let's forget for a moment that function types are sets and try to construct a function type, or more generally, an internal hom-set, from scratch. As usual, we'll take our cues from the $\Set$ category, but carefully avoid using any properties of sets, so that the construction will automatically work for other categories. A function type may be considered a composite type because of its relationship to the argument type and the result type. We've already seen the constructions of composite types --- those that involved relationships between objects. We used universal constructions to define a \hyperref[products-and-coproducts]{product and coproduct types}. We can use the same trick to define a function type. We will need a pattern that involves three objects: the function type that we are constructing, the argument type, and the result type. The obvious pattern that connects these three types is called \newterm{function application} or \newterm{evaluation}. Given a candidate for a function type, let's call it $z$ (notice that, if we are not in the category $\Set$, this is just an object like any other object), and the argument type $a$ (an object), the application maps this pair to the result type $b$ (an object). We have three objects, two of them fixed (the ones representing the argument type and the result type). We also have the application, which is a mapping. How do we incorporate this mapping into our pattern? If we were allowed to look inside objects, we could pair a function $f$ (an element of $z$) with an argument $x$ (an element of $a$) and map it to $f x$ (the application of $f$ to $x$, which is an element of $b$). \begin{figure}[H] \centering\includegraphics[width=0.35\textwidth]{images/functionset.jpg} \caption{In Set we can pick a function $f$ from a set of functions $z$ and we can pick an argument $x$ from the set (type) $a$. We get an element $f x$ in the set (type) $b$.} \end{figure} \noindent But instead of dealing with individual pairs $(f, x)$, we can as well talk about the whole \emph{product} of the function type $z$ and the argument type $a$. The product $z\times{}a$ is an object, and we can pick, as our application morphism, an arrow $g$ from that object to $b$. In $\Set$, $g$ would be the function that maps every pair $(f, x)$ to $f x$. So that's the pattern: a product of two objects $z$ and $a$ connected to another object $b$ by a morphism $g$. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/functionpattern.jpg} \caption{A pattern of objects and morphisms that is the starting point of the universal construction} \end{figure} \noindent Is this pattern specific enough to single out the function type using a universal construction? Not in every category. But in the categories of interest to us it is. And another question: Would it be possible to define a function object without first defining a product? There are categories in which there is no product, or there isn't a product for all pairs of objects. The answer is no: there is no function type, if there is no product type. We'll come back to this later when we talk about exponentials. Let's review the universal construction. We start with a pattern of objects and morphisms. That's our imprecise query, and it usually yields lots and lots of hits. In particular, in $\Set$, pretty much everything is connected to everything. We can take any object $z$, form its product with $a$, and there's going to be a function from it to $b$ (except when $b$ is an empty set). That's when we apply our secret weapon: ranking. This is usually done by requiring that there be a unique mapping between candidate objects --- a mapping that somehow factorizes our construction. In our case, we'll decree that $z$ together with the morphism $g$ from $z \times a$ to $b$ is \emph{better} than some other $z'$ with its own application $g'$, if and only if there is a unique mapping $h$ from $z'$ to $z$ such that the application of $g'$ factors through the application of $g$. (Hint: Read this sentence while looking at the picture.) \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/functionranking.jpg} \caption{Establishing a ranking between candidates for the function object} \end{figure} \noindent Now here's the tricky part, and the main reason I postponed this particular universal construction till now. Given the morphism $h \Colon z'\to z$, we want to close the diagram that has both $z'$ and $z$ crossed with $a$. What we really need, given the mapping $h$ from $z'$ to $z$, is a mapping from $z' \times a$ to $z \times a$. And now, after discussing the \hyperref[functoriality]{functoriality of the product}, we know how to do it. Because the product itself is a functor (more precisely an endo-bi-functor), it's possible to lift pairs of morphisms. In other words, we can define not only products of objects but also products of morphisms. Since we are not touching the second component of the product $z' \times a$, we will lift the pair of morphisms $(h, \id)$, where $\id$ is an identity on $a$. So, here's how we can factor one application, $g$, out of another application $g'$: \[g' = g \circ (h \times \id)\] The key here is the action of the product on morphisms. The third part of the universal construction is selecting the object that is universally the best. Let's call this object $a \Rightarrow b$ (think of this as a symbolic name for one object, not to be confused with a Haskell typeclass constraint --- I'll discuss different ways of naming it later). This object comes with its own application --- a morphism from $(a \Rightarrow b) \times a$ to $b$ --- which we will call $\mathit{eval}$. The object \code{$a \Rightarrow b$} is the best if any other candidate for a function object can be uniquely mapped to it in such a way that its application morphism $g$ factorizes through $\mathit{eval}$. This object is better than any other object according to our ranking. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/universalfunctionobject.jpg} \caption{The definition of the universal function object. This is the same diagram as above, but now the object $a \Rightarrow b$ is \emph{universal}.} \end{figure} \noindent Formally: \begin{longtable}[]{@{}l@{}} \toprule \begin{minipage}[t]{0.97\columnwidth}\raggedright\strut A \emph{function object} from $a$ to $b$ is an object $a \Rightarrow b$ together with the morphism \[\mathit{eval} \Colon ((a \Rightarrow b) \times a) \to b\] such that for any other object $z$ with a morphism \[g \Colon z \times a \to b\] there is a unique morphism \[h \Colon z \to (a \Rightarrow b)\] that factors $g$ through $\mathit{eval}$: \[g = \mathit{eval} \circ (h \times \id)\] \end{minipage}\tabularnewline \bottomrule \end{longtable} \noindent Of course, there is no guarantee that such an object $a \Rightarrow b$ exists for any pair of objects $a$ and $b$ in a given category. But it always does in $\Set$. Moreover, in $\Set$, this object is isomorphic to the hom-set $\Set(a, b)$. This is why, in Haskell, we interpret the function type \code{a -> b} as the categorical function object $a \Rightarrow b$. \section{Currying} Let's have a second look at all the candidates for the function object. This time, however, let's think of the morphism $g$ as a function of two variables, $z$ and $a$. \[g \Colon z \times a \to b\] Being a morphism from a product comes as close as it gets to being a function of two variables. In particular, in $\Set$, $g$ is a function from pairs of values, one from the set $z$ and one from the set $a$. On the other hand, the universal property tells us that for each such $g$ there is a unique morphism $h$ that maps $z$ to a function object $a \Rightarrow b$. \[h \Colon z \to (a \Rightarrow b)\] In $\Set$, this just means that $h$ is a function that takes one variable of type $z$ and returns a function from $a$ to $b$. That makes $h$ a higher order function. Therefore the universal construction establishes a one-to-one correspondence between functions of two variables and functions of one variable returning functions. This correspondence is called \newterm{currying}, and $h$ is called the curried version of $g$. This correspondence is one-to-one, because given any $g$ there is a unique $h$, and given any $h$ you can always recreate the two-argument function $g$ using the formula: \[g = \mathit{eval} \circ (h \times \id)\] The function $g$ can be called the \emph{uncurried} version of $h$. Currying is essentially built into the syntax of Haskell. A function returning a function: \src{snippet01} is often thought of as a function of two variables. That's how we read the un-parenthesized signature: \src{snippet02} This interpretation is apparent in the way we define multi-argument functions. For instance: \src{snippet03} The same function can be written as a one-argument function returning a function --- a lambda: \src{snippet04} These two definitions are equivalent, and either can be partially applied to just one argument, producing a one-argument function, as in: \src{snippet05} Strictly speaking, a function of two variables is one that takes a pair (a product type): \src{snippet06} It's trivial to convert between the two representations, and the two (higher-order) functions that do it are called, unsurprisingly, \code{curry} and \code{uncurry}: \src{snippet07} and \src{snippet08} Notice that \code{curry} is the \emph{factorizer} for the universal construction of the function object. This is especially apparent if it's rewritten in this form: \src{snippet09} (As a reminder: A factorizer produces the factorizing function from a candidate.) In non-functional languages, like C++, currying is possible but nontrivial. You can think of multi-argument functions in C++ as corresponding to Haskell functions taking tuples (although, to confuse things even more, in C++ you can define functions that take an explicit \code{std::tuple}, as well as variadic functions, and functions taking initializer lists). You can partially apply a C++ function using the template \code{std::bind}. For instance, given a function of two strings: \begin{snip}{cpp} std::string catstr(std::string s1, std::string s2) { return s1 + s2; } \end{snip} you can define a function of one string: \begin{snip}{cpp} using namespace std::placeholders; auto greet = std::bind(catstr, "Hello ", _1); std::cout << greet("Haskell Curry"); \end{snip} Scala, which is more functional than C++ or Java, falls somewhere in between. If you anticipate that the function you're defining will be partially applied, you define it with multiple argument lists: \begin{snip}{cpp} def catstr(s1: String)(s2: String) = s1 + s2 \end{snip} Of course that requires some amount of foresight or prescience on the part of a library writer. \section{Exponentials} In mathematical literature, the function object, or the internal hom-object between two objects $a$ and $b$, is often called the \newterm{exponential} and denoted by $b^{a}$. Notice that the argument type is in the exponent. This notation might seem strange at first, but it makes perfect sense if you think of the relationship between functions and products. We've already seen that we have to use the product in the universal construction of the internal hom-object, but the connection goes deeper than that. This is best seen when you consider functions between finite types --- types that have a finite number of values, like \code{Bool}, \code{Char}, or even \code{Int} or \code{Double}. Such functions, at least in principle, can be fully memoized or turned into data structures to be looked up. And this is the essence of the equivalence between functions, which are morphisms, and function types, which are objects. For instance a (pure) function from \code{Bool} is completely specified by a pair of values: one corresponding to \code{False}, and one corresponding to \code{True}. The set of all possible functions from \code{Bool} to, say, \code{Int} is the set of all pairs of \code{Int}s. This is the same as the product \code{Int} \times{} \code{Int} or, being a little creative with notation, \code{Int}\textsuperscript{2}. For another example, let's look at the C++ type \code{char}, which contains 256 values (Haskell \code{Char} is larger, because Haskell uses Unicode). There are several functions in the part of the C++ Standard Library that are usually implemented using lookups. Functions like \code{isupper} or \code{isspace} are implemented using tables, which are equivalent to tuples of 256 Boolean values. A tuple is a product type, so we are dealing with products of 256 Booleans: \code{bool \times{} bool \times{} bool \times{} ... \times{} bool}. We know from arithmetics that an iterated product defines a power. If you ``multiply'' \code{bool} by itself 256 (or \code{char}) times, you get \code{bool} to the power of \code{char}, or \code{bool}\textsuperscript{\code{char}}. How many values are there in the type defined as 256-tuples of \code{bool}? Exactly $2^{256}$. This is also the number of different functions from \code{char} to \code{bool}, each function corresponding to a unique 256-tuple. You can similarly calculate that the number of functions from \code{bool} to \code{char} is $256^{2}$, and so on. The exponential notation for function types makes perfect sense in these cases. We probably wouldn't want to fully memoize a function from \code{int} or \code{double}. But the equivalence between functions and data types, if not always practical, is there. There are also infinite types, for instance lists, strings, or trees. Eager memoization of functions from those types would require infinite storage. But Haskell is a lazy language, so the boundary between lazily evaluated (infinite) data structures and functions is fuzzy. This function vs. data duality explains the identification of Haskell's function type with the categorical exponential object --- which corresponds more to our idea of \emph{data}. \section{Cartesian Closed Categories} Although I will continue using the category of sets as a model for types and functions, it's worth mentioning that there is a larger family of categories that can be used for that purpose. These categories are called \newterm{Cartesian closed}, and $\Set$ is just one example of such a category. A Cartesian closed category must contain: \begin{enumerate} \tightlist \item The terminal object, \item A product of any pair of objects, and \item An exponential for any pair of objects. \end{enumerate} If you consider an exponential as an iterated product (possibly infinitely many times), then you can think of a Cartesian closed category as one supporting products of an arbitrary arity. In particular, the terminal object can be thought of as a product of zero objects --- or the zero-th power of an object. What's interesting about Cartesian closed categories from the perspective of computer science is that they provide models for the simply typed lambda calculus, which forms the basis of all typed programming languages. The terminal object and the product have their duals: the initial object and the coproduct. A Cartesian closed category that also supports those two, and in which product can be distributed over coproduct \begin{gather*} a \times (b + c) = a \times b + a \times c \\ (b + c) \times a = b \times a + c \times a \end{gather*} is called a \newterm{bicartesian closed} category. We'll see in the next section that bicartesian closed categories, of which $\Set$ is a prime example, have some interesting properties. \section{Exponentials and Algebraic Data Types} The interpretation of function types as exponentials fits very well into the scheme of algebraic data types. It turns out that all the basic identities from high-school algebra relating numbers zero and one, sums, products, and exponentials hold pretty much unchanged in any bicartesian closed category for, respectively, initial and final objects, coproducts, products, and exponentials. We don't have the tools yet to prove them (such as adjunctions or the Yoneda lemma), but I'll list them here nevertheless as a source of valuable intuitions. \subsection{Zeroth Power} \[a^{0} = 1\] In the categorical interpretation, we replace 0 with the initial object, 1 with the final object, and equality with isomorphism. The exponential is the internal hom-object. This particular exponential represents the set of morphisms going from the initial object to an arbitrary object $a$. By the definition of the initial object, there is exactly one such morphism, so the hom-set $\cat{C}(0, a)$ is a singleton set. A singleton set is the terminal object in $\Set$, so this identity trivially works in $\Set$. What we are saying is that it works in any bicartesian closed category. In Haskell, we replace 0 with \code{Void}; 1 with the unit type \code{()}; and the exponential with function type. The claim is that the set of functions from \code{Void} to any type \code{a} is equivalent to the unit type --- which is a singleton. In other words, there is only one function \code{Void -> a}. We've seen this function before: it's called \code{absurd}. This is a little bit tricky, for two reasons. One is that in Haskell we don't really have uninhabited types --- every type contains the ``result of a never ending calculation,'' or the bottom. The second reason is that all implementations of \code{absurd} are equivalent because, no matter what they do, nobody can ever execute them. There is no value that can be passed to \code{absurd}. (And if you manage to pass it a never ending calculation, it will never return!) \subsection{Powers of One} \[1^{a} = 1\] This identity, when interpreted in $\Set$, restates the definition of the terminal object: There is a unique morphism from any object to the terminal object. In general, the internal hom-object from $a$ to the terminal object is isomorphic to the terminal object itself. In Haskell, there is only one function from any type \code{a} to unit. We've seen this function before --- it's called \code{unit}. You can also think of it as the function \code{const} partially applied to \code{()}. \subsection{First Power} \[a^{1} = a\] This is a restatement of the observation that morphisms from the terminal object can be used to pick ``elements'' of the object \code{a}. The set of such morphisms is isomorphic to the object itself. In $\Set$, and in Haskell, the isomorphism is between elements of the set \code{a} and functions that pick those elements, \code{() -> a}. \subsection{Exponentials of Sums} \[a^{b+c} = a^{b} \times a^{c}\] Categorically, this says that the exponential from a coproduct of two objects is isomorphic to a product of two exponentials. In Haskell, this algebraic identity has a very practical interpretation. It tells us that a function from a sum of two types is equivalent to a pair of functions from individual types. This is just the case analysis that we use when defining functions on sums. Instead of writing one function definition with a \code{case} clause, we usually split it into two (or more) functions dealing with each type constructor separately. For instance, take a function from the sum type \code{(Either Int Double)}: \src{snippet10} It may be defined as a pair of functions from, respectively, \code{Int} and \code{Double}: \src{snippet11} Here, \code{n} is an \code{Int} and \code{x} is a \code{Double}. \subsection{Exponentials of Exponentials} \[(a^{b})^{c} = a^{b \times c}\] This is just a way of expressing currying purely in terms of exponential objects. A function returning a function is equivalent to a function from a product (a two-argument function). \subsection{Exponentials over Products} \[(a \times b)^{c} = a^{c} \times b^{c}\] In Haskell: A function returning a pair is equivalent to a pair of functions, each producing one element of the pair. It's pretty incredible how those simple high-school algebraic identities can be lifted to category theory and have practical application in functional programming. \section{Curry-Howard Isomorphism} I have already mentioned the correspondence between logic and algebraic data types. The \code{Void} type and the unit type \code{()} correspond to false and true. Product types and sum types correspond to logical conjunction $\wedge$ (AND) and disjunction $\vee$ (OR). In this scheme, the function type we have just defined corresponds to logical implication $\Rightarrow$. In other words, the type \code{a -> b} can be read as ``if a then b.'' According to the Curry-Howard isomorphism, every type can be interpreted as a proposition --- a statement or a judgment that may be true or false. Such a proposition is considered true if the type is inhabited and false if it isn't. In particular, a logical implication is true if the function type corresponding to it is inhabited, which means that there exists a function of that type. An implementation of a function is therefore a proof of a theorem. Writing programs is equivalent to proving theorems. Let's see a few examples. Let's take the function \code{eval} we have introduced in the definition of the function object. Its signature is: \src{snippet12} It takes a pair consisting of a function and its argument and produces a result of the appropriate type. It's the Haskell implementation of the morphism: \[\mathit{eval} \Colon (a \Rightarrow b) \times a \to b\] which defines the function type $a \Rightarrow b$ (or the exponential object $b^{a}$). Let's translate this signature to a logical predicate using the Curry-Howard isomorphism: \[((a \Rightarrow b) \wedge a) \Rightarrow b\] Here's how you can read this statement: If it's true that $b$ follows from $a$, and $a$ is true, then $b$ must be true. This makes perfect intuitive sense and has been known since antiquity as \newterm{modus ponens}. We can prove this theorem by implementing the function: \src{snippet13} If you give me a pair consisting of a function \code{f} taking \code{a} and returning \code{b}, and a concrete value \code{x} of type \code{a}, I can produce a concrete value of type \code{b} by simply applying the function \code{f} to \code{x}. By implementing this function I have just shown that the type \code{((a -> b), a) -> b} is inhabited. Therefore \newterm{modus ponens} is true in our logic. How about a predicate that is blatantly false? For instance: if $a$ or $b$ is true then $a$ must be true. \[a \vee b \Rightarrow a\] This is obviously wrong because you can choose an $a$ that is false and a $b$ that is true, and that's a counter-example. Mapping this predicate into a function signature using the Curry-Howard isomorphism, we get: \src{snippet14} Try as you may, you can't implement this function --- you can't produce a value of type \code{a} if you are called with the \code{Right} value. (Remember, we are talking about \emph{pure} functions.) Finally, we come to the meaning of the \code{absurd} function: \src{snippet15} Considering that \code{Void} translates into false, we get: \[\mathit{false} \Rightarrow a\] Anything follows from falsehood (\emph{ex falso quodlibet}). Here's one possible proof (implementation) of this statement (function) in Haskell: \begin{snip}{haskell} absurd (Void a) = absurd a \end{snip} where \code{Void} is defined as: \begin{snip}{haskell} newtype Void = Void Void \end{snip} As always, the type \code{Void} is tricky. This definition makes it impossible to construct a value because in order to construct one, you would need to provide one. Therefore, the function \code{absurd} can never be called. These are all interesting examples, but is there a practical side to Curry-Howard isomorphism? Probably not in everyday programming. But there are programming languages like Agda or Rocq\footnote{Formerly known as Coq}, which take advantage of the Curry-Howard isomorphism to prove theorems. Computers are not only helping mathematicians do their work --- they are revolutionizing the very foundations of mathematics. The latest hot research topic in that area is called Homotopy Type Theory, and is an outgrowth of type theory. It's full of Booleans, integers, products and coproducts, function types, and so on. And, as if to dispel any doubts, the theory is being formulated in Rocq and Agda. Computers are revolutionizing the world in more than one way. \section{Bibliography} \begin{enumerate} \tightlist \item Ralf Hinze, Daniel W. H. James, \urlref{http://www.cs.ox.ac.uk/ralf.hinze/publications/WGP10.pdf}{Reason Isomorphically!}. This paper contains proofs of all those high-school algebraic identities in category theory that I mentioned in this chapter. \end{enumerate} ================================================ FILE: src/content/2.1/code/haskell/snippet01.hs ================================================ h = g . f ================================================ FILE: src/content/2.1/code/haskell/snippet02.hs ================================================ h x = let y = f x in g y ================================================ FILE: src/content/2.1/code/ocaml/snippet01.ml ================================================ (* Assume g and f are already defined *) let h = compose g f ================================================ FILE: src/content/2.1/code/ocaml/snippet02.ml ================================================ let h x = let y = f x in g y ;; ================================================ FILE: src/content/2.1/code/reason/snippet01.re ================================================ /* Assume g and f are already defined */ let h = compose(g, f); ================================================ FILE: src/content/2.1/code/reason/snippet02.re ================================================ let h = x => { let y = f(x); g(y); }; ================================================ FILE: src/content/2.1/code/scala/snippet01.scala ================================================ val h = g compose f ================================================ FILE: src/content/2.1/code/scala/snippet02.scala ================================================ val h = x => { val y = f(x) g(y) } ================================================ FILE: src/content/2.1/declarative-programming.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{I}{n the first part} of the book I argued that both category theory and programming are about composability. In programming, you keep decomposing a problem until you reach the level of detail that you can deal with, solve each subproblem in turn, and re-compose the solutions bottom-up. There are, roughly speaking, two ways of doing it: by telling the computer what to do, or by telling it how to do it. One is called declarative and the other imperative. You can see this even at the most basic level. Composition itself may be defined declaratively; as in, \code{h} is a composite of \code{g} after \code{f}: \src{snippet01} or imperatively; as in, call \code{f} first, remember the result of that call, then call \code{g} with the result: \src{snippet02} The imperative version of a program is usually described as a sequence of actions ordered in time. In particular, the call to \code{g} cannot happen before the execution of \code{f} completes. At least, that's the conceptual picture --- in a lazy language, with \emph{call-by-need} argument passing, the actual execution may proceed differently. In fact, depending on the cleverness of the compiler, there may be little or no difference between how declarative and imperative code is executed. But the two methodologies differ, sometimes drastically, in the way we approach problem solving and in the maintainability and testability of the resulting code. The main question is: when faced with a problem, do we always have the choice between a declarative and imperative approaches to solving it? And, if there is a declarative solution, can it always be translated into computer code? The answer to this question is far from obvious and, if we could find it, we would probably revolutionize our understanding of the universe. \begin{wrapfigure}{R}{0pt} \includegraphics[width=0.5\textwidth]{images/asteroids.png} \end{wrapfigure} Let me elaborate. There is a similar duality in physics, which either points at some deep underlying principle, or tells us something about how our minds work. Richard Feynman mentions this duality as an inspiration in his own work on quantum electrodynamics. There are two forms of expressing most laws of physics. One uses local, or infinitesimal, considerations. We look at the state of a system around a small neighborhood, and predict how it will evolve within the next instant of time. This is usually expressed using differential equations that have to be integrated, or summed up, over a period of time. Notice how this approach resembles imperative thinking: we reach the final solution by following a sequence of small steps, each depending on the result of the previous one. In fact, computer simulations of physical systems are routinely implemented by turning differential equations into difference equations and iterating them. This is how spaceships are animated in the asteroids game. At each time step, the position of a spaceship is changed by adding a small increment, which is calculated by multiplying its velocity by the time delta. The velocity, in turn, is changed by a small increment proportional to acceleration, which is given by force divided by mass. These are the direct encodings of the differential equations corresponding to Newton's laws of motion: \begin{align*} F & = m \frac{dv}{dt} \\ v & = \frac{dx}{dt} \end{align*} Similar methods may be applied to more complex problems, like the propagation of electromagnetic fields using Maxwell's equations, or even the behavior of quarks and gluons inside a proton using lattice \acronym{QCD} (quantum chromodynamics). This local thinking combined with discretization of space and time that is encouraged by the use of digital computers found its extreme expression in the heroic attempt by Stephen Wolfram to reduce the complexity of the whole universe to a system of cellular automata. The other approach is global. We look at the initial and the final state of the system, and calculate a trajectory that connects them by minimizing a certain functional. The simplest example is the Fermat's principle of least time. It states that light rays propagate along paths that minimize their flight time. In particular, in the absence of reflecting or refracting objects, a light ray from point $A$ to point $B$ will take the shortest path, which is a straight line. But light propagates slower in dense (transparent) materials, like water or glass. So if you pick the starting point in the air, and the ending point under water, it's more advantageous for light to travel longer in the air and then take a shortcut through water. The path of minimum time makes the ray refract at the boundary of air and water, resulting in Snell's law of refraction: \begin{equation*} \frac{sin(\theta_1)}{sin(\theta_2)} = \frac{v_1}{v_2} \end{equation*} where $v_1$ is the speed of light in the air and $v_2$ is the speed of light in the water. \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/snell.jpg} \end{figure} \noindent All of classical mechanics can be derived from the principle of least action. The action can be calculated for any trajectory by integrating the Lagrangian, which is the difference between kinetic and potential energy (notice: it's the difference, not the sum --- the sum would be the total energy). When you fire a mortar to hit a given target, the projectile will first go up, where the potential energy due to gravity is higher, and spend some time there racking up negative contribution to the action. It will also slow down at the top of the parabola, to minimize kinetic energy. Then it will speed up to go quickly through the area of low potential energy. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/mortar.jpg} \end{figure} \noindent Feynman's greatest contribution was to realize that the principle of least action can be generalized to quantum mechanics. There, again, the problem is formulated in terms of initial state and final state. The Feynman path integral between those states is used to calculate the probability of transition. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/feynman.jpg} \end{figure} \noindent The point is that there is a curious unexplained duality in the way we can describe the laws of physics. We can use the local picture, in which things happen sequentially and in small increments. Or we can use the global picture, where we declare the initial and final conditions, and everything in between just follows. The global approach can be also used in programming, for instance when implementing ray tracing. We declare the position of the eye and the positions of light sources, and figure out the paths that the light rays may take to connect them. We don't explicitly minimize the time of flight for each ray, but we do use Snell's law and the geometry of reflection to the same effect. The biggest difference between the local and the global approach is in their treatment of space and, more importantly, time. The local approach embraces the immediate gratification of here and now, whereas the global approach takes a long-term static view, as if the future had been preordained, and we were only analyzing the properties of some eternal universe. Nowhere is it better illustrated than in the Functional Reactive Programming (\acronym{FRP}) approach to user interaction. Instead of writing separate handlers for every possible user action, all having access to some shared mutable state, \acronym{FRP} treats external events as an infinite list, and applies a series of transformations to it. Conceptually, the list of all our future actions is there, available as the input data to our program. From a program's perspective there's no difference between the list of digits of $\pi$, a list of pseudo-random numbers, or a list of mouse positions coming through computer hardware. In each case, if you want to get the $n^\text{th}$ item, you have to first go through the first $n-1$ items. When applied to temporal events, we call this property \emph{causality}. So what does it have to do with category theory? I will argue that category theory encourages a global approach and therefore supports declarative programming. First of all, unlike calculus, it has no built-in notion of distance, or neighborhood, or time. All we have is abstract objects and abstract connections between them. If you can get from $A$ to $B$ through a series of steps, you can also get there in one leap. Moreover, the major tool of category theory is the universal construction, which is the epitome of a global approach. We've seen it in action, for instance, in the definition of the categorical product. It was done by specifying its properties --- a very declarative approach. It's an object equipped with two projections, and it's the best such object --- it optimizes a certain property: the property of factorizing the projections of other such objects. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/productranking.jpg} \end{figure} \noindent Compare this with Fermat's principle of minimum time, or the principle of least action. Conversely, contrast this with the traditional definition of a Cartesian product, which is much more imperative. You describe how to create an element of the product by picking one element from one set and another element from another set. It's a recipe for creating a pair. And there's another for disassembling a pair. In almost every programming language, including functional languages like Haskell, product types, coproduct types, and function types are built in, rather than being defined by universal constructions; although there have been attempts at creating categorical programming languages (see, e.g., \urlref{http://web.sfc.keio.ac.jp/~hagino/thesis.pdf}{Tatsuya Hagino's thesis}). Whether used directly or not, categorical definitions justify pre-existing programming constructs, and give rise to new ones. Most importantly, category theory provides a meta-language for reasoning about computer programs at a declarative level. It also encourages reasoning about problem specification before it is cast into code. ================================================ FILE: src/content/2.2/code/haskell/snippet01.hs ================================================ p' = p . m q' = q . m ================================================ FILE: src/content/2.2/code/haskell/snippet02.hs ================================================ contramap :: (c' -> c) -> (c -> LimD) -> (c' -> LimD) contramap f u = u . f ================================================ FILE: src/content/2.2/code/haskell/snippet03.hs ================================================ f :: a -> b g :: a -> b ================================================ FILE: src/content/2.2/code/haskell/snippet04.hs ================================================ p :: c -> a q :: c -> b ================================================ FILE: src/content/2.2/code/haskell/snippet05.hs ================================================ q = f . p q = g . p ================================================ FILE: src/content/2.2/code/haskell/snippet06.hs ================================================ f . p = g . p ================================================ FILE: src/content/2.2/code/haskell/snippet07.hs ================================================ f (x, y) = 2 * y + x g (x, y) = y - x ================================================ FILE: src/content/2.2/code/haskell/snippet08.hs ================================================ p t = (t, (-2) * t) ================================================ FILE: src/content/2.2/code/haskell/snippet09.hs ================================================ f . p' = g . p' ================================================ FILE: src/content/2.2/code/haskell/snippet10.hs ================================================ p'() = (0, 0) ================================================ FILE: src/content/2.2/code/haskell/snippet11.hs ================================================ p' = p . h ================================================ FILE: src/content/2.2/code/haskell/snippet12.hs ================================================ h () = 0 ================================================ FILE: src/content/2.2/code/haskell/snippet13.hs ================================================ f :: a -> b g :: c -> b ================================================ FILE: src/content/2.2/code/haskell/snippet14.hs ================================================ p :: d -> a q :: d -> c r :: d -> b ================================================ FILE: src/content/2.2/code/haskell/snippet15.hs ================================================ g . q = f . p ================================================ FILE: src/content/2.2/code/haskell/snippet16.hs ================================================ f x = 1.23 ================================================ FILE: src/content/2.2/code/haskell/snippet17.hs ================================================ newtype ToString a = ToString (a -> String) instance Contravariant ToString where contramap f (ToString g) = ToString (g . f) ================================================ FILE: src/content/2.2/code/haskell/snippet18.hs ================================================ ToString (Either b c) ~ (b -> String, c -> String) ================================================ FILE: src/content/2.2/code/haskell/snippet19.hs ================================================ r -> (a, b) ~ (r -> a, r -> b) ================================================ FILE: src/content/2.2/code/ocaml/snippet01.ml ================================================ let p1 = compose p m let q1 = compose q m ================================================ FILE: src/content/2.2/code/ocaml/snippet02.ml ================================================ let contramap : ('c_prime -> 'c) -> ('c -> 'limD) -> 'c_prime -> 'limD = fun f u -> compose u f ;; ================================================ FILE: src/content/2.2/code/ocaml/snippet03.ml ================================================ val f : 'a -> 'b val g : 'a -> 'b ================================================ FILE: src/content/2.2/code/ocaml/snippet04.ml ================================================ val p : 'c -> 'a val q : 'c -> 'b ================================================ FILE: src/content/2.2/code/ocaml/snippet05.ml ================================================ q = compose f p q = compose g p ================================================ FILE: src/content/2.2/code/ocaml/snippet06.ml ================================================ (** Pseudo OCaml expressing function equality **) compose f p = compose g p ================================================ FILE: src/content/2.2/code/ocaml/snippet07.ml ================================================ let f (x, y) = (2 * y) + x let g (x, y) = y - x ================================================ FILE: src/content/2.2/code/ocaml/snippet08.ml ================================================ let p t = t, -2 * t ================================================ FILE: src/content/2.2/code/ocaml/snippet09.ml ================================================ (** Pseudo OCaml expressing function equality **) compose f p' = compose g p' ================================================ FILE: src/content/2.2/code/ocaml/snippet10.ml ================================================ let p' () = 0, 0 ================================================ FILE: src/content/2.2/code/ocaml/snippet11.ml ================================================ let p' = compose p h ================================================ FILE: src/content/2.2/code/ocaml/snippet12.ml ================================================ let h () = 0 ================================================ FILE: src/content/2.2/code/ocaml/snippet13.ml ================================================ val f : 'a -> 'b val g : 'c -> 'b ================================================ FILE: src/content/2.2/code/ocaml/snippet14.ml ================================================ val p : 'd -> 'a val q : 'd -> 'c val r : 'd -> 'b ================================================ FILE: src/content/2.2/code/ocaml/snippet15.ml ================================================ compose g q = compose f p ================================================ FILE: src/content/2.2/code/ocaml/snippet16.ml ================================================ let f x = 1.23 ================================================ FILE: src/content/2.2/code/ocaml/snippet17.ml ================================================ module type Contravariant = sig type 'a t val contramap : ('b -> 'a) -> 'a t -> 'b t end type 'a tostring = ToString of ('a -> string) module ToStringInstance : Contravariant = struct type 'a t = 'a tostring let contramap f (ToString g) = ToString (compose g f) end ================================================ FILE: src/content/2.2/code/ocaml/snippet18.ml ================================================ ('b 'c either) tostring ~ ('b -> string, 'c -> string) ================================================ FILE: src/content/2.2/code/ocaml/snippet19.ml ================================================ 'r -> ('a, 'b) ~ ('r -> 'a, 'r -> 'b) ================================================ FILE: src/content/2.2/code/reason/snippet01.re ================================================ let p1 = compose(p, m); let q1 = compose(q, m); ================================================ FILE: src/content/2.2/code/reason/snippet02.re ================================================ let contramap: ('c_prime => 'c, 'c => 'limD, 'c_prime) => 'limD = ( (f, u) => compose(u, f): ('c_prime => 'c, 'c => 'limD, 'c_prime) => 'limD ); ================================================ FILE: src/content/2.2/code/reason/snippet03.re ================================================ let f: 'a => 'b let g: 'a => 'b ================================================ FILE: src/content/2.2/code/reason/snippet04.re ================================================ let p: 'c => 'a let q: 'c => 'b ================================================ FILE: src/content/2.2/code/reason/snippet05.re ================================================ q == compose(f, p); q == compose(g, p); ================================================ FILE: src/content/2.2/code/reason/snippet06.re ================================================ /* Pseudo ReasonML expressing function equality */ compose(f, p) == compose(g, p); ================================================ FILE: src/content/2.2/code/reason/snippet07.re ================================================ let f = ((x, y)) => 2 * y + x; let g = ((x, y)) => y - x; ================================================ FILE: src/content/2.2/code/reason/snippet08.re ================================================ let p = t => (t, (-2) * t); ================================================ FILE: src/content/2.2/code/reason/snippet09.re ================================================ /* Pseudo ReasonML expressing function equality */ compose(f, p') == compose(g, p'); ================================================ FILE: src/content/2.2/code/reason/snippet10.re ================================================ let p' = () => (0, 0); ================================================ FILE: src/content/2.2/code/reason/snippet11.re ================================================ let p' = compose(p, h); ================================================ FILE: src/content/2.2/code/reason/snippet12.re ================================================ let h = () => 0; ================================================ FILE: src/content/2.2/code/reason/snippet13.re ================================================ let f: 'a => 'b let g: 'c => 'b ================================================ FILE: src/content/2.2/code/reason/snippet14.re ================================================ let p: 'd => 'a let q: 'd => 'c let r: 'd => 'b ================================================ FILE: src/content/2.2/code/reason/snippet15.re ================================================ compose(g, q) == compose(f, p); ================================================ FILE: src/content/2.2/code/reason/snippet16.re ================================================ let f = x => 1.23; ================================================ FILE: src/content/2.2/code/reason/snippet17.re ================================================ module type Contravariant = { type t('a); let contramap: ('b => 'a, t('a)) => t('b); }; type tostring('a) = | ToString('a => string); module ToStringInstance: Contravariant = { type t('a) = tostring('a); let contramap = (f, ToString(g)) => ToString(compose(g, f)); }; ================================================ FILE: src/content/2.2/code/reason/snippet18.re ================================================ toString((either('b, 'c))) ~ (b => string, c => string) ================================================ FILE: src/content/2.2/code/reason/snippet19.re ================================================ 'r => ('a, 'b) ~ ('r => 'a, 'r => 'b) ================================================ FILE: src/content/2.2/code/scala/snippet01.scala ================================================ val p1 = p compose m val q1 = q compose m ================================================ FILE: src/content/2.2/code/scala/snippet02.scala ================================================ def contramap[C, C1, D[_]](f: C1 => C, u: C => Lim[D]): (C1 => Lim[D]) = u compose f ================================================ FILE: src/content/2.2/code/scala/snippet03.scala ================================================ val f : A => B val g : A => B ================================================ FILE: src/content/2.2/code/scala/snippet04.scala ================================================ val p : C => A val q : C => B ================================================ FILE: src/content/2.2/code/scala/snippet05.scala ================================================ q == f compose p q == g compose p ================================================ FILE: src/content/2.2/code/scala/snippet06.scala ================================================ f compose p == g compose p ================================================ FILE: src/content/2.2/code/scala/snippet07.scala ================================================ def f(x, y) = 2 * y + x def g(x, y) = y - x ================================================ FILE: src/content/2.2/code/scala/snippet08.scala ================================================ def p(t) = (t, (-2) * t) ================================================ FILE: src/content/2.2/code/scala/snippet09.scala ================================================ f compose p1 == g compose p1 ================================================ FILE: src/content/2.2/code/scala/snippet10.scala ================================================ def p1 : Unit => (Double, Double) = _ => (0, 0) ================================================ FILE: src/content/2.2/code/scala/snippet11.scala ================================================ val p1 = p compose h ================================================ FILE: src/content/2.2/code/scala/snippet12.scala ================================================ def h(_,_) = 0 ================================================ FILE: src/content/2.2/code/scala/snippet13.scala ================================================ val f : A => B val g : C => B ================================================ FILE: src/content/2.2/code/scala/snippet14.scala ================================================ val p : D => A val q : D => C val r : D => B ================================================ FILE: src/content/2.2/code/scala/snippet15.scala ================================================ g compose q == f compose p ================================================ FILE: src/content/2.2/code/scala/snippet16.scala ================================================ f(x) == 1.23 ================================================ FILE: src/content/2.2/code/scala/snippet17.scala ================================================ trait Contravariant[F[_]] { def contramap[A, B](fa: F[A])(f: B => A) : F[B] } class ToString[A](f: A => String) extends AnyVal implicit val contravariant = new Contravariant[ToString] { def contramap[A, B](fa: ToString[A])(f: B => A): ToString[B] = ToString(fa.f compose f) } ================================================ FILE: src/content/2.2/code/scala/snippet18.scala ================================================ ToString[Either[B, C]] ~ (B => String, C => String) ================================================ FILE: src/content/2.2/code/scala/snippet19.scala ================================================ R => (A, B) ~ (R => A, R => B) ================================================ FILE: src/content/2.2/limits-and-colimits.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{I}{t seems like in category theory} everything is related to everything and everything can be viewed from many angles. Take for instance the universal construction of the \hyperref[products-and-coproducts]{product}. Now that we know more about \hyperref[functors]{functors} and \hyperref[natural-transformations]{natural transformations}, can we simplify and, possibly, generalize it? Let us try. \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/productpattern.jpg} \end{figure} \noindent The construction of a product starts with the selection of two objects $a$ and $b$, whose product we want to construct. But what does it mean to \emph{select objects}? Can we rephrase this action in more categorical terms? Two objects form a pattern --- a very simple pattern. We can abstract this pattern into a category --- a very simple category, but a category nevertheless. It's a category that we'll call $\cat{2}$. It contains just two objects, $1$ and $2$, and no morphisms other than the two obligatory identities. Now we can rephrase the selection of two objects in $\cat{C}$ as the act of defining a functor $D$ from the category $\cat{2}$ to $\cat{C}$. A functor maps objects to objects, so its image is just two objects (or it could be one, if the functor collapses objects, which is fine too). It also maps morphisms --- in this case it simply maps identity morphisms to identity morphisms. What's great about this approach is that it builds on categorical notions, eschewing the imprecise descriptions like ``selecting objects,'' taken straight from the hunter-gatherer lexicon of our ancestors. And, incidentally, it is also easily generalized, because nothing can stop us from using categories more complex than $\cat{2}$ to define our patterns. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/two.jpg} \end{figure} \noindent But let's continue. The next step in the definition of a product is the selection of the candidate object $c$. Here again, we could rephrase the selection in terms of a functor from a singleton category. And indeed, if we were using Kan extensions, that would be the right thing to do. But since we are not ready for Kan extensions yet, there is another trick we can use: a constant functor $\Delta$ from the same category $\cat{2}$ to $\cat{C}$. The selection of $c$ in $\cat{C}$ can be done with $\Delta_c$. Remember, $\Delta_c$ maps all objects into $c$ and all morphisms into $\idarrow[c]$. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/twodelta.jpg} \end{figure} \noindent Now we have two functors, $\Delta_c$ and $D$ going between $\cat{2}$ and $\cat{C}$ so it's only natural to ask about natural transformations between them. Since there are only two objects in $\cat{2}$, a natural transformation will have two components. Object $1$ in $\cat{2}$ is mapped to $c$ by $\Delta_c$ and to $a$ by $D$. So the component of a natural transformation between $\Delta_c$ and $D$ at $1$ is a morphism from $c$ to $a$. We can call it $p$. Similarly, the second component is a morphism $q$ from $c$ to $b$ --- the image of the object $2$ in $\cat{2}$ under $D$. But these are exactly like the two projections we used in our original definition of the product. So instead of talking about selecting objects and projections, we can just talk about picking functors and natural transformations. It so happens that in this simple case the naturality condition for our transformation is trivially satisfied, because there are no morphisms (other than the identities) in $\cat{2}$. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/productcone.jpg} \end{figure} \noindent A generalization of this construction to categories other than $\cat{2}$ --- ones that, for instance, contain non-trivial morphisms --- will impose naturality conditions on the transformation between $\Delta_c$ and $D$. We call such a transformation a \emph{cone}, because the image of $\Delta$ is the apex of a cone/pyramid whose sides are formed by the components of the natural transformation. The image of $D$ forms the base of the cone. In general, to build a cone, we start with a category $\cat{I}$ that defines the pattern. It's a small, often finite category. We pick a functor $D$ from $\cat{I}$ to $\cat{C}$ and call it (or its image) a \emph{diagram}. We pick some $c$ in $\cat{C}$ as the apex of our cone. We use it to define the constant functor $\Delta_c$ from $\cat{I}$ to $\cat{C}$. A natural transformation from $\Delta_c$ to $D$ is then our cone. For a finite $\cat{I}$ it's just a bunch of morphisms connecting $c$ to the diagram: the image of $\cat{I}$ under $D$. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/cone.jpg} \end{figure} \noindent Naturality requires that all triangles (the walls of the pyramid) in this diagram commute. Indeed, take any morphism $f$ in $\cat{I}$. The functor $D$ maps it to a morphism $D f$ in $\cat{C}$, a morphism that forms the base of some triangle. The constant functor $\Delta_c$ maps $f$ to the identity morphism on $c$. $\Delta$ squishes the two ends of the morphism into one object, and the naturality square becomes a commuting triangle. The two arms of this triangle are the components of the natural transformation. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/conenaturality.jpg} \end{figure} \noindent So that's one cone. What we are interested in is the \newterm{universal cone} --- just like we picked a universal object for our definition of a product. There are many ways to go about it. For instance, we may define a \emph{category of cones} based on a given functor $D$. Objects in that category are cones. Not every object $c$ in $\cat{C}$ can be an apex of a cone, though, because there may be no natural transformation between $\Delta_c$ and $D$. To make it a category, we also have to define morphisms between cones. These would be fully determined by morphisms between their apexes. But not just any morphism will do. Remember that, in our construction of the product, we imposed the condition that the morphisms between candidate objects (the apexes) must be common factors for the projections. For instance: \src{snippet01} \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/productranking.jpg} \end{figure} This condition translates, in the general case, to the condition that the triangles whose one side is the factorizing morphism all commute. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/conecommutativity.jpg} \caption{The commuting triangle connecting two cones, with the factorizing morphism $h$ (here, the lower cone is the universal one, with $\Lim[D]$ as its apex)} \end{figure} \noindent We'll take those factorizing morphisms as the morphisms in our category of cones. It's easy to check that those morphisms indeed compose, and that the identity morphism is a factorizing morphism as well. Cones therefore form a category. Now we can define the universal cone as the \emph{terminal object} in the category of cones. The definition of the terminal object states that there is a unique morphism from any other object to that object. In our case it means that there is a unique factorizing morphism from the apex of any other cone to the apex of the universal cone. We call this universal cone the \emph{limit} of the diagram $D$, $\Lim[D]$ (in the literature, you'll often see a left arrow pointing towards $I$ under the $\Lim$ sign). Often, as a shorthand, we call the apex of this cone the limit (or the limit object). The intuition is that the limit embodies the properties of the whole diagram in a single object. For instance, the limit of our two-object diagram is the product of two objects. The product (together with the two projections) contains the information about both objects. And being universal means that it has no extraneous junk. \section{Limit as a Natural Isomorphism} There is still something unsatisfying about this definition of a limit. I mean, it's workable, but we still have this commutativity condition for the triangles that are linking any two cones. It would be so much more elegant if we could replace it with some naturality condition. But how? We are no longer dealing with one cone but with a whole collection (in fact, a category) of cones. If the limit exists (and --- let's make it clear --- there's no guarantee of that), one of those cones is the universal cone. For every other cone we have a unique factorizing morphism that maps its apex, let's call it $c$, to the apex of the universal cone, which we named $\Lim[D]$. (In fact, I can skip the word ``other'', because the identity morphism maps the universal cone to itself and it trivially factorizes through itself.) Let me repeat the important part: given any cone, there is a unique morphism of a special kind. We have a mapping of cones to special morphisms, and it's a one-to-one mapping. This special morphism is a member of the hom-set $\cat{C}(c, \Lim[D])$. The other members of this hom-set are less fortunate, in the sense that they don't factorize the mapping of the two cones. What we want is to be able to pick, for each $c$, one morphism from the set $\cat{C}(c, \Lim[D])$ --- a morphism that satisfies the particular commutativity condition. Does that sound like defining a natural transformation? It most certainly does! But what are the functors that are related by this transformation? One functor is the mapping of $c$ to the set $\cat{C}(c, \Lim[D])$. It's a functor from $\cat{C}$ to $\Set$ --- it maps objects to sets. In fact it's a contravariant functor. Here's how we define its action on morphisms: Let's take a morphism $f$ from $c'$ to $c$: \[f \Colon c' \to c\] Our functor maps $c'$ to the set $\cat{C}(c', \Lim[D])$. To define the action of this functor on $f$ (in other words, to lift $f$), we have to define the corresponding mapping between $\cat{C}(c, \Lim[D])$ and $\cat{C}(c', \Lim[D])$. So let's pick one element $u$ of $\cat{C}(c, \Lim[D])$ and see if we can map it to some element of $\cat{C}(c', \Lim[D])$. An element of a hom-set is a morphism, so we have: \[u \Colon c \to \Lim[D]\] We can precompose $u$ with $f$ to get: \[u \circ f \Colon c' \to \Lim[D]\] And that's an element of $\cat{C}(c', \Lim[D])$--- so indeed, we have found a mapping of morphisms: \src{snippet02} Notice the inversion in the order of $c$ and $c'$ characteristic of a \emph{contravariant} functor. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/homsetmapping.jpg} \end{figure} \noindent To define a natural transformation, we need another functor that's also a mapping from $\cat{C}$ to $\Set$. But this time we'll consider a set of cones. Cones are just natural transformations, so we are looking at the set of natural transformations $\mathit{Nat}(\Delta_c, D)$. The mapping from $c$ to this particular set of natural transformations is a (contravariant) functor. How can we show that? Again, let's define its action on a morphism: \[f \Colon c' \to c\] The lifting of $f$ should be a mapping of natural transformations between two functors that go from $\cat{I}$ to $\cat{C}$: \[\mathit{Nat}(\Delta_c, D) \to \mathit{Nat}(\Delta_{c'}, D)\] How do we map natural transformations? Every natural transformation is a selection of morphisms --- its components --- one morphism per element of $\cat{I}$. A component of some $\alpha$ (a member of $\mathit{Nat}(\Delta_c, D)$) at $a$ (an object in $\cat{I}$) is a morphism: \[\alpha_a \Colon \Delta_c a \to D a\] or, using the definition of the constant functor $\Delta$, \[\alpha_a \Colon c \to D a\] Given $f$ and $\alpha$, we have to construct a $\beta$, a member of $\mathit{Nat}(\Delta_{c'}, D)$. Its component at $a$ should be a morphism: \[\beta_a \Colon c' \to D a\] We can easily get the latter ($\beta_a$) from the former ($\alpha_a$) by precomposing it with $f$: \[\beta_a = \alpha_a \circ f\] It's relatively easy to show that those components indeed add up to a natural transformation. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/natmapping.jpg} \end{figure} \noindent Given our morphism $f$, we have thus built a mapping between two natural transformations, component-wise. This mapping defines \code{contramap} for the functor: \[c \to \mathit{Nat}(\Delta_c, D)\] What I have just done is to show you that we have two (contravariant) functors from $\cat{C}$ to $\Set$. I haven't made any assumptions --- these functors always exist. Incidentally, the first of these functors plays an important role in category theory, and we'll see it again when we talk about Yoneda's lemma. There is a name for contravariant functors from any category $\cat{C}$ to $\Set$: they are called ``presheaves''. This one is called a \newterm{representable presheaf}. The second functor is also a presheaf. Now that we have two functors, we can talk about natural transformations between them. So without further ado, here's the conclusion: A functor $D$ from $\cat{I}$ to $\cat{C}$ has a limit $\Lim[D]$ if and only if there is a natural isomorphism between the two functors I have just defined: \[\cat{C}(c, \Lim[D]) \simeq \mathit{Nat}(\Delta_c, D)\] Let me remind you what a natural isomorphism is. It's a natural transformation whose every component is an isomorphism, that is to say an invertible morphism. I'm not going to go through the proof of this statement. The procedure is pretty straightforward if not tedious. When dealing with natural transformations, you usually focus on components, which are morphisms. In this case, since the target of both functors is $\Set$, the components of the natural isomorphism will be functions. These are higher order functions, because they go from the hom-set to the set of natural transformations. Again, you can analyze a function by considering what it does to its argument: here the argument will be a morphism --- a member of $\cat{C}(c, \Lim[D])$ --- and the result will be a natural transformation --- a member of $\mathit{Nat}(\Delta_c, D)$, or what we have called a cone. This natural transformation, in turn, has its own components, which are morphisms. So it's morphisms all the way down, and if you can keep track of them, you can prove the statement. The most important result is that the naturality condition for this isomorphism is exactly the commutativity condition for the mapping of cones. As a preview of coming attractions, let me mention that the set $\mathit{Nat}(\Delta_c, D)$ can be thought of as a hom-set in the functor category; so our natural isomorphism relates two hom-sets, which points at an even more general relationship called an adjunction. \section{Examples of Limits} We've seen that the categorical product is a limit of a diagram generated by a simple category we called $\cat{2}$. There is an even simpler example of a limit: the terminal object. The first impulse would be to think of a singleton category as leading to a terminal object, but the truth is even starker than that: the terminal object is a limit generated by an empty category. A functor from an empty category selects no object, so a cone shrinks to just the apex. The universal cone is the lone apex that has a unique morphism coming to it from any other apex. You will recognize this as the definition of the terminal object. The next interesting limit is called the \emph{equalizer}. It's a limit generated by a two-element category with two parallel morphisms going between them (and, as always, the identity morphisms). This category selects a diagram in $\cat{C}$ consisting of two objects, $a$ and $b$, and two morphisms: \src{snippet03} To build a cone over this diagram, we have to add the apex, $c$ and two projections: \src{snippet04} \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/equalizercone.jpg} \end{figure} \noindent We have two triangles that must commute: \src{snippet05} This tells us that $q$ is uniquely determined by one of these equations, say, \code{q = f . p}, and we can omit it from the picture. So we are left with just one condition: \src{snippet06} The way to think about it is that, if we restrict our attention to $\Set$, the image of the function $p$ selects a subset of $a$. When restricted to this subset, the functions $f$ and $g$ are equal. For instance, take $a$ to be the two-dimensional plane parameterized by coordinates $x$ and $y$. Take $b$ to be the real line, and take: \src{snippet07} The equalizer for these two functions is the set of real numbers (the apex, $c$) and the function: \src{snippet08} Notice that $(p~t)$ defines a straight line in the two-dimensional plane. Along this line, the two functions are equal. Of course, there are other sets $c'$ and functions $p'$ that may lead to the equality: \src{snippet09} but they all uniquely factor out through $p$. For instance, we can take the singleton set $\cat{()}$ as $c'$ and the function: \src{snippet10} It's a good cone, because $f (0, 0) = g (0, 0)$. But it's not universal, because of the unique factorization through $h$: \src{snippet11} with \src{snippet12} \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/equilizerlimit.jpg} \end{figure} \noindent An equalizer can thus be used to solve equations of the type $f~x = g~x$. But it's much more general, because it's defined in terms of objects and morphisms rather than algebraically. An even more general idea of solving an equation is embodied in another limit --- the pullback. Here, we still have two morphisms that we want to equate, but this time their domains are different. We start with a three-object category of the shape: $1\rightarrow2\leftarrow3$. The diagram corresponding to this category consists of three objects, $a$, $b$, and $c$, and two morphisms: \src{snippet13} This diagram is often called a \emph{cospan}. A cone built on top of this diagram consists of the apex, $d$, and three morphisms: \src{snippet14} \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/pullbackcone.jpg} \end{figure} \noindent Commutativity conditions tell us that $r$ is completely determined by the other morphisms, and can be omitted from the picture. So we are only left with the following condition: \src{snippet15} A pullback is a universal cone of this shape. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/pullbacklimit.jpg} \end{figure} \noindent Again, if you narrow your focus down to sets, you can think of the object $d$ as consisting of pairs of elements from $a$ and $c$ for which $f$ acting on the first component is equal to $g$ acting on the second component. If this is still too general, consider the special case in which $g$ is a constant function, say $g~\_ = 1.23$ (assuming that $b$ is a set of real numbers). Then you are really solving the equation: \src{snippet16} In this case, the choice of $c$ is irrelevant (as long as it's not an empty set), so we can take it to be a singleton set. The set $a$ could, for instance, be the set of three-dimensional vectors, and $f$ the vector length. Then the pullback is the set of pairs $(v, ())$, where $v$ is a vector of length 1.23 (a solution to the equation $\sqrt{(x^{2}+y^{2}+z^{2})} = 1.23$), and $()$ is the dummy element of the singleton set. But pullbacks have more general applications, also in programming. For instance, consider C++ classes as a category in which morphism are arrows that connect subclasses to superclasses. We'll consider inheritance a transitive property, so if \code{C} inherits from \code{B} and \code{B} inherits from \code{A} then we'll say that \code{C} inherits from \code{A} (after all, you can pass a pointer to \code{C} where a pointer to \code{A} is expected). Also, we'll assume that \code{C} inherits from \code{C}, so we have the identity arrow for every class. This way subclassing is aligned with subtyping. C++ also supports multiple inheritance, so you can construct a diamond inheritance diagram with two classes \code{B} and \code{C} inheriting from \code{A}, and a fourth class \code{D} multiply inheriting from \code{B} and \code{C}. Normally, \code{D} would get two copies of \code{A}, which is rarely desirable; but you can use virtual inheritance to have just one copy of \code{A} in \code{D}. What would it mean to have \code{D} be a pullback in this diagram? It would mean that any class \code{E} that multiply inherits from \code{B} and \code{C} is also a subclass of \code{D}. This is not directly expressible in C++, where subtyping is nominal (the C++ compiler wouldn't infer this kind of class relationship --- it would require ``duck typing''). But we could go outside of the subtyping relationship and instead ask whether a cast from \code{E} to \code{D} would be safe or not. This cast would be safe if \code{D} were the bare-bone combination of \code{B} and \code{C}, with no additional data and no overriding of methods. And, of course, there would be no pullback if there is a name conflict between some methods of \code{B} and \code{C}. \begin{figure}[H] \centering \includegraphics[width=0.25\textwidth]{images/classes.jpg} \end{figure} \noindent There's also a more advanced use of a pullback in type inference. There is often a need to \emph{unify} types of two expressions. For instance, suppose that the compiler wants to infer the type of a function: \begin{snip}{haskell} twice f x = f (f x) \end{snip} It will assign preliminary types to all variables and sub-expressions. In particular, it will assign: \begin{snip}{haskell} f :: t0 x :: t1 f x :: t2 f (f x) :: t3 \end{snip} from which it will deduce that: \begin{snip}{haskell} twice :: t0 -> t1 -> t3 \end{snip} It will also come up with a set of constraints resulting from the rules of function application: \begin{snip}{haskell} t0 = t1 -> t2 -- because f is applied to x t0 = t2 -> t3 -- because f is applied to (f x) \end{snip} These constraints have to be unified by finding a set of types (or type variables) that, when substituted for the unknown types in both expressions, produce the same type. One such substitution is: \begin{snip}{haskell} t1 = t2 = t3 = Int twice :: (Int -> Int) -> Int -> Int \end{snip} but, obviously, it's not the most general one. The most general substitution is obtained using a pullback. I won't go into the details, because they are beyond the scope of this book, but you can convince yourself that the result should be: \begin{snip}{haskell} twice :: (t -> t) -> t -> t \end{snip} with \code{t} a free type variable. \section{Colimits} Just like all constructions in category theory, limits have their dual image in opposite categories. When you invert the direction of all arrows in a cone, you get a co-cone, and the universal one of those is called a colimit. Notice that the inversion also affects the factorizing morphism, which now flows from the universal co-cone to any other co-cone. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/colimit.jpg} \caption{Cocone with a factorizing morphism $h$ connecting two apexes.} \end{figure} \noindent A typical example of a colimit is a coproduct, which corresponds to the diagram generated by $\cat{2}$, the category we've used in the definition of the product. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/coproductranking.jpg} \end{figure} \noindent Both the product and the coproduct embody the essence of a pair of objects, each in a different way. Just like the terminal object was a limit, so the initial object is a colimit corresponding to the diagram based on an empty category. The dual of the pullback is called the \emph{pushout}. It's based on a diagram called a span, generated by the category $1\leftarrow2\rightarrow3$. \section{Continuity} I said previously that functors come close to the idea of continuous mappings of categories, in the sense that they never break existing connections (morphisms). The actual definition of a \emph{continuous functor} $F$ from a category $\cat{C}$ to $\cat{C'}$ includes the requirement that the functor preserve limits. Every diagram $D$ in $\cat{C}$ can be mapped to a diagram $F \circ D$ in $\cat{C'}$ by simply composing two functors. The continuity condition for $F$ states that, if the diagram $D$ has a limit $\Lim[D]$, then the diagram $F \circ D$ also has a limit, and it is equal to $F (\Lim[D])$. \begin{figure}[H] \centering \includegraphics[width=0.6\textwidth]{images/continuity.jpg} \end{figure} \noindent Notice that, because functors map morphisms to morphisms, and compositions to compositions, an image of a cone is always a cone. A commuting triangle is always mapped to a commuting triangle (functors preserve composition). The same is true for the factorizing morphisms: the image of a factorizing morphism is also a factorizing morphism. So every functor is \emph{almost} continuous. What may go wrong is the uniqueness condition. The factorizing morphism in $\cat{C'}$ might not be unique. There may also be other ``better cones'' in $\cat{C'}$ that were not available in $\cat{C}$. A hom-functor is an example of a continuous functor. Recall that the hom-functor, $\cat{C}(a, b)$, is contravariant in the first variable and covariant in the second. In other words, it's a functor: \[\cat{C}^\mathit{op} \times \cat{C} \to \Set\] When its second argument is fixed, the hom-set functor (which becomes the representable presheaf) maps colimits in $\cat{C}$ to limits in $\Set$; and when its first argument is fixed, it maps limits to limits. In Haskell, a hom-functor is the mapping of any two types to a function type, so it's just a parameterized function type. When we fix the second parameter, let's say to \code{String}, we get the contravariant functor: \src{snippet17} Continuity means that when \code{ToString} is applied to a colimit, for instance a coproduct \code{Either b c}, it will produce a limit; in this case a product of two function types: \src{snippet18} Indeed, any function of \code{Either b c} is implemented as a case clause with the two cases being serviced by a pair of functions. Similarly, when we fix the first argument of the hom-set, we get the familiar reader functor. Its continuity means that, for instance, any function returning a product is equivalent to a product of functions; in particular: \src{snippet19} I know what you're thinking: You don't need category theory to figure these things out. And you're right! Still, I find it amazing that such results can be derived from first principles with no recourse to bits and bytes, processor architectures, compiler technologies, or even lambda calculus. If you're curious where the names ``limit'' and ``continuity'' come from, they are a generalization of the corresponding notions from calculus. In calculus limits and continuity are defined in terms of open neighborhoods. Open sets, which define topology, form a category (a poset). \section{Challenges} \begin{enumerate} \tightlist \item How would you describe a pushout in the category of C++ classes? \item Show that the limit of the identity functor $\mathbf{Id} \Colon \cat{C} \to \cat{C}$ is the initial object. \item Subsets of a given set form a category. A morphism in that category is defined to be an arrow connecting two sets if the first is the subset of the second. What is a pullback of two sets in such a category? What's a pushout? What are the initial and terminal objects? \item Can you guess what a coequalizer is? \item Show that, in a category with a terminal object, a pullback towards the terminal object is a product. \item Similarly, show that a pushout from an initial object (if one exists) is the coproduct. \end{enumerate} ================================================ FILE: src/content/2.3/code/haskell/snippet01.hs ================================================ class Monoid m where mempty :: m mappend :: m -> m -> m ================================================ FILE: src/content/2.3/code/haskell/snippet02.hs ================================================ instance Monoid [a] where mempty = [] mappend = (++) ================================================ FILE: src/content/2.3/code/haskell/snippet03.hs ================================================ 2 * 3 = 6 [2] ++ [3] = [2, 3] -- not the same as [6] ================================================ FILE: src/content/2.3/code/haskell/snippet04.hs ================================================ h (a * b) = h a * h b ================================================ FILE: src/content/2.3/code/haskell/snippet05.hs ================================================ [2] ++ [3] = [2, 3] ================================================ FILE: src/content/2.3/code/haskell/snippet06.hs ================================================ 2 * 3 = 6 ================================================ FILE: src/content/2.3/code/haskell/snippet07.hs ================================================ p :: x -> U m ================================================ FILE: src/content/2.3/code/haskell/snippet08.hs ================================================ q :: x -> U n ================================================ FILE: src/content/2.3/code/haskell/snippet09.hs ================================================ h :: m -> n ================================================ FILE: src/content/2.3/code/haskell/snippet10.hs ================================================ q = U h . p ================================================ FILE: src/content/2.3/code/ocaml/snippet01.ml ================================================ module type Monoid = sig type m val mempty : m val mappend : m -> m -> m end ================================================ FILE: src/content/2.3/code/ocaml/snippet02.ml ================================================ module ListMonoid (T1 : sig type a end) : Monoid with type m = T1.a list = struct type m = T1.a list let mempty = [] let mappend xs ys = List.append xs ys end ================================================ FILE: src/content/2.3/code/ocaml/snippet03.ml ================================================ 2 * 3 = 6 List.append [2] [3] = [2; 3] ================================================ FILE: src/content/2.3/code/ocaml/snippet04.ml ================================================ let h (a * b) = h a * h b ================================================ FILE: src/content/2.3/code/ocaml/snippet05.ml ================================================ List.append [2] [3] = [2; 3] ================================================ FILE: src/content/2.3/code/ocaml/snippet06.ml ================================================ 2 * 3 = 6 ================================================ FILE: src/content/2.3/code/ocaml/snippet07.ml ================================================ module type FreeMonoidRep = functor (F : Functor) -> sig type x type m val p : x -> m F.t end ================================================ FILE: src/content/2.3/code/ocaml/snippet08.ml ================================================ module type FreeMonoidRep = functor (F : Functor) -> sig type x type n val q : x -> n F.t end ================================================ FILE: src/content/2.3/code/ocaml/snippet09.ml ================================================ val h : m -> n ================================================ FILE: src/content/2.3/code/ocaml/snippet10.ml ================================================ val q = compose uh p ================================================ FILE: src/content/2.3/code/reason/snippet01.re ================================================ module type Monoid = { type m; let mempty: m; let mappend: (m, m) => m; }; ================================================ FILE: src/content/2.3/code/reason/snippet02.re ================================================ module ListMonoid = (T1: {type a;}) : (Monoid with type m = list(T1.a)) => { type m = list(T1.a); let mempty = []; let mappend = (xs, ys) => List.append(xs, ys); }; ================================================ FILE: src/content/2.3/code/reason/snippet03.re ================================================ 2 * 3 == 6 List.append([2], [3]) == [2, 3]; ================================================ FILE: src/content/2.3/code/reason/snippet04.re ================================================ let h (a * b) = h a * h b ================================================ FILE: src/content/2.3/code/reason/snippet05.re ================================================ List.append([2], [3]) == [2, 3]; ================================================ FILE: src/content/2.3/code/reason/snippet06.re ================================================ 2 * 3 == 6; ================================================ FILE: src/content/2.3/code/reason/snippet07.re ================================================ module type FreeMonoidRep = (F: Functor) => { type x; type m; let p: x => F.t(m); }; ================================================ FILE: src/content/2.3/code/reason/snippet08.re ================================================ module type FreeMonoidRep = (F: Functor) => { type x; type n; let q: x => F.t(n); }; ================================================ FILE: src/content/2.3/code/reason/snippet09.re ================================================ let h: m => n ================================================ FILE: src/content/2.3/code/reason/snippet10.re ================================================ let q: compose(uh p) ================================================ FILE: src/content/2.3/code/scala/snippet01.scala ================================================ trait Monoid[M] { def mempty: M def mappend(m1: M, m2: M): M } ================================================ FILE: src/content/2.3/code/scala/snippet02.scala ================================================ object Monoid { implicit def listMonoid[A]: Monoid[List[A]] = new Monoid[List[A]] { def mempty: List[A] = List() def mappend(m1: List[A], m2: List[A]): List[A] = m1 ++ m2 } } ================================================ FILE: src/content/2.3/code/scala/snippet03.scala ================================================ 2 * 3 == 6 List(2) ++ List(3) == List(2, 3) ================================================ FILE: src/content/2.3/code/scala/snippet04.scala ================================================ h(a * b) == h(a) * h(b) ================================================ FILE: src/content/2.3/code/scala/snippet05.scala ================================================ List(2) ++ List(3) == List(2, 3) ================================================ FILE: src/content/2.3/code/scala/snippet06.scala ================================================ 2 * 3 == 6 ================================================ FILE: src/content/2.3/code/scala/snippet07.scala ================================================ val p: X => U[M] ================================================ FILE: src/content/2.3/code/scala/snippet08.scala ================================================ val q: X => U[N] ================================================ FILE: src/content/2.3/code/scala/snippet09.scala ================================================ val h: M => N ================================================ FILE: src/content/2.3/code/scala/snippet10.scala ================================================ val q = uh compose p ================================================ FILE: src/content/2.3/free-monoids.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{M}{onoids are an important} concept in both category theory and in programming. Categories correspond to strongly typed languages, monoids to untyped languages. That's because in a monoid you can compose any two arrows, just as in an untyped language you can compose any two functions (of course, you may end up with a runtime error when you execute your program). We've seen that a monoid may be described as a category with a single object, where all logic is encoded in the rules of morphism composition. This categorical model is fully equivalent to the more traditional set-theoretical definition of a monoid, where we ``multiply'' two elements of a set to get a third element. This process of ``multiplication'' can be further dissected into first forming a pair of elements and then identifying this pair with an existing element --- their ``product.'' What happens when we forgo the second part of multiplication --- the identification of pairs with existing elements? We can, for instance, start with an arbitrary set, form all possible pairs of elements, and call them new elements. Then we'll pair these new elements with all possible elements, and so on. This is a chain reaction --- we'll keep adding new elements forever. The result, an infinite set, will be \emph{almost} a monoid. But a monoid also needs a unit element and the law of associativity. No problem, we can add a special unit element and identify some of the pairs --- just enough to support the unit and associativity laws. Let's see how this works in a simple example. Let's start with a set of two elements, $\{a, b\}$. We'll call them the generators of the free monoid. First, we'll add a special element $e$ to serve as the unit. Next we'll add all the pairs of elements and call them ``products''. The product of $a$ and $b$ will be the pair $(a, b)$. The product of $b$ and $a$ will be the pair $(b, a)$, the product of $a$ with $a$ will be $(a, a)$, the product of $b$ with $b$ will be $(b, b)$. We can also form pairs with $e$, like $(a, e)$, $(e, b)$, etc., but we'll identify them with $a$, $b$, etc. So in this round we'll only add $(a, a)$, $(a, b)$ and $(b, a)$ and $(b, b)$, and end up with the set $\{e, a, b, (a, a), (a, b), (b, a), (b, b)\}$. \begin{figure}[H] \centering \includegraphics[width=0.8\textwidth]{images/bunnies.jpg} \end{figure} \noindent In the next round we'll keep adding elements like: $(a, (a, b))$, $((a, b), a)$, etc. At this point we'll have to make sure that associativity holds, so we'll identify $(a, (b, a))$ with $((a, b), a)$, etc. In other words, we won't be needing internal parentheses. You can guess what the final result of this process will be: we'll create all possible lists of $a$s and $b$s. In fact, if we represent $e$ as an empty list, we can see that our ``multiplication'' is nothing but list concatenation. This kind of construction, in which you keep generating all possible combinations of elements, and perform the minimum number of identifications --- just enough to uphold the laws --- is called a free construction. What we have just done is to construct a \newterm{free monoid} from the set of generators $\{a, b\}$. \section{Free Monoid in Haskell} A two-element set in Haskell is equivalent to the type \code{Bool}, and the free monoid generated by this set is equivalent to the type \code{{[}Bool{]}} (list of \code{Bool}). (I am deliberately ignoring problems with infinite lists.) A monoid in Haskell is defined by the type class: \src{snippet01} This just says that every \code{Monoid} must have a neutral element, which is called \code{mempty}, and a binary function (multiplication) called \code{mappend}. The unit and associativity laws cannot be expressed in Haskell and must be verified by the programmer every time a monoid is instantiated. The fact that a list of any type forms a monoid is described by this instance definition: \src{snippet02} It states that an empty list \code{{[}{]}} is the unit element, and list concatenation \code{(++)} is the binary operation. As we have seen, a list of type \code{a} corresponds to a free monoid with the set \code{a} serving as generators. The set of natural numbers with multiplication is not a free monoid, because we identify lots of products. Compare for instance: \src{snippet03} That was easy, but the question is, can we perform this free construction in category theory, where we are not allowed to look inside objects? We'll use our workhorse: the universal construction. The second interesting question is, can any monoid be obtained from some free monoid by identifying more than the minimum number of elements required by the laws? I'll show you that this follows directly from the universal construction. \section{Free Monoid Universal Construction} If you recall our previous experiences with universal constructions, you might notice that it's not so much about constructing something as about selecting an object that best fits a given pattern. So if we want to use the universal construction to ``construct'' a free monoid, we have to consider a whole bunch of monoids from which to pick one. We need a whole category of monoids to choose from. But do monoids form a category? Let's first look at monoids as sets equipped with additional structure defined by unit and multiplication. We'll pick as morphisms those functions that preserve the monoidal structure. Such structure-preserving functions are called \newterm{homomorphisms}. A monoid homomorphism must map the product of two elements to the product of the mapping of the two elements: \src{snippet04} and it must map unit to unit. For instance, consider a homomorphism from lists of integers to integers. If we map \code{{[}2{]}} to 2 and \code{{[}3{]}} to 3, we have to map \code{{[}2, 3{]}} to 6, because concatenation \src{snippet05} becomes multiplication \src{snippet06} Now let's forget about the internal structure of individual monoids, and only look at them as objects with corresponding morphisms. You get a category $\cat{Mon}$ of monoids. Okay, maybe before we forget about internal structure, let us notice an important property. Every object of $\cat{Mon}$ can be trivially mapped to a set. It's just the set of its elements. This set is called the \newterm{underlying} set. In fact, not only can we map objects of $\cat{Mon}$ to sets, but we can also map morphisms of $\cat{Mon}$ (homomorphisms) to functions. Again, this seems sort of trivial, but it will become useful soon. This mapping of objects and morphisms from $\cat{Mon}$ to $\Set$ is in fact a functor. Since this functor ``forgets'' the monoidal structure --- once we are inside a plain set, we no longer distinguish the unit element or care about multiplication --- it's called a \newterm{forgetful functor}. Forgetful functors come up regularly in category theory. We now have two different views of $\cat{Mon}$. We can treat it just like any other category with objects and morphisms. In that view, we don't see the internal structure of monoids. All we can say about a particular object in $\cat{Mon}$ is that it connects to itself and to other objects through morphisms. The ``multiplication'' table of morphisms --- the composition rules --- are derived from the other view: monoids-as-sets. By going to category theory we haven't lost this view completely --- we can still access it through our forgetful functor. To apply the universal construction, we need to define a special property that would let us search through the category of monoids and pick the best candidate for a free monoid. But a free monoid is defined by its generators. Different choices of generators produce different free monoids (a list of \code{Bool} is not the same as a list of \code{Int}). Our construction must start with a set of generators. So we're back to sets! That's where the forgetful functor comes into play. We can use it to X-ray our monoids. We can identify the generators in the X-ray images of those blobs. Here's how it works: We start with a set of generators, $x$. That's a set in $\Set$. The pattern we are going to match consists of a monoid $m$ --- an object of $\cat{Mon}$ --- and a function $p$ in $\Set$: \src{snippet07} where $U$ is our forgetful functor from $\cat{Mon}$ to $\Set$. This is a weird heterogeneous pattern --- half in $\cat{Mon}$ and half in $\Set$. The idea is that the function $p$ will identify the set of generators inside the X-ray image of $m$. It doesn't matter that functions may be lousy at identifying points inside sets (they may collapse them). It will all be sorted out by the universal construction, which will pick the best representative of this pattern. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/monoid-pattern.jpg} \end{figure} \noindent We also have to define the ranking among candidates. Suppose we have another candidate: a monoid $n$ and a function that identifies the generators in its X-ray image: \src{snippet08} We'll say that $m$ is better than $n$ if there is a morphism of monoids (that's a structure-preserving homomorphism): \src{snippet09} whose image under $U$ (remember, $U$ is a functor, so it maps morphisms to functions) factorizes through $p$: \src{snippet10} If you think of $p$ as selecting the generators in $m$; and $q$ as selecting ``the same'' generators in $n$; then you can think of $h$ as mapping these generators between the two monoids. Remember that $h$, by definition, preserves the monoidal structure. It means that a product of two generators in one monoid will be mapped to a product of the corresponding two generators in the second monoid, and so on. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/monoid-ranking.jpg} \end{figure} \noindent This ranking may be used to find the best candidate --- the free monoid. Here's the definition: \begin{quote} We'll say that $m$ (together with the function $p$) is the \textbf{free monoid} with the generators $x$ if and only if there is a \emph{unique} morphism $h$ from $m$ to any other monoid $n$ (together with the function $q$) that satisfies the above factorization property. \end{quote} Incidentally, this answers our second question. The function $U h$ is the one that has the power to collapse multiple elements of $U m$ to a single element of $U n$. This collapse corresponds to identifying some elements of the free monoid. Therefore any monoid with generators $x$ can be obtained from the free monoid based on $x$ by identifying some of the elements. The free monoid is the one where only the bare minimum of identifications have been made. We'll come back to free monoids when we talk about adjunctions. \section{Challenges} \begin{enumerate} \tightlist \item You might think (as I did, originally) that the requirement that a homomorphism of monoids preserve the unit is redundant. After all, we know that for all $a$ \begin{snip}{text} h a * h e = h (a * e) = h a \end{snip} So $h e$ acts like a right unit (and, by analogy, as a left unit). The problem is that $h a$, for all $a$ might only cover a sub-monoid of the target monoid. There may be a ``true'' unit outside of the image of $h$. Show that an isomorphism between monoids that preserves multiplication must automatically preserve unit. \item Consider a monoid homomorphism from lists of integers with concatenation to integers with multiplication. What is the image of the empty list \code{{[}{]}}? Assume that all singleton lists are mapped to the integers they contain, that is \code{{[}3{]}} is mapped to 3, etc. What's the image of \code{{[}1, 2, 3, 4{]}}? How many different lists map to the integer 12? Is there any other homomorphism between the two monoids? \item What is the free monoid generated by a one-element set? Can you see what it's isomorphic to? \end{enumerate} ================================================ FILE: src/content/2.4/code/haskell/snippet01.hs ================================================ type Reader a x = a -> x ================================================ FILE: src/content/2.4/code/haskell/snippet02.hs ================================================ instance Functor (Reader a) where fmap f h = f . h ================================================ FILE: src/content/2.4/code/haskell/snippet03.hs ================================================ type Op a x = x -> a ================================================ FILE: src/content/2.4/code/haskell/snippet04.hs ================================================ instance Contravariant (Op a) where contramap f h = h . f ================================================ FILE: src/content/2.4/code/haskell/snippet05.hs ================================================ instance Profunctor (->) where dimap ab cd bc = cd . bc . ab lmap = flip (.) rmap = (.) ================================================ FILE: src/content/2.4/code/haskell/snippet06.hs ================================================ alpha :: forall x. (a -> x) -> F x ================================================ FILE: src/content/2.4/code/haskell/snippet07.hs ================================================ fmap f . alpha = alpha . fmap f ================================================ FILE: src/content/2.4/code/haskell/snippet08.hs ================================================ fmap f (alpha h) = alpha (f . h) ================================================ FILE: src/content/2.4/code/haskell/snippet09.hs ================================================ beta :: forall x. F x -> (a -> x) ================================================ FILE: src/content/2.4/code/haskell/snippet10.hs ================================================ alpha :: forall x. (Int -> x) -> [x] alpha h = map h [12] ================================================ FILE: src/content/2.4/code/haskell/snippet11.hs ================================================ map f (map h [12]) = map (f . h) [12] ================================================ FILE: src/content/2.4/code/haskell/snippet12.hs ================================================ beta :: forall x. [x] -> (Int -> x) ================================================ FILE: src/content/2.4/code/haskell/snippet13.hs ================================================ class Representable f where type Rep f :: * tabulate :: (Rep f -> x) -> f x index :: f x -> Rep f -> x ================================================ FILE: src/content/2.4/code/haskell/snippet14.hs ================================================ data Stream x = Cons x (Stream x) ================================================ FILE: src/content/2.4/code/haskell/snippet15.hs ================================================ instance Representable Stream where type Rep Stream = Integer tabulate f = Cons (f 0) (tabulate (f . (+1))) index (Cons b bs) n = if n == 0 then b else index bs (n - 1) ================================================ FILE: src/content/2.4/code/ocaml/snippet01.ml ================================================ type ('a, 'x) reader = 'a -> 'x ================================================ FILE: src/content/2.4/code/ocaml/snippet02.ml ================================================ module ReaderFunctor (T : sig type r end) : Functor = struct type 'a t = (T.r, 'a) reader let fmap f h a = f (h a) end ================================================ FILE: src/content/2.4/code/ocaml/snippet03.ml ================================================ type ('a, 'x) op = 'x -> 'a ================================================ FILE: src/content/2.4/code/ocaml/snippet04.ml ================================================ module OpContravariant (T : sig type r end) : Contravariant = struct type 'a t = (T.r, 'a) op let contramap f h b = h (f b) end ================================================ FILE: src/content/2.4/code/ocaml/snippet05.ml ================================================ module ProfunctorArrow : Profunctor = struct type ('a, 'b) p = 'a -> 'b let dimap f g p = compose g (compose p f) let lmap f p = (flip compose) f p let rmap = compose end ================================================ FILE: src/content/2.4/code/ocaml/snippet06.ml ================================================ module type NT_AX_FX = sig type a type 'x t val alpha : (a -> 'x) -> 'x t end ================================================ FILE: src/content/2.4/code/ocaml/snippet07.ml ================================================ compose (F.fmap f) NT.alpha = compose NT.alpha (F.fmap f) ================================================ FILE: src/content/2.4/code/ocaml/snippet08.ml ================================================ F.fmap f (N.alpha h) = N.alpha (compose f h) ================================================ FILE: src/content/2.4/code/ocaml/snippet09.ml ================================================ module type NT_FX_AX = sig type a type 'x t val beta : 'x t -> a -> 'x end ================================================ FILE: src/content/2.4/code/ocaml/snippet10.ml ================================================ module NT_Impl (F : Functor with type 'a t = 'a list) : NT_AX_FX with type a = int and type 'x t = 'x list = struct type a = int type 'x t = 'x list let alpha : 'x. (int -> 'x) -> 'x list = fun h -> F.fmap h [ 12 ] end ================================================ FILE: src/content/2.4/code/ocaml/snippet11.ml ================================================ F.fmap f (F.fmap h [12]) = F.fmap (compose f h) [12] ================================================ FILE: src/content/2.4/code/ocaml/snippet12.ml ================================================ module type NT_ListX_IntX = sig type a = int type 'x t = 'x list val beta : 'x t -> a -> 'x end ================================================ FILE: src/content/2.4/code/ocaml/snippet13.ml ================================================ module type Representable = sig type 'x t type rep (* Representing type 'a' *) val tabulate : (rep -> 'x) -> 'x t val index : 'x t -> rep -> 'x end ================================================ FILE: src/content/2.4/code/ocaml/snippet14.ml ================================================ type 'a stream = Cons of 'a * 'a stream Lazy.t ================================================ FILE: src/content/2.4/code/ocaml/snippet15.ml ================================================ module StreamRepresentable : Representable = struct type rep = int type 'x t = 'x stream let rec tabulate f = Cons (f 0, lazy (tabulate (compose f succ))) let rec index (Cons (b, bs)) n = if n = 0 then b else index (Lazy.force bs) (n - 1) ;; end ================================================ FILE: src/content/2.4/code/reason/snippet01.re ================================================ type reader('a, 'x) = 'a => 'x; ================================================ FILE: src/content/2.4/code/reason/snippet02.re ================================================ module ReaderFunctor = (T: {type r;}) : Functor => { type t('a) = reader(T.r, 'a); let fmap = (f, h, a) => f(h(a)); }; ================================================ FILE: src/content/2.4/code/reason/snippet03.re ================================================ type op('a, 'x) = 'x => 'a; ================================================ FILE: src/content/2.4/code/reason/snippet04.re ================================================ module OpContravariant = (T: {type r;}) : Contravariant => { type t('a) = op(T.r, 'a); let contramap = (f, h, b) => h(f(b)); }; ================================================ FILE: src/content/2.4/code/reason/snippet05.re ================================================ module ProfunctorArrow: Profunctor = { type p('a, 'b) = 'a => 'b; let dimap = (f, g, p) => compose(g, compose(p, f)); let lmap = (f, p) => (flip(compose))(f, p); let rmap = compose; }; ================================================ FILE: src/content/2.4/code/reason/snippet06.re ================================================ module type NT_AX_FX = { type a; type t('x); let alpha: (a => 'x) => t('x); }; ================================================ FILE: src/content/2.4/code/reason/snippet07.re ================================================ compose(F.fmap(f), NT.alpha) == compose(NT.alpha, F.fmap(f)); ================================================ FILE: src/content/2.4/code/reason/snippet08.re ================================================ F.fmap(f, N.alpha(h)) == N.alpha(compose(f, h)); ================================================ FILE: src/content/2.4/code/reason/snippet09.re ================================================ module type NT_FX_AX = { type a; type t('x); let beta: (t('x), a) => 'x; }; ================================================ FILE: src/content/2.4/code/reason/snippet10.re ================================================ module NT_Impl = (F: Functor with type t('a) = list('a)): (NT_AX_FX with type a = int and type t('x) = list('x)) => { type a = int; type t('x) = list('x); let alpha: 'x . (int => 'x) => list('x) = h => F.fmap(h, [12]); }; ================================================ FILE: src/content/2.4/code/reason/snippet11.re ================================================ F.fmap(f, F.fmap(h, [12])) == F.fmap(compose(f, h), [12]); ================================================ FILE: src/content/2.4/code/reason/snippet12.re ================================================ module type NT_ListX_IntX = { type a = int; type t('x) = list('x); let beta: (t('x), a) => 'x; }; ================================================ FILE: src/content/2.4/code/reason/snippet13.re ================================================ module type Representable = { type t('x); type rep; /* Representing type 'a' */ let tabulate: (rep => 'x) => t('x); let index: (t('x), rep) => 'x; }; ================================================ FILE: src/content/2.4/code/reason/snippet14.re ================================================ type stream('a) = | Cons('a, Lazy.t(stream('a))); ================================================ FILE: src/content/2.4/code/reason/snippet15.re ================================================ module StreamRepresentable: Representable = { type rep = int; type t('x) = stream('x); let rec tabulate = f => Cons(f(0), lazy(tabulate(compose(f, succ)))); let rec index = (Cons(b, bs), n) => n == 0 ? b : index(Lazy.force(bs), n - 1); }; ================================================ FILE: src/content/2.4/code/scala/snippet01.scala ================================================ type Reader[A, X] = A => X ================================================ FILE: src/content/2.4/code/scala/snippet02.scala ================================================ implicit def readerFunctor[A] = new Functor[Reader[A, ?]] { def fmap[X, B](f: X => B)(h: Reader[A, X]): Reader[A, B] = f compose h } ================================================ FILE: src/content/2.4/code/scala/snippet03.scala ================================================ type Op[A, X] = X => A ================================================ FILE: src/content/2.4/code/scala/snippet04.scala ================================================ implicit def opContravariant[A] = new Contravariant[Op[A, ?]] { def contramap[X, B](f: B => X)(h: Op[A, X]): Op[A, B] = h compose f } ================================================ FILE: src/content/2.4/code/scala/snippet05.scala ================================================ implicit def arrowProfunctor = new Profunctor[Function1] { def dimap[A, B, C, D](ab: A => B)(cd: C => D)(bc: B => C): A => D = cd compose bc compose ab def lmap[A, B, C](f: C => A)(ab: A => B): C => B = f andThen ab def rmap[A, B, C](f: B => C)(ab: A => B): A => C = f compose ab } ================================================ FILE: src/content/2.4/code/scala/snippet06.scala ================================================ // In order to make a universal transformation, // another type needs to be introduced. // To read more about FunctionK (~>): // typelevel.org/cats/datatypes/functionk.html trait ~>[F[_], G[_]] { def apply[B](fa: F[B]): G[B] } // Kind Projector plugin provides // a more concise syntax for type lambdas: def alpha[A]: (A => ?) ~> F ================================================ FILE: src/content/2.4/code/scala/snippet07.scala ================================================ (fmap(f) _ compose alpha.apply) == (alpha.apply _ compose fmap(f)) ================================================ FILE: src/content/2.4/code/scala/snippet08.scala ================================================ fmap(f)(alpha(h)) == alpha(f compose h) ================================================ FILE: src/content/2.4/code/scala/snippet09.scala ================================================ def beta[A]: F ~> (A => ?) ================================================ FILE: src/content/2.4/code/scala/snippet10.scala ================================================ def alpha: (Int => ?) ~> List = new ~>[Int => ?, List] { def apply[A](fa: Int => A) = List(12).map(fa) } ================================================ FILE: src/content/2.4/code/scala/snippet11.scala ================================================ fmap(f)(fmap(h)(List(12))) == fmap(f compose h)(List(12)) // Or using scala stdlib: List(12).map(h).map(f) == List(12).map(f compose h) ================================================ FILE: src/content/2.4/code/scala/snippet12.scala ================================================ def beta: List ~> (Int => ?) ================================================ FILE: src/content/2.4/code/scala/snippet13.scala ================================================ trait Representable[F[_]] { type Rep def tabulate[X](f: Rep => X): F[X] def index[X]: F[X] => Rep => X } ================================================ FILE: src/content/2.4/code/scala/snippet14.scala ================================================ case class Stream[X]( h: () => X, t: () => Stream[X] ) ================================================ FILE: src/content/2.4/code/scala/snippet15.scala ================================================ implicit val streamRepresentable = new Representable[Stream] { type Rep = Int def tabulate[X](f: Rep => X): Stream[X] = Stream[X](() => f(0), () => tabulate(f compose (_ + 1))) def index[X]: Stream[X] => Rep => X = { case Stream(b, bs) => n => if (n == 0) b() else index(bs())(n - 1) } } ================================================ FILE: src/content/2.4/representable-functors.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{I}{t's about time} we had a little talk about sets. Mathematicians have a love/hate relationship with set theory. It's the assembly language of mathematics --- at least it used to be. Category theory tries to step away from set theory, to some extent. For instance, it's a known fact that the set of all sets doesn't exist, but the category of all sets, $\Set$, does. So that's good. On the other hand, we assume that morphisms between any two objects in a category form a set. We even called it a hom-set. To be fair, there is a branch of category theory where morphisms don't form sets. Instead they are objects in another category. Those categories that use hom-objects rather than hom-sets, are called \newterm{enriched} categories. In what follows, though, we'll stick to categories with good old-fashioned hom-sets. A set is the closest thing to a featureless blob you can get outside of categorical objects. A set has elements, but you can't say much about these elements. If you have a finite set, you can count the elements. You can kind of count the elements of an infinite set using cardinal numbers. The set of natural numbers, for instance, is smaller than the set of real numbers, even though both are infinite. But, maybe surprisingly, a set of rational numbers is the same size as the set of natural numbers. Other than that, all the information about sets can be encoded in functions between them --- especially the invertible ones called isomorphisms. For all intents and purposes isomorphic sets are identical. Before I summon the wrath of foundational mathematicians, let me explain that the distinction between equality and isomorphism is of fundamental importance. In fact it is one of the main concerns of the latest branch of mathematics, the Homotopy Type Theory (HoTT). I'm mentioning HoTT because it's a pure mathematical theory that takes inspiration from computation, and one of its main proponents, Vladimir Voevodsky, had a major epiphany while studying the Rocq theorem prover. The interaction between mathematics and programming goes both ways. The important lesson about sets is that it's okay to compare sets of unlike elements. For instance, we can say that a given set of natural transformations is isomorphic to some set of morphisms, because a set is just a set. Isomorphism in this case just means that for every natural transformation from one set there is a unique morphism from the other set and vice versa. They can be paired against each other. You can't compare apples with oranges, if they are objects from different categories, but you can compare sets of apples against sets of oranges. Often transforming a categorical problem into a set-theoretical problem gives us the necessary insight or even lets us prove valuable theorems. \section{The Hom Functor} Every category comes equipped with a canonical family of mappings to $\Set$. Those mappings are in fact functors, so they preserve the structure of the category. Let's build one such mapping. Let's fix one object $a$ in $\cat{C}$ and pick another object $x$ also in $\cat{C}$. The hom-set $\cat{C}(a, x)$ is a set, an object in $\Set$. When we vary $x$, keeping $a$ fixed, $\cat{C}(a, x)$ will also vary in $\Set$. Thus we have a mapping from $x$ to $\Set$. \begin{figure}[H] \centering \includegraphics[width=0.45\textwidth]{images/hom-set.jpg} \end{figure} \noindent If we want to stress the fact that we are considering the hom-set as a mapping in its second argument, we use the notation $\cat{C}(a, -)$ with the dash serving as the placeholder for the argument. This mapping of objects is easily extended to the mapping of morphisms. Let's take a morphism $f$ in $\cat{C}$ between two arbitrary objects $x$ and $y$. The object $x$ is mapped to the set $\cat{C}(a, x)$, and the object $y$ is mapped to $\cat{C}(a, y)$, under the mapping we have just defined. If this mapping is to be a functor, $f$ must be mapped to a function between the two sets: $\cat{C}(a, x) \to \cat{C}(a, y)$ Let's define this function point-wise, that is for each argument separately. For the argument we should pick an arbitrary element of $\cat{C}(a, x)$ --- let's call it $h$. Morphisms are composable, if they match end to end. It so happens that the target of $h$ matches the source of $f$, so their composition: \[f \circ h \Colon a \to y\] is a morphism going from $a$ to $y$. It is therefore a member of $\cat{C}(a, y)$. \begin{figure}[H] \centering \includegraphics[width=0.45\textwidth]{images/hom-functor.jpg} \end{figure} \noindent We have just found our function from $\cat{C}(a, x)$ to $\cat{C}(a, y)$, which can serve as the image of $f$. If there is no danger of confusion, we'll write this lifted function as: $\cat{C}(a, f)$ and its action on a morphism $h$ as: \[\cat{C}(a, f) h = f \circ h\] Since this construction works in any category, it must also work in the category of Haskell types. In Haskell, the hom-functor is better known as the \code{Reader} functor: \src{snippet01} \src{snippet02} Now let's consider what happens if, instead of fixing the source of the hom-set, we fix the target. In other words, we're asking the question if the mapping $\cat{C}(-, a)$ is also a functor. It is, but instead of being covariant, it's contravariant. That's because the same kind of matching of morphisms end to end results in postcomposition by $f$; rather than precomposition, as was the case with $\cat{C}(a, -)$. We have already seen this contravariant functor in Haskell. We called it \code{Op}: \src{snippet03} \src{snippet04} Finally, if we let both objects vary, we get a profunctor $\cat{C}(-, =)$, which is contravariant in the first argument and covariant in the second (to underline the fact that the two arguments may vary independently, we use a double dash as the second placeholder). We have seen this profunctor before, when we talked about functoriality: \src{snippet05} The important lesson is that this observation holds in any category: the mapping of objects to hom-sets is functorial. Since contravariance is equivalent to a mapping from the opposite category, we can state this fact succinctly as: \[C(-, =) \Colon \cat{C}^\mathit{op} \times \cat{C} \to \Set\] \section{Representable Functors} We've seen that, for every choice of an object $a$ in $\cat{C}$, we get a functor from $\cat{C}$ to $\Set$. This kind of structure-preserving mapping to $\Set$ is often called a \newterm{representation}. We are representing objects and morphisms of $\cat{C}$ as sets and functions in $\Set$. The functor $\cat{C}(a, -)$ itself is sometimes called representable. More generally, any functor $F$ that is naturally isomorphic to the hom-functor, for some choice of $a$, is called \newterm{representable}. Such a functor must necessarily be $\Set$-valued, since $\cat{C}(a, -)$ is. I said before that we often think of isomorphic sets as identical. More generally, we think of isomorphic \emph{objects} in a category as identical. That's because objects have no structure other than their relation to other objects (and themselves) through morphisms. For instance, we've previously talked about the category of monoids, $\cat{Mon}$, that was initially modeled with sets. But we were careful to pick as morphisms only those functions that preserved the monoidal structure of those sets. So if two objects in $\cat{Mon}$ are isomorphic, meaning there is an invertible morphism between them, they have exactly the same structure. If we peeked at the sets and functions that they were based upon, we'd see that the unit element of one monoid was mapped to the unit element of another, and that a product of two elements was mapped to the product of their mappings. The same reasoning can be applied to functors. Functors between two categories form a category in which natural transformations play the role of morphisms. So two functors are isomorphic, and can be thought of as identical, if there is an invertible natural transformation between them. Let's analyze the definition of the representable functor from this perspective. For $F$ to be representable we require that: There be an object $a$ in $\cat{C}$; one natural transformation $\alpha$ from $\cat{C}(a, -)$ to $F$; another natural transformation, $\beta$, in the opposite direction; and that their composition be the identity natural transformation. Let's look at the component of $\alpha$ at some object $x$. It's a function in $\Set$: \[\alpha_x \Colon \cat{C}(a, x) \to F x\] The naturality condition for this transformation tells us that, for any morphism $f$ from $x$ to $y$, the following diagram commutes: \[F f \circ \alpha_x = \alpha_y \circ \cat{C}(a, f)\] In Haskell, we would replace natural transformations with polymorphic functions: \src{snippet06} with the optional \code{forall} quantifier. The naturality condition \src{snippet07} is automatically satisfied due to parametricity (it's one of those theorems for free I mentioned earlier), with the understanding that \code{fmap} on the left is defined by the functor $F$, whereas the one on the right is defined by the reader functor. Since \code{fmap} for reader is just function precomposition, we can be even more explicit. Acting on $h$, an element of $\cat{C}(a, x)$, the naturality condition simplifies to: \src{snippet08} The other transformation, \code{beta}, goes the opposite way: \src{snippet09} It must respect naturality conditions, and it must be the inverse of \code{alpha}: \begin{snip}{text} alpha . beta = id = beta . alpha \end{snip} We will see later that a natural transformation from $\cat{C}(a, -)$ to any $\Set$-valued functor always exists as long as $F a$ is non-empty (Yoneda's lemma) but it is not necessarily invertible. Let me give you an example in Haskell with the list functor and \code{Int} as \code{a}. Here's a natural transformation that does the job: \src{snippet10} I have arbitrarily picked the number 12 and created a singleton list with it. I can then \code{fmap} the function \code{h} over this list and get a list of the type returned by \code{h}. (There are actually as many such transformations as there are list of integers.) The naturality condition is equivalent to the composability of \code{map} (the list version of \code{fmap}): \src{snippet11} But if we tried to find the inverse transformation, we would have to go from a list of arbitrary type \code{x} to a function returning \code{x}: \src{snippet12} You might think of retrieving an \code{x} from the list, e.g., using \code{head}, but that won't work for an empty list. Notice that there is no choice for the type \code{a} (in place of \code{Int}) that would work here. So the list functor is not representable. Remember when we talked about Haskell (endo-) functors being a little like containers? In the same vein we can think of representable functors as containers for storing memoized results of function calls (the members of hom-sets in Haskell are just functions). The representing object, the type $a$ in $\cat{C}(a, -)$, is thought of as the key type, with which we can access the tabulated values of a function. The transformation we called \code{alpha} is called \code{tabulate}, and its inverse, \code{beta}, is called \code{index}. Here's a (slightly simplified) \code{Representable} class definition: \src{snippet13} Notice that the representing type, our $a$, which is called \code{Rep f} here, is part of the definition of \code{Representable}. The star just means that \code{Rep f} is a type (as opposed to a type constructor, or other more exotic kinds). Infinite lists, or streams, which cannot be empty, are representable. \src{snippet14} You can think of them as memoized values of a function taking an \code{Integer} as an argument. (Strictly speaking, I should be using non-negative natural numbers, but I didn't want to complicate the code.) To \code{tabulate} such a function, you create an infinite stream of values. Of course, this is only possible because Haskell is lazy. The values are evaluated on demand. You access the memoized values using \code{index}: \src{snippet15} It's interesting that you can implement a single memoization scheme to cover a whole family of functions with arbitrary return types. Representability for contravariant functors is similarly defined, except that we keep the second argument of $\cat{C}(-, a)$ fixed. Or, equivalently, we may consider functors from $\cat{C}^\mathit{op}$ to $\Set$, because $\cat{C}^\mathit{op}(a, -)$ is the same as $\cat{C}(-, a)$. There is an interesting twist to representability. Remember that hom-sets can internally be treated as exponential objects, in Cartesian closed categories. The hom-set $\cat{C}(a, x)$ is equivalent to $x^a$, and for a representable functor $F$ we can write: $-^a = F$. Let's take the logarithm of both sides, just for kicks: $a = \mathbf{log}F$ Of course, this is a purely formal transformation, but if you know some of the properties of logarithms, it is quite helpful. In particular, it turns out that functors that are based on product types can be represented with sum types, and that sum-type functors are not in general representable (example: the list functor). Finally, notice that a representable functor gives us two different implementations of the same thing --- one a function, one a data structure. They have exactly the same content --- the same values are retrieved using the same keys. That's the sense of ``sameness'' I was talking about. Two naturally isomorphic functors are identical as far as their contents are involved. On the other hand, the two representations are often implemented differently and may have different performance characteristics. Memoization is used as a performance enhancement and may lead to substantially reduced run times. Being able to generate different representations of the same underlying computation is very valuable in practice. So, surprisingly, even though it's not concerned with performance at all, category theory provides ample opportunities to explore alternative implementations that have practical value. \section{Challenges} \begin{enumerate} \tightlist \item Show that the hom-functors map identity morphisms in $\cat{C}$ to corresponding identity functions in $\Set$. \item Show that \code{Maybe} is not representable. \item Is the \code{Reader} functor representable? \item Using \code{Stream} representation, memoize a function that squares its argument. \item Show that \code{tabulate} and \code{index} for \code{Stream} are indeed the inverse of each other. (Hint: use induction.) \item The functor: \begin{snip}{haskell} Pair a = Pair a a \end{snip} is representable. Can you guess the type that represents it? Implement \code{tabulate} and \code{index}. \end{enumerate} \section{Bibliography} \begin{enumerate} \tightlist \item The Catsters video about \urlref{https://www.youtube.com/watch?v=4QgjKUzyrhM}{representable functors}. \end{enumerate} ================================================ FILE: src/content/2.5/code/haskell/snippet01.hs ================================================ type Reader a x = a -> x ================================================ FILE: src/content/2.5/code/haskell/snippet02.hs ================================================ instance Functor (Reader a) where fmap f h = f . h ================================================ FILE: src/content/2.5/code/haskell/snippet03.hs ================================================ alpha :: forall x . (a -> x) -> F x ================================================ FILE: src/content/2.5/code/haskell/snippet04.hs ================================================ alpha :: forall x . (a -> x) -> F x ================================================ FILE: src/content/2.5/code/haskell/snippet05.hs ================================================ alpha id :: F a ================================================ FILE: src/content/2.5/code/haskell/snippet06.hs ================================================ fa :: F a ================================================ FILE: src/content/2.5/code/haskell/snippet07.hs ================================================ alpha h = fmap h fa ================================================ FILE: src/content/2.5/code/ocaml/snippet01.ml ================================================ type ('a, 'x) reader = 'a -> 'x ================================================ FILE: src/content/2.5/code/ocaml/snippet02.ml ================================================ module ReaderFunctor (T : sig type a end) : Functor = struct type 'x t = (T.a, 'x) reader let fmap : ('x -> 'y) -> 'x t -> 'y t = fun f r a -> f (r a) end ================================================ FILE: src/content/2.5/code/ocaml/snippet03.ml ================================================ module type NT_AX_FX = sig type a type 'x t val alpha : (a -> 'x) -> 'x t end ================================================ FILE: src/content/2.5/code/ocaml/snippet04.ml ================================================ val alpha : (a -> 'x) -> 'x t ================================================ FILE: src/content/2.5/code/ocaml/snippet05.ml ================================================ alpha id : 'a f ================================================ FILE: src/content/2.5/code/ocaml/snippet06.ml ================================================ val fa : 'a f ================================================ FILE: src/content/2.5/code/ocaml/snippet07.ml ================================================ alpha h = F.fmap h fa ================================================ FILE: src/content/2.5/code/reason/snippet01.re ================================================ type reader('a, 'x) = 'a => 'x; ================================================ FILE: src/content/2.5/code/reason/snippet02.re ================================================ module ReaderFunctor = (T: {type a;}) : Functor => { type t('x) = reader(T.a, 'x); let fmap: ('x => 'y, t('x)) => t('y) = ( (f, r, a) => f(r(a)): ('x => 'y, t('x)) => t('y) ); }; ================================================ FILE: src/content/2.5/code/reason/snippet03.re ================================================ module type NT_AX_FX = { type a; type t('x); let alpha: (a => 'x) => t('x); }; ================================================ FILE: src/content/2.5/code/reason/snippet04.re ================================================ let alpha: (a => 'x) => t('x) ================================================ FILE: src/content/2.5/code/reason/snippet05.re ================================================ alpha(id) : f('a) ================================================ FILE: src/content/2.5/code/reason/snippet06.re ================================================ let fa: f('a) ================================================ FILE: src/content/2.5/code/reason/snippet07.re ================================================ alpha(h) == F.fmap(h, fa); ================================================ FILE: src/content/2.5/code/scala/snippet01.scala ================================================ type Reader[A, X] = A => X ================================================ FILE: src/content/2.5/code/scala/snippet02.scala ================================================ implicit def readerFunctor[A] = new Functor[Reader[A, ?]] { def fmap[X, B](f: X => B)(h: Reader[A, X]): Reader[A, B] = f compose h } ================================================ FILE: src/content/2.5/code/scala/snippet03.scala ================================================ // In order to make a universal transformation, // another type needs to be introduced. // To read more about FunctionK (~>): // typelevel.org/cats/datatypes/functionk.html trait ~>[F[_], G[_]] { def apply[B](fa: F[B]): G[B] } // Kind Projector plugin provides // a more concise syntax for type lambdas: def alpha[A]: (A => ?) ~> F ================================================ FILE: src/content/2.5/code/scala/snippet04.scala ================================================ def alpha[A]: (A => ?) ~> F ================================================ FILE: src/content/2.5/code/scala/snippet05.scala ================================================ alpha(identity): F[A] ================================================ FILE: src/content/2.5/code/scala/snippet06.scala ================================================ def fa[A]: F[A] ================================================ FILE: src/content/2.5/code/scala/snippet07.scala ================================================ alpha(h) == fmap(h)(fa) ================================================ FILE: src/content/2.5/the-yoneda-lemma.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{M}{ost constructions in} category theory are generalizations of results from other more specific areas of mathematics. Things like products, coproducts, monoids, exponentials, etc., have been known long before category theory. They might have been known under different names in different branches of mathematics. A Cartesian product in set theory, a meet in order theory, a conjunction in logic --- they are all specific examples of the abstract idea of a categorical product. The Yoneda lemma stands out in this respect as a sweeping statement about categories in general with little or no precedent in other branches of mathematics. Some say that its closest analog is Cayley's theorem in group theory (every group is isomorphic to a permutation group of some set). The setting for the Yoneda lemma is an arbitrary category $\cat{C}$ together with a functor $F$ from $\cat{C}$ to $\Set$. We've seen in the previous section that some $\Set$-valued functors are representable, that is isomorphic to a hom-functor. The Yoneda lemma tells us that all $\Set$-valued functors can be obtained from hom-functors through natural transformations, and it explicitly enumerates all such transformations. When I talked about natural transformations, I mentioned that the naturality condition can be quite restrictive. When you define a component of a natural transformation at one object, naturality may be strong enough to ``transport'' this component to another object that is connected to it through a morphism. The more arrows between objects in the source and the target categories there are, the more constraints you have for transporting the components of natural transformations. $\Set$ happens to be a very arrow-rich category. The Yoneda lemma tells us that a natural transformation between a hom-functor and any other functor $F$ is completely determined by specifying the value of its single component at just one point! The rest of the natural transformation just follows from naturality conditions. So let's review the naturality condition between the two functors involved in the Yoneda lemma. The first functor is the hom-functor. It maps any object $x$ in $\cat{C}$ to the set of morphisms $\cat{C}(a, x)$ --- for $a$ a fixed object in $\cat{C}$. We've also seen that it maps any morphism $f$ from $x \to y$ to $\cat{C}(a, f)$. The second functor is an arbitrary $\Set$-valued functor $F$. Let's call the natural transformation between these two functors $\alpha$. Because we are operating in $\Set$, the components of the natural transformation, like $\alpha_x$ or $\alpha_y$, are just regular functions between sets: \begin{gather*} \alpha_x \Colon \cat{C}(a, x) \to F x \\ \alpha_y \Colon \cat{C}(a, y) \to F y \end{gather*} \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/yoneda1.png} \end{figure} \noindent And because these are just functions, we can look at their values at specific points. But what's a point in the set $\cat{C}(a, x)$? Here's the key observation: Every point in the set $\cat{C}(a, x)$ is also a morphism $h$ from $a$ to $x$. So the naturality square for $\alpha$: \[\alpha_y \circ \cat{C}(a, f) = F f \circ \alpha_x\] becomes, point-wise, when acting on $h$: \[\alpha_y (\cat{C}(a, f) h) = (F f) (\alpha_x h)\] You might recall from the previous section that the action of the hom-functor $\cat{C}(a,-)$ on a morphism $f$ was defined as precomposition: \[\cat{C}(a, f) h = f \circ h\] which leads to: \[\alpha_y (f \circ h) = (F f) (\alpha_x h)\] Just how strong this condition is can be seen by specializing it to the case of $x = a$. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/yoneda2.png} \end{figure} \noindent In that case $h$ becomes a morphism from $a$ to $a$. We know that there is at least one such morphism, $h = \id_a$. Let's plug it in: \[\alpha_y f = (F f) (\alpha_a \id_a)\] Notice what has just happened: The left hand side is the action of $\alpha_y$ on an arbitrary element $f$ of $\cat{C}(a, y)$. And it is totally determined by the single value of $\alpha_a$ at $\id_a$. We can pick any such value and it will generate a natural transformation. Since the values of $\alpha_a$ are in the set $F a$, any point in $F a$ will define some $\alpha$. Conversely, given any natural transformation $\alpha$ from $\cat{C}(a, -)$ to $F$, you can evaluate it at $\id_a$ to get a point in $F a$. We have just proven the Yoneda lemma: \begin{quote} There is a one-to-one correspondence between natural transformations from $\cat{C}(a, -)$ to $F$ and elements of $F a$. \end{quote} in other words, \[\mathit{Nat}(\cat{C}(a, -), F) \cong F a\] Or, if we use the notation $[\cat{C}, \Set]$ for the functor category between $\cat{C}$ and $\Set$, the set of natural transformation is just a hom-set in that category, and we can write: \[[\cat{C}, \Set](\cat{C}(a, -), F) \cong F a\] I'll explain later how this correspondence is in fact a natural isomorphism. Now let's try to get some intuition about this result. The most amazing thing is that the whole natural transformation crystallizes from just one nucleation site: the value we assign to it at $\id_a$. It spreads from that point following the naturality condition. It floods the image of $\cat{C}$ in $\Set$. So let's first consider what the image of $\cat{C}$ is under $\cat{C}(a, -)$. Let's start with the image of $a$ itself. Under the hom-functor $\cat{C}(a, -)$, $a$ is mapped to the set $\cat{C}(a, a)$. Under the functor $F$, on the other hand, it is mapped to the set $F a$. The component of the natural transformation $\alpha_a$ is some function from $\cat{C}(a, a)$ to $F a$. Let's focus on just one point in the set $\cat{C}(a, a)$, the point corresponding to the morphism $\id_a$. To emphasize the fact that it's just a point in a set, let's call it $p$. The component $\alpha_a$ should map $p$ to some point $q$ in $F a$. I'll show you that any choice of $q$ leads to a unique natural transformation. \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/yoneda3.png} \end{figure} \noindent The first claim is that the choice of one point $q$ uniquely determines the rest of the function $\alpha_a$. Indeed, let's pick any other point, $p'$ in $\cat{C}(a, a)$, corresponding to some morphism $g$ from $a$ to $a$. And here's where the magic of the Yoneda lemma happens: $g$ can be viewed as a point $p'$ in the set $\cat{C}(a, a)$. At the same time, it selects two \emph{functions} between sets. Indeed, under the hom-functor, the morphism $g$ is mapped to a function $\cat{C}(a, g)$; and under $F$ it's mapped to $F g$. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/yoneda4.png} \end{figure} \noindent Now let's consider the action of $\cat{C}(a, g)$ on our original $p$ which, as you remember, corresponds to $\id_a$. It is defined as precomposition, $g \circ \id_a$, which is equal to $g$, which corresponds to our point $p'$. So the morphism $g$ is mapped to a function that, when acting on $p$ produces $p'$, which is $g$. We have come full circle! Now consider the action of $F g$ on $q$. It is some $q'$, a point in $F a$. To complete the naturality square, $p'$ must be mapped to $q'$ under $\alpha_a$. We picked an arbitrary $p'$ (an arbitrary $g$) and derived its mapping under $\alpha_a$. The function $\alpha_a$ is thus completely determined. The second claim is that $\alpha_x$ is uniquely determined for any object $x$ in $\cat{C}$ that is connected to $a$. The reasoning is analogous, except that now we have two more sets, $\cat{C}(a, x)$ and $F x$, and the morphism $g$ from $a$ to $x$ is mapped, under the hom-functor, to: \[\cat{C}(a, g) \Colon \cat{C}(a, a) \to \cat{C}(a, x)\] and under $F$ to: \[F g \Colon F a \to F x\] Again, $\cat{C}(a, g)$ acting on our $p$ is given by the precomposition: $g \circ \id_a$, which corresponds to a point $p'$ in $\cat{C}(a, x)$. Naturality determines the value of $\alpha_x$ acting on $p'$ to be: \[q' = (F g) q\] Since $p'$ was arbitrary, the whole function $\alpha_x$ is thus determined. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/yoneda5.png} \end{figure} \noindent What if there are objects in $\cat{C}$ that have no connection to $a$? They are all mapped under $\cat{C}(a, -)$ to a single set --- the empty set. Recall that the empty set is the initial object in the category of sets. It means that there is a unique function from this set to any other set. We called this function \code{absurd}. So here, again, we have no choice for the component of the natural transformation: it can only be \code{absurd}. One way of understanding the Yoneda lemma is to realize that natural transformations between $\Set$-valued functors are just families of functions, and functions are in general lossy. A function may collapse information and it may cover only parts of its codomain. The only functions that are not lossy are the ones that are invertible --- the isomorphisms. It follows then that the best structure-preserving $\Set$-valued functors are the representable ones. They are either the hom-functors or the functors that are naturally isomorphic to hom-functors. Any other functor $F$ is obtained from a hom-functor through a lossy transformation. Such a transformation may not only lose information, but it may also cover only a small part of the image of the functor $F$ in $\Set$. \section{Yoneda in Haskell} We have already encountered the hom-functor in Haskell under the guise of the reader functor: \src{snippet01} The reader maps morphisms (here, functions) by precomposition: \src{snippet02} The Yoneda lemma tells us that the reader functor can be naturally mapped to any other functor. A natural transformation is a polymorphic function. So given a functor \code{F}, we have a mapping to it from the reader functor: \src{snippet03} As usual, \code{forall} is optional, but I like to write it explicitly to emphasize parametric polymorphism of natural transformations. The Yoneda lemma tells us that these natural transformations are in one-to-one correspondence with the elements of \code{F a}: \begin{snipv} forall x . (a -> x) -> F x \ensuremath{\cong} F a \end{snipv} The right hand side of this identity is what we would normally consider a data structure. Remember the interpretation of functors as generalized containers? \code{F a} is a container of \code{a}. But the left hand side is a polymorphic function that takes a function as an argument. The Yoneda lemma tells us that the two representations are equivalent --- they contain the same information. Another way of saying this is: Give me a polymorphic function of the type: \src{snippet04} and I'll produce a container of \code{a}. The trick is the one we used in the proof of the Yoneda lemma: we call this function with \code{id} to get an element of \code{F a}: \src{snippet05} The converse is also true: Given a value of the type \code{F a}: \src{snippet06} one can define a polymorphic function: \src{snippet07} of the correct type. You can easily go back and forth between the two representations. The advantage of having multiple representations is that one might be easier to compose than the other, or that one might be more efficient in some applications than the other. The simplest illustration of this principle is the code transformation that is often used in compiler construction: the continuation passing style or \acronym{CPS}. It's the simplest application of the Yoneda lemma to the identity functor. Replacing \code{F} with identity produces: \begin{snipv} forall r . (a -> r) -> r \ensuremath{\cong} a \end{snipv} The interpretation of this formula is that any type \code{a} can be replaced by a function that takes a ``handler'' for \code{a}. A handler is a function accepting \code{a} and performing the rest of the computation --- the continuation. (The type \code{r} usually encapsulates some kind of status code.) This style of programming is very common in UIs, in asynchronous systems, and in concurrent programming. The drawback of \acronym{CPS} is that it involves inversion of control. The code is split between producers and consumers (handlers), and is not easily composable. Anybody who's done any amount of nontrivial web programming is familiar with the nightmare of spaghetti code from interacting stateful handlers. As we'll see later, judicious use of functors and monads can restore some compositional properties of \acronym{CPS}. \section{Co-Yoneda} As usual, we get a bonus construction by inverting the direction of arrows. The Yoneda lemma can be applied to the opposite category $\cat{C}^\mathit{op}$ to give us a mapping between contravariant functors. Equivalently, we can derive the co-Yoneda lemma by fixing the target object of our hom-functors instead of the source. We get the contravariant hom-functor from $\cat{C}$ to $\Set$: $\cat{C}(-, a)$. The contravariant version of the Yoneda lemma establishes one-to-one correspondence between natural transformations from this functor to any other contravariant functor $F$ and the elements of the set $F a$: \[\mathit{Nat}(\cat{C}(-, a), F) \cong F a\] Here's the Haskell version of the co-Yoneda lemma: \begin{snipv} forall x . (x -> a) -> F x \ensuremath{\cong} F a \end{snipv} Notice that in some literature it's the contravariant version that's called the Yoneda lemma. \section{Challenges} \begin{enumerate} \tightlist \item Show that the two functions \code{phi} and \code{psi} that form the Yoneda isomorphism in Haskell are inverses of each other. \begin{snip}{haskell} phi :: (forall x . (a -> x) -> F x) -> F a phi alpha = alpha id psi :: F a -> (forall x . (a -> x) -> F x) psi fa h = fmap h fa \end{snip} \item A discrete category is one that has objects but no morphisms other than identity morphisms. How does the Yoneda lemma work for functors from such a category? \item A list of units \code{{[}(){]}} contains no other information but its length. So, as a data type, it can be considered an encoding of integers. An empty list encodes zero, a singleton \code{{[}(){]}} (a value, not a type) encodes one, and so on. Construct another representation of this data type using the Yoneda lemma for the list functor. \end{enumerate} \section{Bibliography} \begin{enumerate} \tightlist \item \urlref{https://www.youtube.com/watch?v=TLMxHB19khE}{Catsters} video. \end{enumerate} ================================================ FILE: src/content/2.6/code/haskell/snippet01.hs ================================================ fromY :: (a -> x) -> b -> x fromY f b = f (btoa b) ================================================ FILE: src/content/2.6/code/haskell/snippet02.hs ================================================ fromY id :: b -> a ================================================ FILE: src/content/2.6/code/ocaml/snippet01.ml ================================================ module type BtoA = sig type a type b val btoa : b -> a end (* Define the Yoneda embedding *) module Yoneda_Embedding (E : BtoA) = struct let fromY : 'x. (E.a -> 'x) -> E.b -> 'x = fun f b -> f (E.btoa b) end ================================================ FILE: src/content/2.6/code/ocaml/snippet02.ml ================================================ module YE = Yoneda_Embedding(BtoAImpl) YE.fromY id (* output type : BtoA.b -> BtoA.a *) ================================================ FILE: src/content/2.6/code/reason/snippet01.re ================================================ module type BtoA = { type a; type b; let btoa: b => a; }; /* Define the Yoneda embedding */ module Yoneda_Embedding = (E: BtoA) => { let fromY: 'x. (E.a => 'x, E.b) => 'x = (f, b) => f(E.btoa(b)); }; ================================================ FILE: src/content/2.6/code/reason/snippet02.re ================================================ module YE = Yoneda_Embedding(BtoAImpl); YE.fromY(id) /* output type : BtoA.b => BtoA.a */ ================================================ FILE: src/content/2.6/code/scala/snippet01.scala ================================================ // In order to make a universal transformation, // another type needs to be introduced. // To read more about FunctionK (~>): // typelevel.org/cats/datatypes/functionk.html trait ~>[F[_], G[_]] { def apply[X](fa: F[X]): G[X] } def fromY[A, B]: (A => ?) ~> (B => ?) = new ~>[A => ?, B => ?] { def apply[X](f: A => X): B => X = b => f(btoa(b)) } ================================================ FILE: src/content/2.6/code/scala/snippet02.scala ================================================ fromY(identity): B => A ================================================ FILE: src/content/2.6/yoneda-embedding.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{W}{e've seen previously} that, when we fix an object $a$ in the category $\cat{C}$, the mapping $\cat{C}(a, -)$ is a (covariant) functor from $\cat{C}$ to $\Set$. \[x \to \cat{C}(a, x)\] (The codomain is $\Set$ because the hom-set $\cat{C}(a, x)$ is a \emph{set}.) We call this mapping a hom-functor --- we have previously defined its action on morphisms as well. Now let's vary $a$ in this mapping. We get a new mapping that assigns the hom-\emph{functor} $\cat{C}(a, -)$ to any $a$. \[a \to \cat{C}(a, -)\] It's a mapping of objects from category $\cat{C}$ to functors, which are \emph{objects} in the functor category (see the section about functor categories in \hyperref[natural-transformations]{Natural Transformations}). Let's use the notation $[\cat{C}, \Set]$ for the functor category from $\cat{C}$ to $\Set$. You may also recall that hom-functors are the prototypical \hyperref[representable-functors]{representable functors}. Every time we have a mapping of objects between two categories, it's natural to ask if such a mapping is also a functor. In other words whether we can lift a morphism from one category to a morphism in the other category. A morphism in $\cat{C}$ is just an element of $\cat{C}(a, b)$, but a morphism in the functor category $[\cat{C}, \Set]$ is a natural transformation. So we are looking for a mapping of morphisms to natural transformations. Let's see if we can find a natural transformation corresponding to a morphism $f \Colon a \to b$. First, lets see what $a$ and $b$ are mapped to. They are mapped to two functors: $\cat{C}(a, -)$ and $\cat{C}(b, -)$. We need a natural transformation between those two functors. And here's the trick: we use the Yoneda lemma: \[[\cat{C}, \Set](\cat{C}(a, -), F) \cong F a\] and replace the generic $F$ with the hom-functor $\cat{C}(b, -)$. We get: \[[\cat{C}, \Set](\cat{C}(a, -), \cat{C}(b, -)) \cong \cat{C}(b, a)\] \begin{figure}[H] \centering \includegraphics[width=0.6\textwidth]{images/yoneda-embedding.jpg} \end{figure} \noindent This is exactly the natural transformation between the two hom-functors we were looking for, but with a little twist: We have a mapping between a natural transformation and a morphism --- an element of $\cat{C}(b, a)$ --- that goes in the ``wrong'' direction. But that's okay; it only means that the functor we are looking at is contravariant. \begin{figure}[H] \centering \includegraphics[width=0.65\textwidth]{images/yoneda-embedding-2.jpg} \end{figure} \noindent Actually, we've got even more than we bargained for. The mapping from $\cat{C}$ to $[\cat{C}, \Set]$ is not only a contravariant functor --- it is a \emph{fully faithful} functor. Fullness and faithfulness are properties of functors that describe how they map hom-sets. A \emph{faithful} functor is \newterm{injective} on hom-sets, meaning that it maps distinct morphisms to distinct morphisms. In other words, it doesn't coalesce them. A \emph{full} functor is \newterm{surjective} on hom-sets, meaning that it maps one hom-set \emph{onto} the other hom-set, fully covering the latter. A fully faithful functor $F$ is a \newterm{bijection} on hom-sets --- a one to one matching of all elements of both sets. For every pair of objects $a$ and $b$ in the source category $\cat{C}$ there is a bijection between $\cat{C}(a, b)$ and $\cat{D}(F a, F b)$, where $\cat{D}$ is the target category of $F$ (in our case, the functor category, $[\cat{C}, \Set]$). Notice that this doesn't mean that $F$ is a bijection on \emph{objects}. There may be objects in $\cat{D}$ that are not in the image of $F$, and we can't say anything about hom-sets for those objects. \section{The Embedding} The (contravariant) functor we have just described, the functor that maps objects in $\cat{C}$ to functors in $[\cat{C}, \Set]$: \[a \to \cat{C}(a, -)\] defines the \newterm{Yoneda embedding}. It \emph{embeds} a category $\cat{C}$ (strictly speaking, the category $\cat{C}^\mathit{op}$, because of contravariance) inside the functor category $[\cat{C}, \Set]$. It not only maps objects in $\cat{C}$ to functors, but also faithfully preserves all connections between them. This is a very useful result because mathematicians know a lot about the category of functors, especially functors whose codomain is $\Set$. We can get a lot of insight about an arbitrary category $\cat{C}$ by embedding it in the functor category. Of course there is a dual version of the Yoneda embedding, sometimes called the co-Yoneda embedding. Observe that we could have started by fixing the target object (rather than the source object) of each hom-set, $\cat{C}(-, a)$. That would give us a contravariant hom-functor. Contravariant functors from $\cat{C}$ to $\Set$ are our familiar presheaves (see, for instance, \hyperref[limits-and-colimits]{Limits and Colimits}). The co-Yoneda embedding defines the embedding of a category $\cat{C}$ in the category of presheaves. Its action on morphisms is given by: \[[\cat{C}^\mathit{op}, \Set](\cat{C}(-, a), \cat{C}(-, b)) \cong \cat{C}(a, b)\] Again, mathematicians know a lot about the category of presheaves, so being able to embed an arbitrary category in it is a big win. \section{Application to Haskell} In Haskell, the Yoneda embedding can be represented as the isomorphism between natural transformations amongst reader functors on the one hand, and functions (going in the opposite direction) on the other hand: \begin{snipv} forall x. (a -> x) -> (b -> x) \ensuremath{\cong} b -> a \end{snipv} (Remember, the reader functor is equivalent to \code{((->) a)}.) The left hand side of this identity is a polymorphic function that, given a function from \code{a} to \code{x} and a value of type \code{b}, can produce a value of type \code{x} (I'm uncurrying --- dropping the parentheses around --- the function \code{b -> x}). The only way this can be done for all \code{x} is if our function knows how to convert a \code{b} to an \code{a}. It has to secretly have access to a function \code{b -> a}. Given such a converter, \code{btoa}, one can define the left hand side, call it \code{fromY}, as: \src{snippet01} Conversely, given a function \code{fromY} we can recover the converter by calling \code{fromY} with the identity: \src{snippet02} This establishes the bijection between functions of the type \code{fromY} and \code{btoa}. An alternative way of looking at this isomorphism is that it's a \acronym{CPS} encoding of a function from \code{b} to \code{a}. The argument \code{a -> x} is a continuation (the handler). The result is a function from \code{b} to \code{x} which, when called with a value of type \code{b}, will execute the continuation precomposed with the function being encoded. The Yoneda embedding also explains some of the alternative representations of data structures in Haskell. In particular, it provides a \urlref{https://bartoszmilewski.com/2015/07/13/from-lenses-to-yoneda-embedding/} {very useful representation} of lenses from the \code{Control.Lens} library. \section{Preorder Example} This example was suggested by Robert Harper. It's the application of the Yoneda embedding to a category defined by a preorder. A preorder is a set with an ordering relation between its elements that's traditionally written as $\leqslant$ (less than or equal). The ``pre'' in preorder is there because we're only requiring the relation to be transitive and reflexive but not necessarily antisymmetric (so it's possible to have cycles). A set with the preorder relation gives rise to a category. The objects are the elements of this set. A morphism from object $a$ to $b$ either doesn't exist, if the objects cannot be compared or if it's not true that $a \leqslant b$; or it exists if $a \leqslant b$, and it points from $a$ to $b$. There is never more than one morphism from one object to another. Therefore any hom-set in such a category is either an empty set or a one-element set. Such a category is called \emph{thin}. It's easy to convince yourself that this construction is indeed a category: The arrows are composable because, if $a \leqslant b$ and $b \leqslant c$ then $a \leqslant c$; and the composition is associative. We also have the identity arrows because every element is (less than or) equal to itself (reflexivity of the underlying relation). We can now apply the co-Yoneda embedding to a preorder category. In particular, we're interested in its action on morphisms: \[[\cat{C}, \Set](\cat{C}(-, a), \cat{C}(-, b)) \cong \cat{C}(a, b)\] The hom-set on the right hand side is non-empty if and only if $a \leqslant b$ --- in which case it's a one-element set. Consequently, if $a \leqslant b$, there exists a single natural transformation on the left. Otherwise there is no natural transformation. So what's a natural transformation between hom-functors in a preorder? It should be a family of functions between sets $\cat{C}(-, a)$ and $\cat{C}(-, b)$. In a preorder, each of these sets can either be empty or a singleton. Let's see what kind of functions are there at our disposal. There is a function from an empty set to itself (the identity acting on an empty set), a function \code{absurd} from an empty set to a singleton set (it does nothing, since it only needs to be defined for elements of an empty set, of which there are none), and a function from a singleton to itself (the identity acting on a one-element set). The only combination that is forbidden is the mapping from a singleton to an empty set (what would the value of such a function be when acting on the single element?). So our natural transformation will never connect a singleton hom-set to an empty hom-set. In other words, if $x \leqslant a$ (singleton hom-set $\cat{C}(x, a)$) then $\cat{C}(x, b)$ cannot be empty. A non-empty $\cat{C}(x, b)$ means that $x$ is less or equal to $b$. So the existence of the natural transformation in question requires that, for every $x$, if $x \leqslant a$ then $x \leqslant b$. \[\text{for all } x, x \leqslant a \Rightarrow x \leqslant b\] On the other hand, co-Yoneda tells us that the existence of this natural transformation is equivalent to $\cat{C}(a, b)$ being non-empty, or to $a \leqslant b$. Together, we get: \[a \leqslant b \text{ if and only if for all } x, x \leqslant a \Rightarrow x \leqslant b\] We could have arrived at this result directly. The intuition is that, if $a \leqslant b$ then all elements that are below $a$ must also be below $b$. Conversely, when you substitute $a$ for $x$ on the right hand side, it follows that $a \leqslant b$. But you must admit that arriving at this result through the Yoneda embedding is much more exciting. \section{Naturality} The Yoneda lemma establishes the isomorphism between the set of natural transformations and an object in $\Set$. Natural transformations are morphisms in the functor category $[\cat{C}, \Set]$. The set of natural transformation between any two functors is a hom-set in that category. The Yoneda lemma is the isomorphism: \[[\cat{C}, \Set](\cat{C}(a, -), F) \cong F a\] This isomorphism turns out to be natural in both $F$ and $a$. In other words, it's natural in $(F, a)$, a pair taken from the product category $[\cat{C}, \Set] \times \cat{C}$. Notice that we are now treating $F$ as an \newterm{object} in the functor category. Let's think for a moment what this means. A natural isomorphism is an invertible \emph{natural transformation} between two functors. And indeed, the right hand side of our isomorphism is a functor. It's a functor from $[\cat{C}, \Set] \times \cat{C}$ to $\Set$. Its action on a pair $(F, a)$ is a set --- the result of evaluating the functor $F$ at the object $a$. This is called the evaluation functor. The left hand side is also a functor that takes $(F, a)$ to a set of natural transformations $[\cat{C}, \Set](\cat{C}(a, -), F)$. To show that these are really functors, we should also define their action on morphisms. But what's a morphism between a pair $(F, a)$ and $(G, b)$? It's a pair of morphisms, $(\Phi, f)$; the first being a morphism between functors --- a natural transformation --- the second being a regular morphism in $\cat{C}$. The evaluation functor takes this pair $(\Phi, f)$ and maps it to a function between two sets, $F a$ and $G b$. We can easily construct such a function from the component of $\Phi$ at $a$ (which maps $F a$ to $G a$) and the morphism $f$ lifted by $G$: \[(G f) \circ \Phi_a\] Notice that, because of naturality of $\Phi$, this is the same as: \[\Phi_b \circ (F f)\] I'm not going to prove the naturality of the whole isomorphism --- after you've established what the functors are, the proof is pretty mechanical. It follows from the fact that our isomorphism is built up from functors and natural transformations. There is simply no way for it to go wrong. \section{Challenges} \begin{enumerate} \tightlist \item Express the co-Yoneda embedding in Haskell. \item Show that the bijection we established between \code{fromY} and \code{btoa} is an isomorphism (the two mappings are the inverse of each other). \item Work out the Yoneda embedding for a monoid. What functor corresponds to the monoid's single object? What natural transformations correspond to monoid morphisms? \item What is the application of the \emph{covariant} Yoneda embedding to preorders? (Question suggested by Gershom Bazerman.) \item Yoneda embedding can be used to embed an arbitrary functor category $[\cat{C}, \cat{D}]$ in the functor category $[[\cat{C}, \cat{D}], \Set]$. Figure out how it works on morphisms (which in this case are natural transformations). \end{enumerate} ================================================ FILE: src/content/3.1/its-all-about-morphisms.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{I}{f I haven't} convinced you yet that category theory is all about morphisms then I haven't done my job properly. Since the next topic is adjunctions, which are defined in terms of isomorphisms of hom-sets, it makes sense to review our intuitions about the building blocks of hom-sets. Also, you'll see that adjunctions provide a more general language to describe a lot of constructions we've studied before, so it might help to review them too. \section{Functors} To begin with, you should really think of functors as mappings of morphisms --- the view that's emphasized in the Haskell definition of the \code{Functor} typeclass, which revolves around \code{fmap}. Of course, functors also map objects --- the endpoints of morphisms --- otherwise we wouldn't be able to talk about preserving composition. Objects tell us which pairs of morphisms are composable. The target of one morphism must be equal to the source of the other --- if they are to be composed. So if we want the composition of morphisms to be mapped to the composition of \newterm{lifted} morphisms, the mapping of their endpoints is pretty much determined. \section{Commuting Diagrams} A lot of properties of morphisms are expressed in terms of commuting diagrams. If a particular morphism can be described as a composition of other morphisms in more than one way, then we have a commuting diagram. In particular, commuting diagrams form the basis of almost all universal constructions (with the notable exceptions of the initial and terminal objects). We've seen this in the definitions of products, coproducts, various other (co-)limits, exponential objects, free monoids, etc. The product is a simple example of a universal construction. We pick two objects $a$ and $b$ and see if there exists an object $c$, together with a pair of morphisms $p$ and $q$, that has the universal property of being their product. \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/productranking.jpg} \end{figure} \noindent A product is a special case of a limit. A limit is defined in terms of cones. A general cone is built from commuting diagrams. Commutativity of those diagrams may be replaced with a suitable naturality condition for the mapping of functors. This way commutativity is reduced to the role of the assembly language for the higher level language of natural transformations. \section{Natural Transformations} In general, natural transformations are very convenient whenever we need a mapping from morphisms to commuting squares. Two opposing sides of a naturality square are the mappings of some morphism $f$ under two functors $F$ and $G$. The other sides are the components of the natural transformation (which are also morphisms). \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/3_naturality.jpg} \end{figure} \noindent Naturality means that when you move to the ``neighboring'' component (by neighboring I mean connected by a morphism), you're not going against the structure of either the category or the functors. It doesn't matter whether you first use a component of the natural transformation to bridge the gap between objects, and then jump to its neighbor using the functor; or the other way around. The two directions are orthogonal. A natural transformation moves you left and right, and the functors move you up and down or back and forth --- so to speak. You can visualize the \emph{image} of a functor as a sheet in the target category. A natural transformation maps one such sheet corresponding to $F$, to another, corresponding to $G$. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/sheets.png} \end{figure} \noindent We've seen examples of this orthogonality in Haskell. There the action of a functor modifies the content of a container without changing its shape, while a natural transformation repackages the untouched contents into a different container. The order of these operations doesn't matter. We've seen the cones in the definition of a limit replaced by natural transformations. Naturality ensures that the sides of every cone commute. Still, a limit is defined in terms of mappings \emph{between} cones. These mappings must also satisfy commutativity conditions. (For instance, the triangles in the definition of the product must commute.) These conditions, too, may be replaced by naturality. You may recall that the \emph{universal} cone, or the limit, is defined as a natural transformation between the (contravariant) hom-functor: \[F \Colon c \to \cat{C}(c, \Lim[D])\] and the (also contravariant) functor that maps objects in $\cat{C}$ to cones, which themselves are natural transformations: \[G \Colon c \to \mathit{Nat}(\Delta_c, D)\] Here, $\Delta_c$ is the constant functor, and $D$ is the functor that defines the diagram in $\cat{C}$. Both functors $F$ and $G$ have well defined actions on morphisms in $\cat{C}$. It so happens that this particular natural transformation between $F$ and $G$ is an \emph{isomorphism}. \section{Natural Isomorphisms} A natural isomorphism --- which is a natural transformation whose every component is reversible --- is category theory's way of saying that ``two things are the same.'' A component of such a transformation must be an isomorphism between objects --- a morphism that has the inverse. If you visualize functor images as sheets, a natural isomorphism is a one-to-one invertible mapping between those sheets. \section{Hom-Sets} But what are morphisms? They do have more structure than objects: unlike objects, morphisms have two ends. But if you fix the source and the target objects, the morphisms between the two form a boring set (at least for locally small categories). We can give elements of this set names like $f$ or $g$, to distinguish one from another --- but what is it, really, that makes them different? The essential difference between morphisms in a given hom-set lies in the way they compose with other morphisms (from abutting hom-sets). If there is a morphism $h$ whose composition (either pre- or post-) with $f$ is different than that with $g$, for instance: \[h \circ f \neq h \circ g\] then we can directly ``observe'' the difference between $f$ and $g$. But even if the difference is not directly observable, we might use functors to zoom in on the hom-set. A functor $F$ may map the two morphisms to distinct morphisms: \[F f \neq F g\] in a richer category, where the abutting hom-sets provide more resolution, e.g., \[h' \circ F f \neq h' \circ F g\] where $h'$ is not in the image of $F$. \section{Hom-Set Isomorphisms} A lot of categorical constructions rely on isomorphisms between hom-sets. But since hom-sets are just sets, a plain isomorphism between them doesn't tell you much. For finite sets, an isomorphism just says that they have the same number of elements. If the sets are infinite, their cardinality must be the same. But any meaningful isomorphism of hom-sets must take into account composition. And composition involves more than one hom-set. We need to define isomorphisms that span whole collections of hom-sets, and we need to impose some compatibility conditions that interoperate with composition. And a \newterm{natural} isomorphism fits the bill exactly. But what's a natural isomorphism of hom-sets? Naturality is a property of mappings between functors, not sets. So we are really talking about a natural isomorphism between hom-set-valued functors. These functors are more than just set-valued functors. Their action on morphisms is induced by the appropriate hom-functors. Morphisms are canonically mapped by hom-functors using either pre- or post-composition (depending on the covariance of the functor). The Yoneda embedding is one example of such an isomorphism. It maps hom-sets in $\cat{C}$ to hom-sets in the functor category; and it's natural. One functor in the Yoneda embedding is the hom-functor in $\cat{C}$ and the other maps objects to sets of natural transformations between hom-sets. The definition of a limit is also a natural isomorphism between hom-sets (the second one, again, in the functor category): \[\cat{C}(c, \Lim[D]) \simeq \mathit{Nat}(\Delta_c, D)\] It turns out that our construction of an exponential object, or that of a free monoid, can also be rewritten as a natural isomorphism between hom-sets. This is no coincidence --- we'll see next that these are just different examples of adjunctions, which are defined as natural isomorphisms of hom-sets. \section{Asymmetry of Hom-Sets} There is one more observation that will help us understand adjunctions. Hom-sets are, in general, not symmetric. A hom-set $\cat{C}(a, b)$ is often very different from the hom-set $\cat{C}(b, a)$. The ultimate demonstration of this asymmetry is a partial order viewed as a category. In a partial order, a morphism from $a$ to $b$ exists if and only if $a$ is less than or equal to $b$. If $a$ and $b$ are different, then there can be no morphism going the other way, from $b$ to $a$. So if the hom-set $\cat{C}(a, b)$ is non-empty, which in this case means it's a singleton set, then $\cat{C}(b, a)$ must be empty, unless $a = b$. The arrows in this category have a definite flow in one direction. A preorder, which is based on a relation that's not necessarily antisymmetric, is also ``mostly'' directional, except for occasional cycles. It's convenient to think of an arbitrary category as a generalization of a preorder. A preorder is a thin category --- all hom-sets are either singletons or empty. We can visualize a general category as a ``thick'' preorder. \section{Challenges} \begin{enumerate} \tightlist \item Consider some degenerate cases of a naturality condition and draw the appropriate diagrams. For instance, what happens if either functor $F$ or $G$ map both objects $a$ and $b$ (the ends of $f \Colon a \to b$) to the same object, e.g., $F a = F b$ or $G a = G b$? (Notice that you get a cone or a co-cone this way.) Then consider cases where either $F a = G a$ or $F b = G b$. Finally, what if you start with a morphism that loops on itself --- $f \Colon a \to a$? \end{enumerate} ================================================ FILE: src/content/3.10/code/haskell/snippet01.hs ================================================ class Profunctor p where dimap :: (c -> a) -> (b -> d) -> p a b -> p c d ================================================ FILE: src/content/3.10/code/haskell/snippet02.hs ================================================ dimap f id pbb :: p a b ================================================ FILE: src/content/3.10/code/haskell/snippet03.hs ================================================ dimap id f paa :: p a b ================================================ FILE: src/content/3.10/code/haskell/snippet04.hs ================================================ dimap id f . alpha = dimap f id . alpha ================================================ FILE: src/content/3.10/code/haskell/snippet05.hs ================================================ forall a. p a a ================================================ FILE: src/content/3.10/code/haskell/snippet06.hs ================================================ dimap f id . pi = dimap id f . pi ================================================ FILE: src/content/3.10/code/haskell/snippet07.hs ================================================ Profunctor p => (forall c. p c c) -> p a b ================================================ FILE: src/content/3.10/code/haskell/snippet08.hs ================================================ pi :: Profunctor p => forall c. (forall a. p a a) -> p c c pi e = e ================================================ FILE: src/content/3.10/code/haskell/snippet09.hs ================================================ lambda :: Profunctor p => p a a -> (a -> b) -> p a b lambda paa f = dimap id f paa rho :: Profunctor p => p b b -> (a -> b) -> p a b rho pbb f = dimap f id pbb ================================================ FILE: src/content/3.10/code/haskell/snippet10.hs ================================================ type ProdP p = forall a b. (a -> b) -> p a b ================================================ FILE: src/content/3.10/code/haskell/snippet11.hs ================================================ newtype DiaProd p = DiaProd (forall a. p a a) ================================================ FILE: src/content/3.10/code/haskell/snippet12.hs ================================================ lambdaP :: Profunctor p => DiaProd p -> ProdP p lambdaP (DiaProd paa) = lambda paa rhoP :: Profunctor p => DiaProd p -> ProdP p rhoP (DiaProd pbb) = rho pbb ================================================ FILE: src/content/3.10/code/haskell/snippet13.hs ================================================ forall a. f a -> g a ================================================ FILE: src/content/3.10/code/haskell/snippet14.hs ================================================ data Coend p = forall a. Coend (p a a) ================================================ FILE: src/content/3.10/code/haskell/snippet15.hs ================================================ data SumP p = forall a b. SumP (b -> a) (p a b) ================================================ FILE: src/content/3.10/code/haskell/snippet16.hs ================================================ lambda, rho :: Profunctor p => SumP p -> DiagSum p lambda (SumP f pab) = DiagSum (dimap f id pab) rho (SumP f pab) = DiagSum (dimap id f pab) ================================================ FILE: src/content/3.10/code/haskell/snippet17.hs ================================================ data DiagSum p = forall a. DiagSum (p a a) ================================================ FILE: src/content/3.10/code/haskell/snippet18.hs ================================================ data Procompose q p a b where Procompose :: q a c -> p c b -> Procompose q p a b ================================================ FILE: src/content/3.10/code/ocaml/snippet01.ml ================================================ module type Functor = sig type 'a t val fmap : ('a -> 'b) -> 'a t -> 'b t end module type Profunctor = sig type ('a, 'b) p val dimap : ('c -> 'a) -> ('b -> 'd) -> ('a, 'b) p -> ('c, 'd) p end let id a = a ================================================ FILE: src/content/3.10/code/ocaml/snippet02.ml ================================================ let dimap f id (P (b, b)) : ('a, 'b) p ================================================ FILE: src/content/3.10/code/ocaml/snippet03.ml ================================================ let dimap id f (P (a, a)) : ('a, 'b) p ================================================ FILE: src/content/3.10/code/ocaml/snippet04.ml ================================================ (* There is no compose operator in OCaml *) ;; compose (dimap id f) alpha = compose (dimap f id) alpha ================================================ FILE: src/content/3.10/code/ocaml/snippet05.ml ================================================ 'a. ('a, 'a) p ================================================ FILE: src/content/3.10/code/ocaml/snippet06.ml ================================================ ;; compose (dimap f id) pi = compose (dimap id f) pi ================================================ FILE: src/content/3.10/code/ocaml/snippet07.ml ================================================ module type Polymorphic_Projection = functor (P : Profunctor) -> sig type rank2_p = { p : 'c. ('c, 'c) P.p } val pi : rank2_p -> ('a, 'b) P.p end ================================================ FILE: src/content/3.10/code/ocaml/snippet08.ml ================================================ module Pi (P : Profunctor) = struct type rank2_p = { p : 'a. ('a, 'a) P.p } let pi : rank2_p -> ('c, 'c) P.p = fun e -> e.p end ================================================ FILE: src/content/3.10/code/ocaml/snippet09.ml ================================================ module EndsEqualizer (P : Profunctor) = struct let lambda : ('a, 'a) P.p -> ('a -> 'b) -> ('a, 'b) P.p = fun paa f -> P.dimap id f paa ;; let rho : ('b, 'b) P.p -> ('a -> 'b) -> ('a, 'b) P.p = fun pbb f -> P.dimap f id pbb ;; end ================================================ FILE: src/content/3.10/code/ocaml/snippet10.ml ================================================ module type ProdP = sig type ('a, 'b) p type ('a, 'b) prod_p = ('a -> 'b) -> ('a, 'b) p end ================================================ FILE: src/content/3.10/code/ocaml/snippet11.ml ================================================ module type DiaProd = sig type ('a, 'b) p type 'a diaprod = DiaProd of ('a, 'a) p end ================================================ FILE: src/content/3.10/code/ocaml/snippet12.ml ================================================ module EndsWithDiaProd (P : Profunctor) (D : DiaProd with type ('a, 'b) p = ('a, 'b) P.p) (PP : ProdP with type ('a, 'b) p = ('a, 'b) P.p) = struct module E = EndsEqualizer (P) let lambdaP : 'a D.diaprod -> ('a, 'b) PP.prod_p = fun (DiaProd paa) -> E.lambda paa ;; let rhoP : 'b D.diaprod -> ('a, 'b) PP.prod_p = fun (DiaProd pbb) -> E.rho pbb ;; end ================================================ FILE: src/content/3.10/code/ocaml/snippet13.ml ================================================ (* Higher rank types can be introduced via records *) module NT_as_Ends (F : Functor) (G : Functor) = struct type set_of_nt = { nt : 'a. 'a F.t -> 'a G.t } end ================================================ FILE: src/content/3.10/code/ocaml/snippet14.ml ================================================ module Coend (P : Profunctor) = struct type coend = Coend of { c : 'a. ('a, 'a) P.p } end ================================================ FILE: src/content/3.10/code/ocaml/snippet15.ml ================================================ module type SumP = sig type a type b type ('a, 'b) p val f : b -> a val pab : (a, b) p end ================================================ FILE: src/content/3.10/code/ocaml/snippet16.ml ================================================ module type DiagSum = sig type a type ('a, 'b) p val paa : (a, a) p end module CoEndImpl (P : Profunctor) = struct type a type b module type Sum_P = SumP with type ('a, 'b) p = ('a, 'b) P.p and type a = a and type b = b let lambda (module S : Sum_P) = (module struct type a = S.b type ('a, 'b) p = ('a, 'b) P.p let paa = P.dimap S.f id S.pab end : DiagSum) ;; let rho (module S : Sum_P) = (module struct type a = S.a type ('a, 'b) p = ('a, 'b) P.p let paa = P.dimap id S.f S.pab end : DiagSum) ;; end ================================================ FILE: src/content/3.10/code/ocaml/snippet17.ml ================================================ module type DiagSum = sig type a type ('a, 'b) p val paa : (a, a) p end ================================================ FILE: src/content/3.10/code/ocaml/snippet18.ml ================================================ module type Procompose = sig type ('a, 'b) p type ('a, 'b) q type (_, _) procompose = | Procompose : (('a, 'c) q -> ('c, 'b) p) -> ('a, 'b) procompose end ================================================ FILE: src/content/3.10/code/reason/snippet01.re ================================================ module type Functor = { type t('a); let fmap: ('a => 'b, t('a)) => t('b); }; module type Profunctor = { type p('a, 'b); let dimap: ('c => 'a, 'b => 'd, p('a, 'b)) => p('c, 'd); }; let id = a => a; ================================================ FILE: src/content/3.10/code/reason/snippet02.re ================================================ let dimap(f, id, (P (b, b))) : p('a, 'b); ================================================ FILE: src/content/3.10/code/reason/snippet03.re ================================================ let dimap(id, f, (P (a, a))) : p('a, 'b); ================================================ FILE: src/content/3.10/code/reason/snippet04.re ================================================ /* There is no compose operator in ReasonML */ compose(dimap(id, f), alpha) == compose(dimap(f, id), alpha); ================================================ FILE: src/content/3.10/code/reason/snippet05.re ================================================ 'a. p('a, 'a); ================================================ FILE: src/content/3.10/code/reason/snippet06.re ================================================ compose(dimap(f, id), pi) == compose(dimap(id, f), pi); ================================================ FILE: src/content/3.10/code/reason/snippet07.re ================================================ module type Polymorphic_Projection = (P: Profunctor) => { type rank2_p = {p: 'c. P.p('c, 'c)}; let pi: rank2_p => P.p('a, 'b); }; ================================================ FILE: src/content/3.10/code/reason/snippet08.re ================================================ module Pi = (P: Profunctor) => { type rank2_p = {p: 'a. P.p('a, 'a)}; let pi: rank2_p => P.p('c, 'c) = (e => e.p: rank2_p => P.p('c, 'c)); }; ================================================ FILE: src/content/3.10/code/reason/snippet09.re ================================================ module EndsEqualizer = (P: Profunctor) => { let lambda: (P.p('a, 'a), 'a => 'b) => P.p('a, 'b) = ( (paa, f) => P.dimap(id, f, paa) ); let rho: (P.p('b, 'b), 'a => 'b) => P.p('a, 'b) = ( (pbb, f) => P.dimap(f, id, pbb) ); }; ================================================ FILE: src/content/3.10/code/reason/snippet10.re ================================================ module type ProdP = { type p('a, 'b); type prod_p('a, 'b) = ('a => 'b) => p('a, 'b); }; ================================================ FILE: src/content/3.10/code/reason/snippet11.re ================================================ module type DiaProd = { type p('a, 'b); type diaprod('a) = | DiaProd(p('a, 'a)); }; ================================================ FILE: src/content/3.10/code/reason/snippet12.re ================================================ module EndsWithDiaProd = ( P: Profunctor, D: DiaProd with type p('a, 'b) = P.p('a, 'b), PP: ProdP with type p('a, 'b) = P.p('a, 'b), ) => { module E = EndsEqualizer(P); let lambdaP: D.diaprod('a) => PP.prod_p('a, 'b) = ( (DiaProd(paa)) => E.lambda(paa) ); let rhoP: D.diaprod('b) => PP.prod_p('a, 'b) = ( (DiaProd(pbb)) => E.rho(pbb) ); }; ================================================ FILE: src/content/3.10/code/reason/snippet13.re ================================================ /* Higher rank types can be introduced via records */ module NT_as_Ends = (F: Functor, G: Functor) => { type set_of_nt = {nt: 'a. F.t('a) => G.t('a)}; }; ================================================ FILE: src/content/3.10/code/reason/snippet14.re ================================================ module Coend = (P: Profunctor) => { type coend = | Coend({c: 'a. P.p('a, 'a)}); }; ================================================ FILE: src/content/3.10/code/reason/snippet15.re ================================================ module type SumP = { type a; type b; type p('a, 'b); let f: b => a; let pab: p(a, b); }; ================================================ FILE: src/content/3.10/code/reason/snippet16.re ================================================ module type DiagSum = { type a; type p('a, 'b); let paa: p(a, a); }; module CoEndImpl = (P: Profunctor) => { type a; type b; module type Sum_P = SumP with type p('a, 'b) = P.p('a, 'b) and type a = a and type b = b; let lambda = (module S: Sum_P): (module DiagSum) => (module { type a = S.b; type p('a, 'b) = P.p('a, 'b); let paa = P.dimap(S.f, id, S.pab); }); let rho = (module S: Sum_P): (module DiagSum) => (module { type a = S.a; type p('a, 'b) = P.p('a, 'b); let paa = P.dimap(id, S.f, S.pab); }); }; ================================================ FILE: src/content/3.10/code/reason/snippet17.re ================================================ module type DiagSum = { type a; type p('a, 'b); let paa: p(a, a); }; ================================================ FILE: src/content/3.10/code/reason/snippet18.re ================================================ module type Procompose = { type p('a, 'b); type q('a, 'b); type procompose(_, _) = | Procompose(q('a, 'c) => p('c, 'b)): procompose('a, 'b); }; ================================================ FILE: src/content/3.10/code/scala/snippet01.scala ================================================ trait Profunctor[P[_, _]] { def dimap[A, B, C, D] (f: C => A)(g: B => D)(pab: P[A, B]): P[C, D] } ================================================ FILE: src/content/3.10/code/scala/snippet02.scala ================================================ dimap(f)(identity[B])(pbb): P[A, B] ================================================ FILE: src/content/3.10/code/scala/snippet03.scala ================================================ dimap(identity[A])(f)(paa): P[A, B] ================================================ FILE: src/content/3.10/code/scala/snippet04.scala ================================================ (dimap(identity[A])(f) _ compose alpha) == (dimap(f)(identity[B] _ compose alpha) ================================================ FILE: src/content/3.10/code/scala/snippet05.scala ================================================ // no Rank-N types in Scala // have to introduce a polymorphic function trait PolyFunction1[P[_, _]] { def apply[A](): P[A, A] } ================================================ FILE: src/content/3.10/code/scala/snippet06.scala ================================================ (dimap(f)(identity[B]) _ compose pi.apply) == (dimap(identity[A])(f) _ compose pi.apply) ================================================ FILE: src/content/3.10/code/scala/snippet07.scala ================================================ def side[P[_, _]: Profunctor]: PolyFunction1[P] => P[A, B] ================================================ FILE: src/content/3.10/code/scala/snippet08.scala ================================================ // no Rank-N types in Scala // need a higher rank polymorphic function trait PolyFunction2[P[_, _]] { def apply[C](in: PolyFunction1[P]): P[C, C] } def pi[P[_, _]](implicit P: Profunctor[P]): PolyFunction2[P] = new PolyFunction2[P] { def apply[C](in: PolyFunction1[P]): P[C, C] = in() } ================================================ FILE: src/content/3.10/code/scala/snippet09.scala ================================================ def lambda[A, B, P[_, _]](P: Profunctor[P]): P[A, A] => (A => B) => P[A, B] = paa => f => P.dimap(identity[A])(f)(paa) def rho[A, B, P[_, _]](P: Profunctor[P]): P[B, B] => (A => B) => P[A, B] = pbb => f => P.dimap(f)(identity[B])(pbb) ================================================ FILE: src/content/3.10/code/scala/snippet10.scala ================================================ trait ProdP[P[_, _]] { def apply[A, B](f: A => B): P[A, B] } ================================================ FILE: src/content/3.10/code/scala/snippet11.scala ================================================ case class DiaProd[P[_, _]](paa: PolyFunction1[P]) ================================================ FILE: src/content/3.10/code/scala/snippet12.scala ================================================ def lambdaP[P[_, _]](P: Profunctor[P]): DiaProd[P] => ProdP[P] = { case DiaProd(paa) => new ProdP[P] { def apply[A, B](f: A => B): P[A, B] = lambda(P)(paa[A])(f) } } def rhoP[P[_, _]](P: Profunctor[P]): DiaProd[P] => ProdP[P] = { case DiaProd(paa) => new ProdP[P] { def apply[A, B](f: A => B): P[A, B] = rho(P)(paa[B])(f) } } ================================================ FILE: src/content/3.10/code/scala/snippet13.scala ================================================ // Yet another type needs to be introduced. // To read more about FunctionK (~>): // typelevel.org/cats/datatypes/functionk.html trait ~>[F[_], G[_]] { def apply[B](fa: F[B]): G[B] } F ~> G ================================================ FILE: src/content/3.10/code/scala/snippet14.scala ================================================ trait Coend[P[_, _]] { def paa[A]: P[A, A] } ================================================ FILE: src/content/3.10/code/scala/snippet15.scala ================================================ trait SumP[P[_, _]] { def f[A, B]: B => A def pab[A, B]: P[A, B] } ================================================ FILE: src/content/3.10/code/scala/snippet16.scala ================================================ def lambda[P[- _, _]](P: Profunctor[P]): SumP[P] => DiagSum[P] = sump => new DiagSum[P] { def paa[A]: P[A, A] = P.dimap(sump.f)(identity[A])(sump.pab) } def rho[P[_, _]](P: Profunctor[P]): SumP[P] => DiagSum[P] = sump => new DiagSum[P] { def paa[A]: P[A, A] = P.dimap(identity[A])(sump.f)(sump.pab) } ================================================ FILE: src/content/3.10/code/scala/snippet17.scala ================================================ trait DiagSum[P[_, _]]{ def paa[A]: P[A, A] } ================================================ FILE: src/content/3.10/code/scala/snippet18.scala ================================================ trait Procompose[Q[_, _], P[_, _], A, B] object Procompose{ def apply[Q[_, _], P[_, _], A, B, C] (qac: Q[A, C])(pcb: P[C, B]): Procompose[Q, P, A, B] = ??? } ================================================ FILE: src/content/3.10/ends-and-coends.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{T}{here are many intuitions} that we may attach to morphisms in a category, but we can all agree that if there is a morphism from the object $a$ to the object $b$ then the two objects are in some way ``related.'' A morphism is, in a sense, the proof of this relation. This is clearly visible in any poset category, where a morphism \emph{is} a relation. In general, there may be many ``proofs'' of the same relation between two objects. These proofs form a set that we call the hom-set. When we vary the objects, we get a mapping from pairs of objects to sets of ``proofs.'' This mapping is functorial --- contravariant in the first argument and covariant in the second. We can look at it as establishing a global relationship between objects in the category. This relationship is described by the hom-functor: \[\cat{C}(-, =) \Colon \cat{C}^\mathit{op}\times{}\cat{C} \to \Set\] In general, any functor like this may be interpreted as establishing a relation between objects in a category. A relation may also involve two different categories $\cat{C}$ and $\cat{D}$. A functor, which describes such a relation, has the following signature and is called a profunctor: \[p \Colon \cat{D}^\mathit{op}\times{}\cat{C} \to \Set\] Mathematicians say that it's a profunctor from $\cat{C}$ to $\cat{D}$ (notice the inversion), and use a slashed arrow as a symbol for it: \[\cat{C} \nrightarrow \cat{D}\] You may think of a profunctor as a \newterm{proof-relevant relation} between objects of $\cat{C}$ and objects of $\cat{D}$, where the elements of the set symbolize proofs of the relation. Whenever $p\ a\ b$ is empty, there is no relation between $a$ and $b$. Keep in mind that relations don't have to be symmetric. Another useful intuition is the generalization of the idea that an endofunctor is a container. A profunctor value of the type $p\ a\ b$ could then be considered a container of $b$s that are keyed by elements of type $a$. In particular, an element of the hom-profunctor is a function from $a$ to $b$. In Haskell, a profunctor is defined as a two-argument type constructor \code{p} equipped with the method called \code{dimap}, which lifts a pair of functions, the first going in the ``wrong'' direction: \src{snippet01} The functoriality of the profunctor tells us that if we have a proof that \code{a} is related to \code{b}, then we get the proof that \code{c} is related to \code{d}, as long as there is a morphism from \code{c} to \code{a} and another from \code{b} to \code{d}. Or, we can think of the first function as translating new keys to the old keys, and the second function as modifying the contents of the container. For profunctors acting within one category, we can extract quite a lot of information from diagonal elements of the type $p\ a\ a$. We can prove that $b$ is related to $c$ as long as we have a pair of morphisms $b \to a$ and $a \to c$. Even better, we can use a single morphism to reach off-diagonal values. For instance, if we have a morphism $f \Colon a \to b$, we can lift the pair $\langle f, \idarrow[b] \rangle$ to go from $p\ b\ b$ to $p\ a\ b$: \src{snippet02} Or we can lift the pair $\langle \idarrow[a], f \rangle$ to go from $p\ a\ a$ to $p\ a\ b$: \src{snippet03} \section{Dinatural Transformations} Since profunctors are functors, we can define natural transformations between them in the standard way. In many cases, though, it's enough to define the mapping between diagonal elements of two profunctors. Such a transformation is called a dinatural transformation, provided it satisfies the commuting conditions that reflect the two ways we can connect diagonal elements to non-diagonal ones. A dinatural transformation between two profunctors $p$ and $q$, which are members of the functor category ${[}\cat{C}^\mathit{op}\times{}\cat{C}, \Set{]}$, is a family of morphisms: \[\alpha_a \Colon p\ a\ a \to q\ a\ a\] for which the following diagram commutes, for any $f \Colon a \to b$: \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/end.jpg} \end{figure} \noindent Notice that this is strictly weaker than the naturality condition. If $\alpha$ were a natural transformation in ${[}\cat{C}^\mathit{op}\times{}\cat{C}, \Set{]}$, the above diagram could be constructed from two naturality squares and one functoriality condition (profunctor $q$ preserving composition): \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/end-1.jpg} \end{figure} \noindent Notice that a component of a natural transformation $\alpha$ in ${[}\cat{C}^\mathit{op}\times{}\cat{C}, \Set{]}$ is indexed by a pair of objects $\alpha_{a b}$. A dinatural transformation, on the other hand, is indexed by one object, since it only maps diagonal elements of the respective profunctors. \section{Ends} We are now ready to advance from ``algebra'' to what could be considered the ``calculus'' of category theory. The calculus of ends (and coends) borrows ideas and even some notation from traditional calculus. In particular, the coend may be understood as an infinite sum or an integral, whereas the end is similar to an infinite product. There is even something that resembles the Dirac delta function. An end is a generalization of a limit, with the functor replaced by a profunctor. Instead of a cone, we have a wedge. The base of a wedge is formed by diagonal elements of a profunctor $p$. The apex of the wedge is an object (here, a set, since we are considering $\Set$-valued profunctors), and the sides are a family of functions mapping the apex to the sets in the base. You may think of this family as one polymorphic function --- a function that's polymorphic in its return type: \[\alpha \Colon \forall a\ .\ \mathit{apex} \to p\ a\ a\] Unlike in cones, within a wedge we don't have any functions that would connect vertices of the base. However, as we've seen earlier, given any morphism $f \Colon a \to b$ in $\cat{C}$, we can connect both $p\ a\ a$ and $p\ b\ b$ to the common set $p\ a\ b$. We therefore insist that the following diagram commute: \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/end-2.jpg} \end{figure} \noindent This is called the \newterm{wedge condition}. It can be written as: \[p\ \idarrow[a]\ f \circ \alpha_a = p\ f\ \idarrow[b] \circ \alpha_b\] Or, using Haskell notation: \src{snippet04} We can now proceed with the universal construction and define the end of $p$ as the universal wedge --- a set $e$ together with a family of functions $\pi$ such that for any other wedge with the apex $e'$ and a family $\alpha$ there is a unique function $h \Colon e' \to e$ that makes all triangles commute: \[\pi_a \circ h = \alpha_a\] \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/end-21.jpg} \end{figure} \noindent The symbol for the end is the integral sign, with the ``integration variable'' in the subscript position: \[\int_c p\ c\ c\] Components of $\pi$ are called projection maps for the end: \[\pi_a \Colon \int_c p\ c\ c \to p\ a\ a\] Note that if $\cat{C}$ is a discrete category (no morphisms other than the identities) the end is just a global product of all diagonal entries of $p$ across the whole category $\cat{C}$. Later I'll show you that, in the more general case, there is a relationship between the end and this product through an equalizer. In Haskell, the end formula translates directly to the universal quantifier: \src{snippet05} Strictly speaking, this is just a product of all diagonal elements of $p$, but the wedge condition is satisfied automatically due to \urlref{https://bartoszmilewski.com/2017/04/11/profunctor-parametricity/}{parametricity}. For any function $f \Colon a \to b$, the wedge condition reads: \src{snippet06} or, with type annotations: \begin{snipv} dimap f id\textsubscript{b} . pi\textsubscript{b} = dimap id\textsubscript{a} f . pi\textsubscript{a} \end{snipv} where both sides of the equation have the type: \src{snippet07} and \code{pi} is the polymorphic projection: \src{snippet08} Here, type inference automatically picks the right component of \code{e}. Just as we were able to express the whole set of commutation conditions for a cone as one natural transformation, likewise we can group all the wedge conditions into one dinatural transformation. For that we need the generalization of the constant functor $\Delta_c$ to a constant profunctor that maps all pairs of objects to a single object $c$, and all pairs of morphisms to the identity morphism for this object. A wedge is a dinatural transformation from that functor to the profunctor $p$. Indeed, the dinaturality hexagon shrinks down to the wedge diamond when we realize that $\Delta_c$ lifts all morphisms to one identity function. Ends can also be defined for target categories other than $\Set$, but here we'll only consider $\Set$-valued profunctors and their ends. \section{Ends as Equalizers} The commutation condition in the definition of the end can be written using an equalizer. First, let's define two functions (I'm using Haskell notation, because mathematical notation seems to be less user-friendly in this case). These functions correspond to the two converging branches of the wedge condition: \src{snippet09}[b] Both functions map diagonal elements of the profunctor \code{p} to polymorphic functions of the type: \src{snippet10} These functions have different types. However, we can unify their types, if we form one big product type, gathering together all diagonal elements of \code{p}: \src{snippet11} The functions \code{lambda} and \code{rho} induce two mappings from this product type: \src{snippet12} The end of \code{p} is the equalizer of these two functions. Remember that the equalizer picks the largest subset on which two functions are equal. In this case it picks the subset of the product of all diagonal elements for which the wedge diagrams commute. \section{Natural Transformations as Ends} The most important example of an end is the set of natural transformations. A natural transformation between two functors $F$ and $G$ is a family of morphisms picked from hom-sets of the form $\cat{C}(F a, G a)$. If it weren't for the naturality condition, the set of natural transformations would be just the product of all these hom-sets. In fact, in Haskell, it is: \src{snippet13} The reason it works in Haskell is because naturality follows from parametricity. Outside of Haskell, though, not all diagonal sections across such hom-sets will yield natural transformations. But notice that the mapping: \[\langle a, b \rangle \to \cat{C}(F a, G b)\] is a profunctor, so it makes sense to study its end. This is the wedge condition: \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/end1.jpg} \end{figure} \noindent Let's just pick one element from the set $\int_c \cat{C}(F c, G c)$. The two projections will map this element to two components of a particular transformation, let's call them: \begin{align*} \tau_a & \Colon F a \to G a \\ \tau_b & \Colon F b \to G b \end{align*} In the left branch, we lift a pair of morphisms $\langle \idarrow[a], G f \rangle$ using the hom-functor. You may recall that such lifting is implemented as simultaneous pre- and post-composition. When acting on $\tau_a$ the lifted pair gives us: \[G f \circ \tau_a \circ \idarrow[a]\] The other branch of the diagram gives us: \[\idarrow[b] \circ \tau_b \circ F f\] Their equality, demanded by the wedge condition, is nothing but the naturality condition for $\tau$. \section{Coends} As expected, the dual to an end is called a coend. It is constructed from a dual to a wedge called a cowedge (pronounced co-wedge, not cow-edge). \begin{figure}[H] \centering \includegraphics[width=0.25\textwidth]{images/end-31.jpg} \caption{An edgy cow?} \end{figure} \noindent The symbol for a coend is the integral sign with the ``integration variable'' in the superscript position: \[\int^c p\ c\ c\] Just like the end is related to a product, the coend is related to a coproduct, or a sum (in this respect, it resembles an integral, which is a limit of a sum). Rather than having projections, we have injections going from the diagonal elements of the profunctor down to the coend. If it weren't for the cowedge conditions, we could say that the coend of the profunctor $p$ is either $p\ a\ a$, or $p\ b\ b$, or $p\ c\ c$, and so on. Or we could say that there exists such an $a$ for which the coend is just the set $p\ a\ a$. The universal quantifier that we used in the definition of the end turns into an existential quantifier for the coend. This is why, in pseudo-Haskell, we would define the coend as: \begin{snip}{text} exists a. p a a \end{snip} The standard way of encoding existential quantifiers in Haskell is to use universally quantified data constructors. We can thus define: \src{snippet14} The logic behind this is that it should be possible to construct a coend using a value of any of the family of types $p\ a\ a$, no matter what $a$ we chose. Just like an end can be defined using an equalizer, a coend can be described using a \newterm{coequalizer}. All the cowedge conditions can be summarized by taking one gigantic coproduct of $p\ a\ b$ for all possible functions $b \to a$. In Haskell, that would be expressed as an existential type: \src{snippet15} There are two ways of evaluating this sum type, by lifting the function using \code{dimap} and applying it to the profunctor $p$: \src{snippet16} where \code{DiagSum} is the sum of diagonal elements of $p$: \src{snippet17} The coequalizer of these two functions is the coend. A coequalizer is obtained from \code{DiagSum p} by identifying values that are obtained by applying \code{lambda} or \code{rho} to the same argument. Here, the argument is a pair consisting of a function $b \to a$ and an element of $p\ a\ b$. The application of \code{lambda} and \code{rho} produces two potentially different values of the type \code{DiagSum p}. In the coend, these two values are identified, making the cowedge condition automatically satisfied. The process of identification of related elements in a set is formally known as taking a quotient. To define a quotient we need an \newterm{equivalence relation} $\sim$, a relation that is reflexive, symmetric, and transitive: \begin{align*} & a \sim a \\ & \text{if}\ a \sim b\ \text{then}\ b \sim a \\ & \text{if}\ a \sim b\ \text{and}\ b \sim c\ \text{then}\ a \sim c \end{align*} Such a relation splits the set into equivalence classes. Each class consists of elements that are related to each other. We form a quotient set by picking one representative from each class. A classic example is the definition of rational numbers as pairs of whole numbers, second of which is non-zero, with the following equivalence relation: \[(a, b) \sim (c, d)\ \text{iff}\ a * d = b * c\] It's easy to check that this is an equivalence relation. A pair $(a, b)$ is interpreted as a fraction $\frac{a}{b}$, and fractions whose numerator and denominator have a common divisor are identified. A rational number is an equivalence class of such fractions. You might recall from our earlier discussion of limits and colimits that the hom-functor is continuous, that is, it preserves limits. Dually, the contravariant hom-functor turns colimits into limits. These properties can be generalized to ends and coends, which are a generalization of limits and colimits, respectively. In particular, we get a very useful identity for converting coends to ends: \[\Set(\int^x p\ x\ x, c) \cong \int_x \Set(p\ x\ x, c)\] Let's have a look at it in pseudo-Haskell: \begin{snipv} (exists x. p x x) -> c \ensuremath{\cong} forall x. p x x -> c \end{snipv} It tells us that a function that takes an existential type is equivalent to a polymorphic function. This makes perfect sense, because such a function must be prepared to handle any one of the types that may be encoded in the existential type. It's the same principle that tells us that a function that accepts a sum type must be implemented as a case clause, with a tuple of handlers, one for every type present in the sum. Here, the sum type is replaced by a coend, and a family of handlers becomes an end, or a polymorphic function. \section{Ninja Yoneda Lemma} The set of natural transformations that appears in the Yoneda lemma may be encoded using an end, resulting in the following formulation: \[\int_z \Set(\cat{C}(a, z), F z) \cong F a\] There is also a dual formula: \[\int^z \cat{C}(z, a)\times{}F z \cong F a\] This identity is strongly reminiscent of the formula for the Dirac delta function (a function $\delta(a - z)$, or rather a distribution, that has an infinite peak at $a = z$). Here, the hom-functor plays the role of the delta function. Together these two identities are sometimes called the Ninja Yoneda lemma. To prove the second formula, we will use the consequence of the Yoneda embedding, which states that two objects are isomorphic if and only if their hom-functors are isomorphic. In other words $a \cong b$ if and only if there is a natural transformation of the type: \[[\cat{C}, \Set](\cat{C}(a, -), \cat{C}(b, =))\] that is an isomorphism. We start by inserting the left-hand side of the identity we want to prove inside a hom-functor that's going to some arbitrary object $c$: \[\Set(\int^z \cat{C}(z, a)\times{}F z, c)\] Using the continuity argument, we can replace the coend with the end: \[\int_z \Set(\cat{C}(z, a)\times{}F z, c)\] We can now take advantage of the adjunction between the product and the exponential: \[\int_z \Set(\cat{C}(z, a), c^{(F z)})\] We can ``perform the integration'' by using the Yoneda lemma to get: \[c^{(F a)}\] (Notice that we used the contravariant version of the Yoneda lemma, since the functor $c^{(F z)}$ is contravariant in $z$.) This exponential object is isomorphic to the hom-set: \[\Set(F a, c)\] Finally, we take advantage of the Yoneda embedding to arrive at the isomorphism: \[\int^z \cat{C}(z, a)\times{}F z \cong F a\] \section{Profunctor Composition} Let's explore further the idea that a profunctor describes a relation --- more precisely, a proof-relevant relation, meaning that the set $p\ a\ b$ represents the set of proofs that $a$ is related to $b$. If we have two relations $p$ and $q$ we can try to compose them. We'll say that $a$ is related to $b$ through the composition of $q$ after $p$ if there exist an intermediary object $c$ such that both $q\ b\ c$ and $p\ c\ a$ are non-empty. The proofs of this new relation are all pairs of proofs of individual relations. Therefore, with the understanding that the existential quantifier corresponds to a coend, and the Cartesian product of two sets corresponds to ``pairs of proofs,'' we can define composition of profunctors using the following formula: \[(q \circ p)\ a\ b = \int^c p\ c\ a\times{}q\ b\ c\] Here's the equivalent Haskell definition from \code{Data.Profunctor.Composition}, after some renaming: \src{snippet18} This is using generalized algebraic data type, or \acronym{GADT} syntax, in which a free type variable (here \code{c}) is automatically existentially quantified. The (uncurried) data constructor \code{Procompose} is thus equivalent to: \begin{snip}{text} exists c. (q a c, p c b) \end{snip} The unit of so defined composition is the hom-functor --- this immediately follows from the Ninja Yoneda lemma. It makes sense, therefore, to ask the question if there is a category in which profunctors serve as morphisms. The answer is positive, with the caveat that both associativity and identity laws for profunctor composition hold only up to natural isomorphism. Such a category, where laws are valid up to isomorphism, is called a bicategory (which is more general than a $\cat{2}$-category). So we have a bicategory $\cat{Prof}$, in which objects are categories, morphisms are profunctors, and morphisms between morphisms (a.k.a., two-cells) are natural transformations. In fact, one can go even further, because beside profunctors, we also have regular functors as morphisms between categories. A category which has two types of morphisms is called a double category. Profunctors play an important role in the Haskell lens library and in the arrow library. ================================================ FILE: src/content/3.11/code/haskell/snippet01.hs ================================================ newtype Ran k d a = Ran (forall i. (a -> k i) -> d i) ================================================ FILE: src/content/3.11/code/haskell/snippet02.hs ================================================ f :: String -> Tree Int ================================================ FILE: src/content/3.11/code/haskell/snippet03.hs ================================================ forall i. (a -> k i) -> i ================================================ FILE: src/content/3.11/code/haskell/snippet04.hs ================================================ type Lst a = forall i. Monoid i => (a -> i) -> i ================================================ FILE: src/content/3.11/code/haskell/snippet05.hs ================================================ toLst :: [a] -> Lst a toLst as = \f -> foldMap f as fromLst :: Lst a -> [a] fromLst f = f (\a -> [a]) ================================================ FILE: src/content/3.11/code/haskell/snippet06.hs ================================================ data Lan k d a = forall i. Lan (k i -> a) (d i) ================================================ FILE: src/content/3.11/code/haskell/snippet07.hs ================================================ type Exp a b = Lan ((,) a) Identity b ================================================ FILE: src/content/3.11/code/haskell/snippet08.hs ================================================ toExp :: (a -> b) -> Exp a b toExp f = Lan (f . fst) (Identity ()) fromExp :: Exp a b -> (a -> b) fromExp (Lan f (Identity x)) = \a -> f (a, x) ================================================ FILE: src/content/3.11/code/haskell/snippet09.hs ================================================ data FreeF f a = forall i. FMap (i -> a) (f i) ================================================ FILE: src/content/3.11/code/haskell/snippet10.hs ================================================ instance Functor (FreeF f) where fmap g (FMap h fi) = FMap (g . h) fi ================================================ FILE: src/content/3.11/code/haskell/snippet11.hs ================================================ newtype FreeF f a = FreeF (forall i. (a -> i) -> f i) ================================================ FILE: src/content/3.11/code/haskell/snippet12.hs ================================================ instance Functor (FreeF f) where fmap g (FreeF r) = FreeF (\bi -> r (bi . g)) ================================================ FILE: src/content/3.11/code/ocaml/snippet01.ml ================================================ module type Ran = sig type 'a k type 'a d type 'a ran = Ran of { r : 'i. ('a -> 'i k) -> 'i d } end ================================================ FILE: src/content/3.11/code/ocaml/snippet02.ml ================================================ val f : string -> int tree ================================================ FILE: src/content/3.11/code/ocaml/snippet03.ml ================================================ (* Higher rank polymorphic functions can be achieved using records *) { r : 'i. (a -> 'i k) -> 'i } ================================================ FILE: src/content/3.11/code/ocaml/snippet04.ml ================================================ module type Lst = sig type a type m module M : Monoid with type m = m type lst = (a -> M.m) -> M.m val f : lst end ================================================ FILE: src/content/3.11/code/ocaml/snippet05.ml ================================================ let fold_map (type i) (module M : Monoid with type m = i) xs f = List.fold_left (fun acc a -> M.mappend acc (f a)) M.mempty xs ;; let to_lst (type x) (xs : x list) = let module LM : Monoid with type m = x list = ListMonoid (struct type a = x end) in (module struct type a = x type m = x list module M = LM type lst = (a -> LM.m) -> LM.m let f g = fold_map (module LM) xs g end : Lst with type a = x) ;; let from_lst (type x) (module LstImpl : Lst with type a = x and type m = x list) = LstImpl.f (fun x -> [ x ]) ;; ================================================ FILE: src/content/3.11/code/ocaml/snippet06.ml ================================================ module type Lan = sig type 'a k type 'a d type a type i val fk : i k -> a val di : i d end ================================================ FILE: src/content/3.11/code/ocaml/snippet07.ml ================================================ module type Exp = sig type a type b type 'a d = I of 'a type 'a k = 'a * a include Lan with type 'a k := a * 'a and type 'a d := 'a d and type a := b end ================================================ FILE: src/content/3.11/code/ocaml/snippet08.ml ================================================ let to_exp (type a' b') f = (module struct type a = a' type b = b' type 'a d = I of 'a type 'a k = 'a * a type i = unit let fk (a, _) = f a let di = I () end : Exp with type a = a' and type b = b') ;; let from_exp (type a' b') (module E : Exp with type a = a' and type b = b') a = let (I i) = E.di in E.fk (a, i) ;; ================================================ FILE: src/content/3.11/code/ocaml/snippet09.ml ================================================ module type FreeF = sig type 'a f type a type i val h : i -> a val fi : i -> i f end ================================================ FILE: src/content/3.11/code/ocaml/snippet10.ml ================================================ module FreeFunctor (F : sig type 'a f end) : Functor = struct module type F = FreeF with type 'a f = 'a F.f type 'a t = (module F with type a = 'a) let fmap (type a' b') (f : a' -> b') (module FF : F with type a = a') = (module struct type 'a f = 'a F.f type a = b' type i = FF.i let h i = f (FF.h i) let fi = FF.fi end : F with type a = b') ;; end ================================================ FILE: src/content/3.11/code/ocaml/snippet11.ml ================================================ module type FreeF_Alt = sig type a type 'a f val freeF : (a -> 'i) -> 'i f end ================================================ FILE: src/content/3.11/code/ocaml/snippet12.ml ================================================ module FreeFunctorAlt (F : sig type 'a f end) : Functor = struct module type F = FreeF_Alt with type 'a f = 'a F.f type 'a t = (module F with type a = 'a) let fmap (type a' b') f (module FF : F with type a = a') = (module struct type a = b' type 'a f = 'a F.f let freeF bi = FF.freeF (fun a -> bi (f a)) end : F with type a = b') ;; end ================================================ FILE: src/content/3.11/code/reason/snippet01.re ================================================ module type Ran = { type k('a); type d('a); type ran('a) = | Ran({r: 'i. ('a => k('i)) => d('i)}); }; ================================================ FILE: src/content/3.11/code/reason/snippet02.re ================================================ let f: string => tree(int); ================================================ FILE: src/content/3.11/code/reason/snippet03.re ================================================ /* Higher rank polymorphic functions can be achieved using records */ { r : 'i. (a => k('i)) => 'i } ================================================ FILE: src/content/3.11/code/reason/snippet04.re ================================================ module type Lst = { type a; type m; module M: Monoid with type m = m; type lst = (a => M.m) => M.m; let f: lst; }; ================================================ FILE: src/content/3.11/code/reason/snippet05.re ================================================ let fold_map = (type i, module M: Monoid with type m = i, xs, f) => List.fold_left((acc, a) => M.mappend(acc, f(a)), M.mempty, xs); let to_lst = (type x, xs: list(x)) => { module LM: Monoid with type m = list(x) = ListMonoid({type a = x;}); (module { type a = x; type m = list(x); module M = LM; type lst = (a => LM.m) => LM.m; let f = g => fold_map((module LM), xs, g); }); }; let from_lst = (type x, module LstImpl: Lst with type a = x and type m = list(x) ) => LstImpl.f(x => [x]); ================================================ FILE: src/content/3.11/code/reason/snippet06.re ================================================ module type Lan = { type k('a); type d('a); type a; type i; let fk: k(i) => a; let di: d(i); }; ================================================ FILE: src/content/3.11/code/reason/snippet07.re ================================================ module type Exp = { type a; type b; type d('a) = | I('a); type k('a) = ('a, a); include Lan with type k('a) := (a, 'a) and type d('a) := d('a) and type a := b; }; ================================================ FILE: src/content/3.11/code/reason/snippet08.re ================================================ let to_exp = (type a', type b', f) : (module Exp with type a = a' and type b = b') => (module { type a = a'; type b = b'; type d('a) = | I('a); type k('a) = ('a, a); type i = unit; let fk = ((a, _)) => f(a); let di = I(); }); let from_exp = (type a', type b', module E: Exp with type a = a' and type b = b', a) => { let I(i) = E.di; E.fk((a, i)); }; ================================================ FILE: src/content/3.11/code/reason/snippet09.re ================================================ module type FreeF = { type f('a); type a; type i; let h: i => a; let fi: i => f(i); }; ================================================ FILE: src/content/3.11/code/reason/snippet10.re ================================================ module FreeFunctor = (F: {type f('a);}) : Functor => { module type F = FreeF with type f('a) = F.f('a); type t('a) = (module F with type a = 'a); let fmap = (type a', type b', f: a' => b', module FF: F with type a = a') : (module F with type a = b') => (module { type f('a) = F.f('a); type a = b'; type i = FF.i; let h = i => f(FF.h(i)); let fi = FF.fi; }); }; ================================================ FILE: src/content/3.11/code/reason/snippet11.re ================================================ module type FreeF_Alt = { type a; type f('a); let freeF: (a => 'i) => f('i); }; ================================================ FILE: src/content/3.11/code/reason/snippet12.re ================================================ module FreeFunctorAlt = (F: {type f('a);}) : Functor => { module type F = FreeF_Alt with type f('a) = F.f('a); type t('a) = (module F with type a = 'a); let fmap = (type a', type b', f, module FF: F with type a = a') : (module F with type a = b') => (module { type a = b'; type f('a) = F.f('a); let freeF = bi => FF.freeF(a => bi(f(a))); }); }; ================================================ FILE: src/content/3.11/code/scala/snippet01.scala ================================================ // Another type needs to be introduced. // To read more about FunctionK (~>): // typelevel.org/cats/datatypes/functionk.html trait ~>[F[_], G[_]] { def apply[C](fa: F[C]): G[C] } trait Ran[K[_], D[_], A] { // partially-applied type type AtoK[I] = A => K[I] def apply: AtoK ~> D } ================================================ FILE: src/content/3.11/code/scala/snippet02.scala ================================================ def f: String => Tree[Int] ================================================ FILE: src/content/3.11/code/scala/snippet03.scala ================================================ type Id[I] = I trait PolyFunc[A, K[_]] { type AtoK[I] = A => K[I] def apply(): AtoK ~> Id } ================================================ FILE: src/content/3.11/code/scala/snippet04.scala ================================================ trait `PolyFunctionM`[F[_], G[_]] { def apply[I: Monoid](fa: F[I]): G[I] } trait Lst[A] { type aTo[X] = A => X def apply(): aTo `PolyFunctionM` Id } ================================================ FILE: src/content/3.11/code/scala/snippet05.scala ================================================ // Read more about foldMap: // typelevel.org/cats/typeclasses/foldable.html def foldMap[F[_], A, B](fa: F[A])(f: A => B) (implicit B: Monoid[B]): B = ??? implicit def listMonoid[A]: Monoid[List[A]] = ??? def toLst[A]: List[A] => Lst[A] = as => new Lst[A] { def apply(): `PolyFunctionM`[aTo, Id] = new `PolyFunctionM`[aTo, Id] { def apply[I: Monoid](fa: aTo[I]): Id[I] = foldMap(as)(fa) } } def fromLst[A]: Lst[A] => List[A] = f => f().apply(a => List(a)) ================================================ FILE: src/content/3.11/code/scala/snippet06.scala ================================================ trait Lan[K[_], +D[_], A] { def fk[I](ki: K[I]): A def di[I]: D[I] } ================================================ FILE: src/content/3.11/code/scala/snippet07.scala ================================================ type Exp[A, B] = Lan[(A, ?), I, B] ================================================ FILE: src/content/3.11/code/scala/snippet08.scala ================================================ def fst[I]: ((I, _)) => I = _._1 def toExp[A, B]: (A => B) => Exp[A, B] = f => new Lan[(A, ?), I, B] { def fk[L](ki: (A, L)): B = f.compose(fst[A])(ki) def di[L]: I[L] = I() } def fromExp[A, B]: Exp[A, B] => (A => B) = lan => a => lan.fk((a, lan.di)) ================================================ FILE: src/content/3.11/code/scala/snippet09.scala ================================================ trait FreeF[F[_], A] { def h[I]: I => A def fi[I]: F[I] } ================================================ FILE: src/content/3.11/code/scala/snippet10.scala ================================================ implicit def freeFunctor[F[_]] = new Functor[FreeF[F, ?]] { def fmap[A, B](g: A => B)(fa: FreeF[F, A]): FreeF[F, B] = { new FreeF[F, B] { def h[I]: I => B = g compose fa.h def fi[I]: F[I] = fi } } } ================================================ FILE: src/content/3.11/code/scala/snippet11.scala ================================================ case class FreeF[F[_], A](r: (A => ?) ~> F) ================================================ FILE: src/content/3.11/code/scala/snippet12.scala ================================================ implicit def freeFunctor[F[_]] = new Functor[FreeF[F, ?]] { def fmap[A, B](g: A => B)(fa: FreeF[F, A]): FreeF[F, B] = fa match { case FreeF(r) => FreeF { new ~>[B => ?, F] { def apply[C](bi: B => C): F[C] = r(bi compose g) } } } } ================================================ FILE: src/content/3.11/kan-extensions.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{S}{o far we've been} mostly working with a single category or a pair of categories. In some cases that was a little too constraining. For instance, when defining a limit in a category $\cat{C}$, we introduced an index category $\cat{I}$ as the template for the pattern that would form the basis for our cones. It would have made sense to introduce another category, a trivial one, to serve as a template for the apex of the cone. Instead we used the constant functor $\Delta_c$ from $\cat{I}$ to $\cat{C}$. It's time to fix this awkwardness. Let's define a limit using three categories. Let's start with the functor $D$ from the index category $\cat{I}$ to $\cat{C}$. This is the functor that selects the base of the cone --- the diagram functor. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/kan2.jpg} \end{figure} \noindent The new addition is the category $\cat{1}$ that contains a single object (and a single identity morphism). There is only one possible functor $K$ from $\cat{I}$ to this category. It maps all objects to the only object in $\cat{1}$, and all morphisms to the identity morphism. Any functor $F$ from $\cat{1}$ to $\cat{C}$ picks a potential apex for our cone. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/kan15.jpg} \end{figure} \noindent A cone is a natural transformation $\varepsilon$ from $F \circ K$ to $D$. Notice that $F \circ K$ does exactly the same thing as our original $\Delta_c$. The following diagram shows this transformation. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/kan3-e1492120491591.jpg} \end{figure} \noindent We can now define a universal property that picks the ``best'' such functor $F$. This $F$ will map $\cat{1}$ to the object that is the limit of $D$ in $\cat{C}$, and the natural transformation $\varepsilon$ from $F \circ K$ to $D$ will provide the corresponding projections. This universal functor is called the right Kan extension of $D$ along $K$ and is denoted by $\Ran_{K}D$. Let's formulate the universal property. Suppose we have another cone --- that is another functor $F'$ together with a natural transformation $\varepsilon'$ from $F' \circ K$ to $D$. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/kan31-e1492120512209.jpg} \end{figure} \noindent If the Kan extension $F = Ran_{K}D$ exists, there must be a unique natural transformation $\sigma$ from $F'$ to it, such that $\varepsilon'$ factorizes through $\varepsilon$, that is: \[\varepsilon' = \varepsilon \cdot (\sigma \circ K)\] Here, $\sigma \circ K$ is the horizontal composition of two natural transformations (one of them being the identity natural transformation on $K$). This transformation is then vertically composed with $\varepsilon$. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/kan5.jpg} \end{figure} \noindent In components, when acting on an object $i$ in $\cat{I}$, we get: \[\varepsilon'_i = \varepsilon_i \circ \sigma_{K i}\] In our case, $\sigma$ has only one component corresponding to the single object of $\cat{1}$. So, indeed, this is the unique morphism from the apex of the cone defined by $F'$ to the apex of the universal cone defined by $\Ran_{K}D$. The commuting conditions are exactly the ones required by the definition of a limit. But, importantly, we are free to replace the trivial category $\cat{1}$ with an arbitrary category $\cat{A}$, and the definition of the right Kan extension remains valid. \section{Right Kan Extension} The right Kan extension of the functor $D \Colon \cat{I} \to \cat{C}$ along the functor $K \Colon \cat{I} \to \cat{A}$ is a functor $F \Colon \cat{A} \to \cat{C}$ (denoted $\Ran_{K}D$) together with a natural transformation \[\varepsilon \Colon F \circ K \to D\] such that for any other functor $F' \Colon \cat{A} \to \cat{C}$ and a natural transformation \[\varepsilon' \Colon F' \circ K \to D\] there is a unique natural transformation \[\sigma \Colon F' \to F\] that factorizes $\varepsilon'$: \[\varepsilon' = \varepsilon \cdot (\sigma \circ K)\] This is quite a mouthful, but it can be visualized in this nice diagram: \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/kan7.jpg} \end{figure} \noindent An interesting way of looking at this is to notice that, in a sense, the Kan extension acts like the inverse of ``functor multiplication.'' Some authors go as far as use the notation $D/K$ for $\Ran_{K}D$. Indeed, in this notation, the definition of $\varepsilon$, which is also called the counit of the right Kan extension, looks like simple cancellation: \[\varepsilon \Colon D/K \circ K \to D\] There is another interpretation of Kan extensions. Consider that the functor $K$ embeds the category $\cat{I}$ inside $\cat{A}$. In the simplest case $\cat{I}$ could just be a subcategory of $\cat{A}$. We have a functor $D$ that maps $\cat{I}$ to $\cat{C}$. Can we extend $D$ to a functor $F$ that is defined on the whole of $\cat{A}$? Ideally, such an extension would make the composition $F \circ K$ be isomorphic to $D$. In other words, $F$ would be extending the domain of $D$ to $\cat{A}$. But a full-blown isomorphism is usually too much to ask, and we can do with just half of it, namely a one-way natural transformation $\varepsilon$ from $F \circ K$ to $D$. (The left Kan extension picks the other direction.) \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/kan6.jpg} \end{figure} \noindent Of course, the embedding picture breaks down when the functor $K$ is not injective on objects or not faithful on hom-sets, as in the example of the limit. In that case, the Kan extension tries its best to extrapolate the lost information. \section{Kan Extension as Adjunction} Now suppose that the right Kan extension exists for any $D$ (and a fixed $K$). In that case $\Ran_{K}-$ (with the dash replacing $D$) is a functor from the functor category ${[}\cat{I}, \cat{C}{]}$ to the functor category ${[}\cat{A}, \cat{C}{]}$. It turns out that this functor is the right adjoint to the precomposition functor $- \circ K$. The latter maps functors in ${[}\cat{A}, \cat{C}{]}$ to functors in ${[}\cat{I}, \cat{C}{]}$. The adjunction is: \[[\cat{I}, \cat{C}](F' \circ K, D) \cong [\cat{A}, \cat{C}](F', \Ran_{K}D)\] It is just a restatement of the fact that to every natural transformation we called $\varepsilon'$ corresponds a unique natural transformation we called $\sigma$. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/kan92.jpg} \end{figure} \noindent Furthermore, if we chose the category $\cat{I}$ to be the same as $\cat{C}$, we can substitute the identity functor $I_{\cat{C}}$ for $D$. We get the following identity: \[[\cat{C}, \cat{C}](F' \circ K, I_{\cat{C}}) \cong [\cat{A}, \cat{C}](F', \Ran_{K}I_{\cat{C}})\] We can now chose $F'$ to be the same as $\Ran_{K}I_{\cat{C}}$. In that case the right hand side contains the identity natural transformation and, corresponding to it, the left hand side gives us the following natural transformation: \[\varepsilon \Colon \Ran_{K}I_{\cat{C}} \circ K \to I_{\cat{C}}\] This looks very much like the counit of an adjunction: \[\Ran_{K}I_{\cat{C}} \dashv K\] Indeed, the right Kan extension of the identity functor along a functor $K$ can be used to calculate the left adjoint of $K$. For that, one more condition is necessary: the right Kan extension must be preserved by the functor $K$. The preservation of the extension means that, if we calculate the Kan extension of the functor precomposed with $K$, we should get the same result as precomposing the original Kan extension with $K$. In our case, this condition simplifies to: \[K \circ \Ran_{K}I_{\cat{C}} \cong \Ran_{K}K\] Notice that, using the division-by-$K$ notation, the adjunction can be written as: \[I/K \dashv K\] which confirms our intuition that an adjunction describes some kind of an inverse. The preservation condition becomes: \[K \circ I/K \cong K/K\] The right Kan extension of a functor along itself, $K/K$, is called a codensity monad. The adjunction formula is an important result because, as we'll see soon, we can calculate Kan extensions using ends (coends), thus giving us practical means of finding right (and left) adjoints. \section{Left Kan Extension} There is a dual construction that gives us the left Kan extension. To build some intuition, we'll start with the definition of a colimit and restructure it to use the singleton category $\cat{1}$. We build a cocone by using the functor $D \Colon \cat{I} \to \cat{C}$ to form its base, and the functor $F \Colon \cat{1} \to \cat{C}$ to select its apex. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/kan81.jpg} \end{figure} \noindent The sides of the cocone, the injections, are components of a natural transformation $\eta$ from $D$ to $F \circ K$. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/kan10a.jpg} \end{figure} \noindent The colimit is the universal cocone. So for any other functor $F'$ and a natural transformation \[\eta' \Colon D \to F' \circ K\] \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/kan10b.jpg} \end{figure} \noindent there is a unique natural transformation $\sigma$ from $F$ to $F'$ \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/kan14.jpg} \end{figure} \noindent such that: \[\eta' = (\sigma \circ K) \cdot \eta\] This is illustrated in the following diagram: \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/kan112.jpg} \end{figure} \noindent Replacing the singleton category $\cat{1}$ with $\cat{A}$, this definition naturally generalized to the definition of the left Kan extension, denoted by $\Lan_{K}D$. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/kan12.jpg} \end{figure} \noindent The natural transformation: \[\eta \Colon D \to \Lan_{K}D \circ K\] is called the unit of the left Kan extension. As before, we can recast the one-to-one correspondence between natural transformations: \[\eta' = (\sigma \circ K) \cdot \eta\] in terms of the adjunction: \[[\cat{A}, \cat{C}](\Lan_{K}D, F') \cong [\cat{I}, \cat{C}](D, F' \circ K)\] In other words, the left Kan extension is the left adjoint, and the right Kan extension is the right adjoint of the precomposition with $K$. Just like the right Kan extension of the identity functor could be used to calculate the left adjoint of $K$, the left Kan extension of the identity functor turns out to be the right adjoint of $K$ (with $\eta$ being the unit of the adjunction): \[K \dashv \Lan_{K}I_{\cat{C}}\] Combining the two results, we get: \[\Ran_{K}I_{\cat{C}} \dashv K \dashv \Lan_{K}I_{\cat{C}}\] \section{Kan Extensions as Ends} The real power of Kan extensions comes from the fact that they can be calculated using ends (and coends). For simplicity, we'll restrict our attention to the case where the target category $\cat{C}$ is $\Set$, but the formulas can be extended to any category. Let's revisit the idea that a Kan extension can be used to extend the action of a functor outside of its original domain. Suppose that $K$ embeds $\cat{I}$ inside $\cat{A}$. Functor $D$ maps $\cat{I}$ to $\Set$. We could just say that for any object $a$ in the image of $K$, that is $a = K i$, the extended functor maps $a$ to $D i$. The problem is, what to do with those objects in $\cat{A}$ that are outside of the image of $K$? The idea is that every such object is potentially connected through lots of morphisms to every object in the image of $K$. A functor must preserve these morphisms. The totality of morphisms from an object $a$ to the image of $K$ is characterized by the hom-functor: \[\cat{A}(a, K -)\] \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/kan13.jpg} \end{figure} \noindent Notice that this hom-functor is a composition of two functors: \[\cat{A}(a, K -) = \cat{A}(a, -) \circ K\] The right Kan extension is the right adjoint of functor composition: \[[\cat{I}, \Set](F' \circ K, D) \cong [\cat{A}, \Set](F', \Ran_{K}D)\] Let's see what happens when we replace $F'$ with the hom functor: \[[\cat{I}, \Set](\cat{A}(a, -) \circ K, D) \cong [\cat{A}, \Set](\cat{A}(a, -), \Ran_{K}D)\] and then inline the composition: \[[\cat{I}, \Set](\cat{A}(a, K -), D) \cong [\cat{A}, \Set](\cat{A}(a, -), \Ran_{K}D)\] The right hand side can be reduced using the Yoneda lemma: \[[\cat{I}, \Set](\cat{A}(a, K -), D) \cong \Ran_{K}D a\] We can now rewrite the set of natural transformations as the end to get this very convenient formula for the right Kan extension: \[\Ran_{K}D a \cong \int_i \Set(\cat{A}(a, K i), D i)\] There is an analogous formula for the left Kan extension in terms of a coend: \[\Lan_{K}D a = \int^i \cat{A}(K i, a)\times{}D i\] To see that this is the case, we'll show that this is indeed the left adjoint to functor composition: \[[\cat{A}, \Set](\Lan_{K}D, F') \cong [\cat{I}, \Set](D, F' \circ K)\] Let's substitute our formula in the left hand side: \[[\cat{A}, \Set](\int^i \cat{A}(K i, -)\times{}D i, F')\] This is a set of natural transformations, so it can be rewritten as an end: \[\int_a \Set(\int^i \cat{A}(K i, a)\times{}D i, F' a)\] Using the continuity of the hom-functor, we can replace the coend with the end: \[\int_a \int_i \Set(\cat{A}(K i, a)\times{}D i, F' a)\] We can use the product-exponential adjunction: \[\int_a \int_i \Set(\cat{A}(K i, a),\ (F' a)^{D i})\] The exponential is isomorphic to the corresponding hom-set: \[\int_a \int_i \Set(\cat{A}(K i, a),\ \Set(D i, F' a))\] There is a theorem called the Fubini theorem that allows us to swap the two ends: \[\int_i \int_a \Set(\cat{A}(K i, a),\ \Set(D i, F' a))\] The inner end represents the set of natural transformations between two functors, so we can use the Yoneda lemma: \[\int_i \Set(D i, F' (K i))\] This is indeed the set of natural transformations that forms the right hand side of the adjunction we set out to prove: \[[\cat{I}, \Set](D, F' \circ K)\] These kinds of calculations using ends, coends, and the Yoneda lemma are pretty typical for the ``calculus'' of ends. \section{Kan Extensions in Haskell} The end/coend formulas for Kan extensions can be easily translated to Haskell. Let's start with the right extension: \[\Ran_{K}D a \cong \int_i \Set(\cat{A}(a, K i), D i)\] We replace the end with the universal quantifier, and hom-sets with function types: \src{snippet01} Looking at this definition, it's clear that \code{Ran} must contain a value of type \code{a} to which the function can be applied, and a natural transformation between the two functors \code{k} and \code{d}. For instance, suppose that \code{k} is the tree functor, and \code{d} is the list functor, and you were given a \code{Ran Tree {[}{]} String}. If you pass it a function: \src{snippet02} you'll get back a list of \code{Int}, and so on. The right Kan extension will use your function to produce a tree and then repackage it into a list. For instance, you may pass it a parser that generates a parsing tree from a string, and you'll get a list that corresponds to the depth-first traversal of this tree. The right Kan extension can be used to calculate the left adjoint of a given functor by replacing the functor \code{d} with the identity functor. This leads to the left adjoint of a functor \code{k} being represented by the set of polymorphic functions of the type: \src{snippet03} Suppose that \code{k} is the forgetful functor from the category of monoids. The universal quantifier then goes over all monoids. Of course, in Haskell we cannot express monoidal laws, but the following is a decent approximation of the resulting free functor (the forgetful functor \code{k} is an identity on objects): \src{snippet04} As expected, it generates free monoids, or Haskell lists: \src{snippet05} The left Kan extension is a coend: \[\Lan_{K}D a = \int^i \cat{A}(K i, a)\times{}D i\] so it translates to an existential quantifier. Symbolically: \begin{snip}{haskell} Lan k d a = exists i. (k i -> a, d i) \end{snip} This can be encoded in Haskell using \acronym{GADT}s, or using a universally quantified data constructor: \src{snippet06} The interpretation of this data structure is that it contains a function that takes a container of some unspecified \code{i}s and produces an \code{a}. It also has a container of those \code{i}s. Since you have no idea what \code{i}s are, the only thing you can do with this data structure is to retrieve the container of \code{i}s, repack it into the container defined by the functor \code{k} using a natural transformation, and call the function to obtain the \code{a}. For instance, if \code{d} is a tree, and \code{k} is a list, you can serialize the tree, call the function with the resulting list, and obtain an \code{a}. The left Kan extension can be used to calculate the right adjoint of a functor. We know that the right adjoint of the product functor is the exponential, so let's try to implement it using the Kan extension: \src{snippet07} This is indeed isomorphic to the function type, as witnessed by the following pair of functions: \src{snippet08} Notice that, as described earlier in the general case, we performed the following steps: \begin{enumerate} \tightlist \item Retrieved the container of \code{x} (here, it's just a trivial identity container), and the function \code{f}. \item Repackaged the container using the natural transformation between the identity functor and the pair functor. \item Called the function \code{f}. \end{enumerate} \section{Free Functor} An interesting application of Kan extensions is the construction of a free functor. It's the solution to the following practical problem: suppose you have a type constructor --- that is a mapping of objects. Is it possible to define a functor based on this type constructor? In other words, can we define a mapping of morphisms that would extend this type constructor to a full-blown endofunctor? The key observation is that a type constructor can be described as a functor whose domain is a discrete category. A discrete category has no morphisms other than the identity morphisms. Given a category $\cat{C}$, we can always construct a discrete category $\cat{|C|}$ by simply discarding all non-identity morphisms. A functor $F$ from $\cat{|C|}$ to $\cat{C}$ is then a simple mapping of objects, or what we call a type constructor in Haskell. There is also a canonical functor $J$ that injects $\cat{|C|}$ into $\cat{C}$: it's an identity on objects (and on identity morphisms). The left Kan extension of $F$ along $J$, if it exists, is then a functor for $\cat{C}$ to $\cat{C}$: \[\Lan_{J}F a = \int^i \cat{C}(J i, a)\times{}F i\] It's called a free functor based on $F$. In Haskell, we would write it as: \src{snippet09} Indeed, for any type constructor \code{f}, \code{FreeF f} is a functor: \src{snippet10} As you can see, the free functor fakes the lifting of a function by recording both the function and its argument. It accumulates the lifted functions by recording their composition. Functor rules are automatically satisfied. This construction was used in a paper \urlref{http://okmij.org/ftp/Haskell/extensible/more.pdf}{Freer Monads, More Extensible Effects}. Alternatively, we can use the right Kan extension for the same purpose: \src{snippet11} It's easy to check that this is indeed a functor: \src{snippet12} ================================================ FILE: src/content/3.12/enriched-categories.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{A}{ category is small} if its objects form a set. But we know that there are things larger than sets. Famously, a set of all sets cannot be formed within the standard set theory (the Zermelo-Fraenkel theory, optionally augmented with the Axiom of Choice). So a category of all sets must be large. There are mathematical tricks like Grothendieck universes that can be used to define collections that go beyond sets. These tricks let us talk about large categories. A category is \emph{locally small} if morphisms between any two objects form a set. If they don't form a set, we have to rethink a few definitions. In particular, what does it mean to compose morphisms if we can't even pick them from a set? The solution is to bootstrap ourselves by replacing hom-sets, which are objects in $\Set$, with \emph{objects} from some other category $\cat{V}$. The difference is that, in general, objects don't have elements, so we are no longer allowed to talk about individual morphisms. We have to define all properties of an \emph{enriched} category in terms of operations that can be performed on hom-objects as a whole. In order to do that, the category that provides hom-objects must have additional structure --- it must be a monoidal category. If we call this monoidal category $\cat{V}$, we can talk about a category $\cat{C}$ enriched over $\cat{V}$. Beside size reasons, we might be interested in generalizing hom-sets to something that has more structure than mere sets. For instance, a traditional category doesn't have the notion of a distance between objects. Two objects are either connected by morphisms or not. All objects that are connected to a given object are its neighbors. Unlike in real life; in a category, a friend of a friend of a friend is as close to me as my bosom buddy. In a suitably enriched category, we can define distances between objects. There is one more very practical reason to get some experience with enriched categories, and that's because a very useful online source of categorical knowledge, the \urlref{https://ncatlab.org/}{nLab}, is written mostly in terms of enriched categories. \section{Why Monoidal Category?} When constructing an enriched category we have to keep in mind that we should be able to recover the usual definitions when we replace the monoidal category with $\Set$ and hom-objects with hom-sets. The best way to accomplish this is to start with the usual definitions and keep reformulating them in a point-free manner --- that is, without naming elements of sets. Let's start with the definition of composition. Normally, it takes a pair of morphisms, one from $\cat{C}(b, c)$ and one from $\cat{C}(a, b)$ and maps it to a morphism from $\cat{C}(a, c)$. In other words it's a mapping: \[\cat{C}(b, c)\times{}\cat{C}(a, b) \to \cat{C}(a, c)\] This is a function between sets --- one of them being the Cartesian product of two hom-sets. This formula can be easily generalized by replacing Cartesian product with something more general. A categorical product would work, but we can go even further and use a completely general tensor product. Next come the identity morphisms. Instead of picking individual elements from hom-sets, we can define them using functions from the singleton set $\cat{1}$: \[j_a \Colon \cat{1} \to \cat{C}(a, a)\] Again, we could replace the singleton set with the terminal object, but we can go even further by replacing it with the unit $i$ of the tensor product. As you can see, objects taken from some monoidal category $\cat{V}$ are good candidates for hom-set replacement. \section{Monoidal Category} We've talked about monoidal categories before, but it's worth restating the definition. A monoidal category defines a tensor product that is a bifunctor: \[\otimes \Colon \cat{V}\times{}\cat{V} \to \cat{V}\] We want the tensor product to be associative, but it's enough to satisfy associativity up to natural isomorphism. This isomorphism is called the associator. Its components are: \[\alpha_{a b c} \Colon (a \otimes b) \otimes c \to a \otimes (b \otimes c)\] It must be natural in all three arguments. A monoidal category must also define a special unit object $i$ that serves as the unit of the tensor product; again, up to natural isomorphism. The two isomorphisms are called, respectively, the left and the right unitor, and their components are: \begin{align*} \lambda_a & \Colon i \otimes a \to a \\ \rho_a & \Colon a \otimes i \to a \end{align*} The associator and the unitors must satisfy coherence conditions: \begin{figure}[H] \centering \begin{tikzcd}[row sep=large] ((a \otimes b) \otimes c) \otimes d \arrow[d, "\alpha_{(a \otimes b)cd}"] \arrow[rr, "\alpha_{abc} \otimes \id_d"] & & (a \otimes (b \otimes c)) \otimes d \arrow[d, "\alpha_{a(b \otimes c)d}"] \\ (a \otimes b) \otimes (c \otimes d) \arrow[rd, "\alpha_{ab(c \otimes d)}"] & & a \otimes ((b \otimes c) \otimes d) \arrow[ld, "\id_a \otimes \alpha_{bcd}"] \\ & a \otimes (b \otimes (c \otimes d)) \end{tikzcd} \end{figure} \begin{figure}[H] \centering \begin{tikzcd}[row sep=large] (a \otimes i) \otimes b \arrow[dr, "\rho_{a} \otimes \id_b"'] \arrow[rr, "\alpha_{aib}"] & & a \otimes (i \otimes b) \arrow[dl, "\id_a \otimes \lambda_b"] \\ & a \otimes b \end{tikzcd} \end{figure} \noindent A monoidal category is called \newterm{symmetric} if there is a natural isomorphism with components: \[\gamma_{a b} \Colon a \otimes b \to b \otimes a\] whose ``square is one'': \[\gamma_{b a} \circ \gamma_{a b} = \idarrow[a \otimes b]\] and which is consistent with the monoidal structure. An interesting thing about monoidal categories is that you may be able to define the internal hom (the function object) as the right adjoint to the tensor product. You may recall that the standard definition of the function object, or the exponential, was through the right adjoint to the categorical product. A category in which such an object existed for any pair of objects was called Cartesian closed. Here is the adjunction that defines the internal hom in a monoidal category: \[\cat{V}(a \otimes b, c) \sim \cat{V}(a, [b, c])\] Following \urlref{http://www.tac.mta.ca/tac/reprints/articles/10/tr10.pdf}{G. M. Kelly}, I'm using the notation ${[}b, c{]}$ for the internal hom. The counit of this adjunction is the natural transformation whose components are called evaluation morphisms: \[\varepsilon_{a b} \Colon ([a, b] \otimes a) \to b\] Notice that, if the tensor product is not symmetric, we may define another internal hom, denoted by ${[}{[}a, c{]}{]}$, using the following adjunction: \[\cat{V}(a \otimes b, c) \sim \cat{V}(b, [[a, c]])\] A monoidal category in which both are defined is called \newterm{biclosed}. An example of a category that is not biclosed is the category of endofunctors in $\Set$, with functor composition serving as tensor product. That's the category we used to define monads. \section{Enriched Category} A category $\cat{C}$ enriched over a monoidal category $\cat{V}$ replaces hom-sets with hom-objects. To every pair of objects $a$ and $b$ in $\cat{C}$ we associate an object $\cat{C}(a, b)$ in $\cat{V}$. We use the same notation for hom-objects as we used for hom-sets, with the understanding that they don't contain morphisms. On the other hand, $\cat{V}$ is a regular (non-enriched) category with hom-sets and morphisms. So we are not entirely rid of sets --- we just swept them under the rug. Since we cannot talk about individual morphisms in $\cat{C}$, composition of morphisms is replaced by a family of morphisms in $\cat{V}$: \[\circ \Colon \cat{C}(b, c) \otimes \cat{C}(a, b) \to \cat{C}(a, c)\] \begin{figure}[H] \centering \includegraphics[width=0.45\textwidth]{images/composition.jpg} \end{figure} \noindent Similarly, identity morphisms are replaced by a family of morphisms in $\cat{V}$: \[j_a \Colon i \to \cat{C}(a, a)\] where $i$ is the tensor unit in $\cat{V}$. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/id.jpg} \end{figure} \noindent Associativity of composition is defined in terms of the associator in $\cat{V}$: \begin{figure}[H] \centering \begin{tikzcd}[column sep=large] (\cat{C}(c,d) \otimes \cat{C}(b,c)) \otimes \cat{C}(a,b) \arrow[r, "\circ\otimes\id"] \arrow[dd, "\alpha"] & \cat{C}(b,d) \otimes \cat{C}(a,b) \arrow[dr, "\circ"] \\ & & \cat{C}(a,d) \\ \cat{C}(c,d) \otimes (\cat{C}(b,c) \otimes \cat{C}(a,b)) \arrow[r, "\id\otimes\circ"] & \cat{C}(c,d) \otimes \cat{C}(a,c) \arrow[ur, "\circ"] \end{tikzcd} \end{figure} \noindent Unit laws are likewise expressed in terms of unitors: \begin{figure}[H] \centering \begin{subfigure} \centering \begin{tikzcd}[row sep=large] \cat{C}(a,b) \otimes i \arrow[rr, "\id \otimes j_a"] \arrow[dr, "\rho"] & & \cat{C}(a,b) \otimes \cat{C}(a,a) \arrow[dl, "\circ"] \\ & \cat{C}(a,b) \end{tikzcd} \end{subfigure} \hspace{1cm} \begin{subfigure} \centering \begin{tikzcd}[row sep=large] i \otimes \cat{C}(a,b) \arrow[rr, "j_b \otimes \id"] \arrow[dr, "\lambda"] & & \cat{C}(b,b) \otimes \cat{C}(a,b) \arrow[dl, "\circ"] \\ & \cat{C}(a,b) \end{tikzcd} \end{subfigure} \end{figure} \section{Preorders} A preorder is defined as a thin category, one in which every hom-set is either empty or a singleton. We interpret a non-empty set $\cat{C}(a, b)$ as the proof that $a$ is less than or equal to $b$. Such a category can be interpreted as enriched over a very simple monoidal category that contains just two objects, $0$ and $1$ (sometimes called $\mathit{False}$ and $\mathit{True}$). Besides the mandatory identity morphisms, this category has a single morphism going from $0$ to $1$, let's call it $0 \to 1$. A simple monoidal structure can be established in it, with the tensor product modeling the simple arithmetic of $0$ and $1$ (i.e., the only non-zero product is $1 \otimes 1$). The identity object in this category is $1$. This is a strict monoidal category, that is, the associator and the unitors are identity morphisms. Since in a preorder the-hom set is either empty or a singleton, we can easily replace it with a hom-object from our tiny category. The enriched preorder $\cat{C}$ has a hom-object $\cat{C}(a, b)$ for any pair of objects $a$ and $b$. If $a$ is less than or equal to $b$, this object is $1$; otherwise it's $0$. Let's have a look at composition. The tensor product of any two objects is $0$, unless both of them are $1$, in which case it's $1$. If it's $0$, then we have two options for the composition morphism: it could be either $\idarrow[0]$ or $0 \to 1$. But if it's $1$, then the only option is $\idarrow[1]$. Translating this back to relations, this says that if $a \leqslant b$ and $b \leqslant c$ then $a \leqslant c$, which is exactly the transitivity law we need. What about the identity? It's a morphism from $1$ to $\cat{C}(a, a)$. There is only one morphism going from $1$, and that's the identity $\idarrow[1]$, so $\cat{C}(a, a)$ must be $1$. It means that $a \leqslant a$, which is the reflexivity law for a preorder. So both transitivity and reflexivity are automatically enforced, if we implement a preorder as an enriched category. \section{Metric Spaces} An interesting example is due to \urlref{http://www.tac.mta.ca/tac/reprints/articles/1/tr1.pdf}{William Lawvere}. He noticed that metric spaces can be defined using enriched categories. A metric space defines a distance between any two objects. This distance is a non-negative real number. It's convenient to include infinity as a possible value. If the distance is infinite, there is no way of getting from the starting object to the target object. There are some obvious properties that have to be satisfied by distances. One of them is that the distance from an object to itself must be zero. The other is the triangle inequality: the direct distance is no larger than the sum of distances with intermediate stops. We don't require the distance to be symmetric, which might seem weird at first but, as Lawvere explained, you can imagine that in one direction you're walking uphill, while in the other you're going downhill. In any case, symmetry may be imposed later as an additional constraint. So how can a metric space be cast into a categorical language? We have to construct a category in which hom-objects are distances. Mind you, distances are not morphisms but hom-objects. How can a hom-object be a number? Only if we can construct a monoidal category $\cat{V}$ in which these numbers are objects. Non-negative real numbers (plus infinity) form a total order, so they can be treated as a thin category. A morphism between two such numbers $x$ and $y$ exists if and only if $x \geqslant y$ (note: this is the opposite direction to the one traditionally used in the definition of a preorder). The monoidal structure is given by addition, with zero serving as the unit object. In other words, the tensor product of two numbers is their sum. A metric space is a category enriched over such a monoidal category. A hom-object $\cat{C}(a, b)$ from object $a$ to $b$ is a non-negative (possibly infinite) number that we will call the distance from $a$ to $b$. Let's see what we get for identity and composition in such a category. By our definitions, a morphism from the tensorial unit, which is the number zero, to a hom-object $\cat{C}(a, a)$ is the relation: \[0 \geqslant \cat{C}(a, a)\] Since $\cat{C}(a, a)$ is a non-negative number, this condition tells us that the distance from $a$ to $a$ is always zero. Check! Now let's talk about composition. We start with the tensor product of two abutting hom-objects, $\cat{C}(b, c) \otimes \cat{C}(a, b)$. We have defined the tensor product as the sum of the two distances. Composition is a morphism in $\cat{V}$ from this product to $\cat{C}(a, c)$. A morphism in $\cat{V}$ is defined as the greater-or-equal relation. In other words, the sum of distances from $a$ to $b$ and from $b$ to $c$ is greater than or equal to the distance from $a$ to $c$. But that's just the standard triangle inequality. Check! By re-casting the metric space in terms of an enriched category, we get the triangle inequality and the zero self-distance ``for free.'' \section{Enriched Functors} The definition of a functor involves the mapping of morphisms. In the enriched setting, we don't have the notion of individual morphisms, so we have to deal with hom-objects in bulk. Hom-objects are objects in a monoidal category $\cat{V}$, and we have morphisms between them at our disposal. It therefore makes sense to define enriched functors between categories when they are enriched over the same monoidal category $\cat{V}$. We can then use morphisms in $\cat{V}$ to map the hom-objects between two enriched categories. An \newterm{enriched functor} $F$ between two categories $\cat{C}$ and $\cat{D}$, besides mapping objects to objects, also assigns, to every pair of objects in $\cat{C}$, a morphism in $\cat{V}$: \[F_{a b} \Colon \cat{C}(a, b) \to \cat{D}(F a, F b)\] A functor is a structure-preserving mapping. For regular functors it meant preserving composition and identity. In the enriched setting, the preservation of composition means that the following diagram commute: \begin{figure}[H] \centering \begin{tikzcd}[column sep=large, row sep=large] \cat{C}(b,c) \otimes \cat{C}(a,b) \arrow[r, "\circ"] \arrow[d, "F_{bc} \otimes F_{ab}"] & \cat{C}(a,c) \arrow[d, "F_{ac}"] \\ \cat{D}(F b, F c) \otimes \cat{D}(F a, F b) \arrow[r, "\circ"] & \cat{D}(F a, F c) \end{tikzcd} \end{figure} \noindent The preservation of identity is replaced by the preservation of the morphisms in $\cat{V}$ that ``select'' the identity: \begin{figure}[H] \centering \begin{tikzcd}[row sep=large] & i \arrow[dl, "j_a"'] \arrow[dr, "j_{F a}"] & \\ \cat{C}(a,a) \arrow[rr, "F_{aa}"] & & \cat{D}(F a, F a) \end{tikzcd} \end{figure} \section{Self Enrichment} A closed symmetric monoidal category may be self-enriched by replacing hom-sets with internal homs (see the definition above). To make this work, we have to define the composition law for internal homs. In other words, we have to implement a morphism with the following signature: \[[b, c] \otimes [a, b] \to [a, c]\] This is not much different from any other programming task, except that, in category theory, we usually use point free implementations. We start by specifying the set whose element it's supposed to be. In this case, it's a member of the hom-set: \[\cat{V}([b, c] \otimes [a, b], [a, c])\] This hom-set is isomorphic to: \[\cat{V}(([b, c] \otimes [a, b]) \otimes a, c)\] I just used the adjunction that defined the internal hom ${[}a, c{]}$. If we can build a morphism in this new set, the adjunction will point us at the morphism in the original set, which we can then use as composition. We construct this morphism by composing several morphisms that are at our disposal. To begin with, we can use the associator $\alpha_{{[}b, c{]}\ {[}a, b{]}\ a}$ to reassociate the expression on the left: \[([b, c] \otimes [a, b]) \otimes a \to [b, c] \otimes ([a, b] \otimes a)\] We can follow it with the counit of the adjunction $\varepsilon_{a b}$: \[[b, c] \otimes ([a, b] \otimes a) \to [b, c] \otimes b\] And use the counit $\varepsilon_{b c}$ again to get to $c$. We have thus constructed a morphism: \[\varepsilon_{b c} \circ (\idarrow[{[b, c]}] \otimes \varepsilon_{a b}) \circ \alpha_{[b, c] [a, b] a}\] that is an element of the hom-set: \[\cat{V}(([b, c] \otimes [a, b]) \otimes a, c)\] The adjunction will give us the composition law we were looking for. Similarly, the identity: \[j_a \Colon i \to [a, a]\] is a member of the following hom-set: \[\cat{V}(i, [a, a])\] which is isomorphic, through adjunction, to: \[\cat{V}(i \otimes a, a)\] We know that this hom-set contains the left identity $\lambda_a$. We can define $j_a$ as its image under the adjunction. A practical example of self-enrichment is the category $\Set$ that serves as the prototype for types in programming languages. We've seen before that it's a closed monoidal category with respect to Cartesian product. In $\Set$, the hom-set between any two sets is itself a set, so it's an object in $\Set$. We know that it's isomorphic to the exponential set, so the external and the internal homs are equivalent. Now we also know that, through self-enrichment, we can use the exponential set as the hom-object and express composition in terms of Cartesian products of exponential objects. \section{Relation to $\cat{2}$-Categories} I talked about $\cat{2}$-categories in the context of $\Cat$, the category of (small) categories. The morphisms between categories are functors, but there is an additional structure: natural transformations between functors. In a $\cat{2}$-category, the objects are often called zero-cells; morphisms, $1$-cells; and morphisms between morphisms, $2$-cells. In $\Cat$ the $0$-cells are categories, $1$-cells are functors, and $2$-cells are natural transformations. But notice that functors between two categories form a category too; so, in $\Cat$, we really have a \emph{hom-category} rather than a hom-set. It turns out that, just like $\Set$ can be treated as a category enriched over $\Set$, $\Cat$ can be treated as a category enriched over $\Cat$. Even more generally, just like every category can be treated as enriched over $\Set$, every $\cat{2}$-category can be considered enriched over $\Cat$. ================================================ FILE: src/content/3.13/topoi.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{I}{ realize that we might} be getting away from programming and diving into hard-core math. But you never know what the next big revolution in programming might bring and what kind of math might be necessary to understand it. There are some very interesting ideas going around, like functional reactive programming with its continuous time, the extension of Haskell's type system with dependent types, or the exploration on homotopy type theory in programming. So far I've been casually identifying types with \emph{sets} of values. This is not strictly correct, because such an approach doesn't take into account the fact that, in programming, we \emph{compute} values, and the computation is a process that takes time and, in extreme cases, might not terminate. Divergent computations are part of every Turing-complete language. There are also foundational reasons why set theory might not be the best fit as the basis for computer science or even math itself. A good analogy is that of set theory being the assembly language that is tied to a particular architecture. If you want to run your math on different architectures, you have to use more general tools. One possibility is to use spaces in place of sets. Spaces come with more structure, and may be defined without recourse to sets. One thing usually associated with spaces is topology, which is necessary to define things like continuity. And the conventional approach to topology is, you guessed it, through set theory. In particular, the notion of a subset is central to topology. Not surprisingly, category theorists generalized this idea to categories other than $\Set$. The type of category that has just the right properties to serve as a replacement for set theory is called a \newterm{topos} (plural: topoi), and it provides, among other things, a generalized notion of a subset. \section{Subobject Classifier} Let's start by trying to express the idea of a subset using functions rather than elements. Any function $f$ from some set $a$ to $b$ defines a subset of $b$--that of the image of $a$ under $f$. But there are many functions that define the same subset. We need to be more specific. To begin with, we might focus on functions that are injective --- ones that don't smush multiple elements into one. Injective functions ``inject'' one set into another. For finite sets, you may visualize injective functions as parallel arrows connecting elements of one set to elements of another. Of course, the first set cannot be larger than the second set, or the arrows would necessarily converge. There is still some ambiguity left: there may be another set $a'$ and another injective function $f'$ from that set to $b$ that picks the same subset. But you can easily convince yourself that such a set would have to be isomorphic to $a$. We can use this fact to define a subset as a family of injective functions that are related by isomorphisms of their domains. More precisely, we say that two injective functions: \begin{align*} f & \Colon a \to b \\ f' & \Colon a' \to b \end{align*} are equivalent if there is an isomorphism: \[h \Colon a \to a'\] such that: \[f = f' \circ h\] Such a family of equivalent injections defines a subset of $b$. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/subsetinjection.jpg} \end{figure} \noindent This definition can be lifted to an arbitrary category if we replace injective functions with monomorphism. Just to remind you, a monomorphism $m$ from $a$ to $b$ is defined by its universal property. For any object $c$ and any pair of morphisms: \begin{align*} g & \Colon c \to a \\ g' & \Colon c \to a \end{align*} such that: \[m \circ g = m \circ g'\] it must be that $g = g'$. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/monomorphism.jpg} \end{figure} \noindent On sets, this definition is easier to understand if we consider what it would mean for a function $m$ \emph{not} to be a monomorphism. It would map two different elements of $a$ to a single element of $b$. We could then find two functions $g$ and $g'$ that differ only at those two elements. The postcomposition with $m$ would then mask this difference. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/notmono.jpg} \end{figure} \noindent There is another way of defining a subset: using a single function called the characteristic function. It's a function $\chi$ from the set $b$ to a two-element set $\Omega$. One element of this set is designated as ``true'' and the other as ``false.'' This function assigns ``true'' to those elements of $b$ that are members of the subset, and ``false'' to those that aren't. It remains to specify what it means to designate an element of $\Omega$ as ``true.'' We can use the standard trick: use a function from a singleton set to $\Omega$. We'll call this function $\mathit{true}$: \[\mathit{true} \Colon 1 \to \Omega\] \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/true.jpg} \end{figure} \noindent These definitions can be combined in such a way that they not only define what a subobject is, but also define the special object $\Omega$ without talking about elements. The idea is that we want the morphism $\mathit{true}$ to represent a ``generic'' subobject. In $\Set$, it picks a single-element subset from a two-element set $\Omega$. This is as generic as it gets. It's clearly a proper subset, because $\Omega$ has one more element that's \emph{not} in that subset. In a more general setting, we define $\mathit{true}$ to be a monomorphism from the terminal object to the \emph{classifying object} $\Omega$. A \emph{subobject classifier} consists of $\Omega$ together with $\mathit{true}$. But we have to define the classifying object. We need a universal property that links this object to the characteristic function. It turns out that, in $\Set$, the pullback of $\mathit{true}$ along the characteristic function $\chi$ defines both the subset $a$ and the injective function that embeds it in $b$. Here's the pullback diagram: \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/pullback.jpg} \end{figure} \noindent Let's analyze this diagram. The pullback equation is: \[\mathit{true} \circ \mathit{unit} = \chi \circ f\] The function $\mathit{true} \circ \mathit{unit}$ maps every element of $a$ to ``true.'' Therefore $f$ must map all elements of $a$ to those elements of $b$ for which $\chi$ is ``true.'' These are, by definition, the elements of the subset that is specified by the characteristic function $\chi$. The image of $f$ is indeed the subset in question. The universality of the pullback guarantees that $f$ is injective. This pullback diagram can be used to define the classifying object in categories other than $\Set$. Such a category must have a terminal object, which will let us define the monomorphism $\mathit{true}$. It must also have pullbacks --- the actual requirement is that it must have all finite limits (a pullback is an example of a finite limit). Under those assumptions, we define the classifying object $\Omega$ by the property that, for every monomorphism $f$ there is a unique morphism $\chi$ that completes the pullback diagram. Let's analyze the last statement. When we construct a pullback, we are given three objects $\Omega$, $b$ and $1$; and two morphisms, $\mathit{true}$ and $\chi$. The existence of a pullback means that we can find the best such object $a$, equipped with two morphisms $f$ and $\mathit{unit}$ (the latter is uniquely determined by the definition of the terminal object), that make the diagram commute. Here we are solving a different system of equations. We are solving for $\Omega$ and $\mathit{true}$ while varying both $a$ \emph{and} $b$. For a given $a$ and $b$ there may or may not be a monomorphism $f \Colon a \to b$. But if there is one, we want it to be a pullback of some $\chi$. Moreover, we want this $\chi$ to be uniquely determined by $f$. We can't say that there is a one-to-one correspondence between monomorphisms $f$ and characteristic functions $\chi$, because a pullback is only unique up to isomorphism. But remember our earlier definition of a subset as a family of equivalent injections. We can generalize it by defining a subobject of $b$ as a family of equivalent monomorphisms to $b$. This family of monomorphisms is in one-to-one correspondence with the family of equivalent pullbacks of our diagram. We can thus define a set of subobjects of $b$, $\mathit{Sub}(b)$, as a family of monomorphisms, and see that it is isomorphic to the set of morphisms from $b$ to $\Omega$: \[\mathit{Sub}(b) \cong \cat{C}(b, \Omega)\] This happens to be a natural isomorphism of two functors. In other words, $\mathit{Sub}(-)$ is a representable (contravariant) functor whose representation is the object $\Omega$. \section{Topos} A topos is a category that: \begin{enumerate} \tightlist \item Is Cartesian closed: It has all products, the terminal object, and exponentials (defined as right adjoints to products), \item Has limits for all finite diagrams, \item Has a subobject classifier $\Omega$. \end{enumerate} This set of properties makes a topos a shoe-in for $\Set$ in most applications. It also has additional properties that follow from its definition. For instance, a topos has all finite colimits, including the initial object. It would be tempting to define the subobject classifier as a coproduct (sum) of two copies of the terminal object --that's what it is in $\Set$--- but we want to be more general than that. Topoi in which this is true are called Boolean. \section{Topoi and Logic} In set theory, a characteristic function may be interpreted as defining a property of the elements of a set --- a \newterm{predicate} that is true for some elements and false for others. The predicate $\mathit{isEven}$ selects a subset of even numbers from the set of natural numbers. In a topos, we can generalize the idea of a predicate to be a morphism from object $a$ to $\Omega$. This is why $\Omega$ is sometimes called the truth object. Predicates are the building blocks of logic. A topos contains all the necessary instrumentation to study logic. It has products that correspond to logical conjunctions (logical \emph{and}), coproducts for disjunctions (logical \emph{or}), and exponentials for implications. All standard axioms of logic hold in a topos except for the law of excluded middle (or, equivalently, double negation elimination). That's why the logic of a topos corresponds to constructive or intuitionistic logic. Intuitionistic logic has been steadily gaining ground, finding unexpected support from computer science. The classical notion of excluded middle is based on the belief that there is absolute truth: Any statement is either true or false or, as Ancient Romans would say, \emph{tertium non datur} (there is no third option). But the only way we can know whether something is true or false is if we can prove or disprove it. A proof is a process, a computation --- and we know that computations take time and resources. In some cases, they may never terminate. It doesn't make sense to claim that a statement is true if we cannot prove it in finite amount of time. A topos with its more nuanced truth object provides a more general framework for modeling interesting logics. \section{Challenges} \begin{enumerate} \tightlist \item Show that the function $f$ that is the pullback of $\mathit{true}$ along the characteristic function must be injective. \end{enumerate} ================================================ FILE: src/content/3.14/code/haskell/snippet01.hs ================================================ type Two = Either () () ================================================ FILE: src/content/3.14/code/haskell/snippet02.hs ================================================ raise :: () -> a ================================================ FILE: src/content/3.14/code/haskell/snippet03.hs ================================================ type Maybe a = Either () a ================================================ FILE: src/content/3.14/code/haskell/snippet04.hs ================================================ data Maybe a = Nothing | Just a ================================================ FILE: src/content/3.14/code/ocaml/snippet01.ml ================================================ type ('a, 'b) either = | Left of 'a | Right of 'b type two = (unit, unit) either ================================================ FILE: src/content/3.14/code/ocaml/snippet02.ml ================================================ val raise : unit -> 'a ================================================ FILE: src/content/3.14/code/ocaml/snippet03.ml ================================================ type 'a option = (unit, 'a) either ================================================ FILE: src/content/3.14/code/ocaml/snippet04.ml ================================================ type 'a option = | None | Some of 'a ================================================ FILE: src/content/3.14/code/reason/snippet01.re ================================================ type either('a, 'b) = | Left('a) | Right('b); type two = either(unit, unit); ================================================ FILE: src/content/3.14/code/reason/snippet02.re ================================================ let raise: unit => 'a; ================================================ FILE: src/content/3.14/code/reason/snippet03.re ================================================ type option('a) = either(unit, 'a); ================================================ FILE: src/content/3.14/code/reason/snippet04.re ================================================ type option('a) = | None | Some('a); ================================================ FILE: src/content/3.14/code/scala/snippet01.scala ================================================ type Two = Either[Unit, Unit] ================================================ FILE: src/content/3.14/code/scala/snippet02.scala ================================================ def raise: Unit => A ================================================ FILE: src/content/3.14/code/scala/snippet03.scala ================================================ type Option[A] = Either[Unit, A] ================================================ FILE: src/content/3.14/code/scala/snippet04.scala ================================================ sealed trait Option[+A] case object None extends Option[Nothing] case class Some[A](a: A) extends Option[A] ================================================ FILE: src/content/3.14/lawvere-theories.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{N}{owadays you can't} talk about functional programming without mentioning monads. But there is an alternative universe in which, by chance, Eugenio Moggi turned his attention to Lawvere theories rather than monads. Let's explore that universe. \section{Universal Algebra} There are many ways of describing algebras at various levels of abstraction. We try to find a general language to describe things like monoids, groups, or rings. At the simplest level, all these constructions define \emph{operations} on elements of a set, plus some \emph{laws} that must be satisfied by these operations. For instance, a monoid can be defined in terms of a binary operation that is associative. We also have a unit element and unit laws. But with a little bit of imagination we can turn the unit element to a nullary operation --- an operation that takes no arguments and returns a special element of the set. If we want to talk about groups, we add a unary operator that takes an element and returns its inverse. There are corresponding left and right inverse laws to go with it. A ring defines two binary operators plus some more laws. And so on. The big picture is that an algebra is defined by a set of $n$-ary operations for various values of $n$, and a set of equational identities. These identities are all universally quantified. The associativity equation must be satisfied for all possible combinations of three elements, and so on. Incidentally, this eliminates fields from consideration, for the simple reason that zero (unit with respect to addition) has no inverse with respect to multiplication. The inverse law for a field can't be universally quantified. This definition of a universal algebra can be extended to categories other than $\Set$, if we replace operations (functions) with morphisms. Instead of a set, we select an object $a$ (called a generic object). A unary operation is just an endomorphism of $a$. But what about other arities (\newterm{arity} is the number of arguments for a given operation)? A binary operation (arity 2) can be defined as a morphism from the product $a\times{}a$ back to $a$. A general $n$-ary operation is a morphism from the $n^\text{th}$ power of $a$ to $a$: \[\alpha_n \Colon a^n \to a\] A nullary operation is a morphism from the terminal object (the zeroth power of $a$). So all we need in order to define any algebra is a category whose objects are powers of one special object $a$. The specific algebra is encoded in the hom-sets of this category. This is a Lawvere theory in a nutshell. The derivation of Lawvere theories goes through many steps, so here's the roadmap: \begin{enumerate} \tightlist \item Category of finite sets $\cat{FinSet}$. \item Its skeleton $\cat{F}$. \item Its opposite $\Fop$. \item Lawvere theory $\cat{L}$: an object in the category $\cat{Law}$. \item Model $M$ of a Lawvere category: an object in the category\\ $\cat{Mod}(\cat{L}, \Set)$. \end{enumerate} \begin{figure}[H] \centering \includegraphics[width=0.8\textwidth]{images/lawvere1.png} \end{figure} \section{Lawvere Theories} All Lawvere theories share a common backbone. All objects in a Lawvere theory are generated from just one object using products (really, just powers). But how do we define these products in a general category? It turns out that we can define products using a mapping from a simpler category. In fact this simpler category may define coproducts instead of products, and we'll use a \emph{contravariant} functor to embed them in our target category. A contravariant functor turns coproducts into products and injections to projections. The natural choice for the backbone of a Lawvere category is the category of finite sets, $\cat{FinSet}$. It contains the empty set $\varnothing$, a singleton set $1$, a two-element set $2$, and so on. All objects in this category can be generated from the singleton set using coproducts (treating the empty set as a special case of a nullary coproduct). For instance, a two-element set is a sum of two singletons, $2 = 1 + 1$, as expressed in Haskell: \src{snippet01} However, even though it's natural to think that there's only one empty set, there may be many distinct singleton sets. In particular, the set $1 + \varnothing$ is different from the set $\varnothing + 1$, and different from $1$ --- even though they are all isomorphic. The coproduct in the category of sets is not associative. We can remedy that situation by building a category that identifies all isomorphic sets. Such a category is called a \newterm{skeleton}. In other words, the backbone of any Lawvere theory is the skeleton $\cat{F}$ of $\cat{FinSet}$. The objects in this category can be identified with natural numbers (including zero) that correspond to the element count in $\cat{FinSet}$. Coproduct plays the role of addition. Morphisms in $\cat{F}$ correspond to functions between finite sets. For instance, there is a unique morphism from $\varnothing$ to $n$ (empty set being the initial object), no morphisms from $n$ to $\varnothing$ (except $\varnothing \to \varnothing$), $n$ morphisms from $1$ to $n$ (the injections), one morphism from $n$ to $1$, and so on. Here, $n$ denotes an object in $\cat{F}$ corresponding to all $n$-element sets in $\cat{FinSet}$ that have been identified through isomorphisms. Using the category $\cat{F}$ we can formally define a \newterm{Lawvere theory} as a category $\cat{L}$ equipped with a special functor: \[I_{\cat{L}} \Colon \Fop \to \cat{L}\] This functor must be a bijection on objects and it must preserve finite products (products in $\Fop$ are the same as coproducts in $\cat{F}$): \[I_{\cat{L}} (m\times{}n) = I_{\cat{L}} m\times{}I_{\cat{L}} n\] You may sometimes see this functor characterized as identity-on-objects, which means that the objects in $\cat{F}$ and $\cat{L}$ are the same. We will therefore use the same names for them --- we'll denote them by natural numbers. Keep in mind though that objects in $\cat{F}$ are not the same as sets (they are classes of isomorphic sets). The hom-sets in $\cat{L}$ are, in general, richer than those in $\Fop$. They may contain morphisms other than the ones corresponding to functions in $\cat{FinSet}$ (the latter are sometimes called \newterm{basic product operations}). Equational laws of a Lawvere theory are encoded in those morphisms. The key observation is that the singleton set $1$ in $\cat{F}$ is mapped to some object that we also call $1$ in $\cat{L}$, and all the other objects in $\cat{L}$ are automatically powers of this object. For instance, the two-element set $2$ in $\cat{F}$ is the coproduct $1 + 1$, so it must be mapped to a product $1 \times 1$ (or $1^2$) in $\cat{L}$. In this sense, the category $\cat{F}$ behaves like the logarithm of $\cat{L}$. Among morphisms in $\cat{L}$ we have those transferred by the functor $I_{\cat{L}}$ from $\cat{F}$. They play a structural role in $\cat{L}$. In particular coproduct injections $i_k$ become product projections $p_k$. A useful intuition is to imagine the projection: \[p_k \Colon 1^n \to 1\] as the prototype for a function of $n$ variables that ignores all but the $k^\text{th}$ variable. Conversely, constant morphisms $n \to 1$ in $\cat{F}$ become diagonal morphisms $1 \to 1^n$ in $\cat{L}$. They correspond to duplication of variables. The interesting morphisms in $\cat{L}$ are the ones that define $n$-ary operations other than projections. It's those morphisms that distinguish one Lawvere theory from another. These are the multiplications, the additions, the selections of unit elements, and so on, that define the algebra. But to make $\cat{L}$ a full category, we also need compound operations $n \to m$ (or, equivalently, $1^n \to 1^m$). Because of the simple structure of the category, they turn out to be products of simpler morphisms of the type $n \to 1$. This is a generalization of the statement that a function that returns a product is a product of functions; as we've seen earlier, the hom-functor is continuous. \begin{figure}[H] \centering \includegraphics[width=0.8\textwidth]{images/lawvere1.png} \caption{Lawvere theory $\cat{L}$ is based on $\Fop$, from which it inherits the ``boring'' morphisms that define the products. It adds the ``interesting'' morphisms that describe the $n$-ary operations (dotted arrows).} \end{figure} Lawvere theories form a category $\cat{Law}$, in which morphisms are functors that preserve finite products and commute with the functors $I$. Given two such theories, $(\cat{L}, I_{\cat{L}})$ and $(\cat{L'}, I'_{\cat{L'}})$, a morphism between them is a functor $F \Colon \cat{L} \to \cat{L'}$ such that: \begin{gather*} F (m \times n) = F m \times F n \\ F \circ I_{\cat{L}} = I'_{\cat{L'}} \end{gather*} Morphisms between Lawvere theories encapsulate the idea of the interpretation of one theory inside another. For instance, group multiplication may be interpreted as monoid multiplication if we ignore inverses. The simplest trivial example of a Lawvere category is $\Fop$ itself (corresponding to the choice of the identity functor for $I_{\cat{L}}$). This Lawvere theory that has no operations or laws happens to be the initial object in $\cat{Law}$. At this point it would be very helpful to present a non-trivial example of a Lawvere theory, but it would be hard to explain it without first understanding what models are. \section{Models of Lawvere Theories} The key to understand Lawvere theories is to realize that one such theory generalizes a lot of individual algebras that share the same structure. For instance, the Lawvere theory of monoids describes the essence of being a monoid. It must be valid for all monoids. A particular monoid becomes a model of such a theory. A model is defined as a functor from the Lawvere theory $\cat{L}$ to the category of sets $\Set$. (There are generalizations of Lawvere theories that use other categories for models but here I'll just concentrate on $\Set$.) Since the structure of $\cat{L}$ depends heavily on products, we require that such a functor preserve finite products. A model of $\cat{L}$, also called the algebra over the Lawvere theory $\cat{L}$, is therefore defined by a functor: \begin{gather*} M \Colon \cat{L} \to \Set \\ M (a \times b) \cong M a \times M b \end{gather*} Notice that we require the preservation of products only \emph{up to isomorphism}. This is very important, because strict preservation of products would eliminate most interesting theories. The preservation of products by models means that the image of $M$ in $\Set$ is a sequence of sets generated by powers of the set $M 1$ --- the image of the object $1$ from $\cat{L}$. Let's call this set $a$. (This set is sometimes called a \emph{sort}, and such an algebra is called \newterm{single-sorted}. There exist generalizations of Lawvere theories to multi-sorted algebras.) In particular, binary operations from $\cat{L}$ are mapped to functions: \[a \times a \to a\] As with any functor, it's possible that multiple morphisms in $\cat{L}$ are collapsed to the same function in $\Set$. Incidentally, the fact that all laws are universally quantified equalities means that every Lawvere theory has a trivial model: a constant functor mapping all objects to the singleton set, and all morphisms to the identity function on it. A general morphism in $\cat{L}$ of the form $m \to n$ is mapped to a function: \[a^m \to a^n\] If we have two different models, $M$ and $N$, a natural transformation between them is a family of functions indexed by $n$: \[\mu_n \Colon M n \to N n\] or, equivalently: \[\mu_n \Colon a^n \to b^n\] where $b = N 1$. Notice that the naturality condition guarantees the preservation of $n$-ary operations: \[N f \circ \mu_n = \mu_1 \circ M f\] where $f \Colon n \to 1$ is an $n$-ary operation in $\cat{L}$. The functors that define models form a category of models, $\cat{Mod}(\cat{L}, \Set)$, with natural transformations as morphisms. Consider a model for the trivial Lawvere category $\Fop$. Such a model is completely determined by its value at $1$, $M 1$. Since $M 1$ can be any set, there are as many of these models as there are sets in $\Set$. Moreover, every morphism in $\cat{Mod}(\Fop, \Set)$ (a natural transformation between functors $M$ and $N$) is uniquely determined by its component at $1$. Conversely, every function $M 1 \to N 1$ induces a natural transformation between the two models $M$ and $N$. Therefore $\cat{Mod}(\Fop, \Set)$ is equivalent to $\Set$. \section{The Theory of Monoids} The simplest nontrivial example of a Lawvere theory describes the structure of monoids. It is a single theory that distills the structure of all possible monoids, in the sense that the models of this theory span the whole category $\cat{Mon}$ of monoids. We've already seen a \hyperref[free-monoids]{universal construction}, which showed that every monoid can be obtained from an appropriate free monoid by identifying a subset of morphisms. So a single free monoid already generalizes a whole lot of monoids. There are, however, infinitely many free monoids. The Lawvere theory for monoids $\cat{L}_{\cat{Mon}}$ combines all of them in one elegant construction. Every monoid must have a unit, so we have to have a special morphism $\eta$ in $\cat{L}_{\cat{Mon}}$ that goes from $0$ to $1$. Notice that there can be no corresponding morphism in $\cat{F}$. Such a morphism would go in the opposite direction, from $1$ to $0$ which, in $\cat{FinSet}$, would be a function from the singleton set to the empty set. No such function exists. Next, consider morphisms $2 \to 1$, members of $\cat{L}_{\cat{Mon}}(2, 1)$, which must contain prototypes of all binary operations. When constructing models in $\cat{Mod}(\cat{L}_{\cat{Mon}}, \Set)$, these morphisms will be mapped to functions from the Cartesian product $M 1 \times M 1$ to $M 1$. In other words, functions of two arguments. The question is: how many functions of two arguments can one implement using only the monoidal operator. Let's call the two arguments $a$ and $b$. There is one function that ignores both arguments and returns the monoidal unit. Then there are two projections that return $a$ and $b$, respectively. They are followed by functions that return $ab$, $ba$, $aa$, $bb$, $aab$, and so on\ldots{} In fact there are as many such functions of two arguments as there are elements in the free monoid with generators $a$ and $b$. Notice that $\cat{L}_{\cat{Mon}}(2, 1)$ must contain all those morphisms because one of the models is the free monoid. In a free monoid they correspond to distinct functions. Other models may collapse multiple morphisms in $\cat{L}_{\cat{Mon}}(2, 1)$ down to a single function, but not the free monoid. If we denote the free monoid with $n$ generators $n^*$, we may identify the hom-set $\cat{L}(2, 1)$ with the hom-set $\cat{Mon}(1^*, 2^*)$ in $\cat{Mon}$, the category of monoids. In general, we pick $\cat{L}_{\cat{Mon}}(m, n)$ to be $\cat{Mon}(n^*, m^*)$. In other words, the category $\cat{L}_{\cat{Mon}}$ is the opposite of the category of free monoids. The category of \emph{models} of the Lawvere theory for monoids,\\ $\cat{Mod}(\cat{L}_{\cat{Mon}}, \Set)$, is equivalent to the category of all monoids, $\cat{Mon}$. \section{Lawvere Theories and Monads} As you may remember, algebraic theories can be described using monads --- in particular \hyperref[algebras-for-monads]{algebras for monads}. It should be no surprise then that there is a connection between Lawvere theories and monads. First, let's see how a Lawvere theory induces a monad. It does it through an \hyperref[free-forgetful-adjunctions]{adjunction} between a forgetful functor and a free functor. The forgetful functor $U$ assigns a set to each model. This set is given by evaluating the functor $M$ from $\cat{Mod}(\cat{L}, \Set)$ at the object $1$ in $\cat{L}$. Another way of deriving $U$ is by exploiting the fact that $\Fop$ is the initial object in $\cat{Law}$. It means that, for any Lawvere theory $\cat{L}$, there is a unique functor $\Fop \to \cat{L}$. This functor induces the opposite functor on models (since models are functors \emph{from} theories to sets): \[\cat{Mod}(\cat{L}, \Set) \to \cat{Mod}(\Fop, \Set)\] But, as we discussed, the category of models of $\Fop$ is equivalent to $\Set$, so we get the forgetful functor: \[U \Colon \cat{Mod}(\cat{L}, \Set) \to \Set\] It can be shown that so defined $U$ always has a left adjoint, the free functor $F$. This is easily seen for finite sets. The free functor $F$ produces free algebras. A free algebra is a particular model in $\cat{Mod}(\cat{L}, \Set)$ that is generated from a finite set of generators $n$. We can implement $F$ as the representable functor: \[\cat{L}(n, -) \Colon \cat{L} \to \Set\] To show that it's indeed free, all we have to do is to prove that it's a left adjoint to the forgetful functor: \[\cat{Mod}(\cat{L}, \Set)(\cat{L}(n, -), M) \cong \Set(n, U(M))\] Let's simplify the right hand side: \[\Set(n, U(M)) \cong \Set(n, M 1) \cong (M 1)^n \cong M n\] (I used the fact that a set of morphisms is isomorphic to the exponential which, in this case, is just the iterated product.) The adjunction is the result of the Yoneda lemma: \[[\cat{L}, \Set](\cat{L}(n, -), M) \cong M n\] Together, the forgetful and the free functor define a \hyperref[monads-categorically]{monad} $T = U \circ F$ on $\Set$. Thus every Lawvere theory generates a monad. It turns out that the category of \hyperref[algebras-for-monads]{algebras for this monad} is equivalent to the category of models. You may recall that monad algebras define ways to evaluate expressions that are formed using monads. A Lawvere theory defines $n$-ary operations that can be used to generate expressions. Models provide means to evaluate these expressions. The connection between monads and Lawvere theories doesn't go both ways, though. Only finitary monads lead to Lawvere theories. A finitary monad is based on a finitary functor. A finitary functor on $\Set$ is fully determined by its action on finite sets. Its action on an arbitrary set $a$ can be evaluated using the following coend: \[F a = \int^n a^n \times (F n)\] Since the coend generalizes a coproduct, or a sum, this formula is a generalization of a power series expansion. Or we can use the intuition that a functor is a generalized container. In that case a finitary container of $a$s can be described as a sum of shapes and contents. Here, $F n$ is a set of shapes for storing $n$ elements, and the contents is an $n$-tuple of elements, itself an element of $a^n$. For instance, a list (as a functor) is finitary, with one shape for every arity. A tree has more shapes per arity, and so on. First off, all monads that are generated from Lawvere theories are finitary and they can be expressed as coends: \[T_{\cat{L}} a = \int^n a^n \times \cat{L}(n, 1)\] Conversely, given any finitary monad $T$ on $\Set$, we can construct a Lawvere theory. We start by constructing a Kleisli category for $T$. As you may remember, a morphism in a Kleisli category from $a$ to $b$ is given by a morphism in the underlying category: \[a \to T b\] When restricted to finite sets, this becomes: \[m \to T n\] The category opposite to this Kleisli category, $\cat{Kl}^\mathit{op}_{T}$, restricted to finite sets, is the Lawvere theory in question. In particular, the hom-set $\cat{L}(n, 1)$ that describes $n$-ary operations in $\cat{L}$ is given by the hom-set $\cat{Kl}_{T}(1, n)$. It turns out that most monads that we encounter in programming are finitary, with the notable exception of the continuation monad. It is possible to extend the notion of Lawvere theory beyond finitary operations. \section{Monads as Coends} Let's explore the coend formula in more detail. \[T_{\cat{L}} a = \int^n a^n \times \cat{L}(n, 1)\] To begin with, this coend is taken over a profunctor $P$ in $\cat{F}$ defined as: \[P n m = a^n \times \cat{L}(m, 1)\] This profunctor is contravariant in the first argument, $n$. Consider how it lifts morphisms. A morphism in $\cat{FinSet}$ is a mapping of finite sets $f \Colon m \to n$. Such a mapping describes a selection of $m$ elements from an $n$-element set (repetitions are allowed). It can be lifted to the mapping of powers of $a$, namely (notice the direction): \[a^n \to a^m\] The lifting simply selects $m$ elements from a tuple of $n$ elements\\ $(a_1, a_2, \ldots{}, a_n)$ (possibly with repetitions). \begin{figure}[H] \centering \includegraphics[width=0.5\textwidth]{images/liftpower.png} \end{figure} \noindent For instance, let's take $f_k \Colon 1 \to n$ --- a selection of the $k^\text{th}$ element from an $n$-element set. It lifts to a function that takes a $n$-tuple of elements of $a$ and returns the $k^\text{th}$ one. Or let's take $f \Colon m \to 1$ --- a constant function that maps all $m$ elements to one. Its lifting is a function that takes a single element of $a$ and duplicates it $m$ times: \[\lambda{}x \to (\underbrace{x, x, \ldots{}, x}_{m})\] You might notice that it's not immediately obvious that the profunctor in question is covariant in the second argument. The hom-functor $\cat{L}(m, 1)$ is actually contravariant in $m$. However, we are taking the coend not in the category $\cat{L}$ but in the category $\cat{F}$. The coend variable $n$ goes over finite sets (or the skeletons of such). The category $\cat{L}$ contains the opposite of $\cat{F}$, so a morphism $m \to n$ in $\cat{F}$ is a member of $\cat{L}(n, m)$ in $\cat{L}$ (the embedding is given by the functor $I_{\cat{L}}$). Let's check the functoriality of $\cat{L}(m, 1)$ as a functor from $\cat{F}$ to $\Set$. We want to lift a function $f \Colon m \to n$, so our goal is to implement a function from $\cat{L}(m, 1)$ to $\cat{L}(n, 1)$. Corresponding to the function $f$ there is a morphism in $\cat{L}$ from $n$ to $m$ (notice the direction). Precomposing this morphism with $\cat{L}(m, 1)$ gives us a subset of $\cat{L}(n, 1)$. \begin{figure}[H] \centering \begin{tikzcd}[column sep=large] \cat{L}(m, 1) \arrow[r] & \cat{L}(n, 1)\\ {}^m \bullet \arrow[r, "f"'] & \bullet^n \end{tikzcd} \end{figure} \noindent Notice that, by lifting a function $1 \to n$ we can go from $\cat{L}(1, 1)$ to $\cat{L}(n, 1)$. We'll use this fact later on. The product of a contravariant functor $a^n$ and a covariant functor $\cat{L}(m, 1)$ is a profunctor $\Fop \times \cat{F} \to \Set$. Remember that a coend can be defined as a coproduct (disjoint sum) of all the diagonal members of a profunctor, in which some elements are identified. The identifications correspond to cowedge conditions. Here, the coend starts as the disjoint sum of sets $a^n \times \cat{L}(n, 1)$ over all $n$s. The identifications can be generated by expressing the \hyperref[ends-and-coends]{coend as a coequalizer}. We start with an off-diagonal term $a^n \times \cat{L}(m, 1)$. To get to the diagonal, we can apply a morphism $f \Colon m \to n$ either to the first or the second component of the product. The two results are then identified. \begin{figure}[H] \centering \begin{tikzcd} & a^n \times \cat{L}(m, 1) \arrow[dl, "\langle f {,} \id \rangle"'] \arrow[dr, "\langle \id {,} f \rangle"] & \\ a^m \times \cat{L}(m, 1) & \scalebox{2.5}[1]{\sim} & a^n \times \cat{L}(n, 1) \\ & f \Colon m \to n & \end{tikzcd} \end{figure} \noindent I have shown before that the lifting of $f \Colon 1 \to n$ results in these two transformations: \[a^n \to a\] and: \[\cat{L}(1, 1) \to \cat{L}(n, 1)\] Therefore, starting from $a^n \times \cat{L}(1, 1)$ we can reach both: \[a \times \cat{L}(1, 1)\] when we lift $\langle f, \id \rangle$ and: \[a^n \times \cat{L}(n, 1)\] when we lift $\langle \id, f \rangle$. This doesn't mean, however, that all elements of $a^n \times \cat{L}(n, 1)$ can be identified with $a \times \cat{L}(1, 1)$. That's because not all elements of $\cat{L}(n, 1)$ can be reached from $\cat{L}(1, 1)$. Remember that we can only lift morphisms from $\cat{F}$. A non-trivial $n$-ary operation in $\cat{L}$ cannot be constructed by lifting a morphism $f \Colon 1 \to n$. In other words, we can only identify all addends in the coend formula for which $\cat{L}(n, 1)$ can be reached from $\cat{L}(1, 1)$ through the application of basic morphisms. They are all equivalent to $a \times \cat{L}(1, 1)$. Basic morphisms are the ones that are images of morphisms in $\cat{F}$. Let's see how this works in the simplest case of the Lawvere theory, the $\Fop$ itself. In such a theory, every $\cat{L}(n, 1)$ can be reached from $\cat{L}(1, 1)$. This is because $\cat{L}(1, 1)$ is a singleton containing just the identity morphism, and $\cat{L}(n, 1)$ only contains morphisms corresponding to injections $1 \to n$ in $\cat{F}$, which \emph{are} basic morphisms. Therefore all the addends in the coproduct are equivalent and we get: \[T a = a \times \cat{L}(1, 1) = a\] which is the identity monad. \section{Lawvere Theory of Side Effects} Since there is such a strong connection between monads and Lawvere theories, it's natural to ask the question if Lawvere theories could be used in programming as an alternative to monads. The major problem with monads is that they don't compose nicely. There is no generic recipe for building monad transformers. Lawvere theories have an advantage in this area: they can be composed using coproducts and tensor products. On the other hand, only finitary monads can be easily converted to Lawvere theories. The outlier here is the continuation monad. There is ongoing research in this area (see bibliography). To give you a taste of how a Lawvere theory can be used to describe side effects, I'll discuss the simple case of exceptions that are traditionally implemented using the \code{Maybe} monad. The \code{Maybe} monad is generated by the Lawvere theory with a single nullary operation $0 \to 1$. A model of this theory is a functor that maps $1$ to some set $a$, and maps the nullary operation to a function: \src{snippet02} We can recover the \code{Maybe} monad using the coend formula. Let's consider what the addition of the nullary operation does to the hom-sets $\cat{L}(n, 1)$. Besides creating a new element in $\cat{L}(0, 1)$ (which is absent from $\Fop$), it also adds new morphisms to $\cat{L}(n, 1)$. These are the results of composing morphism of the type $n \to 0$ with our $0 \to 1$. Such contributions are all identified with $a^0 \times \cat{L}(0, 1)$ in the coend formula, because they can be obtained from: \[a^n \times \cat{L}(0, 1)\] by lifting $0 \to n$ in two different ways. \begin{figure}[H] \centering \begin{tikzcd} & a^n \times \cat{L}(0, 1) \arrow[dl, "\langle f {,} \id \rangle"'] \arrow[dr, "\langle \id {,} f \rangle"] & \\ a^0 \times \cat{L}(0, 1) & \scalebox{2.5}[1]{\sim} & a^n \times \cat{L}(n, 1) \\ & f \Colon 0 \to n & \end{tikzcd} \end{figure} \noindent The coend reduces to: \[T_{\cat{L}} a = a^0 + a^1\] or, using Haskell notation: \src{snippet03} which is equivalent to: \src{snippet04} Notice that this Lawvere theory only supports the raising of exceptions, not their handling. \section{Challenges} \begin{enumerate} \tightlist \item Enumerate all morphisms between $2$ and $3$ in $\cat{F}$ (the skeleton of $\cat{FinSet}$). \item Show that the category of models for the Lawvere theory of monoids is equivalent to the category of monad algebras for the list monad. \item The Lawvere theory of monoids generates the list monad. Show that its binary operations can be generated using the corresponding Kleisli arrows. \item $\cat{FinSet}$ is a subcategory of $\Set$ and there is a functor that embeds it in $\Set$. Any functor on $\Set$ can be restricted to $\cat{FinSet}$. Show that a finitary functor is the left Kan extension of its own restriction. \end{enumerate} \section{Further Reading} \begin{enumerate} \tightlist \item \urlref{http://www.tac.mta.ca/tac/reprints/articles/5/tr5.pdf}{Functorial Semantics of Algebraic Theories}, F. William Lawvere \item \urlref{http://homepages.inf.ed.ac.uk/gdp/publications/Comp_Eff_Monads.pdf}{Notions of computation determine monads}, Gordon Plotkin and John Power \end{enumerate} ================================================ FILE: src/content/3.15/monads-monoids-and-categories.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{T}{here is no good place} to end a book on category theory. There's always more to learn. Category theory is a vast subject. At the same time, it's obvious that the same themes, concepts, and patterns keep showing up over and over again. There is a saying that all concepts are Kan extensions and, indeed, you can use Kan extensions to derive limits, colimits, adjunctions, monads, the Yoneda lemma, and much more. The notion of a category itself arises at all levels of abstraction, and so does the concept of a monoid and a monad. Which one is the most basic? As it turns out they are all interrelated, one leading to another in a never-ending cycle of abstractions. I decided that showing these interconnections might be a good way to end this book. \section{Bicategories} One of the most difficult aspects of category theory is the constant switching of perspectives. Take the category of sets, for instance. We are used to defining sets in terms of elements. An empty set has no elements. A singleton set has one element. A Cartesian product of two sets is a set of pairs, and so on. But when talking about the category $\Set$ I asked you to forget about the contents of sets and instead concentrate on morphisms (arrows) between them. You were allowed, from time to time, to peek under the covers to see what a particular universal construction in $\Set$ described in terms of elements. The terminal object turned out to be a set with one element, and so on. But these were just sanity checks. A functor is defined as a mapping of categories. It's natural to consider a mapping as a morphism in a category. A functor turned out to be a morphism in the category of categories (small categories, if we want to avoid questions about size). By treating a functor as an arrow, we forfeit the information about its action on the internals of a category (its objects and morphisms), just like we forfeit the information about the action of a function on elements of a set when we treat it as an arrow in $\Set$. But functors between any two categories also form a category. This time you are asked to consider something that was an arrow in one category to be an object in another. In a functor category functors are objects and natural transformations are morphisms. We have discovered that the same thing can be an arrow in one category and an object in another. The naive view of objects as nouns and arrows as verbs doesn't hold. Instead of switching between two views, we can try to merge them into one. This is how we get the concept of a $\cat{2}$-category, in which objects are called $0$-cells, morphisms are $1$-cells, and morphisms between morphisms are $2$-cells. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/twocat.png} \caption{$0$-cells $a, b$; $1$-cells $f, g$; and a $2$-cell $\alpha$.} \end{figure} \noindent The category of categories $\Cat$ is an immediate example. We have categories as $0$-cells, functors as $1$-cells, and natural transformations as $2$-cells. The laws of a $\cat{2}$-category tell us that $1$-cells between any two $0$-cells form a category (in other words, $\cat{C}(a, b)$ is a hom-category rather than a hom-set). This fits nicely with our earlier assertion that functors between any two categories form a functor category. In particular, $1$-cells from any $0$-cell back to itself also form a category, the hom-category $\cat{C}(a, a)$; but that category has even more structure. Members of $\cat{C}(a, a)$ can be viewed as arrows in $\cat{C}$ or as objects in $\cat{C}(a, a)$. As arrows, they can be composed with each other. But when we look at them as objects, the composition becomes a mapping from a pair of objects to an object. In fact it looks very much like a product --- a tensor product to be precise. This tensor product has a unit: the identity $1$-cell. It turns out that, in any $\cat{2}$-category, a hom-category $\cat{C}(a, a)$ is automatically a monoidal category with the tensor product defined as composition of $1$-cells. Associativity and unit laws simply fall out from the corresponding category laws. Let's see what this means in our canonical example of a $\cat{2}$-category $\Cat$. The hom-category $\Cat(a, a)$ is the category of endofunctors on $a$. Endofunctor composition plays the role of a tensor product in it. The identity functor is the unit with respect to this product. We've seen before that endofunctors form a monoidal category (we used this fact in the definition of a monad), but now we see that this is a more general phenomenon: endo-$1$-cells in any $\cat{2}$-category form a monoidal category. We'll come back to it later when we generalize monads. You might recall that, in a general monoidal category, we did not insist on the monoid laws being satisfied on the nose. It was often enough for the unit laws and the associativity laws to be satisfied up to isomorphism. In a $\cat{2}$-category, monoidal laws in $\cat{C}(a, a)$ follow from composition laws for $1$-cells. These laws are strict, so we will always get a strict monoidal category. It is, however, possible to relax these laws as well. We can say, for instance, that a composition of the identity $1$-cell $\idarrow[a]$ with another $1$-cell, $f \Colon a \to b$, is isomorphic, rather than equal, to $f$. Isomorphism of $1$-cells is defined using $2$-cells. In other words, there is a $2$-cell: \[\rho \Colon f \circ \idarrow[a] \to f\] that has an inverse. \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/bicat.png} \caption{Identity law in a bicategory holds up to isomorphism (an invertible $2$-cell $\rho$).} \end{figure} \noindent We can do the same for the left identity and associativity laws. This kind of relaxed $\cat{2}$-category is called a bicategory (there are some additional coherency laws, which I will omit here). As expected, endo-$1$-cells in a bicategory form a general monoidal category with non-strict laws. An interesting example of a bicategory is the category of spans. A span between two objects $a$ and $b$ is an object $x$ and a pair of morphisms: \begin{gather*} f \Colon x \to a \\ g \Colon x \to b \end{gather*} \begin{figure}[H] \centering \includegraphics[width=0.35\textwidth]{images/span.png} \end{figure} \noindent You might recall that we used spans in the definition of a categorical product. Here, we want to look at spans as $1$-cells in a bicategory. The first step is to define a composition of spans. Suppose that we have an adjoining span: \begin{gather*} f' \Colon y \to b \\ g' \Colon y \to c \end{gather*} \begin{figure}[H] \centering \includegraphics[width=0.5\textwidth]{images/compspan.png} \end{figure} \noindent The composition would be a third span, with some apex $z$. The most natural choice for it is the pullback of $g$ along $f'$. Remember that a pullback is the object $z$ together with two morphisms: \begin{align*} h & \Colon z \to x \\ h' & \Colon z \to y \end{align*} such that: \[g \circ h = f' \circ h'\] which is universal among all such objects. \begin{figure}[H] \centering \includegraphics[width=0.5\textwidth]{images/pullspan.png} \end{figure} \noindent For now, let's concentrate on spans over the category of sets. In that case, the pullback is just a set of pairs $(p, q)$ from the Cartesian product $x \times y$ such that: \[g\ p = f'\ q\] A morphism between two spans that share the same endpoints is defined as a morphism $h$ between their apices, such that the appropriate triangles commute. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/morphspan.png} \caption{A $2$-cell in $\cat{Span}$.} \end{figure} \noindent To summarize, in the bicategory $\cat{Span}$: $0$-cells are sets, $1$-cells are spans, $2$-cells are span morphisms. An identity $1$-cell is a degenerate span in which all three objects are the same, and the two morphisms are identities. We've seen another example of a bicategory before: the bicategory $\cat{Prof}$ of \hyperref[ends-and-coends]{profunctors}, where 0-cells are categories, 1-cells are profunctors, and 2-cells are natural transformations. The composition of profunctors was given by a coend. \section{Monads} By now you should be pretty familiar with the definition of a monad as a monoid in the category of endofunctors. Let's revisit this definition with the new understanding that the category of endofunctors is just one small hom-category of endo-$1$-cells in the bicategory $\Cat$. We know it's a monoidal category: the tensor product comes from the composition of endofunctors. A monoid is defined as an object in a monoidal category --- here it will be an endofunctor $T$ --- together with two morphisms. Morphisms between endofunctors are natural transformations. One morphism maps the monoidal unit --- the identity endofunctor --- to $T$: \[\eta \Colon I \to T\] The second morphism maps the tensor product of $T \otimes T$ to $T$. The tensor product is given by endofunctor composition, so we get: \[\mu \Colon T \circ T \to T\] \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/monad.png} \end{figure} \noindent We recognize these as the two operations defining a monad (they are called \code{return} and \code{join} in Haskell), and we know that monoid laws turn to monad laws. Now let's remove all mention of endofunctors from this definition. We start with a bicategory $\cat{C}$ and pick a $0$-cell $a$ in it. As we've seen earlier, the hom-category $\cat{C}(a, a)$ is a monoidal category. We can therefore define a monoid in $\cat{C}(a, a)$ by picking a $1$-cell, $T$, and two $2$-cells: \begin{align*} \eta & \Colon I \to T \\ \mu & \Colon T \circ T \to T \end{align*} satisfying the monoid laws. We call \emph{this} a monad. \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/bimonad.png} \end{figure} \noindent That's a much more general definition of a monad using only $0$-cells, $1$-cells, and $2$-cells. It reduces to the usual monad when applied to the bicategory $\Cat$. But let's see what happens in other bicategories. Let's construct a monad in $\cat{Span}$. We pick a $0$-cell, which is a set that, for reasons that will become clear soon, I will call $\mathit{Ob}$. Next, we pick an endo-$1$-cell: a span from $\mathit{Ob}$ back to $\mathit{Ob}$. It has a set at the apex, which I will call $\mathit{Arr}$, equipped with two functions: \begin{align*} \mathit{dom} & \Colon \mathit{Arr} \to \mathit{Ob} \\ \mathit{cod} & \Colon \mathit{Arr} \to \mathit{Ob} \end{align*} \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/spanmonad.png} \end{figure} \noindent Let's call the elements of the set $\mathit{Arr}$ ``arrows.'' If I also tell you to call the elements of $\mathit{Ob}$ ``objects,'' you might get a hint where this is leading to. The two functions $\mathit{dom}$ and $\mathit{cod}$ assign the domain and the codomain to an ``arrow.'' To make our span into a monad, we need two $2$-cells, $\eta$ and $\mu$. The monoidal unit, in this case, is the trivial span from $\mathit{Ob}$ to $\mathit{Ob}$ with the apex at $\mathit{Ob}$ and two identity functions. The $2$-cell $\eta$ is a function between the apices $\mathit{Ob}$ and $\mathit{Arr}$. In other words, $\eta$ assigns an ``arrow'' to every ``object.'' A $2$-cell in $\cat{Span}$ must satisfy commutation conditions --- in this case: \begin{align*} \mathit{dom} & \circ \eta = \id \\ \mathit{cod} & \circ \eta = \id \end{align*} \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/spanunit.png} \end{figure} \noindent In components, this becomes: \[\mathit{dom}\ (\eta\ \mathit{ob}) = \mathit{ob} = \mathit{cod}\ (\eta\ \mathit{ob})\] where $\mathit{ob}$ is an ``object'' in $\mathit{Ob}$. In other words, $\eta$ assigns to every ``object'' an ``arrow'' whose domain and codomain are that ``object.'' We'll call this special ``arrow'' the ``identity arrow.'' The second $2$-cell $\mu$ acts on the composition of the span $\mathit{Arr}$ with itself. The composition is defined as a pullback, so its elements are pairs of elements from $\mathit{Arr}$ --- pairs of ``arrows'' $(a_1, a_2)$. The pullback condition is: \[\mathit{cod}\ a_1 = \mathit{dom}\ a_2\] We say that $a_1$ and $a_2$ are ``composable,'' because the domain of one is the codomain of the other. \begin{figure}[H] \centering \includegraphics[width=0.5\textwidth]{images/spanmul.png} \end{figure} \noindent The $2$-cell $\mu$ is a function that maps a pair of composable ``arrows'' $(a_1, a_2)$ to a single ``arrow'' $a_3$ from $\mathit{Arr}$. In other words $\mu$ defines composition of ``arrows''. It's easy to check that monad laws correspond to identity and associativity laws for arrows. We have just defined a category (a small category, mind you, in which objects and arrows form sets). So, all told, a category is just a monad in the bicategory of spans. What is amazing about this result is that it puts categories on the same footing as other algebraic structures like monads and monoids. There is nothing special about being a category. It's just two sets and four functions. In fact we don't even need a separate set for objects, because objects can be identified with identity arrows (they are in one-to-one correspondence). So it's really just a set and a few functions. Considering the pivotal role that category theory plays in all of mathematics, this is a very humbling realization. \section{Challenges} \begin{enumerate} \tightlist \item Derive unit and associativity laws for the tensor product defined as composition of endo-$1$-cells in a bicategory. \item Check that monad laws for a monad in $\cat{Span}$ correspond to identity and associativity laws in the resulting category. \item Show that a monad in $\cat{Prof}$ is an identity-on-objects functor. \item What's a monad algebra for a monad in $\cat{Span}$? \end{enumerate} \section{Bibliography} \begin{enumerate} \tightlist \item \urlref{https://graphicallinearalgebra.net/2017/04/16/a-monoid-is-a-category-a-category-is-a-monad-a-monad-is-a-monoid/}{Paweł Sobociński’s blog}. \end{enumerate} ================================================ FILE: src/content/3.2/adjunctions.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{I}{n mathematics we have} various ways of saying that one thing is like another. The strictest is equality. Two things are equal if there is no way to distinguish one from another. One can be substituted for the other in every imaginable context. For instance, did you notice that we used \newterm{equality} of morphisms every time we talked about commuting diagrams? That's because morphisms form a set (hom-set) and set elements can be compared for equality. But equality is often too strong. There are many examples of things being the same for all intents and purposes, without actually being equal. For instance, the pair type \code{(Bool, Char)} is not strictly equal to \code{(Char, Bool)}, but we understand that they contain the same information. This concept is best captured by an \newterm{isomorphism} between two types --- a morphism that's invertible. Since it's a morphism, it preserves the structure; and being ``iso'' means that it's part of a round trip that lands you in the same spot, no matter on which side you start. In the case of pairs, this isomorphism is called \code{swap}: \src{snippet01} \code{swap} happens to be its own inverse. \section{Adjunction and Unit/Counit Pair} When we talk about categories being isomorphic, we express this in terms of mappings between categories, a.k.a. functors. We would like to be able to say that two categories $\cat{C}$ and $\cat{D}$ are isomorphic if there exists a functor $R$ (``right'') from $\cat{C}$ to $\cat{D}$, which is invertible. In other words, there exists another functor $L$ (``left'') from $\cat{D}$ back to $\cat{C}$ which, when composed with $R$, is equal to the identity functor $I$. There are two possible compositions, $R \circ L$ and $L \circ R$; and two possible identity functors: one in $\cat{C}$ and another in $\cat{D}$. \begin{figure}[H] \centering \includegraphics[width=0.5\textwidth]{images/adj-1.jpg} \end{figure} \noindent But here's the tricky part: What does it mean for two functors to be \emph{equal}? What do we mean by this equality: \[R \circ L = I_{\cat{D}}\] or this one: \[L \circ R = I_{\cat{C}}\] It would be reasonable to define functor equality in terms of equality of objects. Two functors, when acting on equal objects, should produce equal objects. But we don't, in general, have the notion of object equality in an arbitrary category. It's just not part of the definition. (Going deeper into this rabbit hole of ``what equality really is,'' we would end up in Homotopy Type Theory.) You might argue that functors \emph{are} morphisms in the category of categories, so they should be equality-comparable. And indeed, as long as we are talking about small categories, where objects form a set, we can indeed use the equality of elements of a set to equality-compare objects. But, remember, $\Cat$ is really a $\cat{2}$-category. Hom-sets in a $\cat{2}$-category have additional structure --- there are $2$-morphisms acting between $1$-morphisms. In $\Cat$, $1$-morphisms are functors, and $2$-morphisms are natural transformations. So it's more natural (can't avoid this pun!) to consider natural isomorphisms as substitutes for equality when talking about functors. So, instead of isomorphism of categories, it makes sense to consider a more general notion of \newterm{equivalence}. Two categories $\cat{C}$ and $\cat{D}$ are \emph{equivalent} if we can find two functors going back and forth between them, whose composition (either way) is \newterm{naturally isomorphic} to the identity functor. In other words, there is a two-way natural transformation between the composition $R \circ L$ and the identity functor $I_{\cat{D}}$, and another between $L \circ R$ and the identity functor $I_{\cat{C}}$. Adjunction is even weaker than equivalence, because it doesn't require that the composition of the two functors be \emph{isomorphic} to the identity functor. Instead it stipulates the existence of a \newterm{one way} natural transformation from $I_{\cat{D}}$ to $R \circ L$, and another from $L \circ R$ to $I_{\cat{C}}$. Here are the signatures of these two natural transformations: \begin{gather*} \eta \Colon I_{\cat{D}} \to R \circ L \\ \varepsilon \Colon L \circ R \to I_{\cat{C}} \end{gather*} $\eta$ is called the unit, and $\varepsilon$ the counit of the adjunction. Notice the asymmetry between these two definitions. In general, we don't have the two remaining mappings: \begin{gather*} R \circ L \to I_{\cat{D}} \quad\quad\text{not necessarily} \\ I_{\cat{C}} \to L \circ R \quad\quad\text{not necessarily} \end{gather*} Because of this asymmetry, the functor $L$ is called the \newterm{left adjoint} to the functor $R$, while the functor $R$ is the right adjoint to $L$. (Of course, left and right make sense only if you draw your diagrams one particular way.) The compact notation for the adjunction is: \[L \dashv R\] To better understand the adjunction, let's analyze the unit and the counit in more detail. \begin{figure}[H] \centering \includegraphics[width=0.5\textwidth]{images/adj-unit.jpg} \end{figure} \noindent Let's start with the unit. It's a natural transformation, so it's a family of morphisms. Given an object $d$ in $\cat{D}$, the component of $\eta$ is a morphism between $I d$, which is equal to $d$, and $(R \circ L) d$; which, in the picture, is called $d'$: \[\eta_d \Colon d \to (R \circ L) d\] Notice that the composition $R \circ L$ is an endofunctor in $\cat{D}$. This equation tells us that we can pick any object $d$ in $\cat{D}$ as our starting point, and use the round trip functor $R \circ L$ to pick our target object $d'$. Then we shoot an arrow --- the morphism $\eta_d$ --- to our target. \begin{figure}[H] \centering \includegraphics[width=0.5\textwidth]{images/adj-counit.jpg} \end{figure} \noindent By the same token, the component of the counit $\varepsilon$ can be described as: \[\varepsilon_{c} \Colon (L \circ R) c \to c\] It tells us that we can pick any object $c$ in $\cat{C}$ as our target, and use the round trip functor $L \circ R$ to pick the source $c' = (L \circ R) c$. Then we shoot the arrow --- the morphism $\varepsilon_{c}$ --- from the source to the target. Another way of looking at unit and counit is that unit lets us \emph{introduce} the composition $R \circ L$ anywhere we could insert an identity functor on $\cat{D}$; and counit lets us \emph{eliminate} the composition $L \circ R$, replacing it with the identity on $\cat{C}$. That leads to some ``obvious'' consistency conditions, which make sure that introduction followed by elimination doesn't change anything: \begin{gather*} L = L \circ I_{\cat{D}} \to L \circ R \circ L \to I_{\cat{C}} \circ L = L \\ R = I_{\cat{D}} \circ R \to R \circ L \circ R \to R \circ I_{\cat{C}} = R \end{gather*} These are called triangular identities because they make the following diagrams commute: \begin{figure}[H] \centering \begin{subfigure} \centering \begin{tikzcd}[column sep=large, row sep=large] L \arrow[rd, equal] \arrow[r, "L \circ \eta"] & L \circ R \circ L \arrow[d, "\varepsilon \circ L"] \\ & L \end{tikzcd} \end{subfigure}% \hspace{1cm} \begin{subfigure} \centering \begin{tikzcd}[column sep=large, row sep=large] R \arrow[rd, equal] \arrow[r, "\eta \circ R"] & R \circ L \circ R \arrow[d, "R \circ \varepsilon"] \\ & R \end{tikzcd} \end{subfigure} \end{figure} \noindent These are diagrams in the functor category: the arrows are natural transformations, and their composition is the horizontal composition of natural transformations. In components, these identities become: \begin{gather*} \varepsilon_{L d} \circ L \eta_d = \id_{L d} \\ R \varepsilon_{c} \circ \eta_{R c} = \id_{R c} \end{gather*} We often see unit and counit in Haskell under different names. Unit is known as \code{return} (or \code{pure}, in the definition of \code{Applicative}): \src{snippet02} and counit as \code{extract}: \src{snippet03} Here, \code{m} is the (endo-) functor corresponding to $R \circ L$, and \code{w} is the (endo-) functor corresponding to $L \circ R$. As we'll see later, they are part of the definition of a monad and a comonad, respectively. If you think of an endofunctor as a container, the unit (or \code{return}) is a polymorphic function that creates a default box around a value of arbitrary type. The counit (or \code{extract}) does the reverse: it retrieves or produces a single value from a container. We'll see later that every pair of adjoint functors defines a monad and a comonad. Conversely, every monad or comonad may be factorized into a pair of adjoint functors --- this factorization is not unique, though. In Haskell, we use monads a lot, but only rarely factorize them into pairs of adjoint functors, primarily because those functors would normally take us out of $\Hask$. We can however define adjunctions of \newterm{endofunctors} in Haskell. Here's part of the definition taken from \code{Data.Functor.Adjunction}: \src{snippet04} This definition requires some explanation. First of all, it describes a multi-parameter type class --- the two parameters being \code{f} and \code{u}. It establishes a relation called \code{Adjunction} between these two type constructors. Additional conditions, after the vertical bar, specify functional dependencies. For instance, \code{f -> u} means that \code{u} is determined by \code{f} (the relation between \code{f} and \code{u} is a function, here on type constructors). Conversely, \code{u -> f} means that, if we know \code{u}, then \code{f} is uniquely determined. I'll explain in a moment why, in Haskell, we can impose the condition that the right adjoint \code{u} be a \newterm{representable} functor. \section{Adjunctions and Hom-Sets} There is an equivalent definition of the adjunction in terms of natural isomorphisms of hom-sets. This definition ties nicely with universal constructions we've been studying so far. Every time you hear the statement that there is some unique morphism, which factorizes some construction, you should think of it as a mapping of some set to a hom-set. That's the meaning of ``picking a unique morphism.'' Furthermore, factorization can be often described in terms of natural transformations. Factorization involves commuting diagrams --- some morphism being equal to a composition of two morphisms (factors). A natural transformation maps morphisms to commuting diagrams. So, in a universal construction, we go from a morphism to a commuting diagram, and then to a unique morphism. We end up with a mapping from morphism to morphism, or from one hom-set to another (usually in different categories). If this mapping is invertible, and if it can be naturally extended across all hom-sets, we have an adjunction. The main difference between universal constructions and adjunctions is that the latter are defined globally --- for all hom-sets. For instance, using a universal construction you can define a product of two select objects, even if it doesn't exist for any other pair of objects in that category. As we'll see soon, if the product of \emph{any pair} of objects exists in a category, it can be also defined through an adjunction. \begin{figure}[H] \centering \includegraphics[width=0.5\textwidth]{images/adj-homsets.jpg} \end{figure} \noindent Here's the alternative definition of the adjunction using hom-sets. As before, we have two functors $L \Colon \cat{D} \to \cat{C}$ and $R \Colon \cat{C} \to \cat{D}$. We pick two arbitrary objects: the source object $d$ in $\cat{D}$, and the target object $c$ in $\cat{C}$. We can map the source object $d$ to $\cat{C}$ using $L$. Now we have two objects in $\cat{C}$, $L d$ and $c$. They define a hom-set: \[\cat{C}(L d, c)\] Similarly, we can map the target object $c$ using $R$. Now we have two objects in $\cat{D}$, $d$ and $R c$. They, too, define a hom set: \[\cat{D}(d, R c)\] We say that $L$ is left adjoint to $R$ iff there is an isomorphism of hom sets: \[\cat{C}(L d, c) \cong \cat{D}(d, R c)\] that is natural both in $d$ and $c$. Naturality means that the source $d$ can be varied smoothly across $\cat{D}$; and the target $c$, across $\cat{C}$. More precisely, we have a natural transformation $\varphi$ between the following two (covariant) functors from $\cat{C}$ to $\Set$. Here's the action of these functors on objects: \begin{gather*} c \to \cat{C}(L d, c) \\ c \to \cat{D}(d, R c) \end{gather*} The other natural transformation, $\psi$, acts between the following (contravariant) functors: \begin{gather*} d \to \cat{C}(L d, c) \\ d \to \cat{D}(d, R c) \end{gather*} Both natural transformations must be invertible. It's easy to show that the two definitions of the adjunction are equivalent. For instance, let's derive the unit transformation starting from the isomorphism of hom-sets: \[\cat{C}(L d, c) \cong \cat{D}(d, R c)\] Since this isomorphism works for any object $c$, it must also work for $c = L d$: \[\cat{C}(L d, L d) \cong \cat{D}(d, (R \circ L) d)\] We know that the left hand side must contain at least one morphism, the identity. The natural transformation will map this morphism to an element of $\cat{D}(d, (R \circ L) d)$ or, inserting the identity functor $I$, a morphism in: \[\cat{D}(I d, (R \circ L) d)\] We get a family of morphisms parameterized by $d$. They form a natural transformation between the functor $I$ and the functor $R \circ L$ (the naturality condition is easy to verify). This is exactly our unit, $\eta$. Conversely, starting from the existence of the unit and counit, we can define the transformations between hom-sets. For instance, let's pick an arbitrary morphism $f$ in the hom-set $\cat{C}(L d, c)$. We want to define a $\varphi$ that, acting on $f$, produces a morphism in $\cat{D}(d, R c)$. There isn't really much choice. One thing we can try is to lift $f$ using $R$. That will produce a morphism $R f$ from $R (L d)$ to $R c$ --- a morphism that's an element of $\cat{D}((R \circ L) d, R c)$. What we need for a component of $\varphi$, is a morphism from $d$ to $R c$. That's not a problem, since we can use a component of $\eta_d$ to get from $d$ to $(R \circ L) d$. We get: \[\varphi_f = R f \circ \eta_d\] The other direction is analogous, and so is the derivation of $\psi$. Going back to the Haskell definition of \code{Adjunction}, the natural transformations $\varphi$ and $\psi$ are replaced by polymorphic (in \code{a} and \code{b}) functions \code{leftAdjunct} and \code{rightAdjunct}, respectively. The functors $L$ and $R$ are called \code{f} and \code{u}: \src{snippet05} The equivalence between the \code{unit}/\code{counit} formulation and the \code{leftAdjunct}/\allowbreak\code{rightAdjunct} formulation is witnessed by these mappings: \src{snippet06} It's very instructive to follow the translation from the categorical description of the adjunction to Haskell code. I highly encourage this as an exercise. We are now ready to explain why, in Haskell, the right adjoint is automatically a \hyperref[representable-functors]{representable functor}. The reason for this is that, to the first approximation, we can treat the category of Haskell types as the category of sets. When the right category $\cat{D}$ is $\Set$, the right adjoint $R$ is a functor from $\cat{C}$ to $\Set$. Such a functor is representable if we can find an object $\mathit{rep}$ in $\cat{C}$ such that the hom-functor $\cat{C}(\mathit{rep}, \_)$ is naturally isomorphic to $R$. It turns out that, if $R$ is the right adjoint of some functor $L$ from $\Set$ to $\cat{C}$, such an object always exists --- it's the image of the singleton set $()$ under $L$: \[\mathit{rep} = L ()\] Indeed, the adjunction tells us that the following two hom-sets are naturally isomorphic: \[\cat{C}(L (), c) \cong \Set((), R c)\] For a given $c$, the right hand side is the set of functions from the singleton set $()$ to $R c$. We've seen earlier that each such function picks one element from the set $R c$. The set of such functions is isomorphic to the set $R c$. So we have: \[\cat{C}(L (), -) \cong R\] which shows that $R$ is indeed representable. \section{Product from Adjunction} We have previously introduced several concepts using universal constructions. Many of those concepts, when defined globally, are easier to express using adjunctions. The simplest non-trivial example is that of the product. The gist of the \hyperref[products-and-coproducts]{universal construction of the product} is the ability to factorize any product-like candidate through the universal product. More precisely, the product of two objects $a$ and $b$ is the object $(a\times{}b)$ (or \code{(a, b)} in the Haskell notation) equipped with two morphisms $\mathit{fst}$ and $\mathit{snd}$ such that, for any other candidate $c$ equipped with two morphisms $p \Colon c \to a$ and $q \Colon c \to b$, there exists a unique morphism $m \Colon c \to (a, b)$ that factorizes $p$ and $q$ through $\mathit{fst}$ and $\mathit{snd}$. As we've seen earlier, in Haskell, we can implement a \code{factorizer} that generates this morphism from the two projections: \src{snippet07} It's easy to verify that the factorization conditions hold: \src{snippet08} We have a mapping that takes a pair of morphisms \code{p} and \code{q} and produces another morphism \code{m = factorizer p q}. How can we translate this into a mapping between two hom-sets that we need to define an adjunction? The trick is to go outside of $\Hask$ and treat the pair of morphisms as a single morphism in the product category. Let me remind you what a product category is. Take two arbitrary categories $\cat{C}$ and $\cat{D}$. The objects in the product category $\cat{C}\times{}\cat{D}$ are pairs of objects, one from $\cat{C}$ and one from $\cat{D}$. The morphisms are pairs of morphisms, one from $\cat{C}$ and one from $\cat{D}$. To define a product in some category $\cat{C}$, we should start with the product category $\cat{C}\times{}\cat{C}$. Pairs of morphism from $\cat{C}$ are single morphisms in the product category $\cat{C}\times{}\cat{C}$. \begin{figure}[H] \centering \includegraphics[width=0.5\textwidth]{images/adj-productcat.jpg} \end{figure} \noindent It might be a little confusing at first that we are using a product category to define a product. These are, however, very different products. We don't need a universal construction to define a product category. All we need is the notion of a pair of objects and a pair of morphisms. However, a pair of objects from $\cat{C}$ is \emph{not} an object in $\cat{C}$. It's an object in a different category, $\cat{C}\times{}\cat{C}$. We can write the pair formally as $\langle a, b \rangle$, where $a$ and $b$ are objects of $\cat{C}$. The universal construction, on the other hand, is necessary in order to define the object $a\times{}b$ (or \code{(a, b)} in Haskell), which is an object in \emph{the same} category $\cat{C}$. This object is supposed to represent the pair $\langle a, b \rangle$ in a way specified by the universal construction. It doesn't always exist and, even if it exists for some, might not exist for other pairs of objects in $\cat{C}$. Let's now look at the \code{factorizer} as a mapping of hom-sets. The first hom-set is in the product category $\cat{C}\times{}\cat{C}$, and the second is in $\cat{C}$. A general morphism in $\cat{C}\times{}\cat{C}$ would be a pair of morphisms $\langle f, g \rangle$: \begin{gather*} f \Colon c' \to a \\ g \Colon c'' \to b \end{gather*} with $c''$ potentially different from $c'$. But to define a product, we are interested in a special morphism in $\cat{C}\times{}\cat{C}$, the pair $p$ and $q$ that share the same source object $c$. That's okay: In the definition of an adjunction, the source of the left hom-set is not an arbitrary object --- it's the result of the left functor $L$ acting on some object from the right category. The functor that fits the bill is easy to guess --- it's the diagonal functor $\Delta$ from $\cat{C}$ to $\cat{C}\times{}\cat{C}$, whose action on objects is: \[\Delta c = \langle c, c \rangle\] The left-hand side hom-set in our adjunction should thus be: \[(\cat{C}\times{}\cat{C})(\Delta c, \langle a, b \rangle)\] It's a hom-set in the product category. Its elements are pairs of morphisms that we recognize as the arguments to our \code{factorizer}: \[(c \to a) \to (c \to b) \ldots{}\] The right-hand side hom-set lives in $\cat{C}$, and it goes between the source object $c$ and the result of some functor $R$ acting on the target object in $\cat{C}\times{}\cat{C}$. That's the functor that maps the pair $\langle a, b \rangle$ to our product object, $a\times{}b$. We recognize this element of the hom-set as the \emph{result} of the \code{factorizer}: \[\ldots{} \to (c \to (a, b))\] \begin{figure}[H] \centering \includegraphics[width=0.5\textwidth]{images/adj-product.jpg} \end{figure} \noindent We still don't have a full adjunction. For that we first need our \code{factorizer} to be invertible --- we are building an \emph{isomorphism} between hom-sets. The inverse of the \code{factorizer} should start from a morphism $m$ --- a morphism from some object $c$ to the product object $a\times{}b$. In other words, $m$ should be an element of: \[\cat{C}(c, a\times{}b)\] The inverse factorizer should map $m$ to a morphism $\langle p, q \rangle$ in $\cat{C}\times{}\cat{C}$ that goes from $\langle c, c \rangle$ to $\langle a, b \rangle$; in other words, a morphism that's an element of: \[(\cat{C}\times{}\cat{C})(\Delta\ c, \langle a, b \rangle)\] If that mapping exists, we conclude that there exists the right adjoint to the diagonal functor. That functor defines a product. In Haskell, we can always construct the inverse of the \code{factorizer} by composing \code{m} with, respectively, \code{fst} and \code{snd}. \begin{snip}{haskell} p = fst . m q = snd . m \end{snip} To complete the proof of the equivalence of the two ways of defining a product we also need to show that the mapping between hom-sets is natural in $a$, $b$, and $c$. I will leave this as an exercise for the dedicated reader. To summarize what we have done: A categorical product may be defined globally as the \newterm{right adjoint} of the diagonal functor: \[(\cat{C}\times{}\cat{C})(\Delta c, \langle a, b \rangle) \cong \cat{C}(c, a\times{}b)\] Here, $a\times{}b$ is the result of the action of our right adjoint functor $\mathit{Product}$ on the pair $\langle a, b \rangle$. Notice that any functor from $\cat{C}\times{}\cat{C}$ is a bifunctor, so $\mathit{Product}$ is a bifunctor. In Haskell, the $\mathit{Product}$ bifunctor is written simply as \code{(,)}. You can apply it to two types and get their product type, for instance: \src{snippet09} \section{Exponential from Adjunction} The exponential $b^a$, or the function object $a \Rightarrow b$, can be defined using a \hyperref[function-types]{universal construction}. This construction, if it exists for all pairs of objects, can be seen as an adjunction. Again, the trick is to concentrate on the statement: \begin{quote} For any other object $z$ with a morphism $g \Colon z\times{}a \to b$ there is a unique morphism $h \Colon z \to (a \Rightarrow b)$ \end{quote} This statement establishes a mapping between hom-sets. In this case, we are dealing with objects in the same category, so the two adjoint functors are endofunctors. The left (endo-)functor $L$, when acting on object $z$, produces $z\times{}a$. It's a functor that corresponds to taking a product with some fixed $a$. The right (endo-)functor $R$, when acting on $b$ produces the function object $a \Rightarrow b$ (or $b^a$). Again, $a$ is fixed. The adjunction between these two functors is often written as: \[-\times{}a \dashv (-)^a\] The mapping of hom-sets that underlies this adjunction is best seen by redrawing the diagram that we used in the universal construction. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/adj-expo.jpg} \end{figure} \noindent Notice that the $\mathit{eval}$ morphism\footnote{See ch.9 on \hyperref[function-types]{universal construction}.} is nothing else but the counit of this adjunction: \[(a \Rightarrow b)\times{}a \to b\] where: \[(a \Rightarrow b)\times{}a = (L \circ R) b\] I have previously mentioned that a universal construction defines a unique object, up to isomorphism. That's why we have ``the'' product and ``the'' exponential. This property translates to adjunctions as well: if a functor has an adjoint, this adjoint is unique up to isomorphism. \section{Challenges} \begin{enumerate} \tightlist \item Derive the naturality square for $\psi$, the transformation between the two (contravariant) functors: \begin{gather*} a \to \cat{C}(L a, b) \\ a \to \cat{D}(a, R b) \end{gather*} \item Derive the counit $\varepsilon$ starting from the hom-sets isomorphism in the second definition of the adjunction. \item Complete the proof of equivalence of the two definitions of the adjunction. \item Show that the coproduct can be defined by an adjunction. Start with the definition of the factorizer for a coproduct. \item Show that the coproduct is the left adjoint of the diagonal functor. \item Define the adjunction between a product and a function object in Haskell. \end{enumerate} ================================================ FILE: src/content/3.2/code/haskell/snippet01.hs ================================================ swap :: (a,b) -> (b,a) swap (a,b) = (b,a) ================================================ FILE: src/content/3.2/code/haskell/snippet02.hs ================================================ return :: d -> m d ================================================ FILE: src/content/3.2/code/haskell/snippet03.hs ================================================ extract :: w c -> c ================================================ FILE: src/content/3.2/code/haskell/snippet04.hs ================================================ class (Functor f, Representable u) => Adjunction f u | f -> u, u -> f where unit :: a -> u (f a) counit :: f (u a) -> a ================================================ FILE: src/content/3.2/code/haskell/snippet05.hs ================================================ class (Functor f, Representable u) => Adjunction f u | f -> u, u -> f where leftAdjunct :: (f a -> b) -> (a -> u b) rightAdjunct :: (a -> u b) -> (f a -> b) ================================================ FILE: src/content/3.2/code/haskell/snippet06.hs ================================================ unit = leftAdjunct id counit = rightAdjunct id leftAdjunct f = fmap f . unit rightAdjunct f = counit . fmap f ================================================ FILE: src/content/3.2/code/haskell/snippet07.hs ================================================ factorizer :: (c -> a) -> (c -> b) -> (c -> (a, b)) factorizer p q = \x -> (p x, q x) ================================================ FILE: src/content/3.2/code/haskell/snippet08.hs ================================================ fst . factorizer p q = p snd . factorizer p q = q ================================================ FILE: src/content/3.2/code/haskell/snippet09.hs ================================================ (,) Int Bool ~ (Int, Bool) ================================================ FILE: src/content/3.2/code/ocaml/snippet01.ml ================================================ let swap (a, b) = b, a ================================================ FILE: src/content/3.2/code/ocaml/snippet02.ml ================================================ module type Unit_Example = sig type 'a m val return : 'd -> 'd m end ================================================ FILE: src/content/3.2/code/ocaml/snippet03.ml ================================================ module type Counit_Example = sig type 'c w val extract : 'c w -> 'c end ================================================ FILE: src/content/3.2/code/ocaml/snippet04.ml ================================================ (* L is Functor F and R is Representable Functor U *) module type Adjunction = functor (F : Functor) (U : Representable) -> sig val unit : 'a -> 'a F.t U.t val counit : 'a U.t F.t -> 'a end ================================================ FILE: src/content/3.2/code/ocaml/snippet05.ml ================================================ module type Adjunction_HomSet = functor (F : Functor) (U : Representable) -> sig val left_adjunct : ('a F.t -> 'b) -> 'a -> 'b U.t val right_adjunct : ('a -> 'b U.t) -> 'a F.t -> 'b end ================================================ FILE: src/content/3.2/code/ocaml/snippet06.ml ================================================ (* Putting it all together to show the equivalence between unit/counit and left_adjunct/right_adjunct *) module type Adjunction = functor (F : Functor) (U : Representable) -> sig val unit : 'a -> 'a F.t U.t val counit : 'a U.t F.t -> 'a val left_adjunct : ('a F.t -> 'b) -> 'a -> 'b U.t val right_adjunct : ('a -> 'b U.t) -> 'a F.t -> 'b end (* Adjunction via unit/counit *) module type Adjunction_Unit_Counit = functor (F : Functor) (U : Representable) -> sig val unit : 'a -> 'a F.t U.t val counit : 'a U.t F.t -> 'a end (* Adjunction via left and right adjoints *) module type Adjunction_Hom_Set = functor (F : Functor) (U : Representable) -> sig val left_adjunct : ('a F.t -> 'b) -> 'a -> 'b U.t val right_adjunct : ('a -> 'b U.t) -> 'a F.t -> 'b end (* Implementing unit/counit from left and right adjoint definitions *) module Adjunction_From_Hom_Set (A : Adjunction_Hom_Set) : Adjunction = functor (F : Functor) (U : Representable) -> struct type 't f = 't F.t type 't u = 't U.t module M = A (F) (U) include M let unit : 'a. 'a -> 'a f u = fun a -> M.left_adjunct idty a let counit : 'a. 'a u f -> 'a = fun fua -> M.right_adjunct idty fua ;; end (* Implementing left and right adjunct from unit/counit Definitions *) module Adjunction_From_Unit_Counit (A : Adjunction_Unit_Counit) : Adjunction = functor (F : Functor) (U : Representable) -> struct type 't f = 't F.t type 't u = 't U.t module M = A (F) (U) include M let left_adjunct f a = (U.fmap f) (M.unit a) let right_adjunct f fa = M.counit (F.fmap f fa) end ================================================ FILE: src/content/3.2/code/ocaml/snippet07.ml ================================================ let factorizer p q x = p x, q x ================================================ FILE: src/content/3.2/code/ocaml/snippet08.ml ================================================ compose fst (factorizer p q) = p compose snd (factorizer p q) = q ================================================ FILE: src/content/3.2/code/ocaml/snippet09.ml ================================================ int * bool ~ (int, bool) ================================================ FILE: src/content/3.2/code/reason/snippet01.re ================================================ let swap = (a, b) => (b, a); ================================================ FILE: src/content/3.2/code/reason/snippet02.re ================================================ module type Unit_Example = { type m('a); let return: 'd => m('d); }; ================================================ FILE: src/content/3.2/code/reason/snippet03.re ================================================ module type Counit_Example = { type w('c); let extract: w('c) => 'c; }; ================================================ FILE: src/content/3.2/code/reason/snippet04.re ================================================ /* L is Functor F and R is Representable Functor U */ module type Adjunction = (F: Functor, U: Representable) => { let unit: 'a => U.t(F.t('a)); let counit: F.t(U.t('a)) => 'a; }; ================================================ FILE: src/content/3.2/code/reason/snippet05.re ================================================ module type Adjunction_HomSet = (F: Functor, U: Representable) => { let left_adjunct: (F.t('a) => 'b, 'a) => U.t('b); let right_adjunct: ('a => U.t('b), F.t('a)) => 'b; }; ================================================ FILE: src/content/3.2/code/reason/snippet06.re ================================================ /* Putting it all together to show the equivalence between * unit & counit and left_adjunct & right_adjunct */ module type Adjunction = (F: Functor, U: Representable) => { let unit: 'a => U.t(F.t('a)); let counit: F.t(U.t('a)) => 'a; let left_adjunct: (F.t('a) => 'b, 'a) => U.t('b); let right_adjunct: ('a => U.t('b), F.t('a)) => 'b; }; /* Adjunction via unit & counit */ module type Adjunction_Unit_Counit = (F: Functor, U: Representable) => { let unit: 'a => U.t(F.t('a)); let counit: F.t(U.t('a)) => 'a; }; /* Adjunction via left and right adjoints */ module type Adjunction_Hom_Set = (F: Functor, U: Representable) => { let left_adjunct: (F.t('a) => 'b, 'a) => U.t('b); let right_adjunct: ('a => U.t('b), F.t('a)) => 'b; }; /* Implementing unit & counit from left and right adjoint definitions */ module Adjunction_From_Hom_Set = (A: Adjunction_Hom_Set) : Adjunction => (F: Functor, U: Representable) => { type f('t) = F.t('t); type u('t) = U.t('t); module M = A(F, U); include M; let unit: 'a. 'a => u(f('a)) = a => M.left_adjunct(idty, a); let counit: 'a. f(u('a)) => 'a = fua => M.right_adjunct(idty, fua); }; /* Implementing left and right adjunct from unit & counit Definitions */ module Adjunction_From_Unit_Counit = (A: Adjunction_Unit_Counit) : Adjunction => (F: Functor, U: Representable) => { type f('t) = F.t('t); type u('t) = U.t('t); module M = A(F, U); include M; let left_adjunct = (f, a) => (U.fmap(f))(M.unit(a)); let right_adjunct = (f, fa) => M.counit(F.fmap(f, fa)); }; ================================================ FILE: src/content/3.2/code/reason/snippet07.re ================================================ let factorizer = (p, q, x) => (p(x), q(x)); ================================================ FILE: src/content/3.2/code/reason/snippet08.re ================================================ compose(fst, factorizer(p, q)) == p; compose(snd, factorizer(p, q)) == q; ================================================ FILE: src/content/3.2/code/reason/snippet09.re ================================================ (int, bool) ~ (int, bool) ================================================ FILE: src/content/3.2/code/scala/snippet01.scala ================================================ def swap[A, B]: ((A, B)) => (B, A) = { case (a, b) => (b, a) } ================================================ FILE: src/content/3.2/code/scala/snippet02.scala ================================================ // return is a keyword in Scala def pure[D]: D => M[D] ================================================ FILE: src/content/3.2/code/scala/snippet03.scala ================================================ def extract[C]: W[C] => C ================================================ FILE: src/content/3.2/code/scala/snippet04.scala ================================================ abstract class Adjunction[F[_], U[_]]( implicit val F: Functor[F], val U: Representable[U] ){ def unit[A](a: A): U[F[A]] def counit[A](a: F[U[A]]): A } ================================================ FILE: src/content/3.2/code/scala/snippet05.scala ================================================ abstract class Adjunction[F[_], U[_]]( implicit val F: Functor[F], val U: Representable[U] ){ // changed the order of parameters // to help Scala compiler infer types def leftAdjunct[A, B](a: A)(f: F[A] => B): U[B] def rightAdjunct[A, B](fa: F[A])(f: A => U[B]): B } ================================================ FILE: src/content/3.2/code/scala/snippet06.scala ================================================ def unit[A](a: A): U[F[A]] = leftAdjunct(a)(identity) def counit[A](a: F[U[A]]): A = rightAdjunct(a)(identity) def leftAdjunct[A, B](a: A)(f: F[A] => B): U[B] = U.map(unit(a))(f) def rightAdjunct[A, B](a: F[A])(f: A => U[B]): B = counit(F.map(a)(f)) ================================================ FILE: src/content/3.2/code/scala/snippet07.scala ================================================ def factorizer[A, B, C](p: C => A)(q: C => B): (C => (A, B)) = x => (p(x), q(x)) ================================================ FILE: src/content/3.2/code/scala/snippet08.scala ================================================ factorizer(p)(q).andThen(_._1) == p factorizer(p)(q).andThen(_._2) == q ================================================ FILE: src/content/3.2/code/scala/snippet09.scala ================================================ Product2[Int, Boolean] ~ (Int, Boolean) ================================================ FILE: src/content/3.3/code/haskell/snippet01.hs ================================================ type String = [Char] ================================================ FILE: src/content/3.3/code/haskell/snippet02.hs ================================================ toNat :: [()] -> Int toNat = length toLst :: Int -> [()] toLst n = replicate n () ================================================ FILE: src/content/3.3/code/ocaml/snippet01.ml ================================================ type string = char list ================================================ FILE: src/content/3.3/code/ocaml/snippet02.ml ================================================ let to_nat : unit list -> int = List.length let to_lst : int -> unit list = fun n -> List.init n ~f:(fun _ -> ()) ================================================ FILE: src/content/3.3/code/reason/snippet01.re ================================================ type string = list(char); ================================================ FILE: src/content/3.3/code/reason/snippet02.re ================================================ let to_nat: list(unit) => int = List.length; let to_lst: int => list(unit) = n => List.init(n, ~f=_ => ()); ================================================ FILE: src/content/3.3/code/scala/snippet01.scala ================================================ type String = List[Char] ================================================ FILE: src/content/3.3/code/scala/snippet02.scala ================================================ def toNat: List[Unit] => Int = _.length def toLst: Int => List[Unit] = n => List.fill(n)(()) ================================================ FILE: src/content/3.3/free-forgetful-adjunctions.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{F}{ree constructions are} a powerful application of adjunctions. A \newterm{free functor} is defined as the left adjoint to a \newterm{forgetful functor}. A forgetful functor is usually a pretty simple functor that forgets some structure. For instance, lots of interesting categories are built on top of sets. But categorical objects, which abstract those sets, have no internal structure --- they have no elements. Still, those objects often carry the memory of sets, in the sense that there is a mapping --- a functor --- from a given category $\cat{C}$ to $\Set$. A set corresponding to some object in $\cat{C}$ is called its \newterm{underlying set}. Monoids are such objects that have underlying sets --- sets of elements. There is a forgetful functor $U$ from the category of monoids $\cat{Mon}$ to the category of sets, which maps monoids to their underlying sets. It also maps monoid morphisms (homomorphisms) to functions between sets. I like to think of $\cat{Mon}$ as having split personality. On the one hand, it's a bunch of sets with multiplication and unit elements. On the other hand, it's a category with featureless objects whose only structure is encoded in morphisms that go between them. Every set-function that preserves multiplication and unit gives rise to a morphism in $\cat{Mon}$.\\ \newline Things to keep in mind: \begin{itemize} \tightlist \item There may be many monoids that map to the same set, and \item There are fewer (or at most as many as) monoid morphisms than there are functions between their underlying sets. \end{itemize} \noindent The functor $F$ that's the left adjoint to the forgetful functor $U$ is the free functor that builds free monoids from their generator sets. The adjunction follows from the free monoid universal construction we've discussed before.\footnote{See ch.13 on \hyperref[free-monoids]{free monoids}.} \begin{figure}[H] \centering \includegraphics[width=0.6\textwidth]{images/forgetful.jpg} \caption{Monoids $m_1$ and $m_2$ have the same underlying set. There are more functions between the underlying sets of $m_2$ and $m_3$ than there are morphisms between them.} \end{figure} \noindent In terms of hom-sets, we can write this adjunction as: \[\cat{Mon}(F x, m) \cong \Set(x, U m)\] This (natural in $x$ and $m$) isomorphism tells us that: \begin{itemize} \tightlist \item For every monoid homomorphism between the free monoid $F x$ generated by $x$ and an arbitrary monoid $m$ there is a unique function that embeds the set of generators $x$ in the underlying set of $m$. It's a function in $\Set(x, U m)$. \item For every function that embeds $x$ in the underlying set of some $m$ there is a unique monoid morphism between the free monoid generated by $x$ and the monoid $m$. (This is the morphism we called $h$ in our universal construction.) \end{itemize} \begin{figure}[H] \centering \includegraphics[width=0.8\textwidth]{images/freemonadjunction.jpg} \end{figure} \noindent The intuition is that $F x$ is the ``maximum'' monoid that can be built on the basis of $x$. If we could look inside monoids, we would see that any morphism that belongs to $\cat{Mon}(F x, m)$ \emph{embeds} this free monoid in some other monoid $m$. It does it by possibly identifying some elements. In particular, it embeds the generators of $F x$ (i.e., the elements of $x$) in $m$. The adjunction shows that the embedding of $x$, which is given by a function from $\Set(x, U m)$ on the right, uniquely determines the embedding of monoids on the left, and vice versa. In Haskell, the list data structure is a free monoid (with some caveats: see \urlref{http://comonad.com/reader/2015/free-monoids-in-haskell/}{Dan Doel's blog post}). A list type \code{{[}a{]}} is a free monoid with the type \code{a} representing the set of generators. For instance, the type \code{{[}Char{]}} contains the unit element --- the empty list \code{{[}{]}} --- and the singletons like \code{{[}'a'{]}}, \code{{[}'b'{]}} --- the generators of the free monoid. The rest is generated by applying the ``product.'' Here, the product of two lists simply appends one to another. Appending is associative and unital (that is, there is a neutral element --- here, the empty list). A free monoid generated by \code{Char} is nothing but the set of all strings of characters from \code{Char}. It's called \code{String} in Haskell: \src{snippet01} (\code{type} defines a type synonym --- a different name for an existing type). Another interesting example is a free monoid built from just one generator. It's the type of the list of units, \code{{[}(){]}}. Its elements are \code{{[}{]}}, \code{{[}(){]}}, \code{{[}(), (){]}}, etc. Every such list can be described by one natural number --- its length. There is no more information encoded in the list of units. Appending two such lists produces a new list whose length is the sum of the lengths of its constituents. It's easy to see that the type \code{{[}(){]}} is isomorphic to the additive monoid of natural numbers (with zero). Here are the two functions that are the inverse of each other, witnessing this isomorphism: \src{snippet02} For simplicity I used the type \code{Int} rather than \code{Natural}, but the idea is the same. The function \code{replicate} creates a list of length \code{n} pre-filled with a given value --- here, the unit. \section{Some Intuitions} What follows are some hand-waving arguments. Those kind of arguments are far from rigorous, but they help in forming intuitions. To get some intuition about the free/forgetful adjunctions it helps to keep in mind that functors and functions are lossy in nature. Functors may collapse multiple objects and morphisms, functions may bunch together multiple elements of a set. Also, their image may cover only part of their codomain. An ``average'' hom-set in $\Set$ will contain a whole spectrum of functions starting with the ones that are least lossy (e.g., injections or, possibly, isomorphisms) and ending with constant functions that collapse the whole domain to a single element (if there is one). I tend to think of morphisms in an arbitrary category as being lossy too. It's just a mental model, but it's a useful one, especially when thinking of adjunctions --- in particular those in which one of the categories is $\Set$. Formally, we can only speak of morphisms that are invertible (isomorphisms) or non-invertible. It's that latter kind that may be thought of as lossy. There is also a notion of mono- and epi- morphisms that generalize the idea of injective (non-collapsing) and surjective (covering the whole codomain) functions, but it's possible to have a morphism that is both mono and epi, and which is still non-invertible. In the Free ⊣ Forgetful adjunction, we have the more constrained category $\cat{C}$ on the left, and a less constrained category $\cat{D}$ on the right. Morphisms in $\cat{C}$ are ``fewer'' because they have to preserve some additional structure. In the case of $\cat{Mon}$, they have to preserve multiplication and unit. Morphisms in $\cat{D}$ don't have to preserve as much structure, so there are ``more'' of them. When we apply a forgetful functor $U$ to an object $c$ in $\cat{C}$, we think of it as revealing the ``internal structure'' of $c$. In fact, if $\cat{D}$ is $\Set$ we think of $U$ as \emph{defining} the internal structure of $c$ --- its underlying set. (In an arbitrary category, we can't talk about the internals of an object other than through its connections to other objects, but here we are just hand-waving.) If we map two objects $c'$ and $c$ using $U$, we expect that, in general, the mapping of the hom-set $\cat{C}(c', c)$ will cover only a subset of $\cat{D}(U c', U c)$. That's because morphisms in $\cat{C}(c', c)$ have to preserve the additional structure, whereas the ones in $\cat{D}(U c', U c)$ don't. \begin{figure}[H] \centering \includegraphics[width=0.45\textwidth]{images/forgettingmorphisms.jpg} \end{figure} \noindent But since an adjunction is defined as an \newterm{isomorphism} of particular hom-sets, we have to be very picky with our selection of $c'$. In the adjunction, $c'$ is picked not from just anywhere in $\cat{C}$, but from the (presumably smaller) image of the free functor $F$: \[\cat{C}(F d, c) \cong \cat{D}(d, U c)\] The image of $F$ must therefore consist of objects that have lots of morphisms going to an arbitrary $c$. In fact, there has to be as many structure-preserving morphisms from $F d$ to $c$ as there are non-structure preserving morphisms from $d$ to $U c$. It means that the image of $F$ must consist of essentially structure-free objects (so that there is no structure to preserve by morphisms). Such ``structure-free'' objects are called free objects. \begin{figure}[H] \centering \includegraphics[width=0.45\textwidth]{images/freeimage.jpg} \end{figure} \noindent In the monoid example, a free monoid has no structure other than what's generated by unit and associativity laws. Other than that, all multiplications produce brand new elements. In a free monoid, $2 * 3$ is not $6$ --- it's a new element ${[}2, 3{]}$. Since there is no identification of ${[}2, 3{]}$ and $6$, a morphism from this free monoid to any other monoid $m$ is allowed to map them separately. But it's also okay for it to map both ${[}2, 3{]}$ and $6$ (their product) to the same element of $m$. Or to identify ${[}2, 3{]}$ and $5$ (their sum) in an additive monoid, and so on. Different identifications give you different monoids. This leads to another interesting intuition: Free monoids, instead of performing the monoidal operation, accumulate the arguments that were passed to it. Instead of multiplying $2$ and $3$ they remember $2$ and $3$ in a list. The advantage of this scheme is that we don't have to specify what monoidal operation we will use. We can keep accumulating arguments, and only at the end apply an operator to the result. And it's then that we can chose what operator to apply. We can add the numbers, or multiply them, or perform addition modulo 2, and so on. A free monoid separates the creation of an expression from its evaluation. We'll see this idea again when we talk about algebras. This intuition generalizes to other, more elaborate free constructions. For instance, we can accumulate whole expression trees before evaluating them. The advantage of this approach is that we can transform such trees to make the evaluation faster or less memory consuming. This is, for instance, done in implementing matrix calculus, where eager evaluation would lead to lots of allocations of temporary arrays to store intermediate results. \section{Challenges} \begin{enumerate} \tightlist \item Consider a free monoid built from a singleton set as its generator. Show that there is a one-to-one correspondence between morphisms from this free monoid to any monoid $m$, and functions from the singleton set to the underlying set of $m$. \end{enumerate} ================================================ FILE: src/content/3.4/code/haskell/snippet01.hs ================================================ vlen = sqrt . sum . fmap (flip (^) 2) ================================================ FILE: src/content/3.4/code/haskell/snippet02.hs ================================================ newtype Writer w a = Writer (a, w) instance Functor (Writer w) where fmap f (Writer (a, w)) = Writer (f a, w) ================================================ FILE: src/content/3.4/code/haskell/snippet03.hs ================================================ a -> Writer w b ================================================ FILE: src/content/3.4/code/haskell/snippet04.hs ================================================ class Monad m where (>=>) :: (a -> m b) -> (b -> m c) -> (a -> m c) return :: a -> m a ================================================ FILE: src/content/3.4/code/haskell/snippet05.hs ================================================ instance Monoid w => Monad (Writer w) where f >=> g = \a -> let Writer (b, s) = f a Writer (c, s') = g b in Writer (c, s `mappend` s') return a = Writer (a, mempty) ================================================ FILE: src/content/3.4/code/haskell/snippet06.hs ================================================ tell :: w -> Writer w () tell s = Writer ((), s) ================================================ FILE: src/content/3.4/code/haskell/snippet07.hs ================================================ (>=>) :: (a -> m b) -> (b -> m c) -> (a -> m c) f >=> g = \a -> ... ================================================ FILE: src/content/3.4/code/haskell/snippet08.hs ================================================ f >=> g = \a -> let mb = f a in ... ================================================ FILE: src/content/3.4/code/haskell/snippet09.hs ================================================ (>>=) :: m a -> (a -> m b) -> m b ================================================ FILE: src/content/3.4/code/haskell/snippet10.hs ================================================ class Monad m where (>>=) :: m a -> (a -> m b) -> m b return :: a -> m a ================================================ FILE: src/content/3.4/code/haskell/snippet11.hs ================================================ (Writer (a, w)) >>= f = let Writer (b, w') = f a in Writer (b, w `mappend` w') ================================================ FILE: src/content/3.4/code/haskell/snippet12.hs ================================================ join :: m (m a) -> m a ================================================ FILE: src/content/3.4/code/haskell/snippet13.hs ================================================ ma >>= f = join (fmap f ma) ================================================ FILE: src/content/3.4/code/haskell/snippet14.hs ================================================ class Functor m => Monad m where join :: m (m a) -> m a return :: a -> m a ================================================ FILE: src/content/3.4/code/haskell/snippet15.hs ================================================ fmap f ma = ma >>= \a -> return (f a) ================================================ FILE: src/content/3.4/code/haskell/snippet16.hs ================================================ join :: Monoid w => Writer w (Writer w a) -> Writer w a join (Writer ((Writer (a, w')), w)) = Writer (a, w `mappend` w') ================================================ FILE: src/content/3.4/code/haskell/snippet17.hs ================================================ process :: String -> Writer String [String] process = upCase >=> toWords ================================================ FILE: src/content/3.4/code/haskell/snippet18.hs ================================================ process s = do upStr <- upCase s toWords upStr ================================================ FILE: src/content/3.4/code/haskell/snippet19.hs ================================================ upCase :: String -> Writer String String upCase s = Writer (map toUpper s, "upCase ") ================================================ FILE: src/content/3.4/code/haskell/snippet20.hs ================================================ process s = upCase s >>= \upStr -> toWords upStr ================================================ FILE: src/content/3.4/code/haskell/snippet21.hs ================================================ upStr <- upCase s ================================================ FILE: src/content/3.4/code/haskell/snippet22.hs ================================================ process s = do upStr <- upCase s tell "toWords " return (words upStr) ================================================ FILE: src/content/3.4/code/haskell/snippet23.hs ================================================ process s = upCase s >>= \upStr -> tell "toWords " >>= \() -> return (words upStr) ================================================ FILE: src/content/3.4/code/haskell/snippet24.hs ================================================ (>>) :: m a -> m b -> m b m >> k = m >>= (\_ -> k) ================================================ FILE: src/content/3.4/code/haskell/snippet25.hs ================================================ process s = upCase s >>= \upStr -> tell "toWords " >> return (words upStr) ================================================ FILE: src/content/3.4/code/ocaml/snippet01.ml ================================================ (* Depends on OCaml library Core *) module Vlen (F : Functor with type 'a t = 'a list) = struct let summable = (module Float : Base__.Container_intf.Summable with type t = float) let vlen = Float.sqrt <.> List.sum summable ~f:Fn.id <.> F.fmap (flip Float.int_pow 2) end ================================================ FILE: src/content/3.4/code/ocaml/snippet02.ml ================================================ module WriterInstance (W : sig type w end) : Functor with type 'a t = (W.w, 'a) writer = struct type 'a t = (W.w, 'a) writer let fmap f (Writer (a, w)) = Writer (f a, w) end ================================================ FILE: src/content/3.4/code/ocaml/snippet03.ml ================================================ 'a -> ('w, 'b) writer ================================================ FILE: src/content/3.4/code/ocaml/snippet04.ml ================================================ module type Monad = sig type 'a m val ( >=> ) : ('a -> 'b m) -> ('b -> 'c m) -> 'a -> 'c m val return : 'a -> 'a m end ================================================ FILE: src/content/3.4/code/ocaml/snippet05.ml ================================================ module WriterMonad (W : Monoid) : Monad with type 'a m = (W.a, 'a) writer = struct type 'a m = (W.a, 'a) writer let ( >=> ) f g a = let (Writer (b, w)) = f a in let (Writer (c, w')) = g b in Writer (c, W.mappend w w') let return a = Writer (a, W.mempty) end ================================================ FILE: src/content/3.4/code/ocaml/snippet06.ml ================================================ let tell w = Writer ((), w) ================================================ FILE: src/content/3.4/code/ocaml/snippet07.ml ================================================ let (>=>) f g = fun a -> ... ================================================ FILE: src/content/3.4/code/ocaml/snippet08.ml ================================================ let (>=>) f g = fun a -> let mb = f a in ... ================================================ FILE: src/content/3.4/code/ocaml/snippet09.ml ================================================ val ( >>= ) : 'a m -> ('a -> 'b m) -> 'b m ================================================ FILE: src/content/3.4/code/ocaml/snippet10.ml ================================================ module type Monad_Bind = sig type 'a m val ( >>= ) : 'a m -> ('a -> 'b m) -> 'b m val return : 'a -> 'a m end ================================================ FILE: src/content/3.4/code/ocaml/snippet11.ml ================================================ module WriterMonadBind (W : Monoid) = struct let ( >>= ) (Writer (a, w)) f = let (Writer (b, w')) = f a in Writer (b, W.mappend w w') end ================================================ FILE: src/content/3.4/code/ocaml/snippet12.ml ================================================ val join : ('a m) m : 'a m ================================================ FILE: src/content/3.4/code/ocaml/snippet13.ml ================================================ module BindUsingFunctionAndJoin (F : Functor) = struct type 'a m = 'a F.t external join : 'a m m -> 'a m = "%identity" (** Make the type signature of join work without providing an implementation **) let ( >>= ) ma f = join (F.fmap f ma) end ================================================ FILE: src/content/3.4/code/ocaml/snippet14.ml ================================================ module type Monad_Join = functor (F : Functor) -> sig type 'a m = 'a F.t val join : 'a m m -> 'a m val return : 'a -> 'a m end ================================================ FILE: src/content/3.4/code/ocaml/snippet15.ml ================================================ module Fmap_Using_Monad (M : Monad_Bind) = struct let fmap f ma = M.( >>= ) ma (fun a -> M.return (f a)) end ================================================ FILE: src/content/3.4/code/ocaml/snippet16.ml ================================================ module Writer_Join (W : Monoid) = struct let join (Writer (Writer (a, w'), w)) = Writer (a, W.mappend w w') end ================================================ FILE: src/content/3.4/code/ocaml/snippet17.ml ================================================ (* Import Str module using this - #require "str" *) let to_words s = Writer (Str.split (Str.regexp "\b") s, "to_words") module Writer_Process (W : Monad with type 'a m = (string, 'a) writer) = struct let process = W.(up_case >=> to_words) end ================================================ FILE: src/content/3.4/code/ocaml/snippet18.ml ================================================ module Process_Do (W : Monad_Bind with type 'a m = (string, 'a) writer) = struct (* Needs OCaml compiler >= 4.08 *) let ( let* ) = W.( >>= ) let process s = let* up_str = up_case s in to_words up_str end ================================================ FILE: src/content/3.4/code/ocaml/snippet19.ml ================================================ let up_case s = Writer (String.uppercase s, "up_case ") ================================================ FILE: src/content/3.4/code/ocaml/snippet20.ml ================================================ module Process_Bind_Without_Do (W : Monad_Bind with type 'a m = (string, 'a) writer) = struct let process s = W.(up_case s >>= fun up_str -> to_words up_str) end ================================================ FILE: src/content/3.4/code/ocaml/snippet21.ml ================================================ let* up_str <- up_case s ================================================ FILE: src/content/3.4/code/ocaml/snippet22.ml ================================================ module Process_Tell (W : Monad_Bind with type 'a m = (string, 'a) writer) = struct (* Needs OCaml compiler >= 4.08 *) let ( let* ) = W.( >>= ) let tell w = Writer ((), w) let process s = let* up_str = up_case s in let* _ = tell "to_words " in to_words up_str end ================================================ FILE: src/content/3.4/code/ocaml/snippet23.ml ================================================ module Process_Bind_Without_Do (W : Monad_Bind with type 'a m = (string, 'a) writer) = struct let tell w = Writer ((), w) let process s = W.( up_case s >>= fun up_str -> tell "to_words" >>= fun _ -> to_words up_str) end ================================================ FILE: src/content/3.4/code/ocaml/snippet24.ml ================================================ module Monad_Ops (M : Monad_Bind) = struct let ( >> ) m k = M.(m >>= fun _ -> k) end ================================================ FILE: src/content/3.4/code/ocaml/snippet25.ml ================================================ module Process_Bind_Without_Do (W : Monad_Bind with type 'a m = (string, 'a) writer) = struct open Monad_Ops (W) let tell w = Writer ((), w) let process s = W.( up_case s >>= fun up_str -> tell "to_words" >> to_words up_str) end ================================================ FILE: src/content/3.4/code/reason/snippet01.re ================================================ /* Depends on OCaml library Core */ module Vlen = (F: Functor with type t('a) = list('a)) => { let summable: module Base__.Container_intf.Summable with type t = float = (module Float); let vlen = Float.sqrt <.> List.sum(summable, ~f=Fn.id) <.> F.fmap(flip(Float.int_pow, 2)); }; ================================================ FILE: src/content/3.4/code/reason/snippet02.re ================================================ module WriterInstance = (W: {type w;}) : (Functor with type t('a) = writer(W.w, 'a)) => { type t('a) = writer(W.w, 'a); let fmap = (f, Writer(a, w)) => Writer(f(a), w); }; ================================================ FILE: src/content/3.4/code/reason/snippet03.re ================================================ 'a => writer('w, 'b) ================================================ FILE: src/content/3.4/code/reason/snippet04.re ================================================ module type Monad = { type m('a); let (>=>): ('a => m('b), 'b => m('c), 'a) => m('c); let return: 'a => m('a); }; ================================================ FILE: src/content/3.4/code/reason/snippet05.re ================================================ module WriterMonad = (W: Monoid) : (Monad with type m('a) = writer(W.a, 'a)) => { type m('a) = writer(W.a, 'a); let (>=>) = (f, g, a) => { let Writer(b, w) = f(a); let Writer(c, w') = g(b); Writer(c, W.mappend(w, w')); }; let return = a => Writer(a, W.mempty); }; ================================================ FILE: src/content/3.4/code/reason/snippet06.re ================================================ let tell = w => Writer((), w); ================================================ FILE: src/content/3.4/code/reason/snippet07.re ================================================ let (>=>) = (f, g) => a => ... ================================================ FILE: src/content/3.4/code/reason/snippet08.re ================================================ let (>=>) = (f, g) => a => { let mb = f(a); ... } ================================================ FILE: src/content/3.4/code/reason/snippet09.re ================================================ let (>>=): (m('a), 'a => m('b)) => m('b); ================================================ FILE: src/content/3.4/code/reason/snippet10.re ================================================ module type Monad_Bind = { type m('a); let (>>=): (m('a), 'a => m('b)) => m('b); let return: 'a => m('a); }; ================================================ FILE: src/content/3.4/code/reason/snippet11.re ================================================ module WriterMonadBind = (W: Monoid) => { let (>>=) = (Writer(a, w), f) => { let Writer(b, w') = f(a); Writer(b, W.mappend(w, w')); }; }; ================================================ FILE: src/content/3.4/code/reason/snippet12.re ================================================ let join: m(m('a)) => m('a) ================================================ FILE: src/content/3.4/code/reason/snippet13.re ================================================ module BindUsingFunctionAndJoin = (F: Functor) => { type m('a) = F.t('a); /** Make the type signature of join work without providing an implementation **/ external join: m(m('a)) => m('a) = "%identity"; let (>>=) = (ma, f) => join(F.fmap(f, ma)); }; ================================================ FILE: src/content/3.4/code/reason/snippet14.re ================================================ module type Monad_Join = (F: Functor) => { type m('a) = F.t('a); let join: m(m('a)) => m('a); let return: 'a => m('a); }; ================================================ FILE: src/content/3.4/code/reason/snippet15.re ================================================ module Fmap_Using_Monad = (M: Monad_Bind) => { let fmap = (f, ma) => M.(>>=)(ma, a => M.return(f(a))); }; ================================================ FILE: src/content/3.4/code/reason/snippet16.re ================================================ module Writer_Join = (W: Monoid) => { let join = (Writer(Writer(a, w'), w)) => Writer(a, W.mappend(w, w')); }; ================================================ FILE: src/content/3.4/code/reason/snippet17.re ================================================ /* Import Str module using this - #require "str" */ let to_words = s => Writer(Str.split(Str.regexp("\b"), s), "to_words"); module Writer_Process = (W: Monad with type m('a) = writer(string, 'a)) => { let process = W.(up_case >=> to_words); }; ================================================ FILE: src/content/3.4/code/reason/snippet18.re ================================================ module Process_Do = (W: Monad_Bind with type m('a) = writer(string, 'a)) => { /* Needs Reason parser >= 3.6.0 */ let (let*) = W.(>>=); let process = s => { let* up_str = up_case(s); to_words(up_str); }; }; ================================================ FILE: src/content/3.4/code/reason/snippet19.re ================================================ let up_case = s => Writer(String.uppercase(s), "up_case "); ================================================ FILE: src/content/3.4/code/reason/snippet20.re ================================================ module Process_Bind_Without_Do = (W: Monad_Bind with type m('a) = writer(string, 'a)) => { let process = s => W.(up_case(s) >>= (up_str => to_words(up_str))); }; ================================================ FILE: src/content/3.4/code/reason/snippet21.re ================================================ up_str = up_case(s) ================================================ FILE: src/content/3.4/code/reason/snippet22.re ================================================ module Process_Tell = (W: Monad_Bind with type m('a) = writer(string, 'a)) => { /* Needs Reason parser >= 3.6.0 */ let (let*) = W.(>>=); let tell = w => Writer((), w); let process = s => { let* up_str = up_case(s); let* _ = tell("to_words "); to_words(up_str); }; }; ================================================ FILE: src/content/3.4/code/reason/snippet23.re ================================================ module Process_Bind_Without_Do = (W: Monad_Bind with type m('a) = writer(string, 'a)) => { let tell = w => Writer((), w); let process = s => W.( up_case(s) >>= (up_str => tell("to_words") >>= (_ => to_words(up_str))) ); }; ================================================ FILE: src/content/3.4/code/reason/snippet24.re ================================================ module Monad_Ops = (M: Monad_Bind) => { let (>>) = (m, k) => M.(m >>= (_ => k)); }; ================================================ FILE: src/content/3.4/code/reason/snippet25.re ================================================ module Process_Bind_Without_Do = (W: Monad_Bind with type m('a) = writer(string, 'a)) => { open Monad_Ops(W); let tell = w => Writer((), w); let process = s => W.(up_case(s) >>= (up_str => tell("to_words") >> to_words(up_str))); }; ================================================ FILE: src/content/3.4/code/scala/snippet01.scala ================================================ def fmap[A, B]: (A => B) => List[A] => List[B] = f => { case Nil => Nil case x :: xs => f(x) :: fmap[A, B](f)(xs) } def sum: List[Double] => Double = _.sum import math._ def vlen: List[Double] => Double = sqrt _ compose sum compose fmap(pow(_, 2)) ================================================ FILE: src/content/3.4/code/scala/snippet02.scala ================================================ case class Writer[W, A](run: (A, W)) implicit def writerFunctor[W] = new Functor[Writer[W, ?]] { def fmap[A, B](f: A => B)(fa: Writer[W, A]): Writer[W, B] = fa match { case Writer((a, w)) => Writer(f(a), w) } } ================================================ FILE: src/content/3.4/code/scala/snippet03.scala ================================================ A => Writer[W, B] ================================================ FILE: src/content/3.4/code/scala/snippet04.scala ================================================ trait Monad[M[_]] { def >=>[A, B, C](m1: A => M[B], m2: B => M[C]): A => M[C] def pure[A](a: A): M[A] } ================================================ FILE: src/content/3.4/code/scala/snippet05.scala ================================================ implicit def writerMonad[W: Monoid] = new Functor[Writer[W, ?]] { def >=>[A, B, C](f: A => Writer[W, B], g: B => Writer[W, C]) = a => { val Writer((b, s1)) = f(a) val Writer((c, s2)) = g(b) Writer((c, Monoid[W].combine(s1, s2))) } def pure[A](a: A) = Writer(a, Monoid[W].empty) } object kleisliSyntax { //allows us to use >=> as an infix operator implicit class MonadOps[M[_], A, B](m1: A => M[B]) { def >=>[C](m2: B => M[C])(implicit m: Monad[M]): A => M[C] = { m.>=>(m1, m2) } } } ================================================ FILE: src/content/3.4/code/scala/snippet06.scala ================================================ def tell[W](s: W): Writer[W, Unit] = Writer((), s) ================================================ FILE: src/content/3.4/code/scala/snippet07.scala ================================================ def >=>[A, B, C](f: A => M[B], g: B => M[C]) = a => {...} ================================================ FILE: src/content/3.4/code/scala/snippet08.scala ================================================ def >=>[A, B, C](f: A => M[B], g: B => M[C]) = a => { val mb = f(a) ... } ================================================ FILE: src/content/3.4/code/scala/snippet09.scala ================================================ def flatMap[A, B](ma: M[A])(f: A => M[B]): M[B] ================================================ FILE: src/content/3.4/code/scala/snippet10.scala ================================================ trait Monad[M[_]] { def flatMap[A, B](ma: M[A])(f: A => M[B]): M[B] def pure[A](a: A): M[A] } ================================================ FILE: src/content/3.4/code/scala/snippet11.scala ================================================ object bindSyntax { //allows us to use flatMap as an infix operator implicit class MonadOps[A, B, W: Monoid](wa: Writer[W, A]) { def flatMap(f: A => Writer[W, B]): Writer[W, B] = wa match { case Writer((a, w1)) => val Writer((b, w2)) = f(a) Writer(b, Monoid[W].combine(w1, w2)) } } } ================================================ FILE: src/content/3.4/code/scala/snippet12.scala ================================================ def flatten[A](mma: M[M[A]]): M[A] ================================================ FILE: src/content/3.4/code/scala/snippet13.scala ================================================ def flatMap[A, B](ma: M[A])(f: A => M[B]): M[B] = flatten(fmap(f)(ma)) ================================================ FILE: src/content/3.4/code/scala/snippet14.scala ================================================ trait Monad[M[_]] extends Functor[M] { def flatten[A](mma: M[M[A]]): M[A] def pure[A](a: A): M[A] } ================================================ FILE: src/content/3.4/code/scala/snippet15.scala ================================================ def fmap[A, B](f: A => B)(ma: M[A]): M[B] = flatMap(ma)(a => pure(f(a))) ================================================ FILE: src/content/3.4/code/scala/snippet16.scala ================================================ def flatten[A, W: Monoid](wwa: Writer[W, Writer[W, A]]): Writer[W, A] = wwa match { case Writer((Writer((a, w2)), w1)) => Writer(a, Monoid[W].combine(w1, w2)) } ================================================ FILE: src/content/3.4/code/scala/snippet17.scala ================================================ def toWords: String => Writer[String, List[String]] = s => Writer(s.split("\\s+").toList, "toWords ") def process: String => Writer[String, List[String]] = { import kleisliSyntax._ // -Ypartial-unification should be on upCase >=> toWords } ================================================ FILE: src/content/3.4/code/scala/snippet18.scala ================================================ def process: String => Writer[String, List[String]] = s => { import bindSyntax._ for { upStr <- upCase(s) words <- toWords(upStr) } yield words } ================================================ FILE: src/content/3.4/code/scala/snippet19.scala ================================================ def upCase: String => Writer[String, String] = s => Writer(s.toUpperCase, "upCase ") ================================================ FILE: src/content/3.4/code/scala/snippet20.scala ================================================ def process: String => Writer[String, List[String]] = s => { import bindSyntax._ upCase(s) >>= (upStr => toWords(upStr)) } ================================================ FILE: src/content/3.4/code/scala/snippet21.scala ================================================ upStr <- upCase(s) ================================================ FILE: src/content/3.4/code/scala/snippet22.scala ================================================ def words: String => List[String] = _.split("\\s+").toList def process: String => Writer[String, List[String]] = s => { import bindSyntax._ for { upStr <- upCase(s) _ <- tell("toWords ") } yield words(upStr) } ================================================ FILE: src/content/3.4/code/scala/snippet23.scala ================================================ def process: String => Writer[String, List[String]] = s => { upCase(s).flatMap { upStr => tell("toWords ").flatMap { _ => writerMonad.pure(words(upStr)) } } } ================================================ FILE: src/content/3.4/code/scala/snippet24.scala ================================================ object moreSyntax { // allows us to use >> as an infix operator implicit class MoreOps[A, B, W: Monoid](m: Writer[W, A]) extends bindSyntax.MonadOps[A, B, W](m) { def >>(k: Writer[W, B]): Writer[W, B] = m >>= (_ => k) } } ================================================ FILE: src/content/3.4/code/scala/snippet25.scala ================================================ def process: String => Writer[String, List[String]] = s => { import moreSyntax._ upCase(s) flatMap (upStr => tell("toWords ") >> writerMonad.pure(words(upStr))) } ================================================ FILE: src/content/3.4/monads-programmers-definition.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{P}{rogrammers have developed} a whole mythology around monads. It's supposed to be one of the most abstract and difficult concepts in programming. There are people who ``get it'' and those who don't. For many, the moment when they understand the concept of the monad is like a mystical experience. The monad abstracts the essence of so many diverse constructions that we simply don't have a good analogy for it in everyday life. We are reduced to groping in the dark, like those blind men touching different parts of the elephant end exclaiming triumphantly: ``It's a rope,'' ``It's a tree trunk,'' or ``It's a burrito!'' Let me set the record straight: The whole mysticism around the monad is the result of a misunderstanding. The monad is a very simple concept. It's the diversity of applications of the monad that causes the confusion. As part of research for this post I looked up duct tape (a.k.a., duck tape) and its applications. Here's a little sample of things that you can do with it: \begin{itemize} \tightlist \item sealing ducts \item fixing CO\textsubscript{2} scrubbers on board Apollo 13 \item wart treatment \item fixing Apple's iPhone 4 dropped call issue \item making a prom dress \item building a suspension bridge \end{itemize} \noindent Now imagine that you didn't know what duct tape was and you were trying to figure it out based on this list. Good luck! So I'd like to add one more item to the collection of ``the monad is like\ldots{}'' clichés: The monad is like duct tape. Its applications are widely diverse, but its principle is very simple: it glues things together. More precisely, it composes things. This partially explains the difficulties a lot of programmers, especially those coming from the imperative background, have with understanding the monad. The problem is that we are not used to thinking of programming in terms of function composition. This is understandable. We often give names to intermediate values rather than pass them directly from function to function. We also inline short segments of glue code rather than abstract them into helper functions. Here's an imperative-style implementation of the vector-length function in C: \begin{snip}{cpp} double vlen(double * v) { double d = 0.0; int n; for (n = 0; n < 3; ++n) d += v[n] * v[n]; return sqrt(d); } \end{snip} Compare this with the (stylized) Haskell version that makes function composition explicit: \src{snippet01} (Here, to make things even more cryptic, I partially applied the exponentiation operator \code{(\^{})} by setting its second argument to \code{2}.) I'm not arguing that Haskell's point-free style is always better, just that function composition is at the bottom of everything we do in programming. And even though we are effectively composing functions, Haskell does go to great lengths to provide imperative-style syntax called the \code{do} notation for monadic composition. We'll see its use later. But first, let me explain why we need monadic composition in the first place. \section{The Kleisli Category} We have previously arrived at the \hyperref[kleisli-categories]{writer monad} by embellishing regular functions. The particular embellishment was done by pairing their return values with strings or, more generally, with elements of a monoid. We can now recognize that such an embellishment is a functor: \src{snippet02} We have subsequently found a way of composing embellished functions, or Kleisli arrows, which are functions of the form: \src{snippet03} It was inside the composition that we implemented the accumulation of the log. We are now ready for a more general definition of the Kleisli category. We start with a category $\cat{C}$ and an endofunctor $m$. The corresponding Kleisli category $\cat{K}$ has the same objects as $\cat{C}$, but its morphisms are different. A morphism between two objects $a$ and $b$ in $\cat{K}$ is implemented as a morphism: \[a \to m\ b\] in the original category $\cat{C}$. It's important to keep in mind that we treat a Kleisli arrow in $\cat{K}$ as a morphism between $a$ and $b$, and not between $a$ and $m\ b$. In our example, $m$ was specialized to \code{Writer w}, for some fixed monoid \code{w}. Kleisli arrows form a category only if we can define proper composition for them. If there is a composition, which is associative and has an identity arrow for every object, then the functor $m$ is called a \newterm{monad}, and the resulting category is called the Kleisli category. In Haskell, Kleisli composition is defined using the fish operator \code{>=>}, and the identity arrow is a polymorphic function called \code{return}. Here's the definition of a monad using Kleisli composition: \src{snippet04} Keep in mind that there are many equivalent ways of defining a monad, and that this is not the primary one in the Haskell ecosystem. I like it for its conceptual simplicity and the intuition it provides, but there are other definitions that are more convenient when programming. We'll talk about them momentarily. In this formulation, monad laws are very easy to express. They cannot be enforced in Haskell, but they can be used for equational reasoning. They are simply the standard composition laws for the Kleisli category: \begin{snip}{haskell} (f >=> g) >=> h = f >=> (g >=> h) -- associativity return >=> f = f -- left unit f >=> return = f -- right unit \end{snip} This kind of a definition also expresses what a monad really is: it's a way of composing embellished functions. It's not about side effects or state. It's about composition. As we'll see later, embellished functions may be used to express a variety of effects or state, but that's not what the monad is for. The monad is the sticky duct tape that ties one end of an embellished function to the other end of an embellished function. Going back to our \code{Writer} example: The logging functions (the Kleisli arrows for the \code{Writer} functor) form a category because \code{Writer} is a monad: \src{snippet05} Monad laws for \code{Writer w} are satisfied as long as monoid laws for \code{w} are satisfied (they can't be enforced in Haskell either). There's a useful Kleisli arrow defined for the \code{Writer} monad called \code{tell}. It's sole purpose is to add its argument to the log: \src{snippet06} We'll use it later as a building block for other monadic functions. \section{Fish Anatomy} When implementing the fish operator for different monads you quickly realize that a lot of code is repeated and can be easily factored out. To begin with, the Kleisli composition of two functions must return a function, so its implementation may as well start with a lambda taking an argument of type \code{a}: \src{snippet07} The only thing we can do with this argument is to pass it to \code{f}: \src{snippet08} At this point we have to produce the result of type \code{m c}, having at our disposal an object of type \code{m b} and a function \code{g :: b -> m c}. Let's define a function that does that for us. This function is called \emph{bind} and is usually written in the form of an infix operator: \src{snippet09} For every monad, instead of defining the fish operator, we may instead define bind. In fact the standard Haskell definition of a monad uses bind: \src{snippet10} Here's the definition of bind for the \code{Writer} monad: \src{snippet11} It is indeed shorter than the definition of the fish operator. It's possible to further dissect bind, taking advantage of the fact that \code{m} is a functor. We can use \code{fmap} to apply the function \code{a -> m b} to the contents of \code{m a}. This will turn \code{a} into \code{m b}. The result of the application is therefore of type \code{m (m b)}. This is not exactly what we want --- we need the result of type \code{m b} --- but we're close. All we need is a function that collapses or flattens the double application of \code{m}. Such a function is called \code{join}: \src{snippet12} Using \code{join}, we can rewrite bind as: \src{snippet13} That leads us to the third option for defining a monad: \src{snippet14} Here we have explicitly requested that \code{m} be a \code{Functor}. We didn't have to do that in the previous two definitions of the monad. That's because any type constructor \code{m} that either supports the fish or bind operator is automatically a functor. For instance, it's possible to define \code{fmap} in terms of bind and \code{return}: \src{snippet15} For completeness, here's \code{join} for the \code{Writer} monad: \src{snippet16} \section{The \texttt{do} Notation} One way of writing code using monads is to work with Kleisli arrows --- composing them using the fish operator. This mode of programming is the generalization of the point-free style. Point-free code is compact and often quite elegant. In general, though, it can be hard to understand, bordering on cryptic. That's why most programmers prefer to give names to function arguments and intermediate values. When dealing with monads it means favoring the bind operator over the fish operator. Bind takes a monadic value and returns a monadic value. The programmer may choose to give names to those values. But that's hardly an improvement. What we really want is to pretend that we are dealing with regular values, not the monadic containers that encapsulate them. That's how imperative code works --- side effects, such as updating a global log, are mostly hidden from view. And that's what the \code{do} notation emulates in Haskell. You might be wondering then, why use monads at all? If we want to make side effects invisible, why not stick to an imperative language? The answer is that the monad gives us much better control over side effects. For instance, the log in the \code{Writer} monad is passed from function to function and is never exposed globally. There is no possibility of garbling the log or creating a data race. Also, monadic code is clearly demarcated and cordoned off from the rest of the program. The \code{do} notation is just syntactic sugar for monadic composition. On the surface, it looks a lot like imperative code, but it translates directly to a sequence of binds and lambda expressions. For instance, take the example we used previously to illustrate the composition of Kleisli arrows in the \code{Writer} monad. Using our current definitions, it could be rewritten as: \src{snippet17} This function turns all characters in the input string to upper case and splits it into words, all the while producing a log of its actions. In the \code{do} notation it would look like this: \src{snippet18} Here, \code{upStr} is just a \code{String}, even though \code{upCase} produces a \code{Writer}: \src{snippet19} This is because the \code{do} block is desugared by the compiler to: \src{snippet20} The monadic result of \code{upCase} is bound to a lambda that takes a \code{String}. It's the name of this string that shows up in the \code{do} block. When reading the line: \src{snippet21} we say that \code{upStr} \emph{gets} the result of \code{upCase s}. The pseudo-imperative style is even more pronounced when we inline \code{toWords}. We replace it with the call to \code{tell}, which logs the string \code{"toWords "}, followed by the call to \code{return} with the result of splitting the string \code{upStr} using \code{words}. Notice that \code{words} is a regular function working on strings. \src{snippet22} Here, each line in the do block introduces a new nested bind in the desugared code: \src{snippet23} Notice that \code{tell} produces a unit value, so it doesn't have to be passed to the following lambda. Ignoring the contents of a monadic result (but not its effect --- here, the contribution to the log) is quite common, so there is a special operator to replace bind in that case: \src{snippet24} The actual desugaring of our code looks like this: \src{snippet25} In general, \code{do} blocks consist of lines (or sub-blocks) that either use the left arrow to introduce new names that are then available in the rest of the code, or are executed purely for side-effects. Bind operators are implicit between the lines of code. Incidentally, it is possible, in Haskell, to replace the formatting in the \code{do} blocks with braces and semicolons. This provides the justification for describing the monad as a way of overloading the semicolon. Notice that the nesting of lambdas and bind operators when desugaring the \code{do} notation has the effect of influencing the execution of the rest of the \code{do} block based on the result of each line. This property can be used to introduce complex control structures, for instance to simulate exceptions. Interestingly, the equivalent of the \code{do} notation has found its application in imperative languages, C++ in particular. I'm talking about resumable functions or coroutines. It's not a secret that C++ \urlref{https://bartoszmilewski.com/2014/02/26/c17-i-see-a-monad-in-your-future/}{futures form a monad}. It's an example of the continuation monad, which we'll discuss shortly. The problem with continuations is that they are very hard to compose. In Haskell, we use the \code{do} notation to turn the spaghetti of ``my handler will call your handler'' into something that looks very much like sequential code. Resumable functions make the same transformation possible in C++. And the same mechanism can be applied to turn the \urlref{https://bartoszmilewski.com/2014/04/21/getting-lazy-with-c/}{spaghetti of nested loops} into list comprehensions or ``generators,'' which are essentially the \code{do} notation for the list monad. Without the unifying abstraction of the monad, each of these problems is typically addressed by providing custom extensions to the language. In Haskell, this is all dealt with through libraries. ================================================ FILE: src/content/3.5/code/haskell/snippet01.hs ================================================ instance Monad [] where join = concat return x = [x] ================================================ FILE: src/content/3.5/code/haskell/snippet02.hs ================================================ as >>= k = concat (fmap k as) ================================================ FILE: src/content/3.5/code/haskell/snippet03.hs ================================================ triples = do z <- [1..] x <- [1..z] y <- [x..z] guard (x^2 + y^2 == z^2) return (x, y, z) ================================================ FILE: src/content/3.5/code/haskell/snippet04.hs ================================================ guard :: Bool -> [()] guard True = [()] guard False = [] ================================================ FILE: src/content/3.5/code/haskell/snippet05.hs ================================================ triples = [(x, y, z) | z <- [1..] , x <- [1..z] , y <- [x..z] , x^2 + y^2 == z^2] ================================================ FILE: src/content/3.5/code/haskell/snippet06.hs ================================================ newtype Reader e a = Reader (e -> a) ================================================ FILE: src/content/3.5/code/haskell/snippet07.hs ================================================ runReader :: Reader e a -> e -> a runReader (Reader f) e = f e ================================================ FILE: src/content/3.5/code/haskell/snippet08.hs ================================================ ra >>= k = Reader (\e -> ...) ================================================ FILE: src/content/3.5/code/haskell/snippet09.hs ================================================ ra >>= k = Reader (\e -> let a = runReader ra e in ...) ================================================ FILE: src/content/3.5/code/haskell/snippet10.hs ================================================ ra >>= k = Reader (\e -> let a = runReader ra e rb = k a in ...) ================================================ FILE: src/content/3.5/code/haskell/snippet11.hs ================================================ ra >>= k = Reader (\e -> let a = runReader ra e rb = k a in runReader rb e) ================================================ FILE: src/content/3.5/code/haskell/snippet12.hs ================================================ instance Monad (Reader e) where ra >>= k = Reader (\e -> runReader (k (runReader ra e)) e) return x = Reader (\e -> x) ================================================ FILE: src/content/3.5/code/haskell/snippet13.hs ================================================ newtype Writer w a = Writer (a, w) ================================================ FILE: src/content/3.5/code/haskell/snippet14.hs ================================================ runWriter :: Writer w a -> (a, w) runWriter (Writer (a, w)) = (a, w) ================================================ FILE: src/content/3.5/code/haskell/snippet15.hs ================================================ instance (Monoid w) => Monad (Writer w) where (Writer (a, w)) >>= k = let (a', w') = runWriter (k a) in Writer (a', w `mappend` w') return a = Writer (a, mempty) ================================================ FILE: src/content/3.5/code/haskell/snippet16.hs ================================================ newtype State s a = State (s -> (a, s)) ================================================ FILE: src/content/3.5/code/haskell/snippet17.hs ================================================ runState :: State s a -> s -> (a, s) runState (State f) s = f s ================================================ FILE: src/content/3.5/code/haskell/snippet18.hs ================================================ sa >>= k = State (\s -> let (a, s') = runState sa s sb = k a in runState sb s') ================================================ FILE: src/content/3.5/code/haskell/snippet19.hs ================================================ instance Monad (State s) where sa >>= k = State (\s -> let (a, s') = runState sa s in runState (k a) s') return a = State (\s -> (a, s)) ================================================ FILE: src/content/3.5/code/haskell/snippet20.hs ================================================ get :: State s s get = State (\s -> (s, s)) ================================================ FILE: src/content/3.5/code/haskell/snippet21.hs ================================================ put :: s -> State s () put s' = State (\s -> ((), s')) ================================================ FILE: src/content/3.5/code/haskell/snippet22.hs ================================================ instance Monad Maybe where Nothing >>= k = Nothing Just a >>= k = k a return a = Just a ================================================ FILE: src/content/3.5/code/haskell/snippet23.hs ================================================ data Cont r a = Cont ((a -> r) -> r) ================================================ FILE: src/content/3.5/code/haskell/snippet24.hs ================================================ runCont :: Cont r a -> (a -> r) -> r runCont (Cont k) h = k h ================================================ FILE: src/content/3.5/code/haskell/snippet25.hs ================================================ (>>=) :: ((a -> r) -> r) -> (a -> (b -> r) -> r) -> ((b -> r) -> r) ================================================ FILE: src/content/3.5/code/haskell/snippet26.hs ================================================ ka >>= kab = Cont (\hb -> ...) ================================================ FILE: src/content/3.5/code/haskell/snippet27.hs ================================================ runCont ka (\a -> ...) ================================================ FILE: src/content/3.5/code/haskell/snippet28.hs ================================================ runCont ka (\a -> let kb = kab a in runCont kb hb) ================================================ FILE: src/content/3.5/code/haskell/snippet29.hs ================================================ instance Monad (Cont r) where ka >>= kab = Cont (\hb -> runCont ka (\a -> runCont (kab a) hb)) return a = Cont (\ha -> ha a) ================================================ FILE: src/content/3.5/code/haskell/snippet30.hs ================================================ getChar :: () -> IO Char ================================================ FILE: src/content/3.5/code/haskell/snippet31.hs ================================================ main :: IO () ================================================ FILE: src/content/3.5/code/haskell/snippet32.hs ================================================ main :: () -> IO () ================================================ FILE: src/content/3.5/code/haskell/snippet33.hs ================================================ type IO a = RealWorld -> (a, RealWorld) ================================================ FILE: src/content/3.5/code/haskell/snippet34.hs ================================================ type IO = State RealWorld ================================================ FILE: src/content/3.5/code/haskell/snippet35.hs ================================================ putStr :: String -> IO () ================================================ FILE: src/content/3.5/code/haskell/snippet36.hs ================================================ putStr :: String -> () ================================================ FILE: src/content/3.5/code/haskell/snippet37.hs ================================================ main :: IO () main = do putStr "Hello " putStr "World!" ================================================ FILE: src/content/3.5/code/ocaml/snippet01.ml ================================================ module List_Monad : Monad_Join = struct type 'a m = 'a list let join = List.concat let return a = [ a ] end ================================================ FILE: src/content/3.5/code/ocaml/snippet02.ml ================================================ let ( >>= ) xs k = List.concat (List.map k xs) ================================================ FILE: src/content/3.5/code/ocaml/snippet03.ml ================================================ module Pythagorean = struct let ( let* ) = Fn.flip Gen.flat_map let ( let+ ) x f = Gen.map f x let guard b = if b then Gen.return () else Gen.empty let triples = let* z = Gen.init (fun i -> i + 1) in let* x = Gen.init ~limit:z (fun i -> i + 1) in let* y = Gen.init ~limit:z (fun i -> i + x) in let+ _ = guard ((x * x) + (y * y) = z * z) in Gen.return (x, y, z) end ================================================ FILE: src/content/3.5/code/ocaml/snippet04.ml ================================================ let guard = function | true -> [ () ] | false -> [] ================================================ FILE: src/content/3.5/code/ocaml/snippet05.ml ================================================ module Pythagorean = struct let ( let* ) = Fn.flip Gen.flat_map let ( let+ ) x f = Gen.map f x let guard b = if b then Gen.return () else Gen.empty let triples = let* z = Gen.init (fun i -> i + 1) in let* x = Gen.init ~limit:z (fun i -> i + 1) in let* y = Gen.init ~limit:z (fun i -> i + x) in if (x * x) + (y * y) = z * z then Gen.return (x, y, z) else Gen.empty end ================================================ FILE: src/content/3.5/code/ocaml/snippet06.ml ================================================ type ('e, 'a) reader = Reader of ('e -> 'a) ================================================ FILE: src/content/3.5/code/ocaml/snippet07.ml ================================================ let run_reader (Reader f) e = f e ================================================ FILE: src/content/3.5/code/ocaml/snippet08.ml ================================================ let (>>=) ra k = Reader (fun e -> ...) ================================================ FILE: src/content/3.5/code/ocaml/snippet09.ml ================================================ let (>>=) ra k = Reader (fun e -> let a = run_reader ra e in ...) ================================================ FILE: src/content/3.5/code/ocaml/snippet10.ml ================================================ let (>>=) ra k = Reader (fun e -> let a = run_reader ra e in let rb = k a in ...) ================================================ FILE: src/content/3.5/code/ocaml/snippet11.ml ================================================ let ( >>= ) ra k = Reader (fun e -> let a = run_reader ra e in let rb = k a in run_reader rb e) ================================================ FILE: src/content/3.5/code/ocaml/snippet12.ml ================================================ module ReaderMonad (T : sig type t end) : Monad_Bind = struct type 'a m = (T.t, 'a) reader let return a = Reader (fun e -> a) let ( >>= ) ra k = Reader (fun e -> run_reader (k (run_reader ra e)) e) end ================================================ FILE: src/content/3.5/code/ocaml/snippet13.ml ================================================ type ('w, 'a) writer = Writer of ('a * 'w) ================================================ FILE: src/content/3.5/code/ocaml/snippet14.ml ================================================ let run_writer (Writer (a, w)) = a, w ================================================ FILE: src/content/3.5/code/ocaml/snippet15.ml ================================================ module WriterMonad (W : Monoid) : Monad_Bind = struct type 'a m = (W.t, 'a) writer let return a = Writer (a, W.mempty) let ( >>= ) (Writer (a, w)) k = let a', w' = run_writer (k a) in Writer (a', W.mappend w w') end ================================================ FILE: src/content/3.5/code/ocaml/snippet16.ml ================================================ type ('s, 'a) state = State of ('s -> 'a * 's) ================================================ FILE: src/content/3.5/code/ocaml/snippet17.ml ================================================ let run_state (State f) s = f s ================================================ FILE: src/content/3.5/code/ocaml/snippet18.ml ================================================ let ( >>= ) sa k = State (fun s -> let a, s' = run_state sa s in let sb = k a in run_state sb s') ================================================ FILE: src/content/3.5/code/ocaml/snippet19.ml ================================================ module State_Monad (S : sig type t end) : Monad_Bind = struct type 'a m = (S.t, 'a) state let ( >>= ) sa k = State (fun s -> let a, s' = run_state sa s in let sb = k a in run_state sb s') let return a = State (fun s -> a, s) end ================================================ FILE: src/content/3.5/code/ocaml/snippet20.ml ================================================ let get = State (fun s -> s, s) ================================================ FILE: src/content/3.5/code/ocaml/snippet21.ml ================================================ let put s' = State (fun s -> (), s') ================================================ FILE: src/content/3.5/code/ocaml/snippet22.ml ================================================ module OptionMonad : Monad_Bind = struct type 'a m = 'a option let ( >>= ) = function | Some a -> fun k -> k a | None -> fun _ -> None let return a = Some a end ================================================ FILE: src/content/3.5/code/ocaml/snippet23.ml ================================================ type ('r, 'a) cont = Cont of (('a -> 'r) -> 'r) ================================================ FILE: src/content/3.5/code/ocaml/snippet24.ml ================================================ let run_cont (Cont k) h = k h ================================================ FILE: src/content/3.5/code/ocaml/snippet25.ml ================================================ val ( >>= ) : (('a -> 'r) -> 'r) -> ('a -> ('b -> 'r) -> 'r) -> ('b -> 'r) -> 'r ================================================ FILE: src/content/3.5/code/ocaml/snippet26.ml ================================================ let (>>=) ka kab = Cont (fun hb -> ...) ================================================ FILE: src/content/3.5/code/ocaml/snippet27.ml ================================================ run_cont ka (fun a -> ...) ================================================ FILE: src/content/3.5/code/ocaml/snippet28.ml ================================================ ;; run_cont ka (fun a -> let kb = kab a in run_cont kb hb) ================================================ FILE: src/content/3.5/code/ocaml/snippet29.ml ================================================ module Cont_Monad (R : sig type t end) : Monad_Bind = struct type 'a m = (R.t, 'a) cont let return a = Cont (fun ha -> ha a) let ( >>= ) ka kab = Cont (fun hb -> run_cont ka (fun a -> run_cont (kab a) hb)) end ================================================ FILE: src/content/3.5/code/ocaml/snippet30.ml ================================================ module type Terminal_IO = sig (* OCaml doesn't have a built-in IO type*) type 'a io = IO of (unit -> 'a) val get_char : unit -> char io end ================================================ FILE: src/content/3.5/code/ocaml/snippet31.ml ================================================ val main : unit io ================================================ FILE: src/content/3.5/code/ocaml/snippet32.ml ================================================ val main : unit -> unit io ================================================ FILE: src/content/3.5/code/ocaml/snippet33.ml ================================================ type 'a io = realworld -> 'a * realworld ================================================ FILE: src/content/3.5/code/ocaml/snippet34.ml ================================================ type 'a io = realworld state ================================================ FILE: src/content/3.5/code/ocaml/snippet35.ml ================================================ val put_str : string -> unit io ================================================ FILE: src/content/3.5/code/ocaml/snippet36.ml ================================================ val put_str : string -> unit ================================================ FILE: src/content/3.5/code/ocaml/snippet37.ml ================================================ (* Monad implementation for type io *) module IOMonad : Monad_Bind with type 'a m = 'a io = struct type 'a m = 'a io let return x = IO (fun () -> x) let ( >>= ) m f = IO (fun () -> let (IO m') = m in let (IO m'') = f (m' ()) in m'' ()) end (* main *) module IO_Main = struct let ( let* ) = IOMonad.( >>= ) let main = let* _ = put_str "Hello" in put_str "world!" end ================================================ FILE: src/content/3.5/code/reason/snippet01.re ================================================ module List_Monad: Monad_Join = { type m('a) = list('a); let join = List.concat; let return = a => [a]; }; ================================================ FILE: src/content/3.5/code/reason/snippet02.re ================================================ let (>>=) = (xs, k) => List.concat(List.map(k, xs)); ================================================ FILE: src/content/3.5/code/reason/snippet03.re ================================================ module Pythagorean = { let (let*) = Fn.flip(Gen.flat_map); let (let+) = (x, f) => Gen.map(f, x); let guard = b => b ? Gen.return() ? Gen.empty; let triples = { let* z = Gen.init(i => i + 1); let* x = Gen.init(~limit=z, i => i + 1); let* y = Gen.init(~limit=z, i => i + x); let+ _ = guard(x * x + y * y == z * z); Gen.return((x, y, z)); }; }; ================================================ FILE: src/content/3.5/code/reason/snippet04.re ================================================ let guard = fun | true => [()] | false => []; ================================================ FILE: src/content/3.5/code/reason/snippet05.re ================================================ module Pythagorean = { let (let*) = Fn.flip(Gen.flat_map); let (let+) = (x, f) => Gen.map(f, x); let guard = b => b ? Gen.return() : Gen.empty; let triples = { let* z = Gen.init(i => i + 1); let* x = Gen.init(~limit=z, i => i + 1); let* y = Gen.init(~limit=z, i => i + x); if (x * x + y * y == z * z) { Gen.return((x, y, z)); } else { Gen.empty; }; }; }; ================================================ FILE: src/content/3.5/code/reason/snippet06.re ================================================ type reader('e, 'a) = | Reader('e => 'a); ================================================ FILE: src/content/3.5/code/reason/snippet07.re ================================================ let run_reader = (Reader(f), e) => f(e); ================================================ FILE: src/content/3.5/code/reason/snippet08.re ================================================ let (>>=) = (ra, k) => Reader(e => ...); ================================================ FILE: src/content/3.5/code/reason/snippet09.re ================================================ let (>>=) = (ra, k) => Reader(e => { let a = run_reader(ra, e); ... }); ================================================ FILE: src/content/3.5/code/reason/snippet10.re ================================================ let (>>=) = (ra, k) => Reader(e => { let a = run_reader(ra, e); let rb = k(a); ... }); ================================================ FILE: src/content/3.5/code/reason/snippet11.re ================================================ let (>>=) = (ra, k) => Reader(e => { let a = run_reader(ra, e); let rb = k(a); run_reader(rb, e); }); ================================================ FILE: src/content/3.5/code/reason/snippet12.re ================================================ module ReaderMonad = (T: {type t;}) : Monad_Bind => { type m('a) = reader(T.t, 'a); let return = a => Reader(e => a); let (>>=) = (ra, k) => Reader(e => run_reader(k(run_reader(ra, e)), e)); }; ================================================ FILE: src/content/3.5/code/reason/snippet13.re ================================================ type writer('w, 'a) = | Writer(('a, 'w)); ================================================ FILE: src/content/3.5/code/reason/snippet14.re ================================================ let run_writer = (Writer(a, w)) => (a, w); ================================================ FILE: src/content/3.5/code/reason/snippet15.re ================================================ module WriterMonad = (W: Monoid) : Monad_Bind => { type m('a) = writer(W.t, 'a); let return = a => Writer(a, W.mempty); let (>>=) = (Writer(a, w), k) => { let (a', w') = run_writer(k(a)); Writer(a', W.mappend(w, w')); }; }; ================================================ FILE: src/content/3.5/code/reason/snippet16.re ================================================ type state('s, 'a) = | State('s => ('a, 's)); ================================================ FILE: src/content/3.5/code/reason/snippet17.re ================================================ let run_state = (State(f), s) => f(s); ================================================ FILE: src/content/3.5/code/reason/snippet18.re ================================================ let (>>=) = (sa, k) => State(s => { let (a, s') = run_state(sa, s); let sb = k(a); run_state(sb, s'); }); ================================================ FILE: src/content/3.5/code/reason/snippet19.re ================================================ module State_Monad = (S: {type t;}) : Monad_Bind => { type m('a) = state(S.t, 'a); let (>>=) = (sa, k) => State(s => { let (a, s') = run_state(sa, s); let sb = k(a); run_state(sb, s'); }); let return = a => State(s => (a, s)); }; ================================================ FILE: src/content/3.5/code/reason/snippet20.re ================================================ let get = State(s => (s, s)); ================================================ FILE: src/content/3.5/code/reason/snippet21.re ================================================ let put = s' => State(s => ((), s')); ================================================ FILE: src/content/3.5/code/reason/snippet22.re ================================================ module OptionMonad: Monad_Bind = { type m('a) = option('a); let (>>=) = fun | Some(a) => (k => k(a)) | None => (_ => None); let return = a => Some(a); }; ================================================ FILE: src/content/3.5/code/reason/snippet23.re ================================================ type cont('r, 'a) = | Cont(('a => 'r) => 'r); ================================================ FILE: src/content/3.5/code/reason/snippet24.re ================================================ let run_cont = (Cont(k), h) => k(h); ================================================ FILE: src/content/3.5/code/reason/snippet25.re ================================================ let (>>=): (('a => 'r) => 'r, ('a, 'b => 'r) => 'r, 'b => 'r) => 'r; ================================================ FILE: src/content/3.5/code/reason/snippet26.re ================================================ let (>>=) = (ka, kab) => Cont(hb => ...); ================================================ FILE: src/content/3.5/code/reason/snippet27.re ================================================ run_cont(ka, a => ...); ================================================ FILE: src/content/3.5/code/reason/snippet28.re ================================================ run_cont(ka,a => { let kb = kab(a); run_cont(kb, hb); }); ================================================ FILE: src/content/3.5/code/reason/snippet29.re ================================================ module Cont_Monad = (R: {type t;}) : Monad_Bind => { type m('a) = cont(R.t, 'a); let return = a => Cont(ha => ha(a)); let (>>=) = (ka, kab) => Cont(hb => run_cont(ka, a => run_cont(kab(a), hb))); }; ================================================ FILE: src/content/3.5/code/reason/snippet30.re ================================================ module type Terminal_IO = { /* ReasonML doesn't have a built-in IO type*/ type io('a) = | IO(unit => 'a); let get_char: unit => io(char); }; ================================================ FILE: src/content/3.5/code/reason/snippet31.re ================================================ let main: io(unit); ================================================ FILE: src/content/3.5/code/reason/snippet32.re ================================================ let main: unit => io(unit); ================================================ FILE: src/content/3.5/code/reason/snippet33.re ================================================ type io('a) = realworld => ('a, realworld); ================================================ FILE: src/content/3.5/code/reason/snippet34.re ================================================ type io('a) = state(realworld); ================================================ FILE: src/content/3.5/code/reason/snippet35.re ================================================ let put_str: string => io(unit); ================================================ FILE: src/content/3.5/code/reason/snippet36.re ================================================ let put_str: string => unit; ================================================ FILE: src/content/3.5/code/reason/snippet37.re ================================================ /* Monad implementation for type io */ module IOMonad: Monad_Bind with type m('a) = io('a) = { type m('a) = io('a); let return = x => IO(() => x); let (>>=) = (m, f) => IO(() => { let IO(m') = m; let IO(m'') = f(m'()); m''(); }); }; /* main */ module IO_Main = { let (let*) = IOMonad.(>>=); let main = { let* _ = put_str("Hello"); put_str("world!"); }; }; ================================================ FILE: src/content/3.5/code/scala/snippet01.scala ================================================ implicit val listMonad = new Monad[List] { def flatten[A](mma: List[List[A]]): List[A] = mma.reduce(_ ++ _) def pure[A](a: A): List[A] = List(a) } ================================================ FILE: src/content/3.5/code/scala/snippet02.scala ================================================ def flatMap[A, B](as: List[A])(k: A => List[B]): List[B] = flatten(fmap(k)(as)) ================================================ FILE: src/content/3.5/code/scala/snippet03.scala ================================================ // Streams in Scala can be // thought of as lazy lists def triples = for { z <- Stream.from(1) x <- 1 to z y <- x to z _ <- guard(x * x + y * y == z * z) } yield (x, y, z) ================================================ FILE: src/content/3.5/code/scala/snippet04.scala ================================================ def guard: Boolean => List[Unit] = { case true => List(()) case false => List.empty[Unit] } ================================================ FILE: src/content/3.5/code/scala/snippet05.scala ================================================ def triples = for { z <- Stream.from(1) x <- 1 to z y <- x to z if x * x + y * y == z * z } yield (x, y, z) ================================================ FILE: src/content/3.5/code/scala/snippet06.scala ================================================ case class Reader[E, A](run: E => A) ================================================ FILE: src/content/3.5/code/scala/snippet07.scala ================================================ def runReader[E, A]: Reader[E, A] => E => A = { case Reader(f) => e => f(e) } ================================================ FILE: src/content/3.5/code/scala/snippet08.scala ================================================ def flatMap[A, B](ra: Reader[E, A])(k: A => Reader[E, B]): Reader[E, B] = Reader { e => ... } ================================================ FILE: src/content/3.5/code/scala/snippet09.scala ================================================ def flatMap[A, B](ra: Reader[E, A])(k: A => Reader[E, B]): Reader[E, B] = Reader { e => val a = runReader(ra)(e) ... } ================================================ FILE: src/content/3.5/code/scala/snippet10.scala ================================================ def flatMap[A, B](ra: Reader[E, A])(k: A => Reader[E, B]): Reader[E, B] = Reader { e => val a = runReader(ra)(e) val rb = k(a) ... } ================================================ FILE: src/content/3.5/code/scala/snippet11.scala ================================================ def flatMap[A, B](ra: Reader[E, A])(k: A => Reader[E, B]): Reader[E, B] = Reader { e => val a = runReader(ra)(e) val rb = k(a) runReader(rb)(e) } ================================================ FILE: src/content/3.5/code/scala/snippet12.scala ================================================ implicit def readerMonad[E] = new Monad[Reader[E, ?]] { def pure[A](x: A): Reader[E, A] = Reader(e => x) def flatMap[A, B](ra: Reader[E, A])(k: A => Reader[E, B]): Reader[E, B] = Reader(e => runReader(k(runReader(ra)(e)))(e)) } ================================================ FILE: src/content/3.5/code/scala/snippet13.scala ================================================ case class Writer[W, A](run: (A, W)) ================================================ FILE: src/content/3.5/code/scala/snippet14.scala ================================================ def runWriter[W, A]: Writer[W, A] => (A, W) = { case Writer((a, w)) => (a, w) } ================================================ FILE: src/content/3.5/code/scala/snippet15.scala ================================================ implicit def writerMonad[W: Monoid] = new Monad[Writer[W, ?]] { def flatMap[A, B](wa: Writer[W, A])(k: A => Writer[W, B]): Writer[W, B] = wa match { case Writer((a, w)) => val ((a1, w1)) = runWriter(k(a)) Writer((a1, Monoid[W].combine(w, w1))) } def pure[A](a: A) = Writer(a, Monoid[W].empty) } ================================================ FILE: src/content/3.5/code/scala/snippet16.scala ================================================ case class State[S, A](run: S => (A, S)) ================================================ FILE: src/content/3.5/code/scala/snippet17.scala ================================================ def runState[S, A]: State[S, A] => S => (A, S) = { case State(f) => s => f(s) } ================================================ FILE: src/content/3.5/code/scala/snippet18.scala ================================================ def flatMap[A, B](sa: State[S, A])(k: A => State[S, B]): State[S, B] = State { s => val (a, s1) = runState(sa)(s) val sb = k(a) runState(sb)(s1) } ================================================ FILE: src/content/3.5/code/scala/snippet19.scala ================================================ implicit def stateMonad[S] = new Monad[State[S, ?]] { def flatMap[A, B](sa: State[S, A])(k: A => State[S, B]): State[S, B] = State { s => val (a, s1) = runState(sa)(s) runState(k(a))(s1) } def pure[A](a: A): State[S, A] = State(s => (a, s)) } ================================================ FILE: src/content/3.5/code/scala/snippet20.scala ================================================ def get: State[S, S] = State(s => (s, s)) ================================================ FILE: src/content/3.5/code/scala/snippet21.scala ================================================ def put(s1: S): State[S, Unit] = State(s => ((), s1)) ================================================ FILE: src/content/3.5/code/scala/snippet22.scala ================================================ implicit val optionMonad = new Monad[Option] { def flatMap[A, B](ma: Option[A])(k: A => Option[B]): Option[B] = ma match { case None => None case Some(a) => k(a) } def pure[A](a: A): Option[A] = Some(a) } ================================================ FILE: src/content/3.5/code/scala/snippet23.scala ================================================ case class Cont[R, A](run: (A => R) => R) ================================================ FILE: src/content/3.5/code/scala/snippet24.scala ================================================ def runCont[R, A]: Cont[R, A] => (A => R) => R = { case Cont(k) => h => k(h) } ================================================ FILE: src/content/3.5/code/scala/snippet25.scala ================================================ def flatMap[A, B]: ((A => R) => R) => (A => (B => R) => R) => ((B => R) => R) ================================================ FILE: src/content/3.5/code/scala/snippet26.scala ================================================ def flatMap[A, B](ka: Cont[R, A])(kab: A => Cont[R, B]): Cont[R, B] = Cont(hb => ...) ================================================ FILE: src/content/3.5/code/scala/snippet27.scala ================================================ runCont(ka)(a => ...) ================================================ FILE: src/content/3.5/code/scala/snippet28.scala ================================================ runCont(ka) { a => val kb = kab(a) runCont(kb)(hb) } ================================================ FILE: src/content/3.5/code/scala/snippet29.scala ================================================ implicit def contMonad[R] = new Monad[Cont[R, ?]] { def flatMap[A, B](ka: Cont[R, A])(kab: A => Cont[R, B]): Cont[R, B] = Cont { hb => runCont(ka)(a => runCont(kab(a))(hb)) } def pure[A](a: A): Cont[R, A] = Cont(ha => ha(a)) } ================================================ FILE: src/content/3.5/code/scala/snippet30.scala ================================================ def getChar: Unit => IO[Char] ================================================ FILE: src/content/3.5/code/scala/snippet31.scala ================================================ def main: IO[Unit] ================================================ FILE: src/content/3.5/code/scala/snippet32.scala ================================================ def main: Unit => IO[Unit] ================================================ FILE: src/content/3.5/code/scala/snippet33.scala ================================================ type IO[A] = RealWorld => (A, RealWorld) ================================================ FILE: src/content/3.5/code/scala/snippet34.scala ================================================ type IO[A] = State[RealWorld, A] ================================================ FILE: src/content/3.5/code/scala/snippet35.scala ================================================ def putStr: String => IO[Unit] ================================================ FILE: src/content/3.5/code/scala/snippet36.scala ================================================ def putStr: String => Unit ================================================ FILE: src/content/3.5/code/scala/snippet37.scala ================================================ def main: IO[Unit] = for { _ <- putStr("Hello ") _ <- putStr("World!") } yield () ================================================ FILE: src/content/3.5/monads-and-effects.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{N}{ow that we know} what the monad is for --- it lets us compose embellished functions --- the really interesting question is why embellished functions are so important in functional programming. We've already seen one example, the \code{Writer} monad, where embellishment let us create and accumulate a log across multiple function calls. A problem that would otherwise be solved using impure functions (e.g., by accessing and modifying some global state) was solved with pure functions. \section{The Problem} Here is a short list of similar problems, copied from Eugenio Moggi's seminal paper,\footnote{Eugenio Moggi: \href{https://doi.org/10.1016/0890-5401(91)90052-4}{ Notions of Computation and Monads}. Inf. Comput. 93(1): 55-92 (1991).} all of which are traditionally solved by abandoning the purity of functions. \begin{itemize} \tightlist \item Partiality: Computations that may not terminate \item Nondeterminism: Computations that may return many results \item Side effects: Computations that access/modify state \begin{itemize} \tightlist \item Read-only state, or the environment \item Write-only state, or a log \item Read/write state \end{itemize} \item Exceptions: Partial functions that may fail \item Continuations: Ability to save state of the program and then restore it on demand \item Interactive Input \item Interactive Output \end{itemize} What really is mind blowing is that all these problems may be solved using the same clever trick: turning to embellished functions. Of course, the embellishment will be totally different in each case. You have to realize that, at this stage, there is no requirement that the embellishment be monadic. It's only when we insist on composition --- being able to decompose a single embellished function into smaller embellished functions --- that we need a monad. Again, since each of the embellishments is different, monadic composition will be implemented differently, but the overall pattern is the same. It's a very simple pattern: composition that is associative and equipped with identity. The next section is heavy on Haskell examples. Feel free to skim or even skip it if you're eager to get back to category theory or if you're already familiar with Haskell's implementation of monads. \section{The Solution} First, let's analyze the way we used the \code{Writer} monad. We started with a pure function that performed a certain task --- given arguments, it produced a certain output. We replaced this function with another function that embellished the original output by pairing it with a string. That was our solution to the logging problem. We couldn't stop there because, in general, we don't want to deal with monolithic solutions. We needed to be able to decompose one log-producing function into smaller log-producing functions. It's the composition of those smaller functions that led us to the concept of a monad. What's really amazing is that the same pattern of embellishing the function return types works for a large variety of problems that normally would require abandoning purity. Let's go through our list and identify the embellishment that applies to each problem in turn. \subsection{Partiality} We modify the return type of every function that may not terminate by turning it into a ``lifted'' type --- a type that contains all values of the original type plus the special ``bottom'' value $\bot$. For instance, the \code{Bool} type, as a set, would contain two elements: \code{True} and \code{False}. The lifted \code{Bool} contains three elements. Functions that return the lifted \code{Bool} may produce \code{True} or \code{False}, or execute forever. The funny thing is that, in a lazy language like Haskell, a never-ending function may actually return a value, and this value may be passed to the next function. We call this special value the bottom. As long as this value is not explicitly needed (for instance, to be pattern matched, or produced as output), it may be passed around without stalling the execution of the program. Because every Haskell function may be potentially non-terminating, all types in Haskell are assumed to be lifted. This is why we often talk about the category $\Hask$ of Haskell (lifted) types and functions rather than the simpler $\Set$. It is not clear, though, that $\Hask$ is a real category (see this \urlref{http://math.andrej.com/2016/08/06/hask-is-not-a-category/}{Andrej Bauer post}). \subsection{Nondeterminism} If a function can return many different results, it may as well return them all at once. Semantically, a non-deterministic function is equivalent to a function that returns a list of results. This makes a lot of sense in a lazy garbage-collected language. For instance, if all you need is one value, you can just take the head of the list, and the tail will never be evaluated. If you need a random value, use a random number generator to pick the n-th element of the list. Laziness even allows you to return an infinite list of results. In the list monad --- Haskell's implementation of nondeterministic computations --- \code{join} is implemented as \code{concat}. Remember that \code{join} is supposed to flatten a container of containers --- \code{concat} concatenates a list of lists into a single list. \code{return} creates a singleton list: \src{snippet01} The bind operator for the list monad is given by the general formula: \code{fmap} followed by \code{join} which, in this case gives: \src{snippet02} Here, the function \code{k}, which itself produces a list, is applied to every element of the list \code{as}. The result is a list of lists, which is flattened using \code{concat}. From the programmer's point of view, working with a list is easier than, for instance, calling a non-deterministic function in a loop, or implementing a function that returns an iterator (although, \urlref{http://ericniebler.com/2014/04/27/range-comprehensions/}{in modern C++}, returning a lazy range would be almost equivalent to returning a list in Haskell). A good example of using non-determinism creatively is in game programming. For instance, when a computer plays chess against a human, it can't predict the opponent's next move. It can, however, generate a list of all possible moves and analyze them one by one. Similarly, a non-deterministic parser may generate a list of all possible parses for a given expression. Even though we may interpret functions returning lists as non-deterministic, the applications of the list monad are much wider. That's because stitching together computations that produce lists is a perfect functional substitute for iterative constructs --- loops --- that are used in imperative programming. A single loop can be often rewritten using \code{fmap} that applies the body of the loop to each element of the list. The \code{do} notation in the list monad can be used to replace complex nested loops. My favorite example is the program that generates Pythagorean triples --- triples of positive integers that can form sides of right triangles. \src{snippet03} The first line tells us that \code{z} gets an element from an infinite list of positive numbers \code{{[}1..{]}}. Then \code{x} gets an element from the (finite) list \code{{[}1..z{]}} of numbers between 1 and \code{z}. Finally \code{y} gets an element from the list of numbers between \code{x} and \code{z}. We have three numbers $1 \leqslant x \leqslant y \leqslant z$ at our disposal. The function \code{guard} takes a \code{Bool} expression and returns a list of units: \src{snippet04} This function (which is a member of a larger class called \code{MonadPlus}) is used here to filter out non-Pythagorean triples. Indeed, if you look at the implementation of bind (or the related operator \code{>>}), you'll notice that, when given an empty list, it produces an empty list. On the other hand, when given a non-empty list (here, the singleton list containing unit \code{{[}(){]}}), bind will call the continuation, here \code{return (x, y, z)}, which produces a singleton list with a verified Pythagorean triple. All those singleton lists will be concatenated by the enclosing binds to produce the final (infinite) result. Of course, the caller of \code{triples} will never be able to consume the whole list, but that doesn't matter, because Haskell is lazy. The problem that normally would require a set of three nested loops has been dramatically simplified with the help of the list monad and the \code{do} notation. As if that weren't enough, Haskell let's you simplify this code even further using list comprehension: \src{snippet05} This is just further syntactic sugar for the list monad (strictly speaking, \code{MonadPlus}). You might see similar constructs in other functional or imperative languages under the guise of generators and coroutines. \subsection{Read-Only State} A function that has read-only access to some external state, or environment, can be always replaced by a function that takes that environment as an additional argument. A pure function \code{(a, e) -> b} (where \code{e} is the type of the environment) doesn't look, at first sight, like a Kleisli arrow. But as soon as we curry it to \code{a -> (e -> b)} we recognize the embellishment as our old friend the reader functor: \src{snippet06} You may interpret a function returning a \code{Reader} as producing a mini-executable: an action that given an environment produces the desired result. There is a helper function \code{runReader} to execute such an action: \src{snippet07} It may produce different results for different values of the environment. Notice that both the function returning a \code{Reader}, and the \code{Reader} action itself are pure. To implement bind for the \code{Reader} monad, first notice that you have to produce a function that takes the environment \code{e} and produces a \code{b}: \src{snippet08} Inside the lambda, we can execute the action \code{ra} to produce an \code{a}: \src{snippet09} We can then pass the \code{a} to the continuation \code{k} to get a new action \code{rb}: \src{snippet10} Finally, we can run the action \code{rb} with the environment \code{e}: \src{snippet11} To implement \code{return} we create an action that ignores the environment and returns the unchanged value. Putting it all together, after a few simplifications, we get the following definition: \src{snippet12} \subsection{Write-Only State} This is just our initial logging example. The embellishment is given by the \code{Writer} functor: \src{snippet13} For completeness, there's also a trivial helper \code{runWriter} that unpacks the data constructor: \src{snippet14} As we've seen before, in order to make \code{Writer} composable, \code{w} has to be a monoid. Here's the monad instance for \code{Writer} written in terms of the bind operator: \src{snippet15} \subsection{State} Functions that have read/write access to state combine the embellishments of the \code{Reader} and the \code{Writer}. You may think of them as pure functions that take the state as an extra argument and produce a pair value/state as a result: \code{(a, s) -> (b, s)}. After currying, we get them into the form of Kleisli arrows \code{a -> (s -> (b, s))}, with the embellishment abstracted in the \code{State} functor: \src{snippet16} Again, we can look at a Kleisli arrow as returning an action, which can be executed using the helper function: \src{snippet17} Different initial states may not only produce different results, but also different final states. The implementation of bind for the \code{State} monad is very similar to that of the \code{Reader} monad, except that care has to be taken to pass the correct state at each step: \src{snippet18} Here's the full instance: \src{snippet19} There are also two helper Kleisli arrows that may be used to manipulate the state. One of them retrieves the state for inspection: \src{snippet20} and the other replaces it with a completely new state: \src{snippet21} \subsection{Exceptions} An imperative function that throws an exception is really a partial function --- it's a function that's not defined for some values of its arguments. The simplest implementation of exceptions in terms of pure total functions uses the \code{Maybe} functor. A partial function is extended to a total function that returns \code{Just a} whenever it makes sense, and \code{Nothing} when it doesn't. If we want to also return some information about the cause of the failure, we can use the \code{Either} functor instead (with the first type fixed, for instance, to \code{String}). Here's the \code{Monad} instance for \code{Maybe}: \src{snippet22} Notice that monadic composition for \code{Maybe} correctly short-circuits the computation (the continuation \code{k} is never called) when an error is detected. That's the behavior we expect from exceptions. \subsection{Continuations} It's the ``Don't call us, we'll call you!'' situation you may experience after a job interview. Instead of getting a direct answer, you are supposed to provide a handler, a function to be called with the result. This style of programming is especially useful when the result is not known at the time of the call because, for instance, it's being evaluated by another thread or delivered from a remote web site. A Kleisli arrow in this case returns a function that accepts a handler, which represents ``the rest of the computation'': \src{snippet23} The handler \code{a -> r}, when it's eventually called, produces the result of type \code{r}, and this result is returned at the end. A continuation is parameterized by the result type. (In practice, this is often some kind of status indicator.) There is also a helper function for executing the action returned by the Kleisli arrow. It takes the handler and passes it to the continuation: \src{snippet24} The composition of continuations is notoriously difficult, so its handling through a monad and, in particular, the \code{do} notation, is of extreme advantage. Let's figure out the implementation of bind. First let's look at the stripped down signature: \src{snippet25} Our goal is to create a function that takes the handler \code{(b -> r)} and produces the result \code{r}. So that's our starting point: \src{snippet26} Inside the lambda, we want to call the function \code{ka} with the appropriate handler that represents the rest of the computation. We'll implement this handler as a lambda: \src{snippet27} In this case, the rest of the computation involves first calling \code{kab} with \code{a}, and then passing \code{hb} to the resulting action \code{kb}: \src{snippet28} As you can see, continuations are composed inside out. The final handler \code{hb} is called from the innermost layer of the computation. Here's the full instance: \src{snippet29} \subsection{Interactive Input} This is the trickiest problem and a source of a lot of confusion. Clearly, a function like \code{getChar}, if it were to return a character typed at the keyboard, couldn't be pure. But what if it returned the character inside a container? As long as there was no way of extracting the character from this container, we could claim that the function is pure. Every time you call \code{getChar} it would return exactly the same container. Conceptually, this container would contain the superposition of all possible characters. If you're familiar with quantum mechanics, you should have no problem understanding this analogy. It's just like the box with the Schrödinger's cat inside --- except that there is no way to open or peek inside the box. The box is defined using the special built-in \code{IO} functor. In our example, \code{getChar} could be declared as a Kleisli arrow: \src{snippet30} (Actually, since a function from the unit type is equivalent to picking a value of the return type, the declaration of \code{getChar} is simplified to \code{getChar :: IO Char}.) Being a functor, \code{IO} lets you manipulate its contents using \code{fmap}. And, as a functor, it can store the contents of any type, not just a character. The real utility of this approach comes to light when you consider that, in Haskell, \code{IO} is a monad. It means that you are able to compose Kleisli arrows that produce \code{IO} objects. You might think that Kleisli composition would allow you to peek at the contents of the \code{IO} object (thus ``collapsing the wave function,'' if we were to continue the quantum analogy). Indeed, you could compose \code{getChar} with another Kleisli arrow that takes a character and, say, converts it to an integer. The catch is that this second Kleisli arrow could only return this integer as an \code{(IO\ Int)}. Again, you'll end up with a superposition of all possible integers. And so on. The Schrödinger's cat is never out of the bag. Once you are inside the \code{IO} monad, there is no way out of it. There is no equivalent of \code{runState} or \code{runReader} for the \code{IO} monad. There is no \code{runIO}! So what can you do with the result of a Kleisli arrow, the \code{IO} object, other than compose it with another Kleisli arrow? Well, you can return it from \code{main}. In Haskell, \code{main} has the signature: \src{snippet31} and you are free to think of it as a Kleisli arrow: \src{snippet32} From that perspective, a Haskell program is just one big Kleisli arrow in the \code{IO} monad. You can compose it from smaller Kleisli arrows using monadic composition. It's up to the runtime system to do something with the resulting \code{IO} object (also called \code{IO} action). Notice that the arrow itself is a pure function --- it's pure functions all the way down. The dirty work is relegated to the system. When it finally executes the \code{IO} action returned from \code{main}, it does all kinds of nasty things like reading user input, modifying files, printing obnoxious messages, formatting a disk, and so on. The Haskell program never dirties its hands (well, except when it calls \code{unsafePerformIO}, but that's a different story). Of course, because Haskell is lazy, \code{main} returns almost immediately, and the dirty work begins right away. It's during the execution of the \code{IO} action that the results of pure computations are requested and evaluated on demand. So, in reality, the execution of a program is an interleaving of pure (Haskell) and dirty (system) code. There is an alternative interpretation of the \code{IO} monad that is even more bizarre but makes perfect sense as a mathematical model. It treats the whole Universe as an object in a program. Notice that, conceptually, the imperative model treats the Universe as an external global object, so procedures that perform I/O have side effects by virtue of interacting with that object. They can both read and modify the state of the Universe. We already know how to deal with state in functional programming --- we use the state monad. Unlike simple state, however, the state of the Universe cannot be easily described using standard data structures. But we don't have to, as long as we never directly interact with it. It's enough that we assume that there exists a type \code{RealWorld} and, by some miracle of cosmic engineering, the runtime is able to provide an object of this type. An \code{IO} action is just a function: \src{snippet33} Or, in terms of the \code{State} monad: \src{snippet34} However, \code{>=>} and \code{return} for the \code{IO} monad have to be built into the language. \subsection{Interactive Output} The same \code{IO} monad is used to encapsulate interactive output. \code{RealWorld} is supposed to contain all output devices. You might wonder why we can't just call output functions from Haskell and pretend that they do nothing. For instance, why do we have: \src{snippet35} rather than the simpler: \src{snippet36} Two reasons: Haskell is lazy, so it would never call a function whose output --- here, the unit object --- is not used for anything. And, even if it weren't lazy, it could still freely change the order of such calls and thus garble the output. The only way to force sequential execution of two functions in Haskell is through data dependency. The input of one function must depend on the output of another. Having \code{RealWorld} passed between \code{IO} actions enforces sequencing. Conceptually, in this program: \src{snippet37} the action that prints ``World!'' receives, as input, the Universe in which ``Hello '' is already on the screen. It outputs a new Universe, with ``Hello World!'' on the screen. \section{Conclusion} Of course I have just scratched the surface of monadic programming. Monads not only accomplish, with pure functions, what normally is done with side effects in imperative programming, but they also do it with a high degree of control and type safety. They are not without drawbacks, though. The major complaint about monads is that they don't easily compose with each other. Granted, you can combine most of the basic monads using the monad transformer library. It's relatively easy to create a monad stack that combines, say, state with exceptions, but there is no formula for stacking arbitrary monads together. ================================================ FILE: src/content/3.6/code/haskell/snippet01.hs ================================================ f >=> g = join . fmap g . f ================================================ FILE: src/content/3.6/code/haskell/snippet02.hs ================================================ (f >=> g) a = join (fmap g (f a)) ================================================ FILE: src/content/3.6/code/haskell/snippet03.hs ================================================ class Monoid m where mappend :: m -> m -> m mempty :: m ================================================ FILE: src/content/3.6/code/haskell/snippet04.hs ================================================ mappend :: m -> (m -> m) ================================================ FILE: src/content/3.6/code/haskell/snippet05.hs ================================================ mu :: (m, m) -> m ================================================ FILE: src/content/3.6/code/haskell/snippet06.hs ================================================ eta :: () -> m ================================================ FILE: src/content/3.6/code/haskell/snippet07.hs ================================================ mu (x, mu (y, z)) = mu (mu (x, y), z) ================================================ FILE: src/content/3.6/code/haskell/snippet08.hs ================================================ (mu . bimap id mu)(x, (y, z)) ================================================ FILE: src/content/3.6/code/haskell/snippet09.hs ================================================ (mu . bimap mu id)((x, y), z) ================================================ FILE: src/content/3.6/code/haskell/snippet10.hs ================================================ mu . bimap id mu = mu . bimap mu id ================================================ FILE: src/content/3.6/code/haskell/snippet11.hs ================================================ alpha :: ((a, b), c) -> (a, (b, c)) alpha ((x, y), z) = (x, (y, z)) ================================================ FILE: src/content/3.6/code/haskell/snippet12.hs ================================================ mu . bimap id mu . alpha = mu . bimap mu id ================================================ FILE: src/content/3.6/code/haskell/snippet13.hs ================================================ mu (eta (), x) = x mu (x, eta ()) = x ================================================ FILE: src/content/3.6/code/haskell/snippet14.hs ================================================ (mu . bimap eta id) ((), x) = lambda((), x) (mu . bimap id eta) (x, ()) = rho (x, ()) ================================================ FILE: src/content/3.6/code/haskell/snippet15.hs ================================================ lambda :: ((), a) -> a lambda ((), x) = x ================================================ FILE: src/content/3.6/code/haskell/snippet16.hs ================================================ rho :: (a, ()) -> a rho (x, ()) = x ================================================ FILE: src/content/3.6/code/haskell/snippet17.hs ================================================ mu . bimap id eta = rho mu . bimap eta id = lambda ================================================ FILE: src/content/3.6/code/haskell/snippet18.hs ================================================ newtype State s a = State (s -> (a, s)) ================================================ FILE: src/content/3.6/code/haskell/snippet19.hs ================================================ newtype Prod s a = Prod (a, s) ================================================ FILE: src/content/3.6/code/haskell/snippet20.hs ================================================ newtype Reader s a = Reader (s -> a) ================================================ FILE: src/content/3.6/code/haskell/snippet21.hs ================================================ instance Adjunction (Prod s) (Reader s) where counit (Prod (Reader f, s)) = f s unit a = Reader (\s -> Prod (a, s)) ================================================ FILE: src/content/3.6/code/haskell/snippet22.hs ================================================ newtype State s a = State (s -> (a, s)) ================================================ FILE: src/content/3.6/code/haskell/snippet23.hs ================================================ runState :: State s a -> s -> (a, s) runState (State f) s = f s ================================================ FILE: src/content/3.6/code/haskell/snippet24.hs ================================================ ssa :: State s (State s a) runState ssa :: s -> (State s a, s) ================================================ FILE: src/content/3.6/code/haskell/snippet25.hs ================================================ join :: State s (State s a) -> State s a join ssa = State (uncurry runState . runState ssa) ================================================ FILE: src/content/3.6/code/ocaml/snippet01.ml ================================================ module Kleisli (M : MonadJoin) = struct (* compose *) let ( <.> ) f g x = f (g x) let ( >=> ) f g = M.join <.> M.fmap g <.> f end ================================================ FILE: src/content/3.6/code/ocaml/snippet02.ml ================================================ module Kleisli (M : MonadJoin) = struct let ( >=> ) f g a = M.join (M.fmap g (f a)) end ================================================ FILE: src/content/3.6/code/ocaml/snippet03.ml ================================================ module type Monoid = sig type m val mappend : m -> m -> m val mempty : m end ================================================ FILE: src/content/3.6/code/ocaml/snippet04.ml ================================================ val mappend : m -> m -> m ================================================ FILE: src/content/3.6/code/ocaml/snippet05.ml ================================================ val mu : (m, m) -> m ================================================ FILE: src/content/3.6/code/ocaml/snippet06.ml ================================================ val eta : unit -> m ================================================ FILE: src/content/3.6/code/ocaml/snippet07.ml ================================================ ;; mu (x, mu (y, z)) = mu (mu (x, y), z) ================================================ FILE: src/content/3.6/code/ocaml/snippet08.ml ================================================ ;; (compose mu (bimap id mu)) (x, (y, z)) ================================================ FILE: src/content/3.6/code/ocaml/snippet09.ml ================================================ ;; (compose mu (bimap mu id)) ((x, y), z) ================================================ FILE: src/content/3.6/code/ocaml/snippet10.ml ================================================ ;; compose mu (bimap id mu) = compose mu (bimap mu id) ================================================ FILE: src/content/3.6/code/ocaml/snippet11.ml ================================================ let alpha ((x, y), z) = x, (y, z) ================================================ FILE: src/content/3.6/code/ocaml/snippet12.ml ================================================ ;; compose mu (compose (bimap id mu) alpha) = compose mu (bimap mu id) ================================================ FILE: src/content/3.6/code/ocaml/snippet13.ml ================================================ ;; mu (eta (), x) = x mu (x, eta ()) = x ================================================ FILE: src/content/3.6/code/ocaml/snippet14.ml ================================================ ;; (compose mu (bimap eta id)) ((), x) = lambda ((), x) (compose mu (bimap id eta)) (x, ()) = rho (x, ()) ================================================ FILE: src/content/3.6/code/ocaml/snippet15.ml ================================================ let lambda ((), x) = x ================================================ FILE: src/content/3.6/code/ocaml/snippet16.ml ================================================ let rho (x, ()) = x ================================================ FILE: src/content/3.6/code/ocaml/snippet17.ml ================================================ ;; mu.bimap id eta = rho mu.bimap eta id = lambda ================================================ FILE: src/content/3.6/code/ocaml/snippet18.ml ================================================ type ('s, 'a) state = State of ('s -> 'a * 's) ================================================ FILE: src/content/3.6/code/ocaml/snippet19.ml ================================================ type ('s, 'a) prod = Prod of 'a * 's ================================================ FILE: src/content/3.6/code/ocaml/snippet20.ml ================================================ type ('s, 'a) reader = Reader of ('s -> 'a) ================================================ FILE: src/content/3.6/code/ocaml/snippet21.ml ================================================ module AdjunctionState (S : sig type s end) (F : Functor with type 'a t = (S.s, 'a) prod) (R : Representable with type 'a t = (S.s, 'a) reader) : Adjunction = struct type 'a f = (S.s, 'a) prod type 'a r = (S.s, 'a) reader include F include R let unit a = Reader (fun s -> Prod (a, s)) let counit (Prod (Reader f, s)) = f s end ================================================ FILE: src/content/3.6/code/ocaml/snippet22.ml ================================================ type ('s, 'a) state = State of ('s -> 'a * 's) ================================================ FILE: src/content/3.6/code/ocaml/snippet23.ml ================================================ let run_state (State f) s = f s ================================================ FILE: src/content/3.6/code/ocaml/snippet24.ml ================================================ val ssa : ('s, ('s, 'a) state) state val run_state ssa : 's -> (('s, 'a) state, 's) ================================================ FILE: src/content/3.6/code/ocaml/snippet25.ml ================================================ let join : ('s, ('s, 'a) state) state -> ('s, 'a) state = fun ssa -> State (uncurry run_state <.> run_state ssa) ;; ================================================ FILE: src/content/3.6/code/reason/snippet01.re ================================================ module Kleisli = (M: MonadJoin) => { /* compose */ let (<.>) = (f, g, x) => f(g(x)); let (>=>) = (f, g) => M.join <.> M.fmap(g) <.> f; }; ================================================ FILE: src/content/3.6/code/reason/snippet02.re ================================================ module Kleisli = (M: MonadJoin) => { let (>=>) = (f, g, a) => M.join(M.fmap(g, f(a))); }; ================================================ FILE: src/content/3.6/code/reason/snippet03.re ================================================ module type Monoid = { type m; let mappend: (m, m) => m; let mempty: m; }; ================================================ FILE: src/content/3.6/code/reason/snippet04.re ================================================ let mappend: (m, m) => m; ================================================ FILE: src/content/3.6/code/reason/snippet05.re ================================================ let mu: (m, m) => m; ================================================ FILE: src/content/3.6/code/reason/snippet06.re ================================================ let eta: unit => m = ; ================================================ FILE: src/content/3.6/code/reason/snippet07.re ================================================ mu((x, mu((y, z)))) == mu((mu((x, y)), z)); ================================================ FILE: src/content/3.6/code/reason/snippet08.re ================================================ (compose(mu, bimap(id, mu)))((x, (y, z))); ================================================ FILE: src/content/3.6/code/reason/snippet09.re ================================================ (compose(mu, bimap(mu, id)))(((x, y), z)); ================================================ FILE: src/content/3.6/code/reason/snippet10.re ================================================ compose(mu, bimap(id, mu)) == compose(mu, bimap(mu, id)); ================================================ FILE: src/content/3.6/code/reason/snippet11.re ================================================ let alpha = (((x, y), z)) => (x, (y, z)); ================================================ FILE: src/content/3.6/code/reason/snippet12.re ================================================ compose(mu, compose(bimap(id, mu), alpha)) == compose(mu, bimap(mu, id)); ================================================ FILE: src/content/3.6/code/reason/snippet13.re ================================================ mu((eta(), x)) == x(mu, (x, eta())) == x; ================================================ FILE: src/content/3.6/code/reason/snippet14.re ================================================ (compose(mu, bimap(eta, id)))(((), x)) == lambda(((), x), compose(mu, bimap(id, eta)), (x, ())) == rho((x, ())); ================================================ FILE: src/content/3.6/code/reason/snippet15.re ================================================ let lambda = (((), x)) => x; ================================================ FILE: src/content/3.6/code/reason/snippet16.re ================================================ let rho = ((x, ())) => x; ================================================ FILE: src/content/3.6/code/reason/snippet17.re ================================================ mu.bimap(id, eta) == rho(mu.bimap, eta, id) == lambda; ================================================ FILE: src/content/3.6/code/reason/snippet18.re ================================================ type state('s, 'a) = | State('s => ('a, 's)); ================================================ FILE: src/content/3.6/code/reason/snippet19.re ================================================ type prod('s, 'a) = | Prod('a, 's); ================================================ FILE: src/content/3.6/code/reason/snippet20.re ================================================ type reader('s, 'a) = | Reader('s => 'a); ================================================ FILE: src/content/3.6/code/reason/snippet21.re ================================================ module AdjunctionState = ( S: {type s;}, F: Functor with type t('a) = prod(S.s, 'a), R: Representable with type t('a) = reader(S.s, 'a), ): Adjunction => { type f('a) = prod(S.s, 'a); type r('a) = reader(S.s, 'a); include F; include R; let unit = a => Reader(s => Prod(a, s)); let counit = (Prod(Reader(f), s)) => f(s); }; ================================================ FILE: src/content/3.6/code/reason/snippet22.re ================================================ type state('s, 'a) = | State('s => ('a, 's)); ================================================ FILE: src/content/3.6/code/reason/snippet23.re ================================================ let run_state = (State(f), s) => f(s); ================================================ FILE: src/content/3.6/code/reason/snippet24.re ================================================ let ssa: state('s, state('s, 'a)); let run_state ssa: 's => (state('s, 'a), 's); ================================================ FILE: src/content/3.6/code/reason/snippet25.re ================================================ let join: state('s, state('s, 'a)) => state('s, 'a) = ( ssa => State(uncurry(run_state) <.> run_state(ssa)): state('s, state('s, 'a)) => state('s, 'a) ); ================================================ FILE: src/content/3.6/code/scala/snippet01.scala ================================================ (f >=> g) == (flatten compose fmap[B, T[C]](g) compose f) ================================================ FILE: src/content/3.6/code/scala/snippet02.scala ================================================ (f >=> g)(a) == flatten(fmap(g)(f(a))) ================================================ FILE: src/content/3.6/code/scala/snippet03.scala ================================================ trait Monoid[M] { def combine(x: M, y: M): M def empty: M } ================================================ FILE: src/content/3.6/code/scala/snippet04.scala ================================================ def combine(x: M): (M => M) ================================================ FILE: src/content/3.6/code/scala/snippet05.scala ================================================ def mu: ((M, M)) => M ================================================ FILE: src/content/3.6/code/scala/snippet06.scala ================================================ def eta: Unit => M ================================================ FILE: src/content/3.6/code/scala/snippet07.scala ================================================ mu (x, mu (y, z)) == mu (mu (x, y), z) ================================================ FILE: src/content/3.6/code/scala/snippet08.scala ================================================ mu compose bimap(identity)(mu) ================================================ FILE: src/content/3.6/code/scala/snippet09.scala ================================================ mu compose bimap(mu)(identity) ================================================ FILE: src/content/3.6/code/scala/snippet10.scala ================================================ mu.compose(bimap(identity)(mu)) == mu.compose(bimap(mu)(identity)) ================================================ FILE: src/content/3.6/code/scala/snippet11.scala ================================================ def alpha[A, B, C]: (((A, B), C)) => ((A, (B, C))) = { case ((x, y), z) => (x, (y, z)) } ================================================ FILE: src/content/3.6/code/scala/snippet12.scala ================================================ mu.compose( bimap(identity)(mu) compose alpha) == mu.compose(bimap(mu)(identity)) ================================================ FILE: src/content/3.6/code/scala/snippet13.scala ================================================ mu(eta(), x) == x mu(x, eta()) == x ================================================ FILE: src/content/3.6/code/scala/snippet14.scala ================================================ mu.compose(bimap(eta)(identity[M]))(((), x)) == lambda(((), x)) mu.compose(bimap(identity[M])(eta))((x, ())) == rho((x, ())) ================================================ FILE: src/content/3.6/code/scala/snippet15.scala ================================================ def lambda[A]: ((Unit, A)) => A = { case ((), x) => x } ================================================ FILE: src/content/3.6/code/scala/snippet16.scala ================================================ def rho[A]: ((A, Unit)) => A = { case (x, ()) => x } ================================================ FILE: src/content/3.6/code/scala/snippet17.scala ================================================ mu.compose(bimap(eta)(identity[M])) == lambda mu.compose(bimap(identity[M])(eta)) == rho ================================================ FILE: src/content/3.6/code/scala/snippet18.scala ================================================ case class State[S, A](run: S => (A, S)) ================================================ FILE: src/content/3.6/code/scala/snippet19.scala ================================================ case class Prod[S, A](run: (A, S)) ================================================ FILE: src/content/3.6/code/scala/snippet20.scala ================================================ case class Reader[S, A](run: S => A) ================================================ FILE: src/content/3.6/code/scala/snippet21.scala ================================================ implicit def state[S] = new Adjunction[Prod[S, ?], Reader[S, ?]] { def counit[A](a: Prod[S, Reader[S, A]]): A = a match { case Prod((Reader(f), s)) => f(s) } def unit[A](a: A): Reader[S, Prod[S, A]] = Reader(s => Prod((a, s))) } ================================================ FILE: src/content/3.6/code/scala/snippet22.scala ================================================ case class State[S, A](run: S => (A, S)) ================================================ FILE: src/content/3.6/code/scala/snippet23.scala ================================================ def runState[S, A]: State[S, A] => S => (A, S) = { case State(f) => s => f(s) } ================================================ FILE: src/content/3.6/code/scala/snippet24.scala ================================================ def ssa[S, A]: State[S, State[S, A]] def rss[S, A]: S => (State[S, A], S) = runState[S, State[S, A]](ssa) ================================================ FILE: src/content/3.6/code/scala/snippet25.scala ================================================ def join[S, A]: State[S, State[S, A]] => State[S, A] = ssa => { State( Function.uncurried(runState[S, A]) .tupled .compose(runState(ssa)) ) } ================================================ FILE: src/content/3.6/monads-categorically.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{I}{f you mention monads} to a programmer, you'll probably end up talking about effects. To a mathematician, monads are about algebras. We'll talk about algebras later --- they play an important role in programming --- but first I'd like to give you a little intuition about their relation to monads. For now, it's a bit of a hand-waving argument, but bear with me. Algebra is about creating, manipulating, and evaluating expressions. Expressions are built using operators. Consider this simple expression: \[x^2 + 2 x + 1\] This expression is formed using variables like $x$, and constants like $1$ or $2$, bound together with operators like plus or times. As programmers, we often think of expressions as trees. \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/exptree.png} \end{figure} \noindent Trees are containers so, more generally, an expression is a container for storing variables. In category theory, we represent containers as endofunctors. If we assign the type $a$ to the variable $x$, our expression will have the type $m\ a$, where $m$ is an endofunctor that builds expression trees. (Nontrivial branching expressions are usually created using recursively defined endofunctors.) What's the most common operation that can be performed on an expression? It's substitution: replacing variables with expressions. For instance, in our example, we could replace $X$ with $y - 1$ to get: \[(y - 1)^2 + 2 (y - 1) + 1\] Here's what happened: We took an expression of type $m\ a$ and applied a transformation of type $a \to m\ b$ ($b$ represents the type of $y$). The result is an expression of type $m\ b$. Let me spell it out: \[m\ a \to (a \to m\ b) \to m\ b\] Yes, that's the signature of monadic bind. That was a bit of motivation. Now let's get to the math of the monad. Mathematicians use different notation than programmers. They prefer to use the letter $T$ for the endofunctor, and Greek letters: $\mu$ for \code{join} and $\eta$ for \code{return}. Both \code{join} and \code{return} are polymorphic functions, so we can guess that they correspond to natural transformations. Therefore, in category theory, a monad is defined as an endofunctor $T$ equipped with a pair of natural transformations $\mu$ and $\eta$. $\mu$ is a natural transformation from the square of the functor $T^2$ back to $T$. The square is simply the functor composed with itself, $T \circ T$ (we can only do this kind of squaring for endofunctors). \[\mu \Colon T^2 \to T\] The component of this natural transformation at an object $a$ is the morphism: \[\mu_a \Colon T (T a) \to T a\] which, in $\Hask$, translates directly to our definition of \code{join}. $\eta$ is a natural transformation between the identity functor $I$ and $T$: \[\eta \Colon I \to T\] Considering that the action of $I$ on the object $a$ is just $a$, the component of $\eta$ is given by the morphism: \[\eta_a \Colon a \to T a\] which translates directly to our definition of \code{return}. These natural transformations must satisfy some additional laws. One way of looking at it is that these laws let us define a Kleisli category for the endofunctor $T$. Remember that a Kleisli arrow between $a$ and $b$ is defined as a morphism $a \to T b$. The composition of two such arrows (I'll write it as a circle with the subscript $T$) can be implemented using $\mu$: \[g \circ_T f = \mu_c \circ (T g) \circ f\] where \begin{gather*} f \Colon a \to T b \\ g \Colon b \to T c \end{gather*} Here $T$, being a functor, can be applied to the morphism $g$. It might be easier to recognize this formula in Haskell notation: \src{snippet01} or, in components: \src{snippet02} In terms of the algebraic interpretation, we are just composing two successive substitutions. For Kleisli arrows to form a category we want their composition to be associative, and $\eta_a$ to be the identity Kleisli arrow at $a$. This requirement can be translated to monadic laws for $\mu$ and $\eta$. But there is another way of deriving these laws that makes them look more like monoid laws. In fact $\mu$ is often called \emph{multiplication}, and $\eta$ -- \emph{unit}. Roughly speaking, the associativity law states that the two ways of reducing the cube of $T$, $T^3$, down to $T$ must give the same result. Two unit laws (left and right) state that when $\eta$ is applied to $T$ and then reduced by $\mu$, we get back $T$. Things are a little tricky because we are composing natural transformations and functors. So a little refresher on horizontal composition is in order. For instance, $T^3$ can be seen as a composition of $T$ after $T^2$. We can apply to it the horizontal composition of two natural transformations: \[I_T \circ \mu\] \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/assoc1.png} \end{figure} \noindent and get $T \circ T$; which can be further reduced to $T$ by applying $\mu$. $I_T$ is the identity natural transformation from $T$ to $T$. You will often see the notation for this type of horizontal composition $I_T \circ \mu$ shortened to $T \circ \mu$. This notation is unambiguous because it makes no sense to compose a functor with a natural transformation, therefore $T$ must mean $I_T$ in this context. \noindent We can also draw the diagram in the (endo-) functor category ${[}\cat{C}, \cat{C}{]}$: \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/assoc2.png} \end{figure} \noindent Alternatively, we can treat $T^3$ as the composition of $T^2 \circ T$ and apply $\mu \circ T$ to it. The result is also $T \circ T$ which, again, can be reduced to $T$ using $\mu$. We require that the two paths produce the same result. \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/assoc.png} \end{figure} \noindent Similarly, we can apply the horizontal composition $\eta \circ T$ to the composition of the identity functor $I$ after $T$ to obtain $T^2$, which can then be reduced using $\mu$. The result should be the same as if we applied the identity natural transformation directly to \code{T}. And, by analogy, the same should be true for $T \circ \eta$. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/unitlawcomp-1.png} \end{figure} \noindent You can convince yourself that these laws guarantee that the composition of Kleisli arrows indeed satisfies the laws of a category. The similarities between a monad and a monoid are striking. We have multiplication $\mu$, unit $\eta$, associativity, and unit laws. But our definition of a monoid is too narrow to describe a monad as a monoid. So let's generalize the notion of a monoid. \section{Monoidal Categories} Let's go back to the conventional definition of a monoid. It's a set with a binary operation and a special element called unit. In Haskell, this can be expressed as a typeclass: \src{snippet03} The binary operation \code{mappend} must be associative and unital (i.e., multiplication by the unit \code{mempty} is a no-op). Notice that, in Haskell, the definition of \code{mappend} is curried. It can be interpreted as mapping every element of \code{m} to a function: \src{snippet04} It's this interpretation that gives rise to the definition of a monoid as a single-object category where endomorphisms \code{(m -> m)} represent the elements of the monoid. But because currying is built into Haskell, we could as well have started with a different definition of multiplication: \src{snippet05} Here, the Cartesian product \code{(m, m)} becomes the source of pairs to be multiplied. This definition suggests a different path to generalization: replacing the Cartesian product with categorical product. We could start with a category where products are globally defined, pick an object \code{m} there, and define multiplication as a morphism: \[\mu \Colon m\times{}m \to m\] We have one problem though: In an arbitrary category we can't peek inside an object, so how do we pick the unit element? There is a trick to it. Remember how element selection is equivalent to a function from the singleton set? In Haskell, we could replace the definition of \code{mempty} with a function: \src{snippet06} The singleton is the terminal object in $\Set$, so it's natural to generalize this definition to any category that has a terminal object $t$: \[\eta \Colon t \to m\] This lets us pick the unit ``element'' without having to talk about elements. Unlike in our previous definition of a monoid as a single-object category, monoidal laws here are not automatically satisfied --- we have to impose them. But in order to formulate them we have to establish the monoidal structure of the underlying categorical product itself. Let's recall how monoidal structure works in Haskell first. We start with associativity. In Haskell, the corresponding equational law is: \src{snippet07} Before we can generalize it to other categories, we have to rewrite it as an equality of functions (morphisms). We have to abstract it away from its action on individual variables --- in other words, we have to use point-free notation. Knowing that the Cartesian product is a bifunctor, we can write the left hand side as: \src{snippet08} and the right hand side as: \src{snippet09} This is almost what we want. Unfortunately, the Cartesian product is not strictly associative --- \code{(x, (y, z))} is not the same as \code{((x, y), z)} --- so we can't just write point-free: \src{snippet10} On the other hand, the two nestings of pairs are isomorphic. There is an invertible function called the associator that converts between them: \src{snippet11} With the help of the associator, we can write the point-free associativity law for \code{mu}: \src{snippet12} We can apply a similar trick to unit laws which, in the new notation, take the form: \src{snippet13} They can be rewritten as: \src{snippet14} The isomorphisms \code{lambda} and \code{rho} are called the left and right unitor, respectively. They witness the fact that the unit \code{()} is the identity of the Cartesian product up to isomorphism: \src{snippet15} \src{snippet16} The point-free versions of the unit laws are therefore: \src{snippet17} We have formulated point-free monoidal laws for \code{mu} and \code{eta} using the fact that the underlying Cartesian product itself acts like a monoidal multiplication in the category of types. Keep in mind though that the associativity and unit laws for the Cartesian product are valid only up to isomorphism. It turns out that these laws can be generalized to any category with products and a terminal object. Categorical products are indeed associative up to isomorphism and the terminal object is the unit, also up to isomorphism. The associator and the two unitors are natural isomorphisms. The laws can be represented by commuting diagrams. \begin{figure}[H] \centering \includegraphics[width=0.5\textwidth]{images/assocmon.png} \end{figure} \noindent Notice that, because the product is a bifunctor, it can lift a pair of morphisms --- in Haskell this was done using \code{bimap}. We could stop here and say that we can define a monoid on top of any category with categorical products and a terminal object. As long as we can pick an object $m$ and two morphisms $\mu$ and $\eta$ that satisfy monoidal laws, we have a monoid. But we can do better than that. We don't need a full-blown categorical product to formulate the laws for $\mu$ and $\eta$. Recall that a product is defined through a universal construction that uses projections. We haven't used any projections in our formulation of monoidal laws. A bifunctor that behaves like a product without being a product is called a \newterm{tensor product}, often denoted by the infix operator $\otimes$. A definition of a tensor product in general is a bit tricky, but we won't worry about it. We'll just list its properties --- the most important being associativity up to isomorphism. Similarly, we don't need the object $t$ to be terminal. We never used its terminal property --- namely, the existence of a unique morphism from any object to it. What we require is that it works well in concert with the tensor product. Which means that we want it to be the unit of the tensor product, again, up to isomorphism. Let's put it all together: A monoidal category is a category $\cat{C}$ equipped with a bifunctor called the tensor product: \[\otimes \Colon \cat{C}\times{}\cat{C} \to \cat{C}\] and a distinct object $i$ called the unit object, together with three natural isomorphisms called, respectively, the associator and the left and right unitors: \begin{align*} \alpha_{a b c} & \Colon (a \otimes b) \otimes c \to a \otimes (b \otimes c) \\ \lambda_a & \Colon i \otimes a \to a \\ \rho_a & \Colon a \otimes i \to a \end{align*} (There is also a coherence condition for simplifying a quadruple tensor product.) What's important is that a tensor product describes many familiar bifunctors. In particular, it works for a product, a coproduct and, as we'll see shortly, for the composition of endofunctors (and also for some more esoteric products like Day convolution). Monoidal categories will play an essential role in the formulation of enriched categories. \section{Monoid in a Monoidal Category} We are now ready to define a monoid in a more general setting of a monoidal category. We start by picking an object $m$. Using the tensor product we can form powers of $m$. The square of $m$ is $m \otimes m$. There are two ways of forming the cube of $m$, but they are isomorphic through the associator. Similarly for higher powers of $m$ (that's where we need the coherence conditions). To form a monoid we need to pick two morphisms: \begin{align*} \mu & \Colon m \otimes m \to m \\ \eta & \Colon i \to m \end{align*} where $i$ is the unit object for our tensor product. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/monoid-1.jpg} \end{figure} \noindent These morphisms have to satisfy associativity and unit laws, which can be expressed in terms of the following commuting diagrams: \begin{figure}[H] \centering \includegraphics[width=0.5\textwidth]{images/assoctensor.jpg} \end{figure} \begin{figure}[H] \centering \includegraphics[width=0.5\textwidth]{images/unitmon.jpg} \end{figure} \noindent Notice that it's essential that the tensor product be a bifunctor because we need to lift pairs of morphisms to form products such as $\mu \otimes \id$ or $\eta \otimes \id$. These diagrams are just a straightforward generalization of our previous results for categorical products. \section{Monads as Monoids} Monoidal structures pop up in unexpected places. One such place is the functor category. If you squint a little, you might be able to see functor composition as a form of multiplication. The problem is that not any two functors can be composed --- the target category of one has to be the source category of the other. That's just the usual rule of composition of morphisms --- and, as we know, functors are indeed morphisms in the category $\Cat$. But just like endomorphisms (morphisms that loop back to the same object) are always composable, so are endofunctors. For any given category $\cat{C}$, endofunctors from $\cat{C}$ to $\cat{C}$ form the functor category ${[}\cat{C}, \cat{C}{]}$. Its objects are endofunctors, and morphisms are natural transformations between them. We can take any two objects from this category, say endofunctors $F$ and $G$, and produce a third object $F \circ G$ --- an endofunctor that's their composition. Is endofunctor composition a good candidate for a tensor product? First, we have to establish that it's a bifunctor. Can it be used to lift a pair of morphisms --- here, natural transformations? The signature of the analog of \code{bimap} for the tensor product would look something like this: \[\mathit{bimap} \Colon (a \to b) \to (c \to d) \to (a \otimes c \to b \otimes d)\] If you replace objects by endofunctors, arrows by natural transformations, and tensor products by composition, you get: \[(F \to F') \to (G \to G') \to (F \circ G \to F' \circ G')\] which you may recognize as the special case of horizontal composition. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/horizcomp.png} \end{figure} \noindent We also have at our disposal the identity endofunctor $I$, which can serve as the identity for endofunctor composition --- our new tensor product. Moreover, functor composition is associative. In fact associativity and unit laws are strict --- there's no need for the associator or the two unitors. So endofunctors form a strict monoidal category with functor composition as tensor product. What's a monoid in this category? It's an object --- that is an endofunctor $T$; and two morphisms --- that is natural transformations: \begin{gather*} \mu \Colon T \circ T \to T \\ \eta \Colon I \to T \end{gather*} Not only that, here are the monoid laws: \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/assoc.png} \end{figure} \begin{figure}[H] \centering \includegraphics[width=0.5\textwidth]{images/unitlawcomp.png} \end{figure} \noindent They are exactly the monad laws we've seen before. Now you understand the famous quote from Saunders Mac Lane: \begin{quote} All told, monad is just a monoid in the category of endofunctors. \end{quote} You might have seen it emblazoned on some t-shirts at functional programming conferences. \section{Monads from Adjunctions} An \hyperref[adjunctions]{adjunction}\footnote{See ch.18 on \hyperref[adjunctions]{adjunctions}.} $L \dashv R$, is a pair of functors going back and forth between two categories $\cat{C}$ and $\cat{D}$. There are two ways of composing them giving rise to two endofunctors, $R \circ L$ and $L \circ R$. As per an adjunction, these endofunctors are related to identity functors through two natural transformations called unit and counit: \begin{gather*} \eta \Colon I_{\cat{D}} \to R \circ L \\ \varepsilon \Colon L \circ R \to I_{\cat{C}} \end{gather*} Immediately we see that the unit of an adjunction looks just like the unit of a monad. It turns out that the endofunctor $R \circ L$ is indeed a monad. All we need is to define the appropriate $\mu$ to go with the $\eta$. That's a natural transformation between the square of our endofunctor and the endofunctor itself or, in terms of the adjoint functors: \[R \circ L \circ R \circ L \to R \circ L\] And, indeed, we can use the counit to collapse the $L \circ R$ in the middle. The exact formula for $\mu$ is given by the horizontal composition: \[\mu = R \circ \varepsilon \circ L\] Monadic laws follow from the identities satisfied by the unit and counit of the adjunction and the interchange law. We don't see a lot of monads derived from adjunctions in Haskell, because an adjunction usually involves two categories. However, the definitions of an exponential, or a function object, is an exception. Here are the two endofunctors that form this adjunction: \begin{gather*} L z = z\times{}s \\ R b = s \Rightarrow b \end{gather*} You may recognize their composition as the familiar state monad: \[R (L z) = s \Rightarrow (z\times{}s)\] We've seen this monad before in Haskell: \src{snippet18} Let's also translate the adjunction to Haskell. The left functor is the product functor: \src{snippet19} and the right functor is the reader functor: \src{snippet20} They form the adjunction: \src{snippet21} You can easily convince yourself that the composition of the reader functor after the product functor is indeed equivalent to the state functor: \src{snippet22} As expected, the \code{unit} of the adjunction is equivalent to the \code{return} function of the state monad. The \code{counit} acts by evaluating a function acting on its argument. This is recognizable as the uncurried version of the function \code{runState}: \src{snippet23} (uncurried, because in \code{counit} it acts on a pair). We can now define \code{join} for the state monad as a component of the natural transformation $\mu$. For that we need a horizontal composition of three natural transformations: \[\mu = R \circ \varepsilon \circ L\] In other words, we need to sneak the counit $\varepsilon$ across one level of the reader functor. We can't just call \code{fmap} directly, because the compiler would pick the one for the \code{State} functor, rather than the \code{Reader} functor. But recall that \code{fmap} for the reader functor is just left function composition. So we'll use function composition directly. We have to first peel off the data constructor \code{State} to expose the function inside the \code{State} functor. This is done using \code{runState}: \src{snippet24} Then we left-compose it with the counit, which is defined by \code{uncurry runState}. Finally, we clothe it back in the \code{State} data constructor: \src{snippet25} This is indeed the implementation of \code{join} for the \code{State} monad. It turns out that not only every adjunction gives rise to a monad, but the converse is also true: every monad can be factorized into a composition of two adjoint functors. Such a factorization is not unique though. We'll talk about the other endofunctor $L \circ R$ in the next section. ================================================ FILE: src/content/3.7/code/haskell/snippet01.hs ================================================ a -> m b ================================================ FILE: src/content/3.7/code/haskell/snippet02.hs ================================================ w a -> b ================================================ FILE: src/content/3.7/code/haskell/snippet03.hs ================================================ (=>=) :: (w a -> b) -> (w b -> c) -> (w a -> c) ================================================ FILE: src/content/3.7/code/haskell/snippet04.hs ================================================ extract :: w a -> a ================================================ FILE: src/content/3.7/code/haskell/snippet05.hs ================================================ class Functor w => Comonad w where (=>=) :: (w a -> b) -> (w b -> c) -> (w a -> c) extract :: w a -> a ================================================ FILE: src/content/3.7/code/haskell/snippet06.hs ================================================ (a, e) -> b ================================================ FILE: src/content/3.7/code/haskell/snippet07.hs ================================================ a -> (e -> b) ================================================ FILE: src/content/3.7/code/haskell/snippet08.hs ================================================ data Product e a = Prod e a deriving Functor ================================================ FILE: src/content/3.7/code/haskell/snippet09.hs ================================================ (=>=) :: (Product e a -> b) -> (Product e b -> c) -> (Product e a -> c) f =>= g = \(Prod e a) -> let b = f (Prod e a) c = g (Prod e b) in c ================================================ FILE: src/content/3.7/code/haskell/snippet10.hs ================================================ extract (Prod e a) = a ================================================ FILE: src/content/3.7/code/haskell/snippet11.hs ================================================ (=>=) :: (w a -> b) -> (w b -> c) -> (w a -> c) f =>= g = g ... ================================================ FILE: src/content/3.7/code/haskell/snippet12.hs ================================================ extend :: (w a -> b) -> w a -> w b ================================================ FILE: src/content/3.7/code/haskell/snippet13.hs ================================================ f =>= g = g . extend f ================================================ FILE: src/content/3.7/code/haskell/snippet14.hs ================================================ duplicate :: w a -> w (w a) ================================================ FILE: src/content/3.7/code/haskell/snippet15.hs ================================================ class Functor w => Comonad w where extract :: w a -> a duplicate :: w a -> w (w a) duplicate = extend id extend :: (w a -> b) -> w a -> w b extend f = fmap f . duplicate ================================================ FILE: src/content/3.7/code/haskell/snippet16.hs ================================================ data Stream a = Cons a (Stream a) ================================================ FILE: src/content/3.7/code/haskell/snippet17.hs ================================================ instance Functor Stream where fmap f (Cons a as) = Cons (f a) (fmap f as) ================================================ FILE: src/content/3.7/code/haskell/snippet18.hs ================================================ extract (Cons a _) = a ================================================ FILE: src/content/3.7/code/haskell/snippet19.hs ================================================ duplicate (Cons a as) = Cons (Cons a as) (duplicate as) ================================================ FILE: src/content/3.7/code/haskell/snippet20.hs ================================================ instance Comonad Stream where extract (Cons a _) = a duplicate (Cons a as) = Cons (Cons a as) (duplicate as) ================================================ FILE: src/content/3.7/code/haskell/snippet21.hs ================================================ tail :: Stream a -> Stream a tail (Cons a as) = as ================================================ FILE: src/content/3.7/code/haskell/snippet22.hs ================================================ sumS :: Num a => Int -> Stream a -> a sumS n (Cons a as) = if n <= 0 then 0 else a + sumS (n - 1) as ================================================ FILE: src/content/3.7/code/haskell/snippet23.hs ================================================ average :: Fractional a => Int -> Stream a -> a average n stm = (sumS n stm) / (fromIntegral n) ================================================ FILE: src/content/3.7/code/haskell/snippet24.hs ================================================ movingAvg :: Fractional a => Int -> Stream a -> Stream a movingAvg n = extend (average n) ================================================ FILE: src/content/3.7/code/haskell/snippet25.hs ================================================ class Comonoid m where split :: m -> (m, m) destroy :: m -> () ================================================ FILE: src/content/3.7/code/haskell/snippet26.hs ================================================ destroy _ = () ================================================ FILE: src/content/3.7/code/haskell/snippet27.hs ================================================ split x = (f x, g x) ================================================ FILE: src/content/3.7/code/haskell/snippet28.hs ================================================ lambda . bimap destroy id . split = id rho . bimap id destroy . split = id ================================================ FILE: src/content/3.7/code/haskell/snippet29.hs ================================================ lambda (bimap destroy id (split x)) = lambda (bimap destroy id (f x, g x)) = lambda ((), g x) = g x ================================================ FILE: src/content/3.7/code/haskell/snippet30.hs ================================================ split x = (x, x) ================================================ FILE: src/content/3.7/code/haskell/snippet31.hs ================================================ data Store s a = Store (s -> a) s ================================================ FILE: src/content/3.7/code/haskell/snippet32.hs ================================================ counit (Prod (Reader f) s)) = f s ================================================ FILE: src/content/3.7/code/haskell/snippet33.hs ================================================ extract (Store f s) = f s ================================================ FILE: src/content/3.7/code/haskell/snippet34.hs ================================================ unit :: a -> Reader s (Product a s) unit a = Reader (\s -> Prod a s) ================================================ FILE: src/content/3.7/code/haskell/snippet35.hs ================================================ Store f :: s -> Store f s ================================================ FILE: src/content/3.7/code/haskell/snippet36.hs ================================================ duplicate (Store f s) = Store (Store f) s ================================================ FILE: src/content/3.7/code/haskell/snippet37.hs ================================================ instance Comonad (Store s) where extract (Store f s) = f s duplicate (Store f s) = Store (Store f) s ================================================ FILE: src/content/3.7/code/haskell/snippet38.hs ================================================ a -> Store s a ================================================ FILE: src/content/3.7/code/haskell/snippet39.hs ================================================ set :: a -> s -> a get :: a -> s ================================================ FILE: src/content/3.7/code/ocaml/snippet01.ml ================================================ 'a -> 'b m ================================================ FILE: src/content/3.7/code/ocaml/snippet02.ml ================================================ 'a w -> 'b ================================================ FILE: src/content/3.7/code/ocaml/snippet03.ml ================================================ module type CoKleisli = sig type 'a w val ( =>= ) : ('a w -> 'b) -> ('b w -> 'c) -> 'a w -> 'c end ================================================ FILE: src/content/3.7/code/ocaml/snippet04.ml ================================================ val extract : 'a w -> 'a ================================================ FILE: src/content/3.7/code/ocaml/snippet05.ml ================================================ module type Comonad = sig type 'a w include Functor with type 'a t := 'a w val extract : 'a w -> 'a val ( =>= ) : ('a w -> 'b) -> ('b w -> 'c) -> 'a w -> 'c end ================================================ FILE: src/content/3.7/code/ocaml/snippet06.ml ================================================ 'a * 'e -> 'b ================================================ FILE: src/content/3.7/code/ocaml/snippet07.ml ================================================ 'a -> ('e -> 'b) ================================================ FILE: src/content/3.7/code/ocaml/snippet08.ml ================================================ type ('e, 'a) product = P of 'e * 'a ================================================ FILE: src/content/3.7/code/ocaml/snippet09.ml ================================================ let ( =>= ) f g (P (e, a)) = let b = f (P (e, a)) in let c = g (P (e, b)) in c ;; ================================================ FILE: src/content/3.7/code/ocaml/snippet10.ml ================================================ let extract (P (e, a)) = a ================================================ FILE: src/content/3.7/code/ocaml/snippet11.ml ================================================ module CoKleisliImpl = struct type 'a w let (=>=) : ('a w -> 'b) -> ('b w -> 'c) -> ('a w -> 'c) = fun f g -> g ... end ================================================ FILE: src/content/3.7/code/ocaml/snippet12.ml ================================================ val extend : ('a w -> 'b) -> 'a w -> 'b w ================================================ FILE: src/content/3.7/code/ocaml/snippet13.ml ================================================ module type CoKleisliExtend = sig type 'a w val extend : ('a w -> 'b) -> 'a w -> 'b w end module CoKleisliImpl (C : CoKleisliExtend) = struct type 'a w = 'a C.w let ( =>= ) : ('a w -> 'b) -> ('b w -> 'c) -> 'a w -> 'c = fun f g -> compose g (C.extend f) ;; end ================================================ FILE: src/content/3.7/code/ocaml/snippet14.ml ================================================ val duplicate : 'a w -> 'a w w ================================================ FILE: src/content/3.7/code/ocaml/snippet15.ml ================================================ module type ComonadBase = sig type 'a w include Functor with type 'a t = 'a w val extract : 'a w -> 'a end module type ComonadDuplicate = sig type 'a w val duplicate : 'a w -> 'a w w end module type ComonadExtend = sig type 'a w val extend : ('a w -> 'b) -> 'a w -> 'b w end module type Comonad = sig type 'a w include ComonadBase with type 'a w := 'a w include ComonadExtend with type 'a w := 'a w include ComonadDuplicate with type 'a w := 'a w end (* Construct a full comonad instance using one of the following modules *) module ComonadImplViaExtend : functor (C : ComonadBase) (D : ComonadDuplicate with type 'a w = 'a C.w) -> Comonad with type 'a w = 'a C.w = functor (C : ComonadBase) (D : ComonadDuplicate with type 'a w = 'a C.w) -> struct include C include D let extend f wa = (C.fmap f) (D.duplicate wa) end module ComonadImplViaDuplicate : functor (C : ComonadBase) (E : ComonadExtend with type 'a w = 'a C.w) -> Comonad with type 'a w = 'a C.w = functor (C : ComonadBase) (E : ComonadExtend with type 'a w = 'a C.w) -> struct include C include E let duplicate (wa : 'a w) : 'a w w = E.extend id wa end ================================================ FILE: src/content/3.7/code/ocaml/snippet16.ml ================================================ type 'a stream = Cons of 'a * 'a stream Lazy.t ================================================ FILE: src/content/3.7/code/ocaml/snippet17.ml ================================================ module StreamFunctor : Functor with type 'a t = 'a stream = struct type 'a t = 'a stream let rec fmap f = function | Cons (x, xs) -> Cons (f x, Lazy.from_val (fmap f (Lazy.force xs))) ;; end ================================================ FILE: src/content/3.7/code/ocaml/snippet18.ml ================================================ let extract (Cons (x, _)) = x ================================================ FILE: src/content/3.7/code/ocaml/snippet19.ml ================================================ let rec duplicate (Cons (x, xs)) = Cons (Cons (x, xs), Lazy.from_val (duplicate (Lazy.force xs))) ;; ================================================ FILE: src/content/3.7/code/ocaml/snippet20.ml ================================================ (* Implement Extract *) module StreamComonadBase (F : Functor with type 'a t = 'a stream) : ComonadBase with type 'a w = 'a stream = struct type 'a w = 'a stream include F let extract (Cons (x, _)) = x end (* Implement duplicate *) module StreamComonadDuplicate : ComonadDuplicate with type 'a w = 'a stream = struct type 'a w = 'a stream let rec duplicate (Cons (x, xs)) = Cons (Cons (x, xs), Lazy.from_val (duplicate (Lazy.force xs))) ;; end (* Generate full Comonad Instance *) module StreamComonad : Comonad with type 'a w = 'a stream = ComonadImplViaExtend (StreamComonadBase (StreamFunctor)) (StreamComonadDuplicate) ================================================ FILE: src/content/3.7/code/ocaml/snippet21.ml ================================================ let tail (Cons (_, xs)) = Lazy.force xs ================================================ FILE: src/content/3.7/code/ocaml/snippet22.ml ================================================ let rec sum_s n (Cons (x, xs)) = if n <= 0 then 0 else x + sum_s (n - 1) (Lazy.force xs) ;; ================================================ FILE: src/content/3.7/code/ocaml/snippet23.ml ================================================ let average n stm = Float.(of_int (sum_s n stm) /. of_int n) ================================================ FILE: src/content/3.7/code/ocaml/snippet24.ml ================================================ let moving_average n = StreamComonad.extend (average n) ================================================ FILE: src/content/3.7/code/ocaml/snippet25.ml ================================================ module type Comonoid = sig type m val split : m -> m * m val destroy : m -> unit end ================================================ FILE: src/content/3.7/code/ocaml/snippet26.ml ================================================ let destroy _ = () ================================================ FILE: src/content/3.7/code/ocaml/snippet27.ml ================================================ let split x = f x, g x ================================================ FILE: src/content/3.7/code/ocaml/snippet28.ml ================================================ (* lambda is the left unitor and rho is the right unitor *) (* <.> is used as compose below *) ;; lambda <.> bimap destroy id <.> split = id rho <.> bimap id destroy <.> split = id ================================================ FILE: src/content/3.7/code/ocaml/snippet29.ml ================================================ ;; lambda (bimap destroy id (split x)) = lambda (bimap destroy id (f x, g x)) = lambda ((), g x) = g x ================================================ FILE: src/content/3.7/code/ocaml/snippet30.ml ================================================ let split x = x, x ================================================ FILE: src/content/3.7/code/ocaml/snippet31.ml ================================================ type ('s, 'a) store = Store of (('s -> 'a) * 's) ================================================ FILE: src/content/3.7/code/ocaml/snippet32.ml ================================================ let counit (Prod (Reader f, s)) = f s ================================================ FILE: src/content/3.7/code/ocaml/snippet33.ml ================================================ let extract (Store (f, s)) = f s ================================================ FILE: src/content/3.7/code/ocaml/snippet34.ml ================================================ let unit a = Reader (fun s -> Prod (a, s)) ================================================ FILE: src/content/3.7/code/ocaml/snippet35.ml ================================================ let make_store f s = Store (f, s) ================================================ FILE: src/content/3.7/code/ocaml/snippet36.ml ================================================ let duplicate (Store (f, s)) = Store (make_store f, s) ================================================ FILE: src/content/3.7/code/ocaml/snippet37.ml ================================================ module StoreComonadBase (S : sig type s end) (F : Functor with type 'a t = (S.s, 'a) store) : ComonadBase with type 'a w = (S.s, 'a) store = struct type 'a w = (S.s, 'a) store include F let extract (Store (f, s)) = f s end module StoreComonadDuplicate (S : sig type s end) : ComonadDuplicate with type 'a w = (S.s, 'a) store = struct type 'a w = (S.s, 'a) store let duplicate (Store (f, s)) = Store (make_store f, s) end (* Generate Full comonad *) module StoreComonad (S : sig type s end) (F : Functor with type 'a t = (S.s, 'a) store) : Comonad with type 'a w = (S.s, 'a) store = ComonadImplViaExtend (StoreComonadBase (S) (F)) (StoreComonadDuplicate (S)) ================================================ FILE: src/content/3.7/code/ocaml/snippet38.ml ================================================ 'a -> ('s, 'a) store ================================================ FILE: src/content/3.7/code/ocaml/snippet39.ml ================================================ val get : 'a -> 's val set : 'a -> 's -> 'a ================================================ FILE: src/content/3.7/code/reason/snippet01.re ================================================ 'a => m('b); ================================================ FILE: src/content/3.7/code/reason/snippet02.re ================================================ w('a) => 'b; ================================================ FILE: src/content/3.7/code/reason/snippet03.re ================================================ module type CoKleisli = { type w('a); let (=>=): (w('a) => 'b, w('b) => 'c, w('a)) => 'c; }; ================================================ FILE: src/content/3.7/code/reason/snippet04.re ================================================ let extract: w('a) => 'a; ================================================ FILE: src/content/3.7/code/reason/snippet05.re ================================================ module type Comonad = { type w('a); include Functor with type t('a) := w('a); let extract: w('a) => 'a; let (=>=): (w('a) => 'b, w('b) => 'c, w('a)) => 'c; }; ================================================ FILE: src/content/3.7/code/reason/snippet06.re ================================================ (('a, 'e)) => 'b; ================================================ FILE: src/content/3.7/code/reason/snippet07.re ================================================ ('a, 'e) => 'b; ================================================ FILE: src/content/3.7/code/reason/snippet08.re ================================================ type product('e, 'a) = | P('e, 'a); ================================================ FILE: src/content/3.7/code/reason/snippet09.re ================================================ let (=>=) = (f, g, P(e, a)) => { let b = f(P(e, a)); let c = g(P(e, b)); c; }; ================================================ FILE: src/content/3.7/code/reason/snippet10.re ================================================ let extract = (P(e, a)) => a; ================================================ FILE: src/content/3.7/code/reason/snippet11.re ================================================ module CoKleisliImpl = { type w('a); let (=>=): (w('a) => 'b, w('b) => 'c, w('a)) => 'c = (f, g) => g ... }; ================================================ FILE: src/content/3.7/code/reason/snippet12.re ================================================ let extend: (w('a) => 'b, w('a)) => w('b); ================================================ FILE: src/content/3.7/code/reason/snippet13.re ================================================ module type CoKleisliExtend = { type w('a); let extend: (w('a) => 'b, w('a)) => w('b); }; module CoKleisliImpl = (C: CoKleisliExtend) => { type w('a) = C.w('a); let (=>=): (w('a) => 'b, w('b) => 'c, w('a)) => 'c = ( (f, g) => compose(g, C.extend(f)): (w('a) => 'b, w('b) => 'c, w('a)) => 'c ); }; ================================================ FILE: src/content/3.7/code/reason/snippet14.re ================================================ let duplicate: w('a) => w(w('a)); ================================================ FILE: src/content/3.7/code/reason/snippet15.re ================================================ module type ComonadBase = { type w('a); include Functor with type t('a) = w('a); let extract: w('a) => 'a; }; module type ComonadDuplicate = { type w('a); let duplicate: w('a) => w(w('a)); }; module type ComonadExtend = { type w('a); let extend: (w('a) => 'b, w('a)) => w('b); }; module type Comonad = { type w('a); include ComonadBase with type w('a) := w('a); include ComonadExtend with type w('a) := w('a); include ComonadDuplicate with type w('a) := w('a); }; /* Construct a full comonad instance using one of the * following modules */ module ComonadImplViaExtend: (C: ComonadBase, D: ComonadDuplicate with type w('a) = C.w('a)) => Comonad with type w('a) = C.w('a) = (C: ComonadBase, D: ComonadDuplicate with type w('a) = C.w('a)) => { include C; include D; let extend = (f, wa) => (C.fmap(f))(D.duplicate(wa)); }; module ComonadImplViaDuplicate: (C: ComonadBase, E: ComonadExtend with type w('a) = C.w('a)) => Comonad with type w('a) = C.w('a) = (C: ComonadBase, E: ComonadExtend with type w('a) = C.w('a)) => { include C; include E; let duplicate = (wa: w('a)): w(w('a)) => E.extend(id, wa); }; ================================================ FILE: src/content/3.7/code/reason/snippet16.re ================================================ type stream('a) = | Cons('a, Lazy.t(stream('a))); ================================================ FILE: src/content/3.7/code/reason/snippet17.re ================================================ module StreamFunctor: Functor with type t('a) = stream('a) = { type t('a) = stream('a); let rec fmap = f => fun | Cons(x, xs) => Cons(f(x), Lazy.from_val(fmap(f, Lazy.force(xs)))); }; ================================================ FILE: src/content/3.7/code/reason/snippet18.re ================================================ let extract = (Cons(x, _)) => x; ================================================ FILE: src/content/3.7/code/reason/snippet19.re ================================================ let rec duplicate = (Cons(x, xs)) => Cons(Cons(x, xs), Lazy.from_val(duplicate(Lazy.force(xs)))); ================================================ FILE: src/content/3.7/code/reason/snippet20.re ================================================ /* Implement Extract */ module StreamComonadBase = (F: Functor with type t('a) = stream('a)) : (ComonadBase with type w('a) = stream('a)) => { type w('a) = stream('a); include F; let extract = (Cons(x, _)) => x; }; /* Implement duplicate */ module StreamComonadDuplicate: ComonadDuplicate with type w('a) = stream('a) = { type w('a) = stream('a); let rec duplicate = (Cons(x, xs)) => Cons(Cons(x, xs), Lazy.from_val(duplicate(Lazy.force(xs)))); }; /* Generate full Comonad Instance */ module StreamComonad: Comonad with type w('a) = stream('a) = ComonadImplViaExtend( (StreamComonadBase(StreamFunctor)), StreamComonadDuplicate, ); ================================================ FILE: src/content/3.7/code/reason/snippet21.re ================================================ let tail = (Cons(_, xs)) => Lazy.force(xs); ================================================ FILE: src/content/3.7/code/reason/snippet22.re ================================================ let rec sum_s = (n, Cons(x, xs)) => n <= 0 ? 0 : x + sum_s(n - 1, Lazy.force(xs)) ================================================ FILE: src/content/3.7/code/reason/snippet23.re ================================================ let average = (n, stm) => Float.(of_int(sum_s(n, stm)) /. of_int(n)); ================================================ FILE: src/content/3.7/code/reason/snippet24.re ================================================ let moving_average = n => StreamComonad.extend(average(n)); ================================================ FILE: src/content/3.7/code/reason/snippet25.re ================================================ module type Comonoid = { type m; let split: m => (m, m); let destroy: m => unit; }; ================================================ FILE: src/content/3.7/code/reason/snippet26.re ================================================ let destroy = _ => (); ================================================ FILE: src/content/3.7/code/reason/snippet27.re ================================================ let split = x => (f(x), g(x)); ================================================ FILE: src/content/3.7/code/reason/snippet28.re ================================================ /* lambda is the left unitor and rho is the right unitor */ /* <.> is used as compose below */ lambda <.> bimap(destroy, id) <.> split == id(rho) <.> bimap(id, destroy) <.> split == id; ================================================ FILE: src/content/3.7/code/reason/snippet29.re ================================================ lambda(bimap(destroy, id, split(x))) == lambda(bimap(destroy, id, (f(x), g(x)))) == lambda(((), g(x))) == g(x); ================================================ FILE: src/content/3.7/code/reason/snippet30.re ================================================ let split = x => (x, x); ================================================ FILE: src/content/3.7/code/reason/snippet31.re ================================================ type store('s, 'a) = | Store(('s => 'a, 's)); ================================================ FILE: src/content/3.7/code/reason/snippet32.re ================================================ let counit = (Prod(Reader(f), s)) => f(s); ================================================ FILE: src/content/3.7/code/reason/snippet33.re ================================================ let extract = (Store(f, s)) => f(s); ================================================ FILE: src/content/3.7/code/reason/snippet34.re ================================================ let unit = a => Reader(s => Prod(a, s)); ================================================ FILE: src/content/3.7/code/reason/snippet35.re ================================================ let make_store = (f, s) => Store(f, s); ================================================ FILE: src/content/3.7/code/reason/snippet36.re ================================================ let duplicate = (Store(f, s)) => Store(make_store(f), s); ================================================ FILE: src/content/3.7/code/reason/snippet37.re ================================================ module StoreComonadBase = (S: {type s;}, F: Functor with type t('a) = store(S.s, 'a)) : (ComonadBase with type w('a) = store(S.s, 'a)) => { type w('a) = store(S.s, 'a); include F; let extract = (Store(f, s)) => f(s); }; module StoreComonadDuplicate = (S: {type s;}) : (ComonadDuplicate with type w('a) = store(S.s, 'a)) => { type w('a) = store(S.s, 'a); let duplicate = (Store(f, s)) => Store(make_store(f), s); }; /* Generate Full comonad */ module StoreComonad = (S: {type s;}, F: Functor with type t('a) = store(S.s, 'a)) : (Comonad with type w('a) = store(S.s, 'a)) => ComonadImplViaExtend( (StoreComonadBase(S, F)), (StoreComonadDuplicate(S)), ); ================================================ FILE: src/content/3.7/code/reason/snippet38.re ================================================ 'a => store('s, 'a); ================================================ FILE: src/content/3.7/code/reason/snippet39.re ================================================ let get: 'a => 's; let set: ('a, 's) => 'a; ================================================ FILE: src/content/3.7/code/scala/snippet01.scala ================================================ A => M[B] ================================================ FILE: src/content/3.7/code/scala/snippet02.scala ================================================ W[A] => B ================================================ FILE: src/content/3.7/code/scala/snippet03.scala ================================================ def =>=[A, B, C](w1: W[A] => B)(w2: W[B] => C): W[A] => C ================================================ FILE: src/content/3.7/code/scala/snippet04.scala ================================================ def extract[A](wa: W[A]): A ================================================ FILE: src/content/3.7/code/scala/snippet05.scala ================================================ trait Comonad[W[_]] extends Functor[W] { def =>=[A, B, C](w1: W[A] => B)(w2: W[B] => C): W[A] => C def extract[A](wa: W[A]): A } ================================================ FILE: src/content/3.7/code/scala/snippet06.scala ================================================ ((A, E)) => B ================================================ FILE: src/content/3.7/code/scala/snippet07.scala ================================================ A => (E => B) ================================================ FILE: src/content/3.7/code/scala/snippet08.scala ================================================ case class Product[E, A](run: (E, A)) // implicit def productFunctor = ... ================================================ FILE: src/content/3.7/code/scala/snippet09.scala ================================================ def =>=[E, A, B, C]: (Product[E, A] => B) => (Product[E, B] => C) => (Product[E, A] => C) = f => g => { case Product((e, a)) => val b = f(Product((e, a))) val c = g(Product((e, b))) c } ================================================ FILE: src/content/3.7/code/scala/snippet10.scala ================================================ def extract[E, A]: Product[E, A] => A = { case Product((e, a)) => a } ================================================ FILE: src/content/3.7/code/scala/snippet11.scala ================================================ def =>=[W[_], A, B, C]: (W[A] => B) => (W[B] => C) => (W[A] => C) = f => g => g ... ================================================ FILE: src/content/3.7/code/scala/snippet12.scala ================================================ def extend[W[_], A, B]: (W[A] => B) => W[A] => W[B] ================================================ FILE: src/content/3.7/code/scala/snippet13.scala ================================================ def =>=[W[_], A, B, C]: (W[A] => B) => (W[B] => C) => (W[A] => C) = f => g => g compose extend(f) ================================================ FILE: src/content/3.7/code/scala/snippet14.scala ================================================ def duplicate[W[_], A]: W[A] => W[W[A]] ================================================ FILE: src/content/3.7/code/scala/snippet15.scala ================================================ trait Comonad[W[_]] extends Functor[W] { def extract[A](wa: W[A]): A def duplicate[A](wa: W[A]): W[W[A]] = extend(identity[W[A]])(wa) def extend[A, B](f: W[A] => B)(wa: W[A]): W[B] = (fmap(f) _ compose duplicate)(wa) } ================================================ FILE: src/content/3.7/code/scala/snippet16.scala ================================================ case class Stream[A](h: () => A, t: () => Stream[A]) ================================================ FILE: src/content/3.7/code/scala/snippet17.scala ================================================ implicit val streamFunctor = new Functor[Stream] { def fmap[A, B](f: A => B)(fa: Stream[A]): Stream[B] = fa match { case Stream(a, as) => Stream(() => f(a()), () => fmap(f)(as())) } } ================================================ FILE: src/content/3.7/code/scala/snippet18.scala ================================================ def extract[A](wa: Stream[A]): A = wa match { case Stream(a, _) => a() } ================================================ FILE: src/content/3.7/code/scala/snippet19.scala ================================================ def duplicateS[A](wa: Stream[A]): Stream[Stream[A]] = wa match { case s @ Stream(a, as) => Stream(() => s, () => duplicateS(as())) } ================================================ FILE: src/content/3.7/code/scala/snippet20.scala ================================================ implicit val streamComonad = new Comonad[Stream] { def extract[A](wa: Stream[A]): A = wa match { case Stream(a, _) => a() } def duplicate[A](wa: Stream[A]): Stream[Stream[A]] = wa match { case s @ Stream(a, as) => Stream(() => s, () => duplicate(as())) } } ================================================ FILE: src/content/3.7/code/scala/snippet21.scala ================================================ def tail[A]: Stream[A] => Stream[A] = { case Stream(a, as) => as() } ================================================ FILE: src/content/3.7/code/scala/snippet22.scala ================================================ def sumS[A](n: Int)(stm: Stream[A])(implicit numeric: Numeric[A]): A = stm match { case Stream(a, as) => import numeric._ if (n <= 0) zero else a() + sumS(n - 1)(as()) } ================================================ FILE: src/content/3.7/code/scala/snippet23.scala ================================================ def average[A](n: Int) (implicit fractional: Fractional[A]): Stream[A] => A = stm => { import fractional._ sumS(n)(stm) / fromInt(n) } ================================================ FILE: src/content/3.7/code/scala/snippet24.scala ================================================ def movingAvg[A](n: Int)(stm: Stream[A]) (implicit fractional: Fractional[A]): Stream[A] = streamComonad. extend(average(n)(fractional))(stm) ================================================ FILE: src/content/3.7/code/scala/snippet25.scala ================================================ trait Comonoid[M] { def split(x: M): (M, M) def destroy(x: M): Unit } ================================================ FILE: src/content/3.7/code/scala/snippet26.scala ================================================ def destroy(x: M): Unit = () ================================================ FILE: src/content/3.7/code/scala/snippet27.scala ================================================ def split(x: M): (M, M) = (f(x), g(x)) ================================================ FILE: src/content/3.7/code/scala/snippet28.scala ================================================ (lambda compose bimap(destroy)(identity[M])) .compose(split) == identity[M] (rho compose bimap(identity[M])(destroy)) .compose(split) == identity[M] ================================================ FILE: src/content/3.7/code/scala/snippet29.scala ================================================ lambda(bimap(destroy)(identity[M])(split(x))) == lambda(bimap(destroy)(identity[M])((f(x), g(x)))) == lambda(((), g(x))) == g(x) ================================================ FILE: src/content/3.7/code/scala/snippet30.scala ================================================ def split(x: M): (M, M) = (x, x) ================================================ FILE: src/content/3.7/code/scala/snippet31.scala ================================================ case class Store[S, A](run: S => A, s: S) ================================================ FILE: src/content/3.7/code/scala/snippet32.scala ================================================ def counit[S, A](a: Product[S, Reader[S, A]]): A = a match { case Product((Reader(f), s)) => f(s) } ================================================ FILE: src/content/3.7/code/scala/snippet33.scala ================================================ def extract[A](wa: Store[S, A]): A = wa match { case Store(f, s) => f(s) } ================================================ FILE: src/content/3.7/code/scala/snippet34.scala ================================================ def unit[S, A](a: A): Reader[S, Product[S, A]] = Reader(s => Product((a, s))) ================================================ FILE: src/content/3.7/code/scala/snippet35.scala ================================================ object Store { def apply[S, A](run: S => A): S => Store[S, A] = s => new Store(run, s) } ================================================ FILE: src/content/3.7/code/scala/snippet36.scala ================================================ def duplicate[A](wa: Store[S, A]): Store[S, Store[S, A]] = wa match { case Store(f, s) => Store(Store(f), s) } ================================================ FILE: src/content/3.7/code/scala/snippet37.scala ================================================ implicit def storeComonad[S] = new Comonad[Store[S, ?]] { def extract[A](wa: Store[S, A]): A = wa match { case Store(f, s) => f(s) } override def duplicate[A](wa: Store[S, A]): Store[S, Store[S, A]] = wa match { case Store(f, s) => Store(Store(f), s) } } ================================================ FILE: src/content/3.7/code/scala/snippet38.scala ================================================ A => Store[S, A] ================================================ FILE: src/content/3.7/code/scala/snippet39.scala ================================================ def set[A, S]: A => S => A def get[A, S]: A => S ================================================ FILE: src/content/3.7/comonads.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{N}{ow that we have covered} monads, we can reap the benefits of duality and get comonads for free simply by reversing the arrows and working in the opposite category. Recall that, at the most basic level, monads are about composing Kleisli arrows: \src{snippet01} where \code{m} is a functor that is a monad. If we use the letter \code{w} (upside down \code{m}) for the comonad, we can define co-Kleisli arrows as morphism of the type: \src{snippet02} The analog of the fish operator for co-Kleisli arrows is defined as: \src{snippet03} For co-Kleisli arrows to form a category we also have to have an identity co-Kleisli arrow, which is called \code{extract}: \src{snippet04} This is the dual of \code{return}. We also have to impose the laws of associativity as well as left- and right-identity. Putting it all together, we could define a comonad in Haskell as: \src{snippet05} In practice, we use slightly different primitives, as we'll see shortly. The question is, what's the use for comonads in programming? \section{Programming with Comonads} Let's compare the monad with the comonad. A monad provides a way of putting a value in a container using \code{return}. It doesn't give you access to a value or values stored inside. Of course, data structures that implement monads might provide access to their contents, but that's considered a bonus. There is no common interface for extracting values from a monad. And we've seen the example of the \code{IO} monad that prides itself in never exposing its contents. A comonad, on the other hand, provides the means of extracting a single value from it. It does not give the means to insert values. So if you want to think of a comonad as a container, it always comes pre-filled with contents, and it lets you peek at it. Just as a Kleisli arrow takes a value and produces some embellished result --- it embellishes it with context --- a co-Kleisli arrow takes a value together with a whole context and produces a result. It's an embodiment of \newterm{contextual computation}. \section{The Product Comonad} Remember the reader monad? We introduced it to tackle the problem of implementing computations that need access to some read-only environment \code{e}. Such computations can be represented as pure functions of the form: \src{snippet06} We used currying to turn them into Kleisli arrows: \src{snippet07} But notice that these functions already have the form of co-Kleisli arrows. Let's massage their arguments into the more convenient functor form: \src{snippet08} We can easily define the composition operator by making the same environment available to the arrows that we are composing: \src{snippet09} The implementation of \code{extract} simply ignores the environment: \src{snippet10} Not surprisingly, the product comonad can be used to perform exactly the same computations as the reader monad. In a way, the comonadic implementation of the environment is more natural --- it follows the spirit of ``computation in context.'' On the other hand, monads come with the convenient syntactic sugar of the \code{do} notation. The connection between the reader monad and the product comonad goes deeper, having to do with the fact that the reader functor is the right adjoint of the product functor. In general, though, comonads cover different notions of computation than monads. We'll see more examples later. It's easy to generalize the \code{Product} comonad to arbitrary product types including tuples and records. \section{Dissecting the Composition} Continuing the process of dualization, we could go ahead and dualize monadic bind and join. Alternatively, we can repeat the process we used with monads, where we studied the anatomy of the fish operator. This approach seems more enlightening. The starting point is the realization that the composition operator must produce a co-Kleisli arrow that takes \code{w a} and produces a \code{c}. The only way to produce a \code{c} is to apply the second function to an argument of the type \code{w b}: \src{snippet11} But how can we produce a value of type \code{w b} that could be fed to \code{g}? We have at our disposal the argument of type \code{w a} and the function \code{f :: w a -> b}. The solution is to define the dual of bind, which is called extend: \src{snippet12} Using \code{extend} we can implement composition: \src{snippet13} Can we next dissect \code{extend}? You might be tempted to say, why not just apply the function \code{w a -> b} to the argument \code{w a}, but then you quickly realize that you'd have no way of converting the resulting \code{b} to \code{w b}. Remember, the comonad provides no means of lifting values. At this point, in the analogous construction for monads, we used \code{fmap}. The only way we could use \code{fmap} here would be if we had something of the type \code{w (w a)} at our disposal. If we could only turn \code{w a} into\\ \code{w (w a)}. And, conveniently, that would be exactly the dual of \code{join}. We call it \code{duplicate}: \src{snippet14} So, just like with the definitions of the monad, we have three equivalent definitions of the comonad: using co-Kleisli arrows, \code{extend}, or \code{duplicate}. Here's the Haskell definition taken directly from\\ \code{Control.Comonad} library: \src{snippet15} Provided are the default implementations of \code{extend} in terms of \code{duplicate} and vice versa, so you only need to override one of them. The intuition behind these functions is based on the idea that, in general, a comonad can be thought of as a container filled with values of type \code{a} (the product comonad was a special case of just one value). There is a notion of the ``current'' value, one that's easily accessible through \code{extract}. A co-Kleisli arrow performs some computation that is focused on the current value, but it has access to all the surrounding values. Think of the Conway's game of life. Each cell contains a value (usually just \code{True} or \code{False}). A comonad corresponding to the game of life would be a grid of cells focused on the ``current'' cell. So what does \code{duplicate} do? It takes a comonadic container \code{w a} and produces a container of containers \code{w (w a)}. The idea is that each of these containers is focused on a different \code{a} inside \code{w a}. In the game of life, you would get a grid of grids, each cell of the outer grid containing an inner grid that's focused on a different cell. Now look at \code{extend}. It takes a co-Kleisli arrow and a comonadic container \code{w a} filled with \code{a}s. It applies the computation to all of these \code{a}s, replacing them with \code{b}s. The result is a comonadic container filled with \code{b}s. \code{extend} does it by shifting the focus from one \code{a} to another and applying the co-Kleisli arrow to each of them in turn. In the game of life, the co-Kleisli arrow would calculate the new state of the current cell. To do that, it would look at its context --- presumably its nearest neighbors. The default implementation of \code{extend} illustrates this process. First we call \code{duplicate} to produce all possible foci and then we apply \code{f} to each of them. \section{The Stream Comonad} This process of shifting the focus from one element of the container to another is best illustrated with the example of an infinite stream. Such a stream is just like a list, except that it doesn't have the empty constructor: \src{snippet16} It's trivially a \code{Functor}: \src{snippet17} The focus of a stream is its first element, so here's the implementation of \code{extract}: \src{snippet18} \code{duplicate} produces a stream of streams, each focused on a different element. \src{snippet19} The first element is the original stream, the second element is the tail of the original stream, the third element is its tail, and so on, ad infinitum. Here's the complete instance: \src{snippet20} This is a very functional way of looking at streams. In an imperative language, we would probably start with a method \code{advance} that shifts the stream by one position. Here, \code{duplicate} produces all shifted streams in one fell swoop. Haskell's laziness makes this possible and even desirable. Of course, to make a \code{Stream} practical, we would also implement the analog of \code{advance}: \src{snippet21} but it's never part of the comonadic interface. If you had any experience with digital signal processing, you'll see immediately that a co-Kleisli arrow for a stream is just a digital filter, and \code{extend} produces a filtered stream. As a simple example, let's implement the moving average filter. Here's a function that sums \code{n} elements of a stream: \src{snippet22} Here's the function that calculates the average of the first \code{n} elements of the stream: \src{snippet23} Partially applied \code{average n} is a co-Kleisli arrow, so we can \code{extend} it over the whole stream: \src{snippet24} The result is the stream of running averages. A stream is an example of a unidirectional, one-dimensional comonad. It can be easily made bidirectional or extended to two or more dimensions. \section{Comonad Categorically} Defining a comonad in category theory is a straightforward exercise in duality. As with the monad, we start with an endofunctor $T$. The two natural transformations, $\eta$ and $\mu$, that define the monad are simply reversed for the comonad: \begin{align*} \varepsilon & \Colon T \to I \\ \delta & \Colon T \to T^2 \end{align*} The components of these transformations correspond to \code{extract} and \code{duplicate}. Comonad laws are the mirror image of monad laws. No big surprise here. Then there is the derivation of the monad from an adjunction. Duality reverses an adjunction: the left adjoint becomes the right adjoint and vice versa. And, since the composition $R \circ L$ defines a monad, $L \circ R$ must define a comonad. The counit of the adjunction: \[\varepsilon \Colon L \circ R \to I\] is indeed the same $\varepsilon$ that we see in the definition of the comonad --- or, in components, as Haskell's \code{extract}. We can also use the unit of the adjunction: \[\eta \Colon I \to R \circ L\] to insert an $R \circ L$ in the middle of $L \circ R$ and produce $L \circ R \circ L \circ R$. Making $T^2$ from $T$ defines the $\delta$, and that completes the definition of the comonad. We've also seen that the monad is a monoid. The dual of this statement would require the use of a comonoid, so what's a comonoid? The original definition of a monoid as a single-object category doesn't dualize to anything interesting. When you reverse the direction of all endomorphisms, you get another monoid. Recall, however, that in our approach to a monad, we used a more general definition of a monoid as an object in a monoidal category. The construction was based on two morphisms: \begin{align*} \mu & \Colon m \otimes m \to m \\ \eta & \Colon i \to m \end{align*} The reversal of these morphisms produces a comonoid in a monoidal category: \begin{align*} \delta & \Colon m \to m \otimes m \\ \varepsilon & \Colon m \to i \end{align*} One can write a definition of a comonoid in Haskell: \src{snippet25} but it is rather trivial. Obviously \code{destroy} ignores its argument. \src{snippet26} \code{split} is just a pair of functions: \src{snippet27} Now consider comonoid laws that are dual to the monoid unit laws. \src{snippet28} Here, \code{lambda} and \code{rho} are the left and right unitors, respectively (see the definition of \hyperref[monads-categorically]{monoidal categories}). Plugging in the definitions, we get: \src{snippet29} which proves that \code{g = id}. Similarly, the second law expands to \code{f = id}. In conclusion: \src{snippet30} which shows that in Haskell (and, in general, in the category $\Set$) every object is a trivial comonoid. Fortunately there are other more interesting monoidal categories in which to define comonoids. One of them is the category of endofunctors. And it turns out that, just like the monad is a monoid in the category of endofunctors, \begin{quote} The comonad is a comonoid in the category of endofunctors. \end{quote} \section{The Store Comonad} Another important example of a comonad is the dual of the state monad. It's called the costate comonad or, alternatively, the store comonad. We've seen before that the state monad is generated by the adjunction that defines the exponentials: \begin{align*} L z & = z\times{}s \\ R a & = s \Rightarrow a \end{align*} We'll use the same adjunction to define the costate comonad. A comonad is defined by the composition $L \circ R$: \[L (R a) = (s \Rightarrow a)\times{}s\] Translating this to Haskell, we start with the adjunction between the \code{Product} functor on the left and the \code{Reader} functor or the right. Composing \code{Product} after \code{Reader} is equivalent to the following definition: \src{snippet31} The counit of the adjunction taken at the object $a$ is the morphism: \[\varepsilon_a \Colon ((s \Rightarrow a)\times{}s) \to a\] or, in Haskell notation: \src{snippet32} This becomes our \code{extract}: \src{snippet33} The unit of the adjunction: \src{snippet34} can be rewritten as partially applied data constructor: \src{snippet35} We construct $\delta$, or \code{duplicate}, as the horizontal composition: \begin{align*} \delta & \Colon L \circ R \to L \circ R \circ L \circ R \\ \delta & = L \circ \eta \circ R \end{align*} We have to sneak $\eta$ through the leftmost $L$, which is the \code{Product} functor. It means acting with $\eta$, or \code{Store f}, on the left component of the pair (that's what \code{fmap} for \code{Product} would do). We get: \src{snippet36} (Remember that, in the formula for $\delta$, $L$ and $R$ stand for identity natural transformations whose components are identity morphisms.) Here's the complete definition of the \code{Store} comonad: \src{snippet37} You may think of the \code{Reader} part of \code{Store} as a generalized container of \code{a}s that are keyed using elements of the type \code{s}. For instance, if \code{s} is \code{Int}, \code{Reader Int a} is an infinite bidirectional stream of \code{a}s. \code{Store} pairs this container with a value of the key type. For instance, \code{Reader Int a} is paired with an \code{Int}. In this case, \code{extract} uses this integer to index into the infinite stream. You may think of the second component of \code{Store} as the current position. Continuing with this example, \code{duplicate} creates a new infinite stream indexed by an \code{Int}. This stream contains streams as its elements. In particular, at the current position, it contains the original stream. But if you use some other \code{Int} (positive or negative) as the key, you'd obtain a shifted stream positioned at that new index. In general, you can convince yourself that when \code{extract} acts on the \code{duplicate}d \code{Store} it produces the original \code{Store} (in fact, the identity law for the comonad states that \code{extract . duplicate = id}). The \code{Store} comonad plays an important role as the theoretical basis for the \code{lens} library. Conceptually, the \code{Store s a} comonad encapsulates the idea of ``focusing'' (like a lens) on a particular substructure of the data type \code{a} using the type \code{s} as an index. In particular, a function of the type: \src{snippet38} is equivalent to a pair of functions: \src{snippet39} If \code{a} is a product type, \code{set} could be implemented as setting the field of type \code{s} inside of \code{a} while returning the modified version of \code{a}. Similarly, \code{get} could be implemented to read the value of the \code{s} field from \code{a}. We'll explore these ideas more in the next section. \section{Challenges} \begin{enumerate} \tightlist \item Implement the Conway's Game of Life using the \code{Store} comonad. Hint: What type do you pick for \code{s}? \end{enumerate} ================================================ FILE: src/content/3.8/code/haskell/snippet01.hs ================================================ type Algebra f a = f a -> a ================================================ FILE: src/content/3.8/code/haskell/snippet02.hs ================================================ data MonF a = MEmpty | MAppend a a ================================================ FILE: src/content/3.8/code/haskell/snippet03.hs ================================================ data RingF a = RZero | ROne | RAdd a a | RMul a a | RNeg a ================================================ FILE: src/content/3.8/code/haskell/snippet04.hs ================================================ evalZ :: Algebra RingF Integer evalZ RZero = 0 evalZ ROne = 1 evalZ (RAdd m n) = m + n evalZ (RMul m n) = m * n evalZ (RNeg n) = -n ================================================ FILE: src/content/3.8/code/haskell/snippet05.hs ================================================ data Expr = RZero | ROne | RAdd Expr Expr | RMul Expr Expr | RNeg Expr ================================================ FILE: src/content/3.8/code/haskell/snippet06.hs ================================================ evalZ :: Expr -> Integer evalZ RZero = 0 evalZ ROne = 1 evalZ (RAdd e1 e2) = evalZ e1 + evalZ e2 evalZ (RMul e1 e2) = evalZ e1 * evalZ e2 evalZ (RNeg e) = -(evalZ e) ================================================ FILE: src/content/3.8/code/haskell/snippet07.hs ================================================ type RingF1 a = RingF (RingF a) ================================================ FILE: src/content/3.8/code/haskell/snippet08.hs ================================================ type RingF2 a = RingF (RingF (RingF a)) ================================================ FILE: src/content/3.8/code/haskell/snippet09.hs ================================================ type RingF2 a = RingF (RingF1 a) ================================================ FILE: src/content/3.8/code/haskell/snippet10.hs ================================================ newtype Fix f = Fix (f (Fix f)) ================================================ FILE: src/content/3.8/code/haskell/snippet11.hs ================================================ newtype Fix f = In (f (Fix f)) ================================================ FILE: src/content/3.8/code/haskell/snippet12.hs ================================================ Fix :: f (Fix f) -> Fix f ================================================ FILE: src/content/3.8/code/haskell/snippet13.hs ================================================ unFix :: Fix f -> f (Fix f) unFix (Fix x) = x ================================================ FILE: src/content/3.8/code/haskell/snippet14.hs ================================================ data NatF a = ZeroF | SuccF a ================================================ FILE: src/content/3.8/code/haskell/snippet15.hs ================================================ data Nat = Zero | Succ Nat ================================================ FILE: src/content/3.8/code/haskell/snippet16.hs ================================================ cata :: Functor f => (f a -> a) -> Fix f -> a cata alg = alg . fmap (cata alg) . unFix ================================================ FILE: src/content/3.8/code/haskell/snippet17.hs ================================================ data NatF a = ZeroF | SuccF a ================================================ FILE: src/content/3.8/code/haskell/snippet18.hs ================================================ fib :: NatF (Int, Int) -> (Int, Int) fib ZeroF = (1, 1) fib (SuccF (m, n)) = (n, m + n) ================================================ FILE: src/content/3.8/code/haskell/snippet19.hs ================================================ data ListF e a = NilF | ConsF e a ================================================ FILE: src/content/3.8/code/haskell/snippet20.hs ================================================ data List e = Nil | Cons e (List e) ================================================ FILE: src/content/3.8/code/haskell/snippet21.hs ================================================ lenAlg :: ListF e Int -> Int lenAlg (ConsF e n) = n + 1 lenAlg NilF = 0 ================================================ FILE: src/content/3.8/code/haskell/snippet22.hs ================================================ length = foldr (\e n -> n + 1) 0 ================================================ FILE: src/content/3.8/code/haskell/snippet23.hs ================================================ sumAlg :: ListF Double Double -> Double sumAlg (ConsF e s) = e + s sumAlg NilF = 0.0 ================================================ FILE: src/content/3.8/code/haskell/snippet24.hs ================================================ sum = foldr (\e s -> e + s) 0.0 ================================================ FILE: src/content/3.8/code/haskell/snippet25.hs ================================================ ana :: Functor f => (a -> f a) -> a -> Fix f ana coalg = Fix . fmap (ana coalg) . coalg ================================================ FILE: src/content/3.8/code/haskell/snippet26.hs ================================================ data StreamF e a = StreamF e a deriving Functor ================================================ FILE: src/content/3.8/code/haskell/snippet27.hs ================================================ data Stream e = Stream e (Stream e) ================================================ FILE: src/content/3.8/code/haskell/snippet28.hs ================================================ era :: [Int] -> StreamF Int [Int] era (p : ns) = StreamF p (filter (notdiv p) ns) where notdiv p n = n `mod` p /= 0 ================================================ FILE: src/content/3.8/code/haskell/snippet29.hs ================================================ primes = ana era [2..] ================================================ FILE: src/content/3.8/code/haskell/snippet30.hs ================================================ toListC :: Fix (StreamF e) -> [e] toListC = cata al where al :: StreamF e [e] -> [e] al (StreamF e a) = e : a ================================================ FILE: src/content/3.8/code/haskell/snippet31.hs ================================================ unfoldr :: (b -> Maybe (a, b)) -> b -> [a] ================================================ FILE: src/content/3.8/code/haskell/snippet32.hs ================================================ set :: a -> s -> a get :: a -> s ================================================ FILE: src/content/3.8/code/haskell/snippet33.hs ================================================ a -> (s, s -> a) ================================================ FILE: src/content/3.8/code/haskell/snippet34.hs ================================================ a -> Store s a ================================================ FILE: src/content/3.8/code/haskell/snippet35.hs ================================================ data Store s a = Store (s -> a) s ================================================ FILE: src/content/3.8/code/ocaml/snippet01.ml ================================================ module type Algebra = functor (F : sig type 'a f end) -> sig type 'a algebra = 'a F.f -> 'a end ================================================ FILE: src/content/3.8/code/ocaml/snippet02.ml ================================================ type 'a mon_f = | MEmpty | Mappend of ('a * 'a) ================================================ FILE: src/content/3.8/code/ocaml/snippet03.ml ================================================ type 'a ring_f = | RZero | ROne | RAdd of ('a * 'a) | RMul of ('a * 'a) | RNeg of 'a ================================================ FILE: src/content/3.8/code/ocaml/snippet04.ml ================================================ module Ring = struct module RingAlg = Algebra (struct type 'a f = 'a ring_f end) let eval_z : 'a RingAlg.algebra = function | RZero -> 0 | ROne -> 1 | RAdd (m, n) -> m + n | RMul (m, n) -> m * n | RNeg n -> -n ;; end ================================================ FILE: src/content/3.8/code/ocaml/snippet05.ml ================================================ type expr = | RZero | ROne | RAdd of (expr * expr) | RMul of (expr * expr) | RNeg of expr ================================================ FILE: src/content/3.8/code/ocaml/snippet06.ml ================================================ let rec eval_z : expr -> int = function | RZero -> 0 | ROne -> 1 | RAdd (e1, e2) -> eval_z e1 + eval_z e2 | RMul (e1, e2) -> eval_z e1 * eval_z e2 | RNeg e -> -eval_z e ;; ================================================ FILE: src/content/3.8/code/ocaml/snippet07.ml ================================================ type 'a ring_f1 = 'a ring_f ring_f ================================================ FILE: src/content/3.8/code/ocaml/snippet08.ml ================================================ type 'a ring_f2 = 'a ring_f ring_f ring_f ================================================ FILE: src/content/3.8/code/ocaml/snippet09.ml ================================================ type 'a ring_f2 = 'a ring_f ring_f1 ================================================ FILE: src/content/3.8/code/ocaml/snippet10.ml ================================================ module Fix (F : Functor) = struct type 'a fix = Fix of 'a fix F.t end ================================================ FILE: src/content/3.8/code/ocaml/snippet11.ml ================================================ module Fix (F : Functor) = struct type 'a fix = In of 'a fix F.t end ================================================ FILE: src/content/3.8/code/ocaml/snippet12.ml ================================================ module Fix (F : Functor) = struct type 'a fix = Fix of 'a fix F.t let fix : 'a fix F.t -> 'a fix = fun f -> Fix f end ================================================ FILE: src/content/3.8/code/ocaml/snippet13.ml ================================================ module Fix (F : Functor) = struct type 'a fix = Fix of 'a fix F.t let unfix : 'a fix -> 'a fix F.t = fun (Fix f) -> f end ================================================ FILE: src/content/3.8/code/ocaml/snippet14.ml ================================================ type 'a nat_f = | ZeroF | SuccF of 'a ================================================ FILE: src/content/3.8/code/ocaml/snippet15.ml ================================================ type nat = | Zero | Succ of nat ================================================ FILE: src/content/3.8/code/ocaml/snippet16.ml ================================================ module Cata (F : Functor) = struct type 'a fix = Fix of 'a fix F.t let fix : 'a fix F.t -> 'a fix = fun f -> Fix f let unfix : 'a fix -> 'a fix F.t = fun (Fix f) -> f let rec cata : ('a F.t -> 'a) -> 'a fix -> 'a = fun alg fixf -> alg (F.fmap (cata alg) (unfix fixf)) ;; end ================================================ FILE: src/content/3.8/code/ocaml/snippet17.ml ================================================ type 'a nat_f = | ZeroF | SuccF of 'a ================================================ FILE: src/content/3.8/code/ocaml/snippet18.ml ================================================ let rec fib = function | ZeroF -> 1, 1 | SuccF (m, n) -> n, m + n ;; ================================================ FILE: src/content/3.8/code/ocaml/snippet19.ml ================================================ type ('e, 'a) list_f = | NilF | ConsF of ('e * 'a) ================================================ FILE: src/content/3.8/code/ocaml/snippet20.ml ================================================ type 'e list' = | Nil | Cons of ('e * 'e list') ================================================ FILE: src/content/3.8/code/ocaml/snippet21.ml ================================================ let len_alg = function | ConsF (e, n) -> n + 1 | NilF -> 0 ;; ================================================ FILE: src/content/3.8/code/ocaml/snippet22.ml ================================================ let length xs = List.fold_right (fun e n -> n + 1) xs 0 ================================================ FILE: src/content/3.8/code/ocaml/snippet23.ml ================================================ let sum_alg = function | ConsF (e, s) -> e +. s | NilF -> 0.0 ;; ================================================ FILE: src/content/3.8/code/ocaml/snippet24.ml ================================================ let sum xs = List.fold_right (fun e s -> e +. s) xs 0.0 ================================================ FILE: src/content/3.8/code/ocaml/snippet25.ml ================================================ module Ana (F : Functor) = struct type 'a fix = Fix of 'a fix F.t let rec ana : ('a -> 'a F.t) -> 'a -> 'a fix = fun coalg a -> Fix (F.fmap (ana coalg) (coalg a)) ;; end ================================================ FILE: src/content/3.8/code/ocaml/snippet26.ml ================================================ type ('e, 'a) stream_f = StreamF of ('e * 'a) module Stream_Functor (E : sig type e end) : Functor with type 'a t = (E.e, 'a) stream_f = struct type 'a t = (E.e, 'a) stream_f let fmap f = function | StreamF (e, a) -> StreamF (e, f a) ;; end ================================================ FILE: src/content/3.8/code/ocaml/snippet27.ml ================================================ type 'e stream = Stream of ('e * 'e stream) ================================================ FILE: src/content/3.8/code/ocaml/snippet28.ml ================================================ (* OCaml library `gen` provides useful helpers for potentially infinite iterators. You can install it with `opam install gen`. To use it in the toplevel, you need to `#require "gen"` *) let era : int Gen.t -> (int, int Gen.t) stream_f = fun ilist -> let notdiv p n = n mod p != 0 in let p = Gen.get_exn ilist in StreamF (p, Gen.filter (notdiv p) ilist) ;; ================================================ FILE: src/content/3.8/code/ocaml/snippet29.ml ================================================ module Stream_Int = Stream_Functor (struct type e = int end) module Ana_Stream = Ana (Stream_Int) (* The fixpoint translated to OCaml is eager in its evaluation. Hence, executing the following function will cause overflow. So, wrapping it inside a lazy *) let primes = lazy (Ana_Stream.ana era (Gen.init (fun i -> i + 2))) ================================================ FILE: src/content/3.8/code/ocaml/snippet30.ml ================================================ module List_C (E : sig type e end) = struct module Stream_F : Functor with type 'a t = (E.e, 'a) stream_f = Stream_Functor (E) module Cata_Stream = Cata (Stream_F) let to_list_c : E.e list Cata_Stream.fix -> E.e list = fun s_fix -> Cata_Stream.cata (fun (StreamF (e, a)) -> e :: a) s_fix ;; end ================================================ FILE: src/content/3.8/code/ocaml/snippet31.ml ================================================ (* Gen.t is used to represent infinite data structures like haskell's lazy list *) val unfold : ('b -> ('a * 'b) option) -> 'b -> 'a Gen.t ================================================ FILE: src/content/3.8/code/ocaml/snippet32.ml ================================================ val set : 'a -> 's -> 'a val get : 'a -> 's ================================================ FILE: src/content/3.8/code/ocaml/snippet33.ml ================================================ (a, (s, s -> a)) ================================================ FILE: src/content/3.8/code/ocaml/snippet34.ml ================================================ 'a -> ('s, 'a) store ================================================ FILE: src/content/3.8/code/ocaml/snippet35.ml ================================================ (* Store is the comonad version of State *) type ('s, 'a) store = Store of ('s -> 'a) * 's ================================================ FILE: src/content/3.8/code/reason/snippet01.re ================================================ module type Algebra = (F: {type f('a);}) => {type algebra('a) = F.f('a) => 'a;}; ================================================ FILE: src/content/3.8/code/reason/snippet02.re ================================================ type mon_f('a) = | MEmpty | Mappend(('a, 'a)); ================================================ FILE: src/content/3.8/code/reason/snippet03.re ================================================ type ring_f('a) = | RZero | ROne | RAdd(('a, 'a)) | RMul(('a, 'a)) | RNeg('a); ================================================ FILE: src/content/3.8/code/reason/snippet04.re ================================================ module Ring = { module RingAlg = Algebra({type f('a) = ring_f('a);}); let eval_z: RingAlg.algebra('a) = ( fun | RZero => 0 | ROne => 1 | RAdd(m, n) => m + n | RMul(m, n) => m * n | RNeg(n) => - n ); }; ================================================ FILE: src/content/3.8/code/reason/snippet05.re ================================================ type expr = | RZero | ROne | RAdd((expr, expr)) | RMul((expr, expr)) | RNeg(expr); ================================================ FILE: src/content/3.8/code/reason/snippet06.re ================================================ let rec eval_z: expr => int = ( fun | RZero => 0 | ROne => 1 | RAdd(e1, e2) => eval_z(e1) + eval_z(e2) | RMul(e1, e2) => eval_z(e1) * eval_z(e2) | RNeg(e) => - eval_z(e) ); ================================================ FILE: src/content/3.8/code/reason/snippet07.re ================================================ type ring_f1('a) = ring_f(ring_f('a)); ================================================ FILE: src/content/3.8/code/reason/snippet08.re ================================================ type ring_f2('a) = ring_f(ring_f(ring_f('a))); ================================================ FILE: src/content/3.8/code/reason/snippet09.re ================================================ type ring_f2('a) = ring_f1(ring_f('a)); ================================================ FILE: src/content/3.8/code/reason/snippet10.re ================================================ module Fix = (F: Functor) => { type fix('a) = | Fix(F.t(fix('a))); }; ================================================ FILE: src/content/3.8/code/reason/snippet11.re ================================================ module Fix = (F: Functor) => { type fix('a) = | In(F.t(fix('a))); }; ================================================ FILE: src/content/3.8/code/reason/snippet12.re ================================================ module Fix = (F: Functor) => { type fix('a) = | Fix(F.t(fix('a))); let fix: F.t(fix('a)) => fix('a) = ( f => Fix(f): F.t(fix('a)) => fix('a) ); }; ================================================ FILE: src/content/3.8/code/reason/snippet13.re ================================================ module Fix = (F: Functor) => { type fix('a) = | Fix(F.t(fix('a))); let unfix: fix('a) => F.t(fix('a)) = ( (Fix(f)) => f: fix('a) => F.t(fix('a)) ); }; ================================================ FILE: src/content/3.8/code/reason/snippet14.re ================================================ type nat_f('a) = | ZeroF | SuccF('a); ================================================ FILE: src/content/3.8/code/reason/snippet15.re ================================================ type nat = | Zero | Succ(nat); ================================================ FILE: src/content/3.8/code/reason/snippet16.re ================================================ module Cata = (F: Functor) => { type fix('a) = | Fix(F.t(fix('a))); let fix: F.t(fix('a)) => fix('a) = ( f => Fix(f): F.t(fix('a)) => fix('a) ); let unfix: fix('a) => F.t(fix('a)) = ( (Fix(f)) => f: fix('a) => F.t(fix('a)) ); let rec cata: (F.t('a) => 'a, fix('a)) => 'a = ( (alg, fixf) => alg(F.fmap(cata(alg), unfix(fixf))): (F.t('a) => 'a, fix('a)) => 'a ); }; ================================================ FILE: src/content/3.8/code/reason/snippet17.re ================================================ type nat_f('a) = | ZeroF | SuccF('a); ================================================ FILE: src/content/3.8/code/reason/snippet18.re ================================================ let rec fib = fun | ZeroF => (1, 1) | SuccF(m, n) => (n, m + n); ================================================ FILE: src/content/3.8/code/reason/snippet19.re ================================================ type list_f('e, 'a) = | NilF | ConsF(('e, 'a)); ================================================ FILE: src/content/3.8/code/reason/snippet20.re ================================================ type list'('e) = | Nil | Cons(('e, list'('e))); ================================================ FILE: src/content/3.8/code/reason/snippet21.re ================================================ let len_alg = fun | ConsF(e, n) => n + 1 | NilF => 0; ================================================ FILE: src/content/3.8/code/reason/snippet22.re ================================================ let length = xs => List.fold_right((e, n) => n + 1, xs, 0); ================================================ FILE: src/content/3.8/code/reason/snippet23.re ================================================ let sum_alg = fun | ConsF(e, s) => e +. s | NilF => 0.0; ================================================ FILE: src/content/3.8/code/reason/snippet24.re ================================================ let sum = xs => List.fold_right((e, s) => e +. s, xs, 0.0); ================================================ FILE: src/content/3.8/code/reason/snippet25.re ================================================ module Ana = (F: Functor) => { type fix('a) = | Fix(F.t(fix('a))); let rec ana: ('a => F.t('a), 'a) => fix('a) = ( (coalg, a) => Fix(F.fmap(ana(coalg), coalg(a))): ('a => F.t('a), 'a) => fix('a) ); }; ================================================ FILE: src/content/3.8/code/reason/snippet26.re ================================================ type stream_f('e, 'a) = | StreamF(('e, 'a)); module Stream_Functor = (E: {type e;}) : (Functor with type t('a) = stream_f(E.e, 'a)) => { type t('a) = stream_f(E.e, 'a); let fmap = f => fun | StreamF(e, a) => StreamF(e, f(a)); }; ================================================ FILE: src/content/3.8/code/reason/snippet27.re ================================================ type stream('e) = | Stream(('e, stream('e))); ================================================ FILE: src/content/3.8/code/reason/snippet28.re ================================================ /* OCaml library `gen` provides useful helpers for potentially infinite iterators. You can install it with `opam install gen`. To use it in the toplevel, you need to `#require "gen"` */ let era: Gen.t(int) => stream_f(int, Gen.t(int)) = (ilist => { let notdiv = (p, n) => n mod p !== 0; let p = Gen.get_exn(ilist); StreamF(p, Gen.filter(notdiv(p), ilist)); }); ================================================ FILE: src/content/3.8/code/reason/snippet29.re ================================================ module Stream_Int = Stream_Functor({type e = int;}); module Ana_Stream = Ana(Stream_Int); /* The fixpoint translated to ReasonML is eager in its evaluation. Hence, executing the following function will cause overflow. So, wrapping it inside a lazy */ let primes = lazy(Ana_Stream.ana(era, Gen.init(i => i + 2))); ================================================ FILE: src/content/3.8/code/reason/snippet30.re ================================================ module List_C = (E: {type e;}) => { module Stream_F: Functor with type t('a) = stream_f(E.e, 'a) = Stream_Functor(E); module Cata_Stream = Cata(Stream_F); let to_list_c: Cata_Stream.fix(list(E.e)) => list(E.e) = ( s_fix => Cata_Stream.cata((StreamF(e, a)) => [e, ...a], s_fix): Cata_Stream.fix(list(E.e)) => list(E.e) ); }; ================================================ FILE: src/content/3.8/code/reason/snippet31.re ================================================ /* Gen.t is used to represent infinite data structures like haskell's lazy list */ let unfold: ('b => option(('a, 'b)), 'b) => Gen.t('a); ================================================ FILE: src/content/3.8/code/reason/snippet32.re ================================================ let set: ('a, 's) => 'a; let get: 'a => 's; ================================================ FILE: src/content/3.8/code/reason/snippet33.re ================================================ (a, (s, s => a)); ================================================ FILE: src/content/3.8/code/reason/snippet34.re ================================================ 'a => store('s, 'a); ================================================ FILE: src/content/3.8/code/reason/snippet35.re ================================================ /* Store is the comonad version of State */ type store('s, 'a) = | Store('s => 'a, 's); ================================================ FILE: src/content/3.8/code/scala/snippet01.scala ================================================ type Algebra[F[_], A] = F[A] => A ================================================ FILE: src/content/3.8/code/scala/snippet02.scala ================================================ sealed trait MonF[+A] case object MEmpty extends MonF[Nothing] case class MAppend[A](m: A, n: A) extends MonF[A] ================================================ FILE: src/content/3.8/code/scala/snippet03.scala ================================================ sealed trait RingF[+A] case object RZero extends RingF[Nothing] case object ROne extends RingF[Nothing] case class RAdd[A](m: A, n: A) extends RingF[A] case class RMul[A](m: A, n: A) extends RingF[A] case class RNeg[A](n: A) extends RingF[A] ================================================ FILE: src/content/3.8/code/scala/snippet04.scala ================================================ def evalZ: Algebra[RingF, Int] = { case RZero => 0 case ROne => 1 case RAdd(m, n) => m + n case RMul(m, n) => m * n case RNeg(n) => -n } ================================================ FILE: src/content/3.8/code/scala/snippet05.scala ================================================ sealed trait Expr case object RZero extends Expr case object ROne extends Expr case class RAdd(e1: Expr, e2: Expr) extends Expr case class RMul(e1: Expr, e2: Expr) extends Expr case class RNeg(e: Expr) extends Expr ================================================ FILE: src/content/3.8/code/scala/snippet06.scala ================================================ def evalZ: Expr => Int = { case RZero => 0 case ROne => 1 case RAdd(e1, e2) => evalZ(e1) + evalZ(e2) case RMul(e1, e2) => evalZ(e1) * evalZ(e2) case RNeg(e) => -evalZ(e) } ================================================ FILE: src/content/3.8/code/scala/snippet07.scala ================================================ type RingF1[A] = RingF[RingF[A]] ================================================ FILE: src/content/3.8/code/scala/snippet08.scala ================================================ type RingF2[A] = RingF[RingF[RingF[A]]] ================================================ FILE: src/content/3.8/code/scala/snippet09.scala ================================================ type RingF2[A] = RingF[RingF1[A]] ================================================ FILE: src/content/3.8/code/scala/snippet10.scala ================================================ case class Fix[F[_]](x: F[Fix[F]]) ================================================ FILE: src/content/3.8/code/scala/snippet11.scala ================================================ sealed trait Fix[F[_]] case class In[F[_]](f: F[Fix[F]]) ================================================ FILE: src/content/3.8/code/scala/snippet12.scala ================================================ object Fix { def apply[F[_]](f: F[Fix[F]]): Fix[F] = new Fix(f) } ================================================ FILE: src/content/3.8/code/scala/snippet13.scala ================================================ def unFix[F[_]]: Fix[F] => F[Fix[F]] = { case Fix(x) => x } ================================================ FILE: src/content/3.8/code/scala/snippet14.scala ================================================ sealed trait NatF[+A] case object ZeroF extends NatF[Nothing] case class SuccF[A](a: A) extends NatF[A] ================================================ FILE: src/content/3.8/code/scala/snippet15.scala ================================================ sealed trait Nat case object Zero extends Nat case class Succ(n: Nat) extends Nat ================================================ FILE: src/content/3.8/code/scala/snippet16.scala ================================================ def cata[F[_], A](alg: F[A] => A) (implicit F: Functor[F]): Fix[F] => A = alg.compose(F.fmap(cata(alg)) _ compose unFix) ================================================ FILE: src/content/3.8/code/scala/snippet17.scala ================================================ sealed trait NatF[+A] case object ZeroF extends NatF[Nothing] case class SuccF[A](a: A) extends NatF[A] ================================================ FILE: src/content/3.8/code/scala/snippet18.scala ================================================ def fib: NatF[(Int, Int)] => (Int, Int) = { case ZeroF => (1, 1) case SuccF((m, n)) => (n, m + n) } ================================================ FILE: src/content/3.8/code/scala/snippet19.scala ================================================ sealed trait ListF[+E, +A] case object NilF extends ListF[Nothing, Nothing] case class ConsF[E, A](h: E, t: A) extends ListF[E, A] ================================================ FILE: src/content/3.8/code/scala/snippet20.scala ================================================ sealed trait List[+E] case object Nil extends List[Nothing] case class Cons[E](h: E, t: List[E]) extends List[E] ================================================ FILE: src/content/3.8/code/scala/snippet21.scala ================================================ def lenAlg[E]: ListF[E, Int] => Int = { case ConsF(e, n) => n + 1 case NilF => 0 } ================================================ FILE: src/content/3.8/code/scala/snippet22.scala ================================================ def length[E](l: List[E]): Int = l.foldRight(0)((e, n) => n + 1) ================================================ FILE: src/content/3.8/code/scala/snippet23.scala ================================================ def sumAlg: ListF[Double, Double] => Double = { case ConsF(e, s) => e + s case NilF => 0.0 } ================================================ FILE: src/content/3.8/code/scala/snippet24.scala ================================================ def sum(l: List[Double]): Double = l.foldRight(0.0)((e, s) => e + s) ================================================ FILE: src/content/3.8/code/scala/snippet25.scala ================================================ def ana[F[_], A](coalg: A => F[A]) (implicit F: Functor[F]): A => Fix[F] = (Fix.apply[F] _).compose(F.fmap(ana(coalg)) _ compose coalg) ================================================ FILE: src/content/3.8/code/scala/snippet26.scala ================================================ case class StreamF[E, A](h: E, t: A) implicit def streamFFunctor[E] = new Functor[StreamF[E, ?]] { def fmap[A, B](f: A => B)(fa: StreamF[E, A]): StreamF[E, B] = ... } ================================================ FILE: src/content/3.8/code/scala/snippet27.scala ================================================ case class Stream[E](h: E, t: Stream[E]) ================================================ FILE: src/content/3.8/code/scala/snippet28.scala ================================================ def era: List[Int] => StreamF[Int, List[Int]] = { case p :: ns => def notdiv(p: Int)(n: Int): Boolean = n % p != 0 StreamF(p, ns.filter(notdiv(p))) } ================================================ FILE: src/content/3.8/code/scala/snippet29.scala ================================================ // just imagine that the list is infinite def primes = ana(era)(streamFFunctor)((1 to 10).toList) ================================================ FILE: src/content/3.8/code/scala/snippet30.scala ================================================ def toListC[E]: Fix[StreamF[E, ?]] => List[E] = { def al: StreamF[E, List[E]] => List[E] = { case StreamF(e, a) => e :: a } cata[StreamF[E, ?], List[E]](al) } ================================================ FILE: src/content/3.8/code/scala/snippet31.scala ================================================ def unfoldr[A, B]: (B => Option[(A, B)]) => B => List[A] ================================================ FILE: src/content/3.8/code/scala/snippet32.scala ================================================ def set[A, S]: A => S => A def get[A, S]: A => S ================================================ FILE: src/content/3.8/code/scala/snippet33.scala ================================================ A => ((S, S => A)) ================================================ FILE: src/content/3.8/code/scala/snippet34.scala ================================================ A => Store[S, A] ================================================ FILE: src/content/3.8/code/scala/snippet35.scala ================================================ case class Store[S, A](run: S => A, s: S) ================================================ FILE: src/content/3.8/f-algebras.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{W}{e've seen several formulations} of a monoid: as a set, as a single-object category, as an object in a monoidal category. How much more juice can we squeeze out of this simple concept? Let's try. Take this definition of a monoid as a set $m$ with a pair of functions: \begin{align*} \mu & \Colon m\times{}m \to m \\ \eta & \Colon 1 \to m \end{align*} Here, 1 is the terminal object in $\Set$ --- the singleton set. The first function defines multiplication (it takes a pair of elements and returns their product), the second selects the unit element from $m$. Not every choice of two functions with these signatures results in a monoid. For that we need to impose additional conditions: associativity and unit laws. But let's forget about that for a moment and just consider ``potential monoids.'' A pair of functions is an element of a Cartesian product of two sets of functions. We know that these sets may be represented as exponential objects: \begin{align*} \mu & \in m^{m\times{}m} \\ \eta & \in m^1 \end{align*} The Cartesian product of these two sets is: \[m^{m\times{}m}\times{}m^1\] Using some high-school algebra (which works in every Cartesian closed category), we can rewrite it as: \[m^{m\times{}m + 1}\] The $+$ sign stands for the coproduct in $\Set$. We have just replaced a pair of functions with a single function --- an element of the set: \[m\times{}m + 1 \to m\] Any element of this set of functions is a potential monoid. The beauty of this formulation is that it leads to interesting generalizations. For instance, how would we describe a group using this language? A group is a monoid with one additional function that assigns the inverse to every element. The latter is a function of the type $m \to m$. As an example, integers form a group with addition as a binary operation, zero as the unit, and negation as the inverse. To define a group we would start with a triple of functions: \begin{align*} m\times{}m \to m \\ m \to m \\ 1 \to m \end{align*} As before, we can combine all these triples into one set of functions: \[m\times{}m + m + 1 \to m\] We started with one binary operator (addition), one unary operator (negation), and one nullary operator (identity --- here zero). We combined them into one function. All functions with this signature define potential groups. We can go on like this. For instance, to define a ring, we would add one more binary operator and one nullary operator, and so on. Each time we end up with a function type whose left-hand side is a sum of powers (possibly including the zeroth power --- the terminal object), and the right-hand side being the set itself. Now we can go crazy with generalizations. First of all, we can replace sets with objects and functions with morphisms. We can define n-ary operators as morphisms from n-ary products. It means that we need a category that supports finite products. For nullary operators we require the existence of the terminal object. So we need a Cartesian category. In order to combine these operators we need exponentials, so that's a Cartesian closed category. Finally, we need coproducts to complete our algebraic shenanigans. Alternatively, we can just forget about the way we derived our formulas and concentrate on the final product. The sum of products on the left hand side of our morphism defines an endofunctor. What if we pick an arbitrary endofunctor $F$ instead? In that case we don't have to impose any constraints on our category. What we obtain is called an $F$-algebra. An $F$-algebra is a triple consisting of an endofunctor $F$, an object $a$, and a morphism \[F a \to a\] The object is often called the carrier, an underlying object or, in the context of programming, the carrier \emph{type}. The morphism is often called the evaluation function or the structure map. Think of the functor $F$ as forming expressions and the morphism as evaluating them. Here's the Haskell definition of an $F$-algebra: \src{snippet01} It identifies the algebra with its evaluation function. In the monoid example, the functor in question is: \src{snippet02} This is Haskell for $1 + a\times{}a$ (remember \hyperref[simple-algebraic-data-types]{algebraic data structures}). A ring would be defined using the following functor: \src{snippet03} which is Haskell for $1 + 1 + a\times{}a + a\times{}a + a$. An example of a ring is the set of integers. We can choose \code{Integer} as the carrier type and define the evaluation function as: \src{snippet04} There are more $F$-algebras based on the same functor \code{RingF}. For instance, polynomials form a ring and so do square matrices. As you can see, the role of the functor is to generate expressions that can be evaluated using the evaluator of the algebra. So far we've only seen very simple expressions. We are often interested in more elaborate expressions that can be defined using recursion. \section{Recursion} One way to generate arbitrary expression trees is to replace the variable \code{a} inside the functor definition with recursion. For instance, an arbitrary expression in a ring is generated by this tree-like data structure: \src{snippet05} We can replace the original ring evaluator with its recursive version: \src{snippet06} This is still not very practical, since we are forced to represent all integers as sums of ones, but it will do in a pinch. But how can we describe expression trees using the language of $F$-algebras? We have to somehow formalize the process of replacing the free type variable in the definition of our functor, recursively, with the result of the replacement. Imagine doing this in steps. First, define a depth-one tree as: \src{snippet07} We are filling the holes in the definition of \code{RingF} with depth-zero trees generated by \code{RingF a}. Depth-2 trees are similarly obtained as: \src{snippet08} which we can also write as: \src{snippet09} Continuing this process, we can write a symbolic equation: \begin{snipv} type RingF\textsubscript{n+1} a = RingF (RingF\textsubscript{n} a) \end{snipv} Conceptually, after repeating this process infinitely many times, we end up with our \code{Expr}. Notice that \code{Expr} does not depend on \code{a}. The starting point of our journey doesn't matter, we always end up in the same place. This is not always true for an arbitrary endofunctor in an arbitrary category, but in the category $\Set$ things are nice. Of course, this is a hand-waving argument, and I'll make it more rigorous later. Applying an endofunctor infinitely many times produces a \newterm{fixed point}, an object defined as: \[\mathit{Fix}\ f = f\ (\mathit{Fix}\ f)\] The intuition behind this definition is that, since we applied $f$ infinitely many times to get $\mathit{Fix}\ f$, applying it one more time doesn't change anything. In Haskell, the definition of a fixed point is: \src{snippet10} Arguably, this would be more readable if the constructor's name were different than the name of the type being defined, as in: \src{snippet11} but I'll stick with the accepted notation. The constructor \code{Fix} (or \code{In}, if you prefer) can be seen as a function: \src{snippet12} There is also a function that peels off one level of functor application: \src{snippet13} The two functions are the inverse of each other. We'll use these functions later. \section{Category of $F$-Algebras} Here's the oldest trick in the book: Whenever you come up with a way of constructing some new objects, see if they form a category. Not surprisingly, algebras over a given endofunctor $F$ form a category. Objects in that category are algebras --- pairs consisting of a carrier object $a$ and a morphism $F a \to a$, both from the original category $\cat{C}$. To complete the picture, we have to define morphisms in the category of $F$-algebras. A morphism must map one algebra $(a, f)$ to another algebra $(b, g)$. We'll define it as a morphism $m$ that maps the carriers --- it goes from $a$ to $b$ in the original category. Not any morphism will do: we want it to be compatible with the two evaluators. (We call such a structure-preserving morphism a \newterm{homomorphism}.) Here's how you define a homomorphism of $F$-algebras. First, notice that we can lift $m$ to the mapping: \[F m \Colon F a \to F b\] we can then follow it with $g$ to get to $b$. Equivalently, we can use $f$ to go from $F a$ to $a$ and then follow it with $m$. We want the two paths to be equal: \[g \circ F m = m \circ f\] \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/alg.png} \end{figure} \noindent It's easy to convince yourself that this is indeed a category (hint: identity morphisms from $\cat{C}$ work just fine, and a composition of homomorphisms is a homomorphism). An initial object in the category of $F$-algebras, if it exists, is called the \newterm{initial algebra}. Let's call the carrier of this initial algebra $i$ and its evaluator $j \Colon F i \to i$. It turns out that $j$, the evaluator of the initial algebra, is an isomorphism. This result is known as Lambek's theorem. The proof relies on the definition of the initial object, which requires that there be a unique homomorphism $m$ from it to any other $F$-algebra. Since $m$ is a homomorphism, the following diagram must commute: \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/alg2.png} \end{figure} \noindent Now let's construct an algebra whose carrier is $F i$. The evaluator of such an algebra must be a morphism from $F (F i)$ to $F i$. We can easily construct such an evaluator simply by lifting $j$: \[F j \Colon F (F i) \to F i\] Because $(i, j)$ is the initial algebra, there must be a unique homomorphism $m$ from it to $(F i, F j)$. The following diagram must commute: \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/alg3a.png} \end{figure} \noindent But we also have this trivially commuting diagram (both paths are the same!): \begin{figure}[H] \centering \includegraphics[width=0.3\textwidth]{images/alg3.png} \end{figure} \noindent which can be interpreted as showing that $j$ is a homomorphism of algebras, mapping $(F i, F j)$ to $(i, j)$. We can glue these two diagrams together to get: \begin{figure}[H] \centering \includegraphics[width=0.6\textwidth]{images/alg4.png} \end{figure} \noindent This diagram may, in turn, be interpreted as showing that $j \circ m$ is a homomorphism of algebras. Only in this case the two algebras are the same. Moreover, because $(i, j)$ is initial, there can only be one homomorphism from it to itself, and that's the identity morphism $\id_i$ --- which we know is a homomorphism of algebras. Therefore $j \circ m = \id_i$. Using this fact and the commuting property of the left diagram we can prove that $m \circ j = \id_{Fi}$. This shows that $m$ is the inverse of $j$ and therefore $j$ is an isomorphism between $F i$ and $i$: \[F i \cong i\] But that is just saying that $i$ is a fixed point of $F$. That's the formal proof behind the original hand-waving argument. Back to Haskell: We recognize $i$ as our \code{Fix f}, $j$ as our constructor \code{Fix}, and its inverse as \code{unFix}. The isomorphism in Lambek's theorem tells us that, in order to get the initial algebra, we take the functor $f$ and replace its argument $a$ with \code{Fix f}. We also see why the fixed point does not depend on $a$. \section{Natural Numbers} Natural numbers can also be defined as an $F$-algebra. The starting point is the pair of morphisms: \begin{align*} zero & \Colon 1 \to N \\ succ & \Colon N \to N \end{align*} The first one picks the zero, and the second one maps all numbers to their successors. As before, we can combine the two into one: \[1 + N \to N\] The left hand side defines a functor which, in Haskell, can be written like this: \src{snippet14} The fixed point of this functor (the initial algebra that it generates) can be encoded in Haskell as: \src{snippet15} A natural number is either zero or a successor of another number. This is known as the Peano representation for natural numbers. \section{Catamorphisms} Let's rewrite the initiality condition using Haskell notation. We call the initial algebra \code{Fix f}. Its evaluator is the constructor \code{Fix}. There is a unique morphism \code{m} from the initial algebra to any other algebra over the same functor. Let's pick an algebra whose carrier is \code{a} and the evaluator is \code{alg}. \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/alg5.png} \end{figure} \noindent By the way, notice what \code{m} is: It's an evaluator for the fixed point, an evaluator for the whole recursive expression tree. Let's find a general way of implementing it. Lambek's theorem tells us that the constructor \code{Fix} is an isomorphism. We called its inverse \code{unFix}. We can therefore flip one arrow in this diagram to get: \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/alg6.png} \end{figure} \noindent Let's write down the commutation condition for this diagram: \begin{snip}{haskell} m = alg . fmap m . unFix \end{snip} We can interpret this equation as a recursive definition of \code{m}. The recursion is bound to terminate for any finite tree created using the functor \code{f}. We can see that by noticing that \code{fmap m} operates underneath the top layer of the functor \code{f}. In other words, it works on the children of the original tree. The children are always one level shallower than the original tree. Here's what happens when we apply \code{m} to a tree constructed using \code{Fix\ f}. The action of \code{unFix} peels off the constructor, exposing the top level of the tree. We then apply \code{m} to all the children of the top node. This produces results of type \code{a}. Finally, we combine those results by applying the non-recursive evaluator \code{alg}. The key point is that our evaluator \code{alg} is a simple non-recursive function. Since we can do this for any algebra \code{alg}, it makes sense to define a higher order function that takes the algebra as a parameter and gives us the function we called \code{m}. This higher order function is called a catamorphism: \src{snippet16} Let's see an example of that. Take the functor that defines natural numbers: \src{snippet17} Let's pick \code{(Int, Int)} as the carrier type and define our algebra as: \src{snippet18} You can easily convince yourself that the catamorphism for this algebra, \code{cata fib}, calculates Fibonacci numbers. In general, an algebra for \code{NatF} defines a recurrence relation: the value of the current element in terms of the previous element. A catamorphism then evaluates the n-th element of that sequence. \section{Folds} A list of \code{e} is the initial algebra of the following functor: \src{snippet19} Indeed, replacing the variable \code{a} with the result of recursion, which we'll call \code{List e}, we get: \src{snippet20} An algebra for a list functor picks a particular carrier type and defines a function that does pattern matching on the two constructors. Its value for \code{NilF} tells us how to evaluate an empty list, and its value for \code{ConsF} tells us how to combine the current element with the previously accumulated value. For instance, here's an algebra that can be used to calculate the length of a list (the carrier type is \code{Int}): \src{snippet21} Indeed, the resulting catamorphism \code{cata lenAlg} calculates the length of a list. Notice that the evaluator is a combination of (1) a function that takes a list element and an accumulator and returns a new accumulator, and (2) a starting value, here zero. The type of the value and the type of the accumulator are given by the carrier type. Compare this to the traditional Haskell definition: \src{snippet22} The two arguments to \code{foldr} are exactly the two components of the algebra. Let's try another example: \src{snippet23} Again, compare this with: \src{snippet24} As you can see, \code{foldr} is just a convenient specialization of a catamorphism to lists. \section{Coalgebras} As usual, we have a dual construction of an F-coalgebra, where the direction of the morphism is reversed: \[a \to F a\] Coalgebras for a given functor also form a category, with homomorphisms preserving the coalgebraic structure. The terminal object $(t, u)$ in that category is called the terminal (or final) coalgebra. For every other algebra $(a, f)$ there is a unique homomorphism $m$ that makes the following diagram commute: \begin{figure}[H] \centering \includegraphics[width=0.4\textwidth]{images/alg7.png} \end{figure} \noindent A terminal coalgebra is a fixed point of the functor, in the sense that the morphism $u \Colon t \to F t$ is an isomorphism (Lambek's theorem for coalgebras): \[F t \cong t\] A terminal coalgebra is usually interpreted in programming as a recipe for generating (possibly infinite) data structures or transition systems. Just like a catamorphism can be used to evaluate an initial algebra, an anamorphism can be used to coevaluate a terminal coalgebra: \src{snippet25} A canonical example of a coalgebra is based on a functor whose fixed point is an infinite stream of elements of type \code{e}. This is the functor: \src{snippet26} and this is its fixed point: \src{snippet27} A coalgebra for \code{StreamF e} is a function that takes the seed of type \code{a} and produces a pair (\code{StreamF} is a fancy name for a pair) consisting of an element and the next seed. You can easily generate simple examples of coalgebras that produce infinite sequences, like the list of squares, or reciprocals. A more interesting example is a coalgebra that produces a list of primes. The trick is to use an infinite list as a carrier. Our starting seed will be the list \code{{[}2..{]}}. The next seed will be the tail of this list with all multiples of 2 removed. It's a list of odd numbers starting with 3. In the next step, we'll take the tail of this list and remove all multiples of 3, and so on. You might recognize the makings of the sieve of Eratosthenes. This coalgebra is implemented by the following function: \src{snippet28} The anamorphism for this coalgebra generates the list of primes: \src{snippet29} A stream is an infinite list, so it should be possible to convert it to a Haskell list. To do that, we can use the same functor \code{StreamF} to form an algebra, and we can run a catamorphism over it. For instance, this is a catamorphism that converts a stream to a list: \src{snippet30} Here, the same fixed point is simultaneously an initial algebra and a terminal coalgebra for the same endofunctor. It's not always like this, in an arbitrary category. In general, an endofunctor may have many (or no) fixed points. The initial algebra is the so called least fixed point, and the terminal coalgebra is the greatest fixed point. In Haskell, though, both are defined by the same formula, and they coincide. The anamorphism for lists is called unfold. To create finite lists, the functor is modified to produce a \code{Maybe} pair: \src{snippet31} The value of \code{Nothing} will terminate the generation of the list. An interesting case of a coalgebra is related to lenses. A lens can be represented as a pair of a getter and a setter: \src{snippet32} Here, \code{a} is usually some product data type with a field of type \code{s}. The getter retrieves the value of that field and the setter replaces this field with a new value. These two functions can be combined into one: \src{snippet33} We can rewrite this function further as: \src{snippet34} where we have defined a functor: \src{snippet35} Notice that this is not a simple algebraic functor constructed from sums of products. It involves an exponential $a^s$. A lens is a coalgebra for this functor with the carrier type \code{a}. We've seen before that \code{Store s} is also a comonad. It turns out that a very well behaved lens corresponds to a coalgebra that is compatible with the comonad structure. We'll talk about this in the next section. \section{Challenges} \begin{enumerate} \tightlist \item Implement the evaluation function for a ring of polynomials of one variable. You can represent a polynomial as a list of coefficients in front of powers of $x$. For instance, $4x^2-1$ would be represented as (starting with the zero'th power) \code{{[}-1, 0, 4{]}}. \item Generalize the previous construction to polynomials of many independent variables, like $x^2y-3y^3z$. \item Implement the algebra for the ring of $2\times{}2$ matrices. \item Define a coalgebra whose anamorphism produces a list of squares of natural numbers. \item Use \code{unfoldr} to generate a list of the first $n$ primes. \end{enumerate} ================================================ FILE: src/content/3.9/algebras-for-monads.tex ================================================ % !TEX root = ../../ctfp-print.tex \lettrine[lhang=0.17]{I}{f we interpret} endofunctors as ways of defining expressions, algebras let us evaluate them and monads let us form and manipulate them. By combining algebras with monads we not only gain a lot of functionality but we can also answer a few interesting questions. One such question concerns the relation between monads and adjunctions. As we've seen, every adjunction \hyperref[monads-categorically]{defines a monad} (and a comonad). The question is: Can every monad (comonad) be derived from an adjunction? The answer is positive. There is a whole family of adjunctions that generate a given monad. I'll show you two such adjunctions. \begin{figure}[H] \centering \includegraphics[width=0.25\textwidth]{images/pigalg.png} \end{figure} \noindent Let's review the definitions. A monad is an endofunctor $m$ equipped with two natural transformations that satisfy some coherence conditions. The components of these transformations at $a$ are: \begin{align*} \eta_a & \Colon a \to m\ a \\ \mu_a & \Colon m\ (m\ a) \to m\ a \end{align*} An algebra for the same endofunctor is a selection of a particular object --- the carrier $a$ --- together with the morphism: \[\mathit{alg} \Colon m\ a \to a\] The first thing to notice is that the algebra goes in the opposite direction to $\eta_a$. The intuition is that $\eta_a$ creates a trivial expression from a value of type $a$. The first coherence condition that makes the algebra compatible with the monad ensures that evaluating this expression using the algebra whose carrier is $a$ gives us back the original value: \[\mathit{alg} \circ \eta_a = \id_a\] The second condition arises from the fact that there are two ways of evaluating the doubly nested expression $m\ (m\ a)$. We can first apply $\mu_a$ to flatten the expression, and then use the evaluator of the algebra; or we can apply the lifted evaluator to evaluate the inner expressions, and then apply the evaluator to the result. We'd like the two strategies to be equivalent: \[\mathit{alg} \circ \mu_a = \mathit{alg} \circ m\ \mathit{alg}\] Here, $m\ \mathit{alg}$ is the morphism resulting from lifting $\mathit{alg}$ using the functor $m$. The following commuting diagrams describe the two conditions (I replaced $m$ with $T$ in anticipation of what follows): \begin{figure}[H] \centering \begin{subfigure} \centering \begin{tikzcd}[column sep=large, row sep=large] a \arrow[rd, equal] \arrow[r, "\eta_a"] & Ta \arrow[d, "\mathit{alg}"] \\ & a \end{tikzcd} \end{subfigure} \hspace{1cm} \begin{subfigure} \centering \begin{tikzcd}[column sep=large, row sep=large] T(Ta) \arrow[r, "T\ \mathit{alg}"] \arrow[d, "\mu_a"] & Ta \arrow[d, "\mathit{alg}"] \\ Ta \arrow[r, "\mathit{alg}"] & a \end{tikzcd} \end{subfigure} \end{figure} \noindent We can also express these conditions in Haskell: \src{snippet01} Let's look at a small example. An algebra for a list endofunctor consists of some type \code{a} and a function that produces an \code{a} from a list of \code{a}. Suppose that we can express this function using \code{foldr} by choosing both the element type and the accumulator type to be equal to \code{a}: \src{snippet02} This particular algebra is specified by a two-argument function, let's call it \code{f}, and a value \code{z}. The list functor happens to also be a monad, with \code{return} turning a value into a singleton list. The composition of the algebra, here \code{foldr f z}, after \code{return} takes \code{x} to: \src{snippet03} where the action of \code{f} is written in the infix notation. The algebra is compatible with the monad if the following coherence condition is satisfied for every \code{x}: \src{snippet04} If we look at \code{f} as a binary operator, this condition tells us that \code{z} is the right unit. The second coherence condition operates on a list of lists. The action of \code{join} concatenates the individual lists. We can then fold the resulting list. On the other hand, we can first fold the individual lists, and then fold the resulting list. Again, if we interpret \code{f} as a binary operator, this condition tells us that this binary operation is associative. These conditions are certainly fulfilled when \code{(a, f, z)} is a monoid. \section{$T$-algebras} Since mathematicians prefer to call their monads $T$, they call algebras compatible with them $T$-algebras. $T$-algebras for a given monad $T$ in a category $\cat{C}$ form a category called the Eilenberg-Moore category, often denoted by $\cat{C}^T$. Morphisms in that category are homomorphisms of algebras. These are the same homomorphisms we've seen defined for $F$-algebras. A $T$-algebra is a pair consisting of a carrier object and an evaluator, $(a, f)$. There is an obvious forgetful functor $U^T$ from $\cat{C}^T$ to $\cat{C}$, which maps $(a, f)$ to $a$. It also maps a homomorphism of $T$-algebras to a corresponding morphism between carrier objects in $\cat{C}$. You may remember from our discussion of adjunctions that the left adjoint to a forgetful functor is called a free functor. The left adjoint to $U^T$ is called $F^T$. It maps an object $a$ in $\cat{C}$ to a free algebra in $\cat{C}^T$. The carrier of this free algebra is $T a$. Its evaluator is a morphism from $T (T a)$ back to $T a$. Since $T$ is a monad, we can use the monadic $\mu_a$ (\code{join} in Haskell) as the evaluator. We still have to show that this is a $T$-algebra. For that, two coherence conditions must be satisfied: \begin{align*} \mathit{alg} & \circ \eta_{Ta} = \id_{Ta} \\ \mathit{alg} & \circ \mu_a = \mathit{alg} \circ T\ \mathit{alg} \end{align*} But these are just monadic laws, if you plug in $\mu$ for the algebra. As you may recall, every adjunction defines a monad. It turns out that the adjunction between $F^T$ and $U^T$ defines the very monad $T$ that was used in the construction of the Eilenberg-Moore category. Since we can perform this construction for every monad, we conclude that every monad can be generated from an adjunction. Later I'll show you that there is another adjunction that generates the same monad. Here's the plan: First I'll show you that $F^T$ is indeed the left adjoint of $U^T$. I'll do it by defining the unit and the counit of this adjunction and proving that the corresponding triangular identities are satisfied. Then I'll show you that the monad generated by this adjunction is indeed our original monad. The unit of the adjunction is the natural transformation: \[\eta \Colon I \to U^T \circ F^T\] Let's calculate the $a$ component of this transformation. The identity functor gives us $a$. The free functor produces the free algebra $(T a, \mu_a)$, and the forgetful functor reduces it to $T a$. Altogether we get a mapping from $a$ to $T a$. We'll simply use the unit of the monad $T$ as the unit of this adjunction. Let's look at the counit: \[\varepsilon \Colon F^T \circ U^T \to I\] Let's calculate its component at some $T$-algebra $(a, f)$. The forgetful functor forgets the $f$, and the free functor produces the pair $(T a, \mu_a)$. So in order to define the component of the counit $\varepsilon$ at $(a, f)$, we need the right morphism in the Eilenberg-Moore category, or a homomorphism of $T$-algebras: \[(T a, \mu_a) \to (a, f)\] Such a homomorphism should map the carrier $T a$ to $a$. Let's just resurrect the forgotten evaluator $f$. This time we'll use it as a homomorphism of $T$-algebras. Indeed, the same commuting diagram that makes $f$ a $T$-algebra may be re-interpreted to show that it's a homomorphism of $T$-algebras: \begin{figure}[H] \centering \begin{tikzcd}[column sep=large, row sep=large] T(Ta) \arrow[r, "T f"] \arrow[d, "\mu_a"] & Ta \arrow[d, "f"] \\ Ta \arrow[r, "f"] & a \end{tikzcd} \end{figure} \noindent We have thus defined the component of the counit natural transformation $\varepsilon$ at $(a, f)$ (an object in the category of $T$-algebras) to be $f$. To complete the adjunction we also need to show that the unit and the counit satisfy triangular identities. These are: \begin{figure}[H] \centering \begin{subfigure} \centering \begin{tikzcd}[column sep=large, row sep=large] Ta \arrow[rd, equal] \arrow[r, "T \eta_a"] & T(Ta) \arrow[d, "\mu_a"] \\ & Ta \end{tikzcd} \end{subfigure}% \hspace{1cm} \begin{subfigure} \centering \begin{tikzcd}[column sep=large, row sep=large] a \arrow[rd, equal] \arrow[r, "\eta_a"] & Ta \arrow[d, "f"] \\ & a \end{tikzcd} \end{subfigure} \end{figure} \noindent The first one holds because of the unit law for the monad $T$. The second is just the law of the $T$-algebra $(a, f)$. We have established that the two functors form an adjunction: \[F^T \dashv U^T\] Every adjunction gives rise to a monad. The round trip \[U^T \circ F^T\] is the endofunctor in $\cat{C}$ that gives rise to the corresponding monad. Let's see what its action on an object $a$ is. The free algebra created by $F^T$ is $(T a, \mu_a)$. The forgetful functor $U^T$ drops the evaluator. So, indeed, we have: \[U^T \circ F^T = T\] As expected, the unit of the adjunction is the unit of the monad $T$. You may remember that the counit of the adjunction produces monadic multiplication through the following formula: \[\mu = R \circ \varepsilon \circ L\] This is a horizontal composition of three natural transformations, two of them being identity natural transformations mapping, respectively, $L$ to $L$ and $R$ to $R$. The one in the middle, the counit, is a natural transformation whose component at an algebra $(a, f)$ is $f$. Let's calculate the component $\mu_a$. We first horizontally compose $\varepsilon$ after $F^T$, which results in the component of $\varepsilon$ at $F^T a$. Since $F^T$ takes $a$ to the algebra $(T a, \mu_a)$, and $\varepsilon$ picks the evaluator, we end up with $\mu_a$. Horizontal composition on the left with $U^T$ doesn't change anything, since the action of $U^T$ on morphisms is trivial. So, indeed, the $\mu$ obtained from the adjunction is the same as the $\mu$ of the original monad $T$. \section{The Kleisli Category} We've seen the Kleisli category before. It's a category constructed from another category $\cat{C}$ and a monad $T$. We'll call this category $\cat{C}_T$. The objects in the Kleisli category $\cat{C}_T$ are the objects of $\cat{C}$, but the morphisms are different. A morphism $f_{\cat{K}}$ from $a$ to $b$ in the Kleisli category corresponds to a morphism $f$ from $a$ to $T b$ in the original category. We call this morphism a Kleisli arrow from $a$ to $b$. Composition of morphisms in the Kleisli category is defined in terms of monadic composition of Kleisli arrows. For instance, let's compose $g_{\cat{K}}$ after $f_{\cat{K}}$. In the Kleisli category we have: \begin{gather*} f_{\cat{K}} \Colon a \to b \\ g_{\cat{K}} \Colon b \to c \end{gather*} which, in the category $\cat{C}$, corresponds to: \begin{gather*} f \Colon a \to T b \\ g \Colon b \to T c \end{gather*} We define the composition: \[h_{\cat{K}} = g_{\cat{K}} \circ f_{\cat{K}}\] as a Kleisli arrow in $\cat{C}$ \begin{align*} h & \Colon a \to T c \\ h & = \mu \circ (T g) \circ f \end{align*} In Haskell we would write it as: \src{snippet05} There is a functor $F$ from $\cat{C}$ to $\cat{C}_T$ which acts trivially on objects. On morphisms, it maps $f$ in $\cat{C}$ to a morphism in $\cat{C}_T$ by creating a Kleisli arrow that embellishes the return value of $f$. Given a morphism: \[f \Colon a \to b\] it creates a morphism in $\cat{C}_T$ with the corresponding Kleisli arrow: \[\eta \circ f\] In Haskell we'd write it as: \src{snippet06} We can also define a functor $G$ from $\cat{C}_T$ back to $\cat{C}$. It takes an object $a$ from the Kleisli category and maps it to an object $T a$ in $\cat{C}$. Its action on a morphism $f_{\cat{K}}$ corresponding to a Kleisli arrow: \[f \Colon a \to T b\] is a morphism in $\cat{C}$: \[T a \to T b\] given by first lifting $f$ and then applying $\mu$: \[\mu_{T b} \circ T f\] In Haskell notation this would read: \begin{snipv} G f\textsubscript{T} = join . fmap f \end{snipv} You may recognize this as the definition of monadic bind in terms of \code{join}. It's easy to see that the two functors form an adjunction: \[F \dashv G\] and their composition $G \circ F$ reproduces the original monad $T$. So this is the second adjunction that produces the same monad. In fact there is a whole category of adjunctions $\cat{Adj}(\cat{C}, T)$ that result in the same monad $T$ on $\cat{C}$. The Kleisli adjunction we've just seen is the initial object in this category, and the Eilenberg-Moore adjunction is the terminal object. \section{Coalgebras for Comonads} Analogous constructions can be done for any \hyperref[comonads]{comonad} $W$. We can define a category of coalgebras that are compatible with a comonad. They make the following diagrams commute: \begin{figure}[H] \centering \begin{subfigure} \centering \begin{tikzcd}[column sep=large, row sep=large] a \arrow[rd, equal] & Wa \arrow[l, "\varepsilon_a"'] \\ & a \arrow[u, "\mathit{coa}"'] \end{tikzcd} \end{subfigure}% \hspace{1cm} \begin{subfigure} \centering \begin{tikzcd}[column sep=large, row sep=large] W(Wa) & Wa \arrow[l, "W\ \mathit{coa}"'] \\ Wa \arrow[u, "\delta_a"] & a \arrow[u, "\mathit{coa}"] \arrow[l, "\mathit{coa}"'] \end{tikzcd} \end{subfigure} \end{figure} \noindent where $\mathit{coa}$ is the coevaluation morphism of the coalgebra whose carrier is $a$: \[\mathit{coa} \Colon a \to W a\] and $\varepsilon$ and $\delta$ are the two natural transformations defining the comonad (in Haskell, their components are called \code{extract} and \code{duplicate}). There is an obvious forgetful functor $U^W$ from the category of these coalgebras to $\cat{C}$. It just forgets the coevaluation. We'll consider its right adjoint $F^W$. \[U^W \dashv F^W\] The right adjoint to a forgetful functor is called a cofree functor. $F^W$ generates cofree coalgebras. It assigns, to an object $a$ in $\cat{C}$, the coalgebra $(W a, \delta_a)$. The adjunction reproduces the original comonad as the composite $U^W \circ F^W$. Similarly, we can construct a co-Kleisli category with co-Kleisli arrows and regenerate the comonad from the corresponding adjunction. \section{Lenses} Let's go back to our discussion of lenses. A lens can be written as a coalgebra: \[\mathit{coalg}_s \Colon a \to \mathit{Store}\ s\ a\] for the functor $\mathit{Store}\ s$: \src{snippet07} This coalgebra can be also expressed as a pair of functions: \begin{align*} \mathit{set} & \Colon a \to s \to a \\ \mathit{get} & \Colon a \to s \end{align*} (Think of $a$ as standing for ``all,'' and $s$ as a ``small'' part of it.) In terms of this pair, we have: \[\mathit{coalg}_s\ a = \mathit{Store}\ (\mathit{set}\ a)\ (\mathit{get}\ a)\] Here, $a$ is a value of type $a$. Notice that partially applied \code{set} is a function $s \to a$. We also know that $\mathit{Store}\ s$ is a comonad: \src{snippet08} The question is: Under what conditions is a lens a coalgebra for this comonad? The first coherence condition: \[\varepsilon_a \circ \mathit{coalg} = \idarrow[a]\] translates to: \src{snippet16} This is the lens law that expresses the fact that if you set a field of the structure $a$ to its previous value, nothing changes. This law is also known as GETPUT. The second condition: \[\mathit{fmap}\ \mathit{coalg} \circ \mathit{coalg} = \delta_a \circ \mathit{coalg}\] requires a little more work. First, recall the definition of \code{fmap} for the \code{Store} functor: \src{snippet09} Applying \code{fmap coalg} to the result of \code{coalg} gives us: \src{snippet10} On the other hand, applying \code{duplicate} to the result of \code{coalg} produces: \src{snippet11} For these two expressions to be equal, the two functions under \code{Store} must be equal when acting on an arbitrary \code{s}: \src{snippet12} Expanding \code{coalg}, we get: \src{snippet13} This is equivalent to two remaining lens laws. The first one: \src{snippet14} tells us that setting the value of a field twice is the same as setting it once. This law is also known as PUTPUT. The second law: \src{snippet15} tells us that getting a value of a field that was set to $s$ gives $s$ back. This law is also known as PUTGET. A lens that satisfies GETPUT and PUTGET is called a well-behaved lens. If the lens also satisfies PUTPUT, it is called a very well behaved lens. So, in other words, a very well behaved lens is indeed a comonad coalgebra for the \code{Store} functor. \section{Challenges} \begin{enumerate} \tightlist \item What is the action of the free functor $F^T \Colon C \to C^T$ on morphisms. Hint: use the naturality condition for monadic $\mu$. \item Define the adjunction: \[U^W \dashv F^W\] \item Prove that the above adjunction reproduces the original comonad. \end{enumerate} ================================================ FILE: src/content/3.9/code/haskell/snippet01.hs ================================================ alg . return = id alg . join = alg . fmap alg ================================================ FILE: src/content/3.9/code/haskell/snippet02.hs ================================================ foldr :: (a -> a -> a) -> a -> [a] -> a ================================================ FILE: src/content/3.9/code/haskell/snippet03.hs ================================================ foldr f z [x] = x `f` z ================================================ FILE: src/content/3.9/code/haskell/snippet04.hs ================================================ x `f` z = x ================================================ FILE: src/content/3.9/code/haskell/snippet05.hs ================================================ h = join . fmap g . f ================================================ FILE: src/content/3.9/code/haskell/snippet06.hs ================================================ return . f ================================================ FILE: src/content/3.9/code/haskell/snippet07.hs ================================================ data Store s a = Store (s -> a) s ================================================ FILE: src/content/3.9/code/haskell/snippet08.hs ================================================ instance Comonad (Store s) where extract (Store f s) = f s duplicate (Store f s) = Store (Store f) s ================================================ FILE: src/content/3.9/code/haskell/snippet09.hs ================================================ fmap g (Store f s) = Store (g . f) s ================================================ FILE: src/content/3.9/code/haskell/snippet10.hs ================================================ Store (coalg . set a) (get a) ================================================ FILE: src/content/3.9/code/haskell/snippet11.hs ================================================ Store (Store (set a)) (get a) ================================================ FILE: src/content/3.9/code/haskell/snippet12.hs ================================================ coalg (set a s) = Store (set a) s ================================================ FILE: src/content/3.9/code/haskell/snippet13.hs ================================================ Store (set (set a s)) (get (set a s)) = Store (set a) s ================================================ FILE: src/content/3.9/code/haskell/snippet14.hs ================================================ set (set a s) = set a ================================================ FILE: src/content/3.9/code/haskell/snippet15.hs ================================================ get (set a s) = s ================================================ FILE: src/content/3.9/code/haskell/snippet16.hs ================================================ set a (get a) = a ================================================ FILE: src/content/3.9/code/ocaml/snippet01.ml ================================================ ;; compose alg return = id compose alg join = compose alg (fmap alg) ================================================ FILE: src/content/3.9/code/ocaml/snippet02.ml ================================================ val fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b ================================================ FILE: src/content/3.9/code/ocaml/snippet03.ml ================================================ (* List module in the OCaml standard library accepts list before z *) ;; List.fold_right f [ x ] z = f x z ================================================ FILE: src/content/3.9/code/ocaml/snippet04.ml ================================================ ;; f x z = x ================================================ FILE: src/content/3.9/code/ocaml/snippet05.ml ================================================ module Kleisli_Composition (T : MonadJoin) = struct let h g f = T.join <.> T.fmap g <.> f end ================================================ FILE: src/content/3.9/code/ocaml/snippet06.ml ================================================ module C_to_CT (T : Monad) = struct let on_objects = T.return <.> f end ================================================ FILE: src/content/3.9/code/ocaml/snippet07.ml ================================================ type ('s, 'a) store = Store of ('s -> 'a) * 's ================================================ FILE: src/content/3.9/code/ocaml/snippet08.ml ================================================ module Store_comonad (S : sig type s end) (F : Functor with type 'a t = (S.s, 'a) store) : Comonad with type 'a w = (S.s, 'a) store = struct type 'a w = (S.s, 'a) store include F let extract : 'a w -> 'a = fun (Store (f, s)) -> f s let duplicate : 'a w -> 'a w w = fun (Store (f, s)) -> Store ((fun s -> Store (f, s)), s) ;; end ================================================ FILE: src/content/3.9/code/ocaml/snippet09.ml ================================================ module Store_Functor (S : sig type s end) : Functor with type 'a t = (S.s, 'a) store = struct type 'a w = (S.s, 'a) store type 'a t = 'a w let fmap g (Store (f, s)) = Store (compose g f, s) end ================================================ FILE: src/content/3.9/code/ocaml/snippet10.ml ================================================ (* Assume <.> acts as compose *) ;; Store (coalg_store <.> set a, get a) ================================================ FILE: src/content/3.9/code/ocaml/snippet11.ml ================================================ ;; Store ((fun s -> Store (set a, s)), get a) ================================================ FILE: src/content/3.9/code/ocaml/snippet12.ml ================================================ (* Pseudo OCaml *) let coalg_store (set a s) = Store ((set a), s) ================================================ FILE: src/content/3.9/code/ocaml/snippet13.ml ================================================ (* Expaning coalg_store *) ;; Store (set (set a s), get (set a s)) = Store (set a, s) ================================================ FILE: src/content/3.9/code/ocaml/snippet14.ml ================================================ ;; set (set a s) = set a ================================================ FILE: src/content/3.9/code/ocaml/snippet15.ml ================================================ ;; get (set a s) = s ================================================ FILE: src/content/3.9/code/ocaml/snippet16.ml ================================================ ;; set a (get a) = a ================================================ FILE: src/content/3.9/code/reason/snippet01.re ================================================ compose(alg, return) == id(compose, alg, join) == compose(alg, fmap(alg)); ================================================ FILE: src/content/3.9/code/reason/snippet02.re ================================================ let fold_right: (('a, 'b) => 'b, list('a), 'b) => 'b; ================================================ FILE: src/content/3.9/code/reason/snippet03.re ================================================ /* List module in the OCaml standard library accepts list before z */ List.fold_right(f, [x], z) == f(x, z); ================================================ FILE: src/content/3.9/code/reason/snippet04.re ================================================ f(x, z) == x; ================================================ FILE: src/content/3.9/code/reason/snippet05.re ================================================ module Kleisli_Composition = (T: MonadJoin) => { let h = (g, f) => T.join <.> T.fmap(g) <.> f; }; ================================================ FILE: src/content/3.9/code/reason/snippet06.re ================================================ module C_to_CT = (T: Monad) => { let on_objects = T.return <.> f; }; ================================================ FILE: src/content/3.9/code/reason/snippet07.re ================================================ type store('s, 'a) = | Store('s => 'a, 's); ================================================ FILE: src/content/3.9/code/reason/snippet08.re ================================================ module Store_comonad = (S: {type s;}, F: Functor with type t('a) = store(S.s, 'a)) : (Comonad with type w('a) = store(S.s, 'a)) => { type w('a) = store(S.s, 'a); include F; let extract: w('a) => 'a = ((Store(f, s)) => f(s): w('a) => 'a); let duplicate: w('a) => w(w('a)) = ( (Store(f, s)) => Store(s => Store(f, s), s): w('a) => w(w('a)) ); }; ================================================ FILE: src/content/3.9/code/reason/snippet09.re ================================================ module Store_Functor = (S: {type s;}) : (Functor with type t('a) = store(S.s, 'a)) => { type w('a) = store(S.s, 'a); type t('a) = w('a); let fmap = (g, Store(f, s)) => Store(compose(g, f), s); }; ================================================ FILE: src/content/3.9/code/reason/snippet10.re ================================================ /* Assume <.> acts as compose */ Store(coalg_store <.> set(a), get(a)); ================================================ FILE: src/content/3.9/code/reason/snippet11.re ================================================ Store(s => Store(set(a), s), get(a)); ================================================ FILE: src/content/3.9/code/reason/snippet12.re ================================================ /* Pseudo ReasonML */ let coalg_store = (set, a, s) => Store((set(a)), s) ================================================ FILE: src/content/3.9/code/reason/snippet13.re ================================================ /* Expaning coalg_store */ Store(set(set(a, s)), get(set(a, s))) == Store(set(a), s); ================================================ FILE: src/content/3.9/code/reason/snippet14.re ================================================ set(set(a, s)) == set(a); ================================================ FILE: src/content/3.9/code/reason/snippet15.re ================================================ get(set(a, s)) == s; ================================================ FILE: src/content/3.9/code/reason/snippet16.re ================================================ set(a, get(a))) == a; ================================================ FILE: src/content/3.9/code/scala/snippet01.scala ================================================ alg compose pure == id alg compose flatten == alg compose fmap(alg) ================================================ FILE: src/content/3.9/code/scala/snippet02.scala ================================================ def foldr[A]: (A => A => A) => A => List[A] => A ================================================ FILE: src/content/3.9/code/scala/snippet03.scala ================================================ def foldr[A]: (A => A => A) => A => List[A] => A = f => z => { case x :: Nil => f(x)(z) } ================================================ FILE: src/content/3.9/code/scala/snippet04.scala ================================================ f(x)(z) == x ================================================ FILE: src/content/3.9/code/scala/snippet05.scala ================================================ h == flatten.compose(fmap(g) _ compose f) ================================================ FILE: src/content/3.9/code/scala/snippet06.scala ================================================ unit compose f ================================================ FILE: src/content/3.9/code/scala/snippet07.scala ================================================ case class Store[S, A](run: S => A, s: S) // convenient partially applied constructor object Store { def apply[S, A](run: S => A): S => Store[S, A] = s => new Store(run, s) } ================================================ FILE: src/content/3.9/code/scala/snippet08.scala ================================================ implicit def storeComonad[S] = new Comonad[Store[S, ?]] { def extract[A](wa: Store[S, A]): A = wa match { case Store(f, s) => f(s) } def duplicate[A](wa: Store[S, A]): Store[S, Store[S, A]] = wa match { case Store(f, s) => Store(Store(f), s) } } ================================================ FILE: src/content/3.9/code/scala/snippet09.scala ================================================ implicit def storeFunctor[S] = new Functor[Store[S, ?]] { def fmap[A, B](g: A => B)(fa: Store[S, A]): Store[S, B] = fa match { case Store(f, s) => Store(g compose f, s) } } ================================================ FILE: src/content/3.9/code/scala/snippet10.scala ================================================ Store(coalg compose set(a), get(a)) ================================================ FILE: src/content/3.9/code/scala/snippet11.scala ================================================ Store(Store(set(a)), get(a)) ================================================ FILE: src/content/3.9/code/scala/snippet12.scala ================================================ coalg(set(a)(s)) == Store(set(a))(s) ================================================ FILE: src/content/3.9/code/scala/snippet13.scala ================================================ Store(set(set(a)(s)))(get(set(a)(s))) == Store(set(a))(s) ================================================ FILE: src/content/3.9/code/scala/snippet14.scala ================================================ set(set(a)(s)) == set(a) ================================================ FILE: src/content/3.9/code/scala/snippet15.scala ================================================ get(set(a)(s)) == s ================================================ FILE: src/content/3.9/code/scala/snippet16.scala ================================================ set(a)(get(a)) == a ================================================ FILE: src/content/editor-note.tex ================================================ % !TEX root = ../ctfp-print.tex \ifdefined\OPTCustomLanguage{% \chapter*{A note from the editor} \addcontentsline{toc}{chapter}{A note from the editor} \input{content/\OPTCustomLanguage/editor-note} } \fi ================================================ FILE: src/content/ocaml/colophon.tex ================================================ OCaml code translation was done by \urlref{https://github.com/ArulselvanMadhavan/ocaml-ctfp}{Arulselvan Madhavan} and reviewed by \urlref{http://www.mseri.me}{Marcello Seri} and \urlref{https://github.com/XVilka}{Anton Kochkov}. ================================================ FILE: src/content/ocaml/editor-note.tex ================================================ % !TEX root = ctfp-print.tex \lettrine[lhang=0.17]{T}{his is the} OCaml edition of \emph{Category Theory for Programmers}. It's been a tremendous success, making Bartosz Milewski's blog post series available as a nicely- typeset \acronym{PDF}, as well as a hardcover book. There have been numerous contributions made to improve the book, by fixing typos and errors, as well as translating the code snippets into other programming languages. I am thrilled to present this edition of the book, containing the original Haskell code, followed by its OCaml counterpart. The OCaml code snippets were generously provided by \urlref{https://github.com/ArulselvanMadhavan/ocaml-ctfp}{ocaml-ctfp} contributors, slightly modified to suit the format of this book. To support code snippets in multiple languages, I am using a \LaTeX{} macro to load the code snippets from external files. This allows easily extending the book with other languages, while leaving the original text intact. Which is why you should mentally append the words ``and OCaml'' whenever you see ``in Haskell'' in the text. The code is laid out in the following manner: the original Haskell code, followed by OCaml code. To distinguish between them, the code snippets are braced from the left with a vertical bar, in the primary color of the language's logo, \raisebox{-.2mm}{\includegraphics[height=.3cm]{fig/icons/haskell.png}}, and \raisebox{-.2mm}{\includegraphics[height=.3cm]{fig/icons/ocaml.png}} respectively, e.g.: \srcsnippet{content/1.1/code/haskell/snippet03.hs}{blue}{haskell} \unskip \srcsnippet{content/1.1/code/ocaml/snippet03.ml}{orange}{ocaml} \NoIndentAfterThis ================================================ FILE: src/content/reason/colophon.tex ================================================ ReasonML code translation was done by \urlref{https://github.com/fhammerschmidt/}{Florian Hammerschmidt}. ================================================ FILE: src/content/reason/editor-note.tex ================================================ % !TEX root = ctfp-print.tex \lettrine[lhang=0.17]{T}{his is the} ReasonML edition of \emph{Category Theory for Programmers}. It's been a tremendous success, making Bartosz Milewski's blog post series available as a nicely- typeset \acronym{PDF}, as well as a hardcover book. There have been numerous contributions made to improve the book, by fixing typos and errors, as well as translating the code snippets into other programming languages. I am thrilled to present this edition of the book, containing the original Haskell code, followed by its ReasonML counterpart. The ReasonML code snippets were converted from the OCaml snippets which were generously provided by \urlref{https://github.com/ArulselvanMadhavan/ocaml-ctfp}{ocaml-ctfp} contributors, and slightly modified to suit the format of this book. To support code snippets in multiple languages, I am using a \LaTeX{} macro to load the code snippets from external files. This allows easily extending the book with other languages, while leaving the original text intact. Which is why you should mentally append the words ``and ReasonML'' whenever you see ``in Haskell'' in the text. The code is laid out in the following manner: the original Haskell code, followed by ReasonML code. To distinguish between them, the code snippets are braced from the left with a vertical bar, in the primary color of the language's logo, \raisebox{-.2mm}{\includegraphics[height=.3cm]{fig/icons/haskell.png}}, and \raisebox{-.2mm}{\includegraphics[height=.3cm]{fig/icons/reason.png}} respectively, e.g.: \srcsnippet{content/1.1/code/haskell/snippet03.hs}{blue}{haskell} \unskip \srcsnippet{content/1.1/code/reason/snippet03.re}{RedOrange}{reason} \NoIndentAfterThis ================================================ FILE: src/content/scala/colophon.tex ================================================ Scala code translation was done by \urlref{https://github.com/typelevel/CT_from_Programmers.scala}{Typelevel} contributors. ================================================ FILE: src/content/scala/editor-note.tex ================================================ % !TEX root = ctfp-print.tex \lettrine[lhang=0.17]{T}{his is the Scala edition} of \emph{Category Theory for Programmers}. It's been a tremendous success, making Bartosz Milewski's blog post series available as a nicely typeset \acronym{PDF}, as well as a hardcover book. There have been numerous contributions made to improve the book, by fixing typos and errors, as well as translating the code snippets into other programming languages. I am thrilled to present this edition of the book, containing the original Haskell code, followed by its Scala counterpart. The Scala code snippets were generously provided by \urlref{https://github.com/typelevel/CT_from_Programmers.scala}{Typelevel} contributors, slightly modified to suit the format of this book. To support code snippets in multiple languages, I am using a \LaTeX{} macro to load the code snippets from external files. This allows easily extending the book with other languages, while leaving the original text intact. Which is why you should mentally append the words ``and Scala'' whenever you see ``in Haskell'' in the text. The code is laid out in the following manner: the original Haskell code, followed by Scala code. To distinguish between them, the code snippets are braced from the left with a vertical bar, in the primary color of the language's logo, \raisebox{-.2mm}{\includegraphics[height=.3cm]{fig/icons/haskell.png}}, and \raisebox{-.2mm}{\includegraphics[height=.3cm]{fig/icons/scala.png}} respectively, e.g.: \srcsnippet{content/3.6/code/haskell/snippet03.hs}{blue}{haskell} \unskip \srcsnippet{content/3.6/code/scala/snippet03.scala}{red}{scala} \NoIndentAfterThis In addition, some Scala snippets make use of the \urlref{https://github.com/non/kind-projector}{Kind Projector} compiler plugin, to support nicer syntax for partially-applied types. ================================================ FILE: src/cover/blurb.tex ================================================ % !TEX root = cover-blurb.tex % Blurb on back cover { \fontsize{14}{17}\selectfont \scshape{Category Theory}\normalfont\sffamily{} is one of the most abstract branches of mathematics. It is usually taught to graduate students after they have mastered several other branches of mathematics, like algebra, topology, and group theory. It might, therefore, come as a shock that the basic concepts of category theory can be explained in relatively simple terms to anybody with some experience in programming. \\ That's because, just like programming, category theory is about structure. Mathematicians discover structure in mathematical theories, programmers discover structure in computer programs. Well-structured programs are easier to understand and maintain and are less likely to contain bugs. Category theory provides the language to talk about structure and learning it will make you a better programmer. \vfil } ================================================ FILE: src/cover/cover-hardcover-ocaml.tex ================================================ \documentclass[ coverheight=9.249in, coverwidth=6.319in, % (pagesize - spinewidth) / 2 spinewidth=1.125in, bleedwidth=0.306in, 11pt, marklength=0pt, ]{bookcover} \usepackage{fancybox} \usepackage{wrapfig} \usepackage[many]{tcolorbox} \usetikzlibrary{calc,positioning, shadings} \usepackage[T1]{fontenc} \usepackage{fontspec} \usepackage{Alegreya} \newcommand{\olpath}{../} \newcommand{\whitebg}[1]{% \tikz\node[circle,draw,minimum size=1.1cm, fill=white, path picture={ \node at (path picture bounding box){ \includegraphics[width=1.1cm]{\olpath#1} }; }]{}; } \newcommand{\bartosz}{ \vspace{0pt} \begin{tcolorbox}[beamer, width=3.6cm, arc=0pt, boxsep=0pt, left=0pt,right=0pt,top=0pt,bottom=0pt, ] \includegraphics[width=3.6cm]{bartosz} \end{tcolorbox} } \input{\olpath/version} \definecolor{BackgroundColor}{HTML}{f3f6ed} \definecolor{SpineBackColor}{HTML}{262626} \begin{document} \begin{bookcover} \bookcovercomponent{color}{bg whole}{color=BackgroundColor} \bookcovercomponent{color}{spine}{color=SpineBackColor} \bookcovercomponent{normal}{front}{ \input{ribbon-ocaml} \vspace{1.1cm} \begin{center} \fontsize{40pt}{5em}\selectfont\bfseries CATEGORY THEORY \\FOR PROGRAMMERS \vfil \hspace*{-.8cm}\includegraphics[width=.5\coverwidth]{piggie} \linebreak \rule[1.5cm]{\textwidth/2}{.5pt}\\ \vspace{-1.5cm} \normalfont\Huge\textbf{Bartosz Milewski} \vfil \vspace*{1cm} \end{center}} \bookcovercomponent{center}{spine}{ \rotatebox[origin=c]{-90}{\color{orange} \Huge\bfseries Category Theory for Programmers \hspace{2em} Bartosz Milewski}} \bookcovercomponent{normal}{back}{% \begin{minipage}[b][\coverheight][t]{\coverwidth} \begin{center} \vspace{1cm} \includegraphics[width=.8\coverwidth]{bunnies} \begin{minipage}[t]{.8\coverwidth} \input{blurb} \vspace{.5cm} \end{minipage} \begin{minipage}{.85\textwidth} \rule{\textwidth}{.5pt} \begin{tabular}[h]{p{3.4cm} p{\textwidth}} \bartosz & \vspace{5pt} \begin{minipage}[b]{.58\coverwidth} \fontsize{11pt}{1.4em}\selectfont\textit{Category Theory for Programmers} is a series of blog posts by Bartosz Milewski, originally posted on bartoszmilewski.com.\\ Edited by Igal Tabachnik. Licenced under CC BY-SA 4.0.\\ \end{minipage} \end{tabular} \begin{flushright} \vspace{-2.6cm} \begin{minipage}[b]{4cm} \raggedleft \whitebg{fig/icons/by} \whitebg{fig/icons/cc} \whitebg{fig/icons/sa} \centering\footnotesize{\texttt{\OPTversion}} \end{minipage} \end{flushright} \end{minipage} \end{center} \end{minipage} } \end{bookcover} \end{document} ================================================ FILE: src/cover/cover-hardcover-reason.tex ================================================ \documentclass[ coverheight=9.249in, coverwidth=6.319in, % (pagesize - spinewidth) / 2 spinewidth=1.125in, bleedwidth=0.306in, 11pt, marklength=0pt, ]{bookcover} \usepackage{fancybox} \usepackage{wrapfig} \usepackage[many]{tcolorbox} \usetikzlibrary{calc,positioning, shadings} \usepackage[T1]{fontenc} \usepackage{fontspec} \usepackage{Alegreya} \newcommand{\olpath}{../} \newcommand{\whitebg}[1]{% \tikz\node[circle,draw,minimum size=1.1cm, fill=white, path picture={ \node at (path picture bounding box){ \includegraphics[width=1.1cm]{\olpath#1} }; }]{}; } \newcommand{\bartosz}{ \vspace{0pt} \begin{tcolorbox}[beamer, width=3.6cm, arc=0pt, boxsep=0pt, left=0pt,right=0pt,top=0pt,bottom=0pt, ] \includegraphics[width=3.6cm]{bartosz} \end{tcolorbox} } \input{\olpath/version} \definecolor{BackgroundColor}{HTML}{f3f6ed} \definecolor{SpineBackColor}{HTML}{262626} \begin{document} \begin{bookcover} \bookcovercomponent{color}{bg whole}{color=BackgroundColor} \bookcovercomponent{color}{spine}{color=SpineBackColor} \bookcovercomponent{normal}{front}{ \input{ribbon-reason} \vspace{1.1cm} \begin{center} \fontsize{40pt}{5em}\selectfont\bfseries CATEGORY THEORY \\FOR PROGRAMMERS \vfil \hspace*{-.8cm}\includegraphics[width=.5\coverwidth]{piggie} \linebreak \rule[1.5cm]{\textwidth/2}{.5pt}\\ \vspace{-1.5cm} \normalfont\Huge\textbf{Bartosz Milewski} \vfil \vspace*{1cm} \end{center}} \bookcovercomponent{center}{spine}{ \rotatebox[origin=c]{-90}{\color{orange} \Huge\bfseries Category Theory for Programmers \hspace{2em} Bartosz Milewski}} \bookcovercomponent{normal}{back}{% \begin{minipage}[b][\coverheight][t]{\coverwidth} \begin{center} \vspace{1cm} \includegraphics[width=.8\coverwidth]{bunnies} \begin{minipage}[t]{.8\coverwidth} \input{blurb} \vspace{.5cm} \end{minipage} \begin{minipage}{.85\textwidth} \rule{\textwidth}{.5pt} \begin{tabular}[h]{p{3.4cm} p{\textwidth}} \bartosz & \vspace{5pt} \begin{minipage}[b]{.58\coverwidth} \fontsize{11pt}{1.4em}\selectfont\textit{Category Theory for Programmers} is a series of blog posts by Bartosz Milewski, originally posted on bartoszmilewski.com.\\ Edited by Igal Tabachnik. Licenced under CC BY-SA 4.0.\\ \end{minipage} \end{tabular} \begin{flushright} \vspace{-2.6cm} \begin{minipage}[b]{4cm} \raggedleft \whitebg{fig/icons/by} \whitebg{fig/icons/cc} \whitebg{fig/icons/sa} \centering\footnotesize{\texttt{\OPTversion}} \end{minipage} \end{flushright} \end{minipage} \end{center} \end{minipage} } \end{bookcover} \end{document} ================================================ FILE: src/cover/cover-hardcover-scala.tex ================================================ \documentclass[ coverheight=9.249in, coverwidth=6.319in, % (pagesize - spinewidth) / 2 spinewidth=1.125in, bleedwidth=0.306in, 11pt, marklength=0pt, ]{bookcover} \usepackage{fancybox} \usepackage{wrapfig} \usepackage[many]{tcolorbox} \usetikzlibrary{calc,positioning, shadings} \usepackage[T1]{fontenc} \usepackage{fontspec} \usepackage{Alegreya} \newcommand{\olpath}{../} \newcommand{\whitebg}[1]{% \tikz\node[circle,draw,minimum size=1.1cm, fill=white, path picture={ \node at (path picture bounding box){ \includegraphics[width=1.1cm]{\olpath#1} }; }]{}; } \newcommand{\bartosz}{ \vspace{0pt} \begin{tcolorbox}[beamer, width=3.6cm, arc=0pt, boxsep=0pt, left=0pt,right=0pt,top=0pt,bottom=0pt, ] \includegraphics[width=3.6cm]{bartosz} \end{tcolorbox} } \input{\olpath/version} \definecolor{BackgroundColor}{HTML}{f3f6ed} \definecolor{SpineBackColor}{HTML}{262626} \begin{document} \begin{bookcover} \bookcovercomponent{color}{bg whole}{color=BackgroundColor} \bookcovercomponent{color}{spine}{color=SpineBackColor} \bookcovercomponent{normal}{front}{ \input{ribbon-scala} \vspace{1.1cm} \begin{center} \fontsize{40pt}{5em}\selectfont\bfseries CATEGORY THEORY \\FOR PROGRAMMERS \vfil \hspace*{-.8cm}\includegraphics[width=.5\coverwidth]{piggie} \linebreak \rule[1.5cm]{\textwidth/2}{.5pt}\\ \vspace{-1.5cm} \normalfont\Huge\textbf{Bartosz Milewski} \vfil \vspace*{1cm} \end{center}} \bookcovercomponent{center}{spine}{ \rotatebox[origin=c]{-90}{\color{orange} \Huge\bfseries Category Theory for Programmers \hspace{2em} Bartosz Milewski}} \bookcovercomponent{normal}{back}{% \begin{minipage}[b][\coverheight][t]{\coverwidth} \begin{center} \vspace{1cm} \includegraphics[width=.8\coverwidth]{bunnies} \begin{minipage}[t]{.8\coverwidth} \input{blurb} \vspace{.5cm} \end{minipage} \begin{minipage}{.85\textwidth} \rule{\textwidth}{.5pt} \begin{tabular}[h]{p{3.4cm} p{\textwidth}} \bartosz & \vspace{5pt} \begin{minipage}[b]{.58\coverwidth} \fontsize{11pt}{1.4em}\selectfont\textit{Category Theory for Programmers} is a series of blog posts by Bartosz Milewski, originally posted on bartoszmilewski.com.\\ Edited by Igal Tabachnik. Licenced under CC BY-SA 4.0.\\ \end{minipage} \end{tabular} \begin{flushright} \vspace{-2.6cm} \begin{minipage}[b]{4cm} \raggedleft \whitebg{fig/icons/by} \whitebg{fig/icons/cc} \whitebg{fig/icons/sa} \centering\footnotesize{\texttt{\OPTversion}} \end{minipage} \end{flushright} \end{minipage} \end{center} \end{minipage} } \end{bookcover} \end{document} ================================================ FILE: src/cover/cover-hardcover.tex ================================================ \documentclass[ coverheight=9.249in, coverwidth=6.319in, % (pagesize - spinewidth) / 2 spinewidth=1.069in, bleedwidth=0in, 11pt, marklength=0pt, ]{bookcover} \usepackage{fancybox} \usepackage{wrapfig} \usepackage[many]{tcolorbox} \usetikzlibrary{calc,positioning, shadings} \usepackage[T1]{fontenc} \usepackage{Alegreya} %% Option 'black' gives heavier bold face \setmainfont{Alegreya Sans}[ UprightFeatures={SmallCapsFont=* SC}, ItalicFeatures={SmallCapsFont=* SC Italic}, BoldFeatures={SmallCapsFont=* SC Bold}, BoldItalicFeatures={SmallCapsFont=* SC Bold Italic}, ] \newcommand{\olpath}{../} \newcommand{\whitebg}[1]{% \tikz\node[circle,draw,minimum size=1.1cm, fill=white, path picture={ \node at (path picture bounding box){ \includegraphics[width=1.1cm]{\olpath#1} }; }]{}; } \newcommand{\bartosz}{ \vspace{0pt} \begin{tcolorbox}[beamer, width=3.6cm, arc=0pt, boxsep=0pt, left=0pt,right=0pt,top=0pt,bottom=0pt, ] \includegraphics[width=3.6cm]{bartosz} \end{tcolorbox} } \input{\olpath/version} \definecolor{BackgroundColor}{HTML}{f3f6ed} \definecolor{SpineBackColor}{HTML}{262626} \begin{document} \begin{bookcover} \bookcovercomponent{color}{bg whole}{color=BackgroundColor} \bookcovercomponent{color}{spine}{color=SpineBackColor} \bookcovercomponent{normal}{front}{ \vspace{2cm} \begin{center} \fontsize{40pt}{5em}\selectfont\bfseries CATEGORY THEORY \\FOR PROGRAMMERS \vfil \hspace*{-.8cm}\includegraphics[width=.5\coverwidth]{piggie} \linebreak \rule[1.5cm]{\textwidth/2}{.5pt}\\ \vspace{-1.5cm} \normalfont\Huge\textbf{Bartosz Milewski} \vfil \vspace*{1cm} \end{center}} \bookcovercomponent{center}{spine}{ \rotatebox[origin=c]{-90}{\color{orange} \Huge\bfseries Category Theory for Programmers \hspace{2em} Bartosz Milewski}} \bookcovercomponent{normal}{back}{% \begin{minipage}[b][\coverheight][t]{\coverwidth} \begin{center} \vspace{1cm} \includegraphics[width=.8\coverwidth]{bunnies} \begin{minipage}[t]{.8\coverwidth} \input{blurb} \vspace{.5cm} \end{minipage} \begin{minipage}{.85\textwidth} \rule{\textwidth}{.5pt} \begin{tabular}[h]{p{3.4cm} p{\textwidth}} \bartosz & \vspace{5pt} \begin{minipage}[b]{.58\coverwidth} \fontsize{11pt}{1.4em}\selectfont\textit{Category Theory for Programmers} is a series of blog posts by Bartosz Milewski, originally posted on bartoszmilewski.com.\\ Edited by Igal Tabachnik. Licenced under CC BY-SA 4.0.\\ \end{minipage} \end{tabular} \begin{flushright} \vspace{-2.6cm} \begin{minipage}[b]{4cm} \raggedleft \whitebg{fig/icons/by} \whitebg{fig/icons/cc} \whitebg{fig/icons/sa} \centering\footnotesize{\texttt{\OPTversion}} \end{minipage} \end{flushright} \end{minipage} \end{center} \end{minipage} } \end{bookcover} \end{document} ================================================ FILE: src/cover/cover-paperback-ocaml.tex ================================================ \documentclass[ % bleed is added to the final result coverheight=9.25in, coverwidth=6.125in, % (pagesize - spinewidth) / 2 == (13.042 - 1.042) / 2 spinewidth=1.042in, bleedwidth=0in, 11pt, marklength=0in, ]{bookcover} \usepackage{fancybox} \usepackage{wrapfig} \usepackage[many]{tcolorbox} \usetikzlibrary{calc,positioning, shadings} \usepackage[T1]{fontenc} \usepackage{fontspec} \usepackage{Alegreya} \newcommand{\olpath}{../} \newcommand{\whitebg}[1]{% \tikz\node[circle,draw,minimum size=1.1cm, fill=white, path picture={ \node at (path picture bounding box){ \includegraphics[width=1.1cm]{\olpath#1} }; }]{}; } \newcommand{\bartosz}{ \vspace{0pt} \begin{tcolorbox}[beamer, width=3.6cm, arc=0pt, boxsep=0pt, left=0pt,right=0pt,top=0pt,bottom=0pt, ] \includegraphics[width=3.6cm]{bartosz} \end{tcolorbox} } \input{\olpath/version} \definecolor{BackgroundColor}{HTML}{f3f6ed} \definecolor{SpineBackColor}{HTML}{262626} \begin{document} \begin{bookcover} \bookcovercomponent{color}{bg whole}{color=BackgroundColor} \bookcovercomponent{color}{spine}{color=SpineBackColor} \bookcovercomponent{normal}{front}{ \input{ribbon-ocaml} \vspace{1.1cm} \begin{center} \fontsize{40pt}{5em}\selectfont\bfseries CATEGORY THEORY \\FOR PROGRAMMERS \vfil \hspace*{-.8cm}\includegraphics[width=.5\coverwidth]{piggie} \linebreak \rule[1.5cm]{\textwidth/2}{.5pt}\\ \vspace{-1.5cm} \normalfont\Huge\textbf{Bartosz Milewski} \vfil \vspace*{1cm} \end{center}} \bookcovercomponent{center}{spine}{ \rotatebox[origin=c]{-90}{\color{orange} \Huge\bfseries Category Theory for Programmers \hspace{2em} Bartosz Milewski}} \bookcovercomponent{normal}{back}{% \begin{minipage}[b][\coverheight][t]{\coverwidth} \begin{center} \vspace{1cm} \includegraphics[width=.8\coverwidth]{bunnies} \begin{minipage}[t]{.75\coverwidth} \input{blurb} \vspace{0.6cm} \end{minipage} \begin{minipage}{.85\textwidth} \rule{\textwidth}{.5pt} \begin{tabular}[h]{p{3.4cm} p{\textwidth}} \vspace{5pt} \bartosz & \vspace{10pt} \begin{minipage}[b]{.58\coverwidth} \fontsize{11pt}{1.4em}\selectfont\textit{Category Theory for Programmers} is a series of blog posts by Bartosz Milewski, originally posted on bartoszmilewski.com.\\ Edited by Igal Tabachnik. Licenced under CC BY-SA 4.0.\\ \end{minipage} \end{tabular} \begin{flushright} \vspace{-2.6cm} \begin{minipage}[b]{4cm} \raggedleft \whitebg{fig/icons/by} \whitebg{fig/icons/cc} \whitebg{fig/icons/sa} \centering\footnotesize{\texttt{\OPTversion}} \end{minipage} \end{flushright} \end{minipage} \end{center} \end{minipage} } \end{bookcover} \end{document} ================================================ FILE: src/cover/cover-paperback-reason.tex ================================================ \documentclass[ % bleed is added to the final result coverheight=9.25in, coverwidth=6.125in, % (pagesize - spinewidth) / 2 == (13.042 - 1.042) / 2 spinewidth=1.042in, bleedwidth=0in, 11pt, marklength=0in, ]{bookcover} \usepackage{fancybox} \usepackage{wrapfig} \usepackage[many]{tcolorbox} \usetikzlibrary{calc,positioning, shadings} \usepackage[T1]{fontenc} \usepackage{fontspec} \usepackage{Alegreya} \newcommand{\olpath}{../} \newcommand{\whitebg}[1]{% \tikz\node[circle,draw,minimum size=1.1cm, fill=white, path picture={ \node at (path picture bounding box){ \includegraphics[width=1.1cm]{\olpath#1} }; }]{}; } \newcommand{\bartosz}{ \vspace{0pt} \begin{tcolorbox}[beamer, width=3.6cm, arc=0pt, boxsep=0pt, left=0pt,right=0pt,top=0pt,bottom=0pt, ] \includegraphics[width=3.6cm]{bartosz} \end{tcolorbox} } \input{\olpath/version} \definecolor{BackgroundColor}{HTML}{f3f6ed} \definecolor{SpineBackColor}{HTML}{262626} \begin{document} \begin{bookcover} \bookcovercomponent{color}{bg whole}{color=BackgroundColor} \bookcovercomponent{color}{spine}{color=SpineBackColor} \bookcovercomponent{normal}{front}{ \input{ribbon-reason} \vspace{1.1cm} \begin{center} \fontsize{40pt}{5em}\selectfont\bfseries CATEGORY THEORY \\FOR PROGRAMMERS \vfil \hspace*{-.8cm}\includegraphics[width=.5\coverwidth]{piggie} \linebreak \rule[1.5cm]{\textwidth/2}{.5pt}\\ \vspace{-1.5cm} \normalfont\Huge\textbf{Bartosz Milewski} \vfil \vspace*{1cm} \end{center}} \bookcovercomponent{center}{spine}{ \rotatebox[origin=c]{-90}{\color{orange} \Huge\bfseries Category Theory for Programmers \hspace{2em} Bartosz Milewski}} \bookcovercomponent{normal}{back}{% \begin{minipage}[b][\coverheight][t]{\coverwidth} \begin{center} \vspace{1cm} \includegraphics[width=.8\coverwidth]{bunnies} \begin{minipage}[t]{.75\coverwidth} \input{blurb} \vspace{0.6cm} \end{minipage} \begin{minipage}{.85\textwidth} \rule{\textwidth}{.5pt} \begin{tabular}[h]{p{3.4cm} p{\textwidth}} \vspace{5pt} \bartosz & \vspace{10pt} \begin{minipage}[b]{.58\coverwidth} \fontsize{11pt}{1.4em}\selectfont\textit{Category Theory for Programmers} is a series of blog posts by Bartosz Milewski, originally posted on bartoszmilewski.com.\\ Edited by Igal Tabachnik. Licenced under CC BY-SA 4.0.\\ \end{minipage} \end{tabular} \begin{flushright} \vspace{-2.6cm} \begin{minipage}[b]{4cm} \raggedleft \whitebg{fig/icons/by} \whitebg{fig/icons/cc} \whitebg{fig/icons/sa} \centering\footnotesize{\texttt{\OPTversion}} \end{minipage} \end{flushright} \end{minipage} \end{center} \end{minipage} } \end{bookcover} \end{document} ================================================ FILE: src/cover/cover-paperback-scala.tex ================================================ \documentclass[ % bleed is added to the final result coverheight=9.25in, coverwidth=6.125in, % (pagesize - spinewidth) / 2 == (13.042 - 1.042) / 2 spinewidth=1.042in, bleedwidth=0in, 11pt, marklength=0in, ]{bookcover} \usepackage{fancybox} \usepackage{wrapfig} \usepackage[many]{tcolorbox} \usetikzlibrary{calc,positioning, shadings} \usepackage[T1]{fontenc} \usepackage{fontspec} \usepackage{Alegreya} \newcommand{\olpath}{../} \newcommand{\whitebg}[1]{% \tikz\node[circle,draw,minimum size=1.1cm, fill=white, path picture={ \node at (path picture bounding box){ \includegraphics[width=1.1cm]{\olpath#1} }; }]{}; } \newcommand{\bartosz}{ \vspace{0pt} \begin{tcolorbox}[beamer, width=3.6cm, arc=0pt, boxsep=0pt, left=0pt,right=0pt,top=0pt,bottom=0pt, ] \includegraphics[width=3.6cm]{bartosz} \end{tcolorbox} } \input{\olpath/version} \definecolor{BackgroundColor}{HTML}{f3f6ed} \definecolor{SpineBackColor}{HTML}{262626} \begin{document} \begin{bookcover} \bookcovercomponent{color}{bg whole}{color=BackgroundColor} \bookcovercomponent{color}{spine}{color=SpineBackColor} \bookcovercomponent{normal}{front}{ \input{ribbon-scala} \vspace{1.1cm} \begin{center} \fontsize{40pt}{5em}\selectfont\bfseries CATEGORY THEORY \\FOR PROGRAMMERS \vfil \hspace*{-.8cm}\includegraphics[width=.5\coverwidth]{piggie} \linebreak \rule[1.5cm]{\textwidth/2}{.5pt}\\ \vspace{-1.5cm} \normalfont\Huge\textbf{Bartosz Milewski} \vfil \vspace*{1cm} \end{center}} \bookcovercomponent{center}{spine}{ \rotatebox[origin=c]{-90}{\color{orange} \Huge\bfseries Category Theory for Programmers \hspace{2em} Bartosz Milewski}} \bookcovercomponent{normal}{back}{% \begin{minipage}[b][\coverheight][t]{\coverwidth} \begin{center} \vspace{1cm} \includegraphics[width=.8\coverwidth]{bunnies} \begin{minipage}[t]{.75\coverwidth} \input{blurb} \vspace{0.6cm} \end{minipage} \begin{minipage}{.85\textwidth} \rule{\textwidth}{.5pt} \begin{tabular}[h]{p{3.4cm} p{\textwidth}} \vspace{5pt} \bartosz & \vspace{10pt} \begin{minipage}[b]{.58\coverwidth} \fontsize{11pt}{1.4em}\selectfont\textit{Category Theory for Programmers} is a series of blog posts by Bartosz Milewski, originally posted on bartoszmilewski.com.\\ Edited by Igal Tabachnik. Licenced under CC BY-SA 4.0.\\ \end{minipage} \end{tabular} \begin{flushright} \vspace{-2.6cm} \begin{minipage}[b]{4cm} \raggedleft \whitebg{fig/icons/by} \whitebg{fig/icons/cc} \whitebg{fig/icons/sa} \centering\footnotesize{\texttt{\OPTversion}} \end{minipage} \end{flushright} \end{minipage} \end{center} \end{minipage} } \end{bookcover} \end{document} ================================================ FILE: src/cover/cover-paperback.tex ================================================ \documentclass[ coverheight=9.25in, coverwidth=6.125in, % (pagesize - spinewidth) / 2 spinewidth=1.125in, bleedwidth=0in, 11pt, marklength=0pt, ]{bookcover} \usepackage{fancybox} \usepackage{wrapfig} \usepackage[many]{tcolorbox} \usetikzlibrary{calc,positioning, shadings} \usepackage[T1]{fontenc} \usepackage{Alegreya} %% Option 'black' gives heavier bold face \setmainfont{Alegreya Sans}[ UprightFeatures={SmallCapsFont=* SC}, ItalicFeatures={SmallCapsFont=* SC Italic}, BoldFeatures={SmallCapsFont=* SC Bold}, BoldItalicFeatures={SmallCapsFont=* SC Bold Italic}, ] \newcommand{\olpath}{../} \newcommand{\whitebg}[1]{% \tikz\node[circle,draw,minimum size=1.1cm, fill=white, path picture={ \node at (path picture bounding box){ \includegraphics[width=1.1cm]{\olpath#1} }; }]{}; } \newcommand{\bartosz}{ \vspace{0pt} \begin{tcolorbox}[beamer, width=3.6cm, arc=0pt, boxsep=0pt, left=0pt,right=0pt,top=0pt,bottom=0pt, ] \includegraphics[width=3.6cm]{bartosz} \end{tcolorbox} } \input{\olpath/version} \definecolor{BackgroundColor}{HTML}{f3f6ed} \definecolor{SpineBackColor}{HTML}{262626} \begin{document} \begin{bookcover} \bookcovercomponent{color}{bg whole}{color=BackgroundColor} \bookcovercomponent{color}{spine}{color=SpineBackColor} \bookcovercomponent{normal}{front}{ \vspace{2cm} \begin{center} \fontsize{40pt}{5em}\selectfont\bfseries CATEGORY THEORY \\FOR PROGRAMMERS \vfil \hspace*{-.8cm}\includegraphics[width=.5\coverwidth]{piggie} \linebreak \rule[1.5cm]{\textwidth/2}{.5pt}\\ \vspace{-1.5cm} \normalfont\Huge\textbf{Bartosz Milewski} \vfil \vspace*{1cm} \end{center}} \bookcovercomponent{center}{spine}{ \rotatebox[origin=c]{-90}{\color{orange} \Huge\bfseries Category Theory for Programmers \hspace{2em} Bartosz Milewski}} \bookcovercomponent{normal}{back}{% \begin{minipage}[b][\coverheight][t]{\coverwidth} \begin{center} \vspace{1cm} \includegraphics[width=.8\coverwidth]{bunnies} \begin{minipage}[t]{.8\coverwidth} \input{blurb} \vspace{.5cm} \end{minipage} \begin{minipage}{.85\textwidth} \rule{\textwidth}{.5pt} \begin{tabular}[h]{p{3.4cm} p{\textwidth}} \bartosz & \vspace{5pt} \begin{minipage}[b]{.58\coverwidth} \fontsize{11pt}{1.4em}\selectfont\textit{Category Theory for Programmers} is a series of blog posts by Bartosz Milewski, originally posted on bartoszmilewski.com.\\ Edited by Igal Tabachnik. Licenced under CC BY-SA 4.0.\\ \end{minipage} \end{tabular} \begin{flushright} \vspace{-2.6cm} \begin{minipage}[b]{4cm} \raggedleft \whitebg{fig/icons/by} \whitebg{fig/icons/cc} \whitebg{fig/icons/sa} \centering\footnotesize{\texttt{\OPTversion}} \end{minipage} \end{flushright} \end{minipage} \end{center} \end{minipage} } \end{bookcover} \end{document} ================================================ FILE: src/cover/ribbon-ocaml.tex ================================================ % !TeX root = ./cover-paperback-ocaml.tex \newcommand{\stripskip}{4} \newcommand{\stripwidth}{3} \definecolor{BurntOrange}{rgb}{0.8, 0.33, 0.0} \begin{tikzpicture}[ overlay, remember picture, ribbon/.style={anchor=center, rotate = 45, font={\fontsize{22}{1}\selectfont\bfseries}} ] \coordinate (A) at ($ (current page.south east) + (-\stripskip,0) $);% <-- changed coordinate from 'north' to south' \coordinate (A') at ($(A) + (-\stripwidth,0) $); \coordinate (B) at ($ (current page.south east) + (0,\stripskip) $);% <-- changed coordinate from 'north' to south' and sign for \stripskip \coordinate (B') at ($(B) + (0,\stripwidth) $);% <-- changed sign for \stripskip \fill [BurntOrange!20] (A) -- (A') -- (B') -- (B) -- cycle; \coordinate (tempA) at ($(A)!.5!(A')$); \coordinate (tempB) at ($(B)!.5!(B')$); \node [ribbon](text) at ($(tempA)!.5!(tempB)$) { \raisebox{-.15\height}{\includegraphics[width=.8cm]{\olpath/fig/icons/ocaml}} \hspace{.5mm} OCaml Edition }; \end{tikzpicture} ================================================ FILE: src/cover/ribbon-reason.tex ================================================ % !TeX root = ./cover-paperback-reason.tex \newcommand{\stripskip}{4} \newcommand{\stripwidth}{3} \definecolor{RedOrange}{rgb}{0.86, 0.30, 0.25} \begin{tikzpicture}[ overlay, remember picture, ribbon/.style={anchor=center, rotate = 45, font={\fontsize{22}{1}\selectfont\bfseries}} ] \coordinate (A) at ($ (current page.south east) + (-\stripskip,0) $);% <-- changed coordinate from 'north' to south' \coordinate (A') at ($(A) + (-\stripwidth,0) $); \coordinate (B) at ($ (current page.south east) + (0,\stripskip) $);% <-- changed coordinate from 'north' to south' and sign for \stripskip \coordinate (B') at ($(B) + (0,\stripwidth) $);% <-- changed sign for \stripskip \fill [RedOrange!20] (A) -- (A') -- (B') -- (B) -- cycle; \coordinate (tempA) at ($(A)!.5!(A')$); \coordinate (tempB) at ($(B)!.5!(B')$); \node [ribbon](text) at ($(tempA)!.5!(tempB)$) { \raisebox{-.15\height}{\includegraphics[width=.8cm]{\olpath/fig/icons/reason}} \hspace{.5mm} ReasonML Edition }; \end{tikzpicture} ================================================ FILE: src/cover/ribbon-scala.tex ================================================ \newcommand{\stripskip}{4} \newcommand{\stripwidth}{3} \begin{tikzpicture}[ overlay, remember picture, ribbon/.style={anchor=center, rotate = 45, font={\fontsize{22}{1}\selectfont\bfseries}} ] \coordinate (A) at ($ (current page.south east) + (-\stripskip,0) $);% <-- changed coordinate from 'north' to south' \coordinate (A') at ($(A) + (-\stripwidth,0) $); \coordinate (B) at ($ (current page.south east) + (0,\stripskip) $);% <-- changed coordinate from 'north' to south' and sign for \stripskip \coordinate (B') at ($(B) + (0,\stripwidth) $);% <-- changed sign for \stripskip \fill [red!20] (A) -- (A') -- (B') -- (B) -- cycle; \coordinate (tempA) at ($(A)!.5!(A')$); \coordinate (tempB) at ($(B)!.5!(B')$); \node [ribbon](text) at ($(tempA)!.5!(tempB)$) { \raisebox{-.30\height}{\includegraphics[width=.8cm]{\olpath/fig/icons/scala}} \hspace{.5mm} Scala Edition }; \end{tikzpicture} ================================================ FILE: src/ctfp-print-ocaml.tex ================================================ \input{opt-ocaml} \input{ctfp-print} \input{postamble} ================================================ FILE: src/ctfp-print-reason.tex ================================================ \input{opt-reason} \input{ctfp-print} \input{postamble} ================================================ FILE: src/ctfp-print-scala.tex ================================================ \input{opt-scala} \input{ctfp-print} \input{postamble} ================================================ FILE: src/ctfp-print.tex ================================================ % This is a main LaTeX file for generating the CTFP book % for printing in US-trade format, suitable for binding. % The links are black in this version. \input{opt-print-ustrade} \input{preamble} \hypersetup{hidelinks} \input{ctfp} \input{postamble} ================================================ FILE: src/ctfp-reader-ocaml.tex ================================================ \input{opt-ocaml} \input{ctfp-reader} \input{postamble} ================================================ FILE: src/ctfp-reader-reason.tex ================================================ \input{opt-reason} \input{ctfp-reader} \input{postamble} ================================================ FILE: src/ctfp-reader-scala.tex ================================================ \input{opt-scala} \input{ctfp-reader} \input{postamble} ================================================ FILE: src/ctfp-reader.tex ================================================ % This is a main LaTeX file for generating the CTFP book % for printing in US-trade format, suitable for binding. % The links are black in this version. \input{opt-reader-10in} \input{preamble} \input{ctfp} \input{postamble} ================================================ FILE: src/ctfp.tex ================================================ % !TEX root = ctfp-print.tex \input{half-title} \frontmatter \tableofcontents \input{content/editor-note} \chapter*{Preface} \addcontentsline{toc}{chapter}{Preface} \label{Preface} \subfile{content/0.0/Preface} \mainmatter \part*{Part One} \addcontentsline{toc}{part}{Part One} \chapter{Category: The Essence of Composition}\label{category-the-essence-of-composition} \subfile{content/1.1/category-the-essence-of-composition} \chapter{Types and Functions}\label{types-and-functions} \subfile{content/1.2/types-and-functions} \chapter{Categories Great and Small}\label{categories-great-and-small} \subfile{content/1.3/categories-great-and-small} \chapter{Kleisli Categories}\label{kleisli-categories} \subfile{content/1.4/kleisli-categories} \chapter{Products and Coproducts}\label{products-and-coproducts} \subfile{content/1.5/products-and-coproducts} \chapter{Simple Algebraic Data Types}\label{simple-algebraic-data-types} \subfile{content/1.6/simple-algebraic-data-types} \chapter{Functors}\label{functors} \subfile{content/1.7/functors} \chapter{Functoriality}\label{functoriality} \subfile{content/1.8/functoriality} \chapter{Function Types}\label{function-types} \subfile{content/1.9/function-types} \chapter{Natural Transformations}\label{natural-transformations} \subfile{content/1.10/natural-transformations} \part*{Part Two} \addcontentsline{toc}{part}{Part Two} \chapter{Declarative Programming}\label{declarative-programming} \subfile{content/2.1/declarative-programming} \chapter{Limits and Colimits}\label{limits-and-colimits} \subfile{content/2.2/limits-and-colimits} \chapter{Free Monoids}\label{free-monoids} \subfile{content/2.3/free-monoids} \chapter{Representable Functors}\label{representable-functors} \subfile{content/2.4/representable-functors} \chapter{The Yoneda Lemma}\label{the-yoneda-lemma} \subfile{content/2.5/the-yoneda-lemma} \chapter{Yoneda Embedding}\label{yoneda-embedding} \subfile{content/2.6/yoneda-embedding} \part*{Part Three} \addcontentsline{toc}{part}{Part Three} \chapter{It's All About Morphisms}\label{all-about-morphisms} \subfile{content/3.1/its-all-about-morphisms} \chapter{Adjunctions}\label{adjunctions} \subfile{content/3.2/adjunctions} \chapter{Free/Forgetful Adjunctions}\label{free-forgetful-adjunctions} \subfile{content/3.3/free-forgetful-adjunctions} \chapter{Monads: Programmer's Definition}\label{monads-programmers-definition} \subfile{content/3.4/monads-programmers-definition} \chapter{Monads and Effects}\label{monads-and-effects} \subfile{content/3.5/monads-and-effects} \chapter{Monads Categorically}\label{monads-categorically} \subfile{content/3.6/monads-categorically} \chapter{Comonads}\label{comonads} \subfile{content/3.7/comonads} \chapter{$F$-Algebras}\label{f-algebras} \subfile{content/3.8/f-algebras} \chapter{Algebras for Monads}\label{algebras-for-monads} \subfile{content/3.9/algebras-for-monads} \chapter{Ends and Coends}\label{ends-and-coends} \subfile{content/3.10/ends-and-coends} \chapter{Kan Extensions}\label{kan-extensions} \subfile{content/3.11/kan-extensions} \chapter{Enriched Categories}\label{enriched-categories} \subfile{content/3.12/enriched-categories} \chapter{Topoi}\label{topoi} \subfile{content/3.13/topoi} \chapter{Lawvere Theories}\label{lawvere-theories} \subfile{content/3.14/lawvere-theories} \chapter{Monads, Monoids, and Categories}\label{monads-monoids-categories} \subfile{content/3.15/monads-monoids-and-categories} \backmatter \appendix \addcontentsline{toc}{part}{Appendices} \input{index} \makeatletter\@openrightfalse \chapter*{Acknowledgments}\label{acknowledgments} \addcontentsline{toc}{chapter}{Acknowledgments} \input{acknowledgments} \chapter*{Colophon}\label{colophon} \addcontentsline{toc}{chapter}{Colophon} \input{colophon} \chapter*{Copyleft notice}\label{copyleft} \addcontentsline{toc}{chapter}{Copyleft notice} \input{free-software} \@openrighttrue\makeatother \afterpage{\blankpage} ================================================ FILE: src/free-software.tex ================================================ \lettrine[lraise=-0.03,loversize=0.08]{T}{his book} is \textbf{Libre} and follows the philosophy of \urlref{https://www.gnu.org/philosophy/free-sw.en.html}{Free Software}: you can use this book as you like, the source is available, you can redistribute this book and you can distribute your own version. That means you can print it, photocopy it, e-mail it, upload it to websites, change it, translate it, remix it, delete bits, and draw all over it. This book is Copyleft: if you change the book and distribute your own version, you must also pass these freedoms to its recipients. This book uses the Creative Commons Attribution-ShareAlike 4.0 International License (\href{http://creativecommons.org/licenses/by-sa/4.0/}{\acronym{CC BY-SA 4.0}}). ================================================ FILE: src/half-title.tex ================================================ % !TEX root = ctfp-print.tex \input{version} \thispagestyle{empty} \vspace*{80pt} \begin{raggedleft} \fontsize{24pt}{24pt}\selectfont \textbf{Category Theory \\ for Programmers}\\ \ifdefined\OPTCustomLanguage{% \vspace*{1cm} \small\selectfont{ \textbf{\OPTDisplayLanguageName{} Edition}\\ \textit{Contains code snippets in Haskell and \OPTDisplayLanguageName}\\ } } \fi \vspace*{1cm} \fontsize{16pt}{18pt}\selectfont \textit{By } \textbf{Bartosz Milewski}\\ \vspace{1cm} \fontsize{12pt}{14pt}\selectfont \textit{compiled and edited by}\\ \textbf{Igal Tabachnik}\\ \end{raggedleft} \newpage \vspace*{0.3\textheight} \thispagestyle{empty} \begin{small} \begin{center} \textsc{Category Theory for Programmers}\\ \vspace{1.0em} \noindent Bartosz Milewski\\ \vspace{1.26em} \noindent Version \texttt{\OPTversion}\\\today \vspace{1.6em} \noindent \includegraphics[width=3mm]{fig/icons/cc.pdf} \includegraphics[width=3mm]{fig/icons/by.pdf} \includegraphics[width=3mm]{fig/icons/sa.pdf} \vspace{0.4em} \noindent This work is licensed under a Creative Commons\\ Attribution-ShareAlike 4.0 International License (\href{http://creativecommons.org/licenses/by-sa/4.0/}{\acronym{CC BY-SA 4.0}}). \vspace{1.26em} \noindent Converted from a series of blog posts by \href{https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/}{Bartosz Milewski}.\\ PDF and book compiled by \href{https://hmemcpy.com}{Igal Tabachnik}.\\ \vspace{1.26em} \noindent \LaTeX{} source code is available on GitHub: \href{https://github.com/hmemcpy/milewski-ctfp-pdf}{https://github.com/hmemcpy/milewski-ctfp-pdf} \end{center} \end{small} ================================================ FILE: src/index.tex ================================================ \setindexprenote{\normalsize \begin{quote} Any inaccuracies in this index may be explained by the fact that it has been prepared with the help of a computer. ---Donald E. Knuth, \textit{Fundamental Algorithms}\\ (Volume 1 of \textit{The Art of Computer Programming}) \end{quote}} \printindex ================================================ FILE: src/opt-ocaml.tex ================================================ \def\OPTCustomLanguage{ocaml} \def\OPTCustomLanguageExt{ml} \def\OPTCustomLanguageColor{BurntOrange} \def\OPTDisplayLanguageName{OCaml} ================================================ FILE: src/opt-print-ustrade.tex ================================================ % !TeX program = xelatex % !TEX root = ctfp-print-scala.tex \def\OPTmodes{twoside,final,openright,10pt} % After struggling a while to get it right, here's how to calculate: % 1. Use the calculator to get your specifications % https://www.blurb.com/make/pdf_to_book/booksize_calculator % 2. Get the specifications. For a 6x9 hardcover (ImageWrap) book, I got: % % Page Specifications Inches % Final, exported PDF should measure (w x h) 6.125 x 9.25 % Page size / trim line (w x h) 6.0 x 9.0 % Bleed (top, bottom, and outside edges) 0.125 % Safe boundary (Top, Bottom, Outside Edge) 0.25 % Safe boundary (Binding Edge) 0.5 % % 3. Trim line is at 0.625in, so to get safe text boundary (magenta line): % Top margin: 0.625in (start at trim line, header space fills to safe line) % Bottom margin: 0.625in + 0.25in = 0.875in % Inner margin: 0.625in (start on the trim line) % Binding offset: 0.5in % Outer margin: 0.625in + 0.25in = 0.875in % Specifications for blurb.com \def\OPTpaperesize{6.125in,9.25in} % Page size \def\OPTtopmargin{.625in} % Margin at the top of the page \def\OPTbottommargin{0.875in} % Margin at the bottom of the page \def\OPTinnermargin{0.625in} % Margin on the inner side of the page \def\OPTbindingoffset{0.4in} % Extra offset on the inner side \def\OPToutermargin{0.625in} % Margin on the outer side of the page ================================================ FILE: src/opt-reader-10in.tex ================================================ % !TEX root = ctfp.tex % Options for reading on 10" tables (e.g. Kindle DX) \def\OPTmodes{oneside,openany,11pt} % PAGE FORMAT % % Page geometry for 10-inch tablets \def\OPTpaperesize{148mm,197mm} % Page size \def\OPTtopmargin{21mm} % Margin at the top of the page \def\OPTbottommargin{28mm} % Margin at the bottom of the page \def\OPTinnermargin{18.5mm} % Margin on the inner side of the page \def\OPToutermargin{18.5mm} % Margin on the outer side of the page \def\OPTbindingoffset{0mm} % Extra offset on the inner side \def\OPThidelinks{false} ================================================ FILE: src/opt-reason.tex ================================================ \def\OPTCustomLanguage{reason} \def\OPTCustomLanguageExt{re} \def\OPTCustomLanguageColor{RedOrange} \def\OPTDisplayLanguageName{ReasonML} ================================================ FILE: src/opt-scala.tex ================================================ \def\OPTCustomLanguage{scala} \def\OPTCustomLanguageExt{scala} \def\OPTCustomLanguageColor{red} \def\OPTDisplayLanguageName{Scala} ================================================ FILE: src/postamble.tex ================================================ \end{document} ================================================ FILE: src/preamble.tex ================================================ \documentclass[\OPTmodes]{book} \usepackage[papersize={\OPTpaperesize}, top=\OPTtopmargin, bottom=\OPTbottommargin, inner=\OPTinnermargin, outer=\OPToutermargin, bindingoffset=\OPTbindingoffset, centering, % showframe, ]{geometry} % New line height: 1.05 * 1.2 = 1.26 \renewcommand{\baselinestretch}{1.05} % Uncomment for diagnostics! % \usepackage[cam,width=6in,height=9in]{crop} % Font settings %--------------------------------------------------------------% Note: \usepackage[no-math]{fontspec} % This exact order \usepackage{libertine} % of declarations \defaultfontfeatures{% % is important for Scale=MatchLowercase, % needed here ... % single quotes not } % to turn out curly \setmonofont[BoldFont=Inconsolata-LGC-Bold.ttf]{Inconsolata-LGC-Bold.ttf} % ("typographic") % in verbatim blocks \defaultfontfeatures{% % of Haskell code. Scale=MatchLowercase, % ... and here again % Now the quote is Mapping=tex-text, % upright and safely SmallCapsFeatures={LetterSpace=2.5,WordSpace=1.05}, % copy-pasteable to } % the REPL without %--------------------------------------------------------------% giving errors. % To use Libertine letters and numbers, % but tx-style operators in math environment: \usepackage[libertine]{newtxmath} \usepackage{amsmath} % Needed to display additional math unicode symbols (like double-colon) % https://github.com/wspr/unicode-math/issues/469 \expandafter\let\csname not=\endcsname\relax \expandafter\let\csname not<\endcsname\relax \expandafter\let\csname not>\endcsname\relax \usepackage{unicode-math} \setmathfont{LibertinusMath-Regular.otf} \usepackage[all]{nowidow} \usepackage{emptypage} \usepackage{noindentafter} \usepackage{graphicx} \usepackage{caption, wrapfig, framed, subfigure} \usepackage[export]{adjustbox} \captionsetup{labelformat=empty,font=scriptsize} \usepackage{titlecaps} \usepackage[usenames,dvipsnames,x11names]{xcolor} \usepackage{longtable} \usepackage{booktabs} % Workaround to fix mismatched left and right math delimiters. Taken from: % http://tex.stackexchange.com/questions/63410/parentheses-differ-xelatex-fontspec-newtxmath-libertine \DeclareSymbolFont{parenthesis}{T1}{fxl}{m}{n} \DeclareMathDelimiter{(}{\mathopen}{parenthesis}{"28}{largesymbols}{"00} \DeclareMathDelimiter{)}{\mathclose}{parenthesis}{"29}{largesymbols}{"01} \DeclareMathDelimiter{[}{\mathopen}{parenthesis}{"5B}{largesymbols}{"02} \DeclareMathDelimiter{]}{\mathclose}{parenthesis}{"5D}{largesymbols}{"03} \DeclareMathDelimiter{\lbrace}{\mathopen}{parenthesis}{"7B}{largesymbols}{"08} \DeclareMathDelimiter{\rbrace}{\mathclose}{parenthesis}{"7D}{largesymbols}{"09} % Use tikz-cd for commutative diagrams \usepackage{tikz-cd} \usepackage[framemethod=TikZ]{mdframed} \usepackage{ifthen} \usepackage{fancyvrb} \fvset{fontsize=\small} \usepackage[cachedir=_minted-ctfp]{minted} \setminted[text]{fontsize=\small,breaklines} \usemintedstyle{github} \makeatletter % prevents cleaning up the cache at the end of the run (needed to keep the "unused" caches, generated by other editions) \renewcommand*{\minted@cleancache}{} \makeatother \usepackage{imakeidx} \usepackage[totoc,font=footnotesize]{idxlayout} \usepackage{fancyhdr} \pagestyle{plain} \usepackage[final]{pdfpages} % inserts pages from a pdf file \usepackage{titlesec} % to change the appearance of section titles \usepackage{listings} % for syntax highlighted code listings \usepackage{enumerate} % allows customized labels in enumerations \usepackage{enumitem} % allows nested enumeration with numbers \PassOptionsToPackage{hyphens}{url}\usepackage{hyperref} % makes cross references and URLs clickable \definecolor{LinkRed}{HTML}{80171F} \hypersetup{ pdfauthor={Bartosz Milewski}, pdftitle={Category Theory for Programmers}, pdfsubject={category theory, computer science, programming, abstraction, composition}, colorlinks=true, linkcolor=LinkRed, urlcolor=LinkRed, } \usepackage{afterpage} \newcommand\blankpage{% \null \thispagestyle{empty}% \addtocounter{page}{-1}% \newpage} \usepackage{subfiles} \makeatletter \let\org@subfile\subfile \renewcommand*{\subfile}[1]{% \filename@parse{#1} \expandafter \graphicspath\expandafter{\expandafter{\filename@area}} \org@subfile{#1}% } \makeatother % Document colors \definecolor{Dark} {HTML} {262626} \definecolor{DropCapGray} {HTML} {B8B8B8} \definecolor{ChapterGray} {HTML} {C8C8C8} \definecolor{ChapterViolet} {HTML} {AEAECE} \definecolor{DropCapViolet} {HTML} {9090C0} \usepackage{lettrine} % adds commands that make drop capitals \renewcommand{\LettrineFontHook}{\rmfamily\mdseries\color{DropCapViolet}} \renewcommand{\DefaultLraise}{0.00} \renewcommand{\DefaultLoversize}{0.02} \renewcommand{\DefaultLhang}{0.12} \setlength{\DefaultFindent}{1pt} \setlength{\DefaultNindent}{0em} % removes the "Part X" text from \part \renewcommand{\partname}{} \renewcommand{\thepart}{} \usepackage{xparse} \usepackage[abspath]{currfile} \newenvironment{snip}[2][] {\VerbatimEnvironment \mdfsetup{ linecolor=gray!20, linewidth=2pt, topline=false, bottomline=false, rightline=false } \begin{mdframed}\begin{minted}[#1]{#2}} {\end{minted}\end{mdframed} \vspace{-1ex}} \newenvironment{snipv} {\VerbatimEnvironment \mdfsetup{ linecolor=gray!20, linewidth=2pt, topline=false, bottomline=false, rightline=false } \begin{mdframed}\begin{Verbatim}[commandchars=\\\{\}]} {\end{Verbatim}\end{mdframed} \vspace{-1ex}} % See the context about this workaround at % https://github.com/gpoore/minted/issues/354 \makeatletter \ifwindows \def\minted@opt@quote#1{\detokenize\expandafter{\expandafter"\expanded{#1}"}} \else \def\minted@opt@quote#1{\detokenize\expandafter{\expandafter'\expanded{#1}'}} \fi \makeatother \NewDocumentCommand\src{mO{}}{ \srcsnippet{\currfileabsdir/code/haskell/#1.hs}{blue}{haskell}{#2} \ifdefined\OPTCustomLanguage{% \unskip \srcsnippet{\currfileabsdir/code/\OPTCustomLanguage/#1.\OPTCustomLanguageExt}{\OPTCustomLanguageColor}{\OPTCustomLanguage}{#2} } \fi \NoIndentAfterThis } \NewDocumentCommand\srcsnippet{mmmm}{ \newif\ifbreak \def\snippet{#1} \mdfsetup{% linecolor=#2!20,% linewidth=2pt, topline=false, bottomline=false, rightline=false, } \def\x{#4} \ifthenelse{\equal{\x}{b}}{\breaktrue}{\breakfalse} \begin{mdframed} \ifbreak \inputminted[autogobble,breaklines]{#3}{\snippet} \else \inputminted[autogobble]{#3}{\snippet} \fi \end{mdframed} } \newcommand{\acronym}[1]{\textsc{\MakeLowercase{#1}}} \newcommand{\newterm}[1]{\index{#1}\emph{#1}} \newcommand{\code}[1]{\Verb+{#1}+} \newcommand{\heading}[1]{{\sffamily\bfseries #1}} \newcommand{\mono}[1]{\hbox{\ttfamily\scriptsize #1}} \newcommand{\monoit}[1]{\hbox{\ttfamily\itshape\scriptsize #1}} \newcommand{\mathtext}[1]{\ensuremath{\pmb{#1}}} \newcommand{\urlref}[2]{\href{#1}{#2}\urlfootnote{#1}} % https://tex.stackexchange.com/questions/219445/line-break-in-texttt/219497 \renewcommand{\code}[1]{% \begingroup \ttfamily \begingroup\lccode`~=`/\lowercase{\endgroup\def~}{/\discretionary{}{}{}}% \begingroup\lccode`~=`[\lowercase{\endgroup\def~}{[\discretionary{}{}{}}% \begingroup\lccode`~=`.\lowercase{\endgroup\def~}{.\discretionary{}{}{}}% \catcode`/=\active\catcode`[=\active\catcode`.=\active \scantokens{#1\noexpand}% \endgroup } \makeatletter \newcommand\urlfootnote@[1]{\footnote{\url@{#1}}} \DeclareRobustCommand{\urlfootnote}{\hyper@normalise\urlfootnote@} \makeatother \titleformat{\chapter}[display] {\color{Dark}\normalfont\sffamily\bfseries\LARGE} {\filright \color{ChapterViolet}\sffamily\mdseries \fontsize{6em}{0em}\selectfont {\addfontfeature{Numbers=OldStyle}\thechapter}} {1em} {\filright} \titleformat{\section} {\color{Dark}\normalfont\Large\sffamily\bfseries} {\color{DropCapViolet}\thesection}{0.8em}{} \titleformat{\subsection} {\color{Dark}\normalfont\large\sffamily\bfseries} {\color{DropCapViolet}\thesubsection}{0.8em}{} \titleformat{\subsubsection} {\color{black}\normalfont\normalsize\sffamily\bfseries} {\color{DropCapViolet}\thesubsubsection}{0.8em}{} \setcounter{secnumdepth}{3} \setcounter{tocdepth}{3} \sloppy \frenchspacing \makeindex % reduce space between items in a list \providecommand{\tightlist}{% \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} %====================% % End of preamble. % %====================% \input{category} \begin{document} ================================================ FILE: src/scraper.py ================================================ import os import pypandoc import pathlib from requests import get from bs4 import BeautifulSoup from mercury_parser import ParserAPI # get the API key at https://mercury.postlight.com/web-parser/ mercury = ParserAPI(api_key='') index_page = 'https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/' def get_toc(): result = [] part = 0 chapter = 0 lists = get_content(get(index_page).content).find_all('ol') for part_list in lists: part += 1 for li in part_list.find_all('li'): chapter += 1 result.append({'chapter': '{}.{}'.format(part, chapter), 'title': li.a.text, 'url': li.a['href']}) chapter = 0 return result def get_content(html): soup = BeautifulSoup(html, 'html.parser') return soup.find(class_='post-content') def write_content(file, content): with file.open('w') as f: f.write(content) def save_images(markup, path): import urllib.request html = get_content(markup) images = html.find_all('img') for img in images: image_url = img.parent['href'] image_file = image_url.split('/')[-1] target_file = path.joinpath(image_file) urllib.request.urlretrieve(image_url, target_file) img['src'] = 'images/' + target_file.name img.parent.replaceWithChildren() # removes the parent and replaces it with the image return html def save_url(chapter, title, url): file_name = '{}.tex'.format(title.replace('/', '\\').replace(':', ' -')) path = pathlib.Path(os.path.join('content', chapter, 'images')) path.mkdir(parents=True, exist_ok=True) p = mercury.parse(url) html = save_images(p.content, path) content = pypandoc.convert_text(html, 'tex', format='html') write_content(path.parent.joinpath(file_name), content) for toc in get_toc(): print(toc) title = toc['title'] chapter = toc['chapter'] url = toc['url'] save_url(chapter, title, url) save_url('0.0', 'Preface', index_page) ================================================ FILE: src/version.tex ================================================ \newcommand\OPTversion{dev}