summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Letan <lthms@soap.coffee>2023-05-13 03:44:38 +0200
committerThomas Letan <lthms@soap.coffee>2023-05-13 03:44:38 +0200
commit1f46d843e7a929015fa10875112bb63ead3b01d7 (patch)
tree7437578fe23cf496875c141759dc2aff0cbfd50c
parentIntegrate the neovim/lsp post to the Misc series (diff)
The great rewrite of 2023
-rw-r--r--.gitignore193
-rw-r--r--commands.mk8
-rw-r--r--coq.mk29
-rw-r--r--dependencies.mk19
-rw-r--r--dependencies.opam26
-rw-r--r--dune-project13
-rwxr-xr-xhooks/post-commit3
-rw-r--r--img/flycheck-inline.pngbin0 -> 95774 bytes
-rw-r--r--img/good-highlighting.pngbin0 -> 54642 bytes
-rw-r--r--img/select-theme.pngbin0 -> 30903 bytes
-rw-r--r--img/spatial-shell-example.pngbin0 -> 190003 bytes
-rw-r--r--img/spatial-shell.pngbin0 -> 726132 bytes
-rw-r--r--img/spatial-sway-preview.pngbin0 -> 1459382 bytes
-rw-r--r--img/thinking.pngbin0 -> 293501 bytes
-rw-r--r--img/wrong-highlighting.pngbin0 -> 54066 bytes
-rw-r--r--literate-programming.mk19
-rw-r--r--logs/.gitkeep0
-rw-r--r--makefile66
-rw-r--r--org.mk14
-rw-r--r--package.json11
-rw-r--r--plugins/archives-index.lua22
-rw-r--r--plugins/clean-up.lua19
-rw-r--r--plugins/external-urls.lua11
-rw-r--r--plugins/footnote-postprocess.lua48
-rw-r--r--plugins/meta.lua19
-rw-r--r--plugins/move-tags.lua7
-rw-r--r--plugins/notes.lua14
-rw-r--r--plugins/series-index.lua39
-rw-r--r--plugins/series.lua8
-rw-r--r--plugins/tags-index.lua72
-rw-r--r--plugins/urls-rewriting.lua19
-rwxr-xr-xscripts/capture.sh14
-rwxr-xr-xscripts/gen-deps.sh14
-rwxr-xr-xscripts/history.sh107
-rw-r--r--scripts/init.el183
-rwxr-xr-xscripts/md-render.js112
-rwxr-xr-xscripts/pretty-echo.sh6
-rw-r--r--scripts/render-equations.js11
-rwxr-xr-xscripts/update-gitignore.sh16
-rw-r--r--site/files/coqffi-tutorial.tar.gzbin0 -> 1714 bytes
-rw-r--r--site/files/coqffi_jfla21.pdfbin196859 -> 0 bytes
-rw-r--r--site/img/echo-deps.svg75
-rw-r--r--site/img/eldoc-overlay.pngbin81303 -> 0 bytes
-rw-r--r--site/img/flycheck-inline.pngbin95774 -> 80913 bytes
-rw-r--r--site/img/good-highlighting.pngbin54642 -> 42107 bytes
-rw-r--r--site/img/icons.svg45
-rw-r--r--site/img/knitting-20200901.jpegbin1616958 -> 0 bytes
-rw-r--r--site/img/pixel.pngbin26319 -> 0 bytes
-rw-r--r--site/img/select-theme.pngbin30903 -> 26716 bytes
-rw-r--r--site/img/spatial-shell-example.pngbin190003 -> 140752 bytes
-rw-r--r--site/img/spatial-shell.pngbin726132 -> 607954 bytes
-rw-r--r--site/img/spatial-sway-preview.pngbin1459382 -> 1200162 bytes
-rw-r--r--site/img/thinking.pngbin293501 -> 136549 bytes
-rw-r--r--site/img/vampy.jpgbin26633 -> 0 bytes
-rw-r--r--site/img/wrong-highlighting.pngbin54066 -> 42076 bytes
-rw-r--r--site/index.md42
-rw-r--r--site/index.org35
-rw-r--r--site/news/August2022.org126
-rw-r--r--site/news/ColorlessThemes-0.2.org23
-rw-r--r--site/news/MonthlyRetrospectives.org16
-rw-r--r--site/news/November2022.org33
-rw-r--r--site/news/September2022.org96
-rw-r--r--site/news/index.html143
-rw-r--r--site/opinions/StackedGit.org344
-rw-r--r--site/opinions/StackedGit2.org121
-rw-r--r--site/opinions/StackedGitPatchTheory.org112
-rw-r--r--site/opinions/index.org31
-rw-r--r--site/posts/AlgebraicDatatypes.md711
-rw-r--r--site/posts/AlgebraicDatatypes.v682
-rw-r--r--site/posts/August2022.md136
-rw-r--r--site/posts/CFTSpatialShell.md (renamed from site/news/CFTSpatialShell.org)156
-rw-r--r--site/posts/CleopatraV1.md402
-rw-r--r--site/posts/CleopatraV1.org324
-rw-r--r--site/posts/ClightIntroduction.md386
-rw-r--r--site/posts/ClightIntroduction.v357
-rw-r--r--site/posts/ColorlessThemes-0.2.md29
-rw-r--r--site/posts/Coqffi-1-0-0.md522
-rw-r--r--site/posts/Coqffi.org18
-rw-r--r--site/posts/CoqffiEcho.md519
-rw-r--r--site/posts/CoqffiEcho.org471
-rw-r--r--site/posts/CoqffiIntro.org516
-rw-r--r--site/posts/DiscoveringCommonLisp.md252
-rw-r--r--site/posts/DiscoveringCommonLisp.org264
-rw-r--r--site/posts/EndOfPhd.md81
-rw-r--r--site/posts/ExtensibleTypeSafeErrorHandling.md420
-rw-r--r--site/posts/ExtensibleTypeSafeErrorHandling.org398
-rw-r--r--site/posts/Ltac.org34
-rw-r--r--site/posts/LtacMetaprogramming.md279
-rw-r--r--site/posts/LtacMetaprogramming.v254
-rw-r--r--site/posts/LtacPatternMatching.md203
-rw-r--r--site/posts/LtacPatternMatching.v188
-rw-r--r--site/posts/MixingLtacAndGallina.md186
-rw-r--r--site/posts/MixingLtacAndGallina.v165
-rw-r--r--site/posts/MonadTransformers.md (renamed from site/opinions/MonadTransformers.org)83
-rw-r--r--site/posts/NeoVimOcamlInterfacesAndLSP.org54
-rw-r--r--site/posts/NeovimOCamlTreeSitterAndLSP.md56
-rw-r--r--site/posts/November2022.md46
-rw-r--r--site/posts/RankNTypesInOCaml.md57
-rw-r--r--site/posts/RankNTypesInOCaml.org54
-rw-r--r--site/posts/RewritingInCoq.md384
-rw-r--r--site/posts/RewritingInCoq.v349
-rw-r--r--site/posts/September2022.md116
-rw-r--r--site/posts/StackedGit.md275
-rw-r--r--site/posts/StackedGit2.md138
-rw-r--r--site/posts/StackedGitPatchTheory.md119
-rw-r--r--site/posts/StronglySpecifiedFunctions.org16
-rw-r--r--site/posts/StronglySpecifiedFunctionsProgram.md553
-rw-r--r--site/posts/StronglySpecifiedFunctionsProgram.v526
-rw-r--r--site/posts/StronglySpecifiedFunctionsRefine.md409
-rw-r--r--site/posts/StronglySpecifiedFunctionsRefine.v396
-rw-r--r--site/posts/Thanks.org47
-rw-r--r--site/posts/cleopatra.org66
-rw-r--r--site/posts/cleopatra/commands.org36
-rw-r--r--site/posts/cleopatra/coq.org47
-rw-r--r--site/posts/cleopatra/dependencies.org95
-rw-r--r--site/posts/cleopatra/literate-programming.org82
-rw-r--r--site/posts/cleopatra/org.org144
-rw-r--r--site/posts/cleopatra/soupault.org702
-rw-r--r--site/posts/cleopatra/theme.org573
-rw-r--r--site/posts/coq.org44
-rw-r--r--site/posts/haskell.org13
-rw-r--r--site/posts/index.md3
-rw-r--r--site/posts/index.org28
-rw-r--r--site/posts/meta.org16
-rw-r--r--site/posts/miscellaneous.org24
-rw-r--r--site/projects/index.org5
-rw-r--r--site/projects/keyr/stats.html107
-rw-r--r--site/projects/knitting.html10
-rw-r--r--site/series/Ltac.md22
-rw-r--r--site/series/Retrospectives.md9
-rw-r--r--site/series/StronglySpecifiedFunctions.md19
-rw-r--r--site/series/index.md7
-rw-r--r--site/styles/highlight.css10
-rw-r--r--site/tags/index.md3
-rw-r--r--soupault.conf102
-rw-r--r--soupault.mk8
-rw-r--r--soupault.toml146
-rw-r--r--style.css459
-rw-r--r--templates/history.html33
-rw-r--r--templates/index_archives_full.html17
-rw-r--r--templates/index_archives_short.html12
-rw-r--r--templates/index_series.html9
-rw-r--r--templates/index_tags.html8
-rw-r--r--templates/main.html67
-rw-r--r--theme.mk7
145 files changed, 7525 insertions, 9423 deletions
diff --git a/.gitignore b/.gitignore
index 015567f..3387aa9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,189 +1,8 @@
-*~
-.soupault-cache
-
-# begin generated files
-.commands.deps
-.coq.deps
-.dependencies.deps
-.emacs.d
-.emacs.d/cache
-.lia.cache
-.literate-programming.deps
-.org.deps
-.soupault.deps
-.theme.deps
-_opam
-logs/coqffi-tutorial.stderr
-logs/site--index.html.stderr
-logs/site--news--August2022.html.stderr
-logs/site--news--CFTSpatialShell.html.stderr
-logs/site--news--ColorlessThemes-0.2.html.stderr
-logs/site--news--MonthlyRetrospectives.html.stderr
-logs/site--news--November2022.html.stderr
-logs/site--news--September2022.html.stderr
-logs/site--opinions--index.html.stderr
-logs/site--opinions--MonadTransformers.html.stderr
-logs/site--opinions--StackedGit2.html.stderr
-logs/site--opinions--StackedGit.html.stderr
-logs/site--opinions--StackedGitPatchTheory.html.stderr
-logs/site--posts--cleopatra--commands.html.stderr
-logs/site--posts--cleopatra--coq.html.stderr
-logs/site--posts--cleopatra--dependencies.html.stderr
-logs/site--posts--cleopatra.html.stderr
-logs/site--posts--cleopatra--literate-programming.html.stderr
-logs/site--posts--cleopatra--org.html.stderr
-logs/site--posts--cleopatra--soupault.html.stderr
-logs/site--posts--cleopatra--theme.html.stderr
-logs/site--posts--CleopatraV1.html.stderr
-logs/site--posts--CoqffiEcho.html.stderr
-logs/site--posts--Coqffi.html.stderr
-logs/site--posts--CoqffiIntro.html.stderr
-logs/site--posts--coq.html.stderr
-logs/site--posts--DiscoveringCommonLisp.html.stderr
-logs/site--posts--ExtensibleTypeSafeErrorHandling.html.stderr
-logs/site--posts--haskell.html.stderr
-logs/site--posts--index.html.stderr
-logs/site--posts--Ltac.html.stderr
-logs/site--posts--meta.html.stderr
-logs/site--posts--miscellaneous.html.stderr
-logs/site--posts--NeoVimOcamlInterfacesAndLSP.html.stderr
-logs/site--posts--RankNTypesInOCaml.html.stderr
-logs/site--posts--StronglySpecifiedFunctions.html.stderr
-logs/site--posts--Thanks.html.stderr
-logs/site--projects--index.html.stderr
-logs/start-server.stderr
-logs/tangling-lp.stderr
-logs/coqffi-tutorial.stdout
-logs/site--index.html.stdout
-logs/site--news--August2022.html.stdout
-logs/site--news--CFTSpatialShell.html.stdout
-logs/site--news--ColorlessThemes-0.2.html.stdout
-logs/site--news--MonthlyRetrospectives.html.stdout
-logs/site--news--November2022.html.stdout
-logs/site--news--September2022.html.stdout
-logs/site--opinions--index.html.stdout
-logs/site--opinions--MonadTransformers.html.stdout
-logs/site--opinions--StackedGit2.html.stdout
-logs/site--opinions--StackedGit.html.stdout
-logs/site--opinions--StackedGitPatchTheory.html.stdout
-logs/site--posts--cleopatra--commands.html.stdout
-logs/site--posts--cleopatra--coq.html.stdout
-logs/site--posts--cleopatra--dependencies.html.stdout
-logs/site--posts--cleopatra.html.stdout
-logs/site--posts--cleopatra--literate-programming.html.stdout
-logs/site--posts--cleopatra--org.html.stdout
-logs/site--posts--cleopatra--soupault.html.stdout
-logs/site--posts--cleopatra--theme.html.stdout
-logs/site--posts--CleopatraV1.html.stdout
-logs/site--posts--CoqffiEcho.html.stdout
-logs/site--posts--Coqffi.html.stdout
-logs/site--posts--CoqffiIntro.html.stdout
-logs/site--posts--coq.html.stdout
-logs/site--posts--DiscoveringCommonLisp.html.stdout
-logs/site--posts--ExtensibleTypeSafeErrorHandling.html.stdout
-logs/site--posts--haskell.html.stdout
-logs/site--posts--index.html.stdout
-logs/site--posts--Ltac.html.stdout
-logs/site--posts--meta.html.stdout
-logs/site--posts--miscellaneous.html.stdout
-logs/site--posts--NeoVimOcamlInterfacesAndLSP.html.stdout
-logs/site--posts--RankNTypesInOCaml.html.stdout
-logs/site--posts--StronglySpecifiedFunctions.html.stdout
-logs/site--posts--Thanks.html.stdout
-logs/site--projects--index.html.stdout
-logs/start-server.stdout
-logs/tangling-lp.stdout
-lp/
-node_modules
-out
+out/
+_build/
+_opam/
+node_modules/
package-lock.json
-rss.json
-site/files/coqffi-tutorial.tar.gz
-site/index.html
-site/news/August2022.html
-site/news/CFTSpatialShell.html
-site/news/ColorlessThemes-0.2.html
-site/news/MonthlyRetrospectives.html
-site/news/November2022.html
-site/news/September2022.html
-site/opinions/MonadTransformers.html
-site/opinions/StackedGit.html
-site/opinions/StackedGit2.html
-site/opinions/StackedGitPatchTheory.html
-site/opinions/index.html
-site/posts/.AlgebraicDatatypes.aux
-site/posts/.ClightIntroduction.aux
-site/posts/.LtacMetaprogramming.aux
-site/posts/.LtacPatternMatching.aux
-site/posts/.MixingLtacAndGallina.aux
-site/posts/.RewritingInCoq.aux
-site/posts/.StronglySpecifiedFunctionsProgram.aux
-site/posts/.StronglySpecifiedFunctionsRefine.aux
-site/posts/AlgebraicDatatypes.glob
-site/posts/AlgebraicDatatypes.html
-site/posts/AlgebraicDatatypes.vo
-site/posts/AlgebraicDatatypes.vok
-site/posts/AlgebraicDatatypes.vos
-site/posts/CleopatraV1.html
-site/posts/ClightIntroduction.glob
-site/posts/ClightIntroduction.html
-site/posts/ClightIntroduction.vo
-site/posts/ClightIntroduction.vok
-site/posts/ClightIntroduction.vos
-site/posts/Coqffi.html
-site/posts/CoqffiEcho.html
-site/posts/CoqffiIntro.html
-site/posts/DiscoveringCommonLisp.html
-site/posts/ExtensibleTypeSafeErrorHandling.html
-site/posts/Ltac.html
-site/posts/LtacMetaprogramming.glob
-site/posts/LtacMetaprogramming.html
-site/posts/LtacMetaprogramming.vo
-site/posts/LtacMetaprogramming.vok
-site/posts/LtacMetaprogramming.vos
-site/posts/LtacPatternMatching.glob
-site/posts/LtacPatternMatching.html
-site/posts/LtacPatternMatching.vo
-site/posts/LtacPatternMatching.vok
-site/posts/LtacPatternMatching.vos
-site/posts/MixingLtacAndGallina.glob
-site/posts/MixingLtacAndGallina.html
-site/posts/MixingLtacAndGallina.vo
-site/posts/MixingLtacAndGallina.vok
-site/posts/MixingLtacAndGallina.vos
-site/posts/NeoVimOcamlInterfacesAndLSP.html
-site/posts/RankNTypesInOCaml.html
-site/posts/RewritingInCoq.glob
-site/posts/RewritingInCoq.html
-site/posts/RewritingInCoq.vo
-site/posts/RewritingInCoq.vok
-site/posts/RewritingInCoq.vos
-site/posts/StronglySpecifiedFunctions.html
-site/posts/StronglySpecifiedFunctionsProgram.glob
-site/posts/StronglySpecifiedFunctionsProgram.html
-site/posts/StronglySpecifiedFunctionsProgram.vo
-site/posts/StronglySpecifiedFunctionsProgram.vok
-site/posts/StronglySpecifiedFunctionsProgram.vos
-site/posts/StronglySpecifiedFunctionsRefine.glob
-site/posts/StronglySpecifiedFunctionsRefine.html
-site/posts/StronglySpecifiedFunctionsRefine.vo
-site/posts/StronglySpecifiedFunctionsRefine.vok
-site/posts/StronglySpecifiedFunctionsRefine.vos
-site/posts/Thanks.html
-site/posts/cleopatra.html
-site/posts/cleopatra/commands.html
-site/posts/cleopatra/coq.html
-site/posts/cleopatra/dependencies.html
-site/posts/cleopatra/literate-programming.html
-site/posts/cleopatra/org.html
-site/posts/cleopatra/soupault.html
-site/posts/cleopatra/theme.html
-site/posts/coq.html
-site/posts/deps.svg
-site/posts/haskell.html
-site/posts/index.html
-site/posts/meta.html
-site/posts/miscellaneous.html
-site/projects/index.html
+.soupault-cache/
style.min.css
-# end generated files
+site/img/*.png
diff --git a/commands.mk b/commands.mk
deleted file mode 100644
index 1f93255..0000000
--- a/commands.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-serve :
- @pretty-echo.sh "Spwaning" "HTTP server"
- @cd out && python -m http.server
-
-update :
- @pretty-echo.sh "Updating" "OCaml dependencies"
- @opam update
- @opam upgrade -y
diff --git a/coq.mk b/coq.mk
deleted file mode 100644
index 54ce8ae..0000000
--- a/coq.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-COQ_POSTS := $(shell find site/ -name "*.v")
-COQ_HTML := $(COQ_POSTS:.v=.html)
-COQ_ARTIFACTS := $(COQ_POSTS:.v=.vo) \
- $(COQ_POSTS:.v=.vok) \
- $(COQ_POSTS:.v=.vos) \
- $(COQ_POSTS:.v=.glob) \
- $(join $(dir ${COQ_POSTS}),$(addprefix ".",$(notdir $(COQ_POSTS:.v=.aux))))
-
-coq-build : ${COQ_HTML}
-
-soupault-build : coq-build
-
-ARTIFACTS += ${COQ_ARTIFACTS} .lia.cache
-ARTIFACTS += ${COQ_HTML}
-
-COQLIB := "https://coq.inria.fr/distrib/current/stdlib/"
-COQCARG := -async-proofs-cache force \
- -w -custom-entry-overriden
-COQDOCARG := --no-index --charset utf8 --short \
- --body-only --coqlib "${COQLIB}" \
- --external "https://coq-community.org/coq-ext-lib/v0.11.2/" ExtLib \
- --external "https://compcert.org/doc/html" compcert \
- --external "https://lysxia.github.io/coq-simple-io" SimpleIO
-
-%.html : %.v coq.mk _opam/init
- @pretty-echo.sh Exporting "$*.v"
- @coqc ${COQCARG} $<
- @coqdoc ${COQDOCARG} -d $(shell dirname $<) $<
- @rm -f $(shell dirname $<)/coqdoc.css
diff --git a/dependencies.mk b/dependencies.mk
deleted file mode 100644
index e933f77..0000000
--- a/dependencies.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-OCAML_VERSION := 4.14.1
-OCAML := ocaml-base-compiler.${OCAML_VERSION}
-
-_opam/init :
- @pretty-echo.sh "Creating" "a local Opam switch"
- @opam switch create . ${OCAML} --repos default,coq-released || true
- @pretty-echo.sh "Installing" "OCaml dependencies"
- @opam install dune.3.7.1 coq-coqffi.1.0.0~beta8 coq-simple-io.1.5.0 soupault.4.5.0 coq.8.13.2 coq-compcert.3.8 coq-serapi mustache -y
- @touch $@
-
-CONFIGURE += _opam
-
-package-lock.json : package.json
- @pretty-echo.sh "Installing" "frontend dependencies"
- @npm install
-
-CONFIGURE += package-lock.json node_modules
-
-dependencies-prebuild : _opam/init package-lock.json
diff --git a/dependencies.opam b/dependencies.opam
new file mode 100644
index 0000000..5bcff36
--- /dev/null
+++ b/dependencies.opam
@@ -0,0 +1,26 @@
+# This file is generated by dune, edit dune-project instead
+opam-version: "2.0"
+synopsis: "OCaml dependencies for lthms’ website"
+maintainer: ["Thomas Letan <lthms@soap.coffee>"]
+authors: ["Thomas Letan <lthms@soap.coffee>"]
+homepage: "https://soap.coffee/~lthms"
+bug-reports: "mailto:~lthms/public-inbox@lists.sr.ht"
+depends: [
+ "dune" {>= "3.7"}
+ "soupault"
+ "odoc" {with-doc}
+]
+build: [
+ ["dune" "subst"] {dev}
+ [
+ "dune"
+ "build"
+ "-p"
+ name
+ "-j"
+ jobs
+ "@install"
+ "@runtest" {with-test}
+ "@doc" {with-doc}
+ ]
+]
diff --git a/dune-project b/dune-project
new file mode 100644
index 0000000..f19e328
--- /dev/null
+++ b/dune-project
@@ -0,0 +1,13 @@
+(lang dune 3.7)
+(generate_opam_files true)
+(authors "Thomas Letan <lthms@soap.coffee>")
+(maintainers "Thomas Letan <lthms@soap.coffee>")
+(homepage "https://soap.coffee/~lthms")
+(bug_reports "mailto:~lthms/public-inbox@lists.sr.ht")
+
+(package
+ (allow_empty)
+ (synopsis "OCaml dependencies for lthms’ website")
+ (name dependencies)
+ (depends soupault))
+
diff --git a/hooks/post-commit b/hooks/post-commit
deleted file mode 100755
index 9aad251..0000000
--- a/hooks/post-commit
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-rm -rf $(git rev-parse --show-toplevel)/.soupault-cache
diff --git a/img/flycheck-inline.png b/img/flycheck-inline.png
new file mode 100644
index 0000000..5c84fa2
--- /dev/null
+++ b/img/flycheck-inline.png
Binary files differ
diff --git a/img/good-highlighting.png b/img/good-highlighting.png
new file mode 100644
index 0000000..d05dbd5
--- /dev/null
+++ b/img/good-highlighting.png
Binary files differ
diff --git a/img/select-theme.png b/img/select-theme.png
new file mode 100644
index 0000000..4467c36
--- /dev/null
+++ b/img/select-theme.png
Binary files differ
diff --git a/img/spatial-shell-example.png b/img/spatial-shell-example.png
new file mode 100644
index 0000000..4e03e10
--- /dev/null
+++ b/img/spatial-shell-example.png
Binary files differ
diff --git a/img/spatial-shell.png b/img/spatial-shell.png
new file mode 100644
index 0000000..cb348c2
--- /dev/null
+++ b/img/spatial-shell.png
Binary files differ
diff --git a/img/spatial-sway-preview.png b/img/spatial-sway-preview.png
new file mode 100644
index 0000000..b579587
--- /dev/null
+++ b/img/spatial-sway-preview.png
Binary files differ
diff --git a/img/thinking.png b/img/thinking.png
new file mode 100644
index 0000000..a47f2c4
--- /dev/null
+++ b/img/thinking.png
Binary files differ
diff --git a/img/wrong-highlighting.png b/img/wrong-highlighting.png
new file mode 100644
index 0000000..3926ead
--- /dev/null
+++ b/img/wrong-highlighting.png
Binary files differ
diff --git a/literate-programming.mk b/literate-programming.mk
deleted file mode 100644
index 7a3142b..0000000
--- a/literate-programming.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-literate-programming-prebuild : site/posts/CoqffiEcho.org
- @pretty-echo.sh "Tangling" "literate programming project"
- @capture.sh tangling-lp ${EMACS} --eval "(cleopatra:export-lp)"
-
-ARTIFACTS += lp/ site/posts/deps.svg
-
-COQFFI_ARCHIVE := site/files/coqffi-tutorial.tar.gz
-
-${COQFFI_ARCHIVE} : literate-programming-prebuild _opam/init
- @pretty-echo.sh "Building" "coqffi tutorial"
- @cd lp/coqffi-tutorial; dune build --display quiet
- @pretty-echo.sh "Archiving" "coqffi tutorial"
- @rm -f ${COQFFI_ARCHIVE}
- @capture.sh coqffi-tutorial tar --exclude="_build" -C lp/ -czvf ${COQFFI_ARCHIVE} coqffi-tutorial
-
-literate-programming-build : ${COQFFI_ARCHIVE}
-soupault-build : ${COQFFI_ARCHIVE}
-
-ARTIFACTS += ${COQFFI_ARCHIVE}
diff --git a/logs/.gitkeep b/logs/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/logs/.gitkeep
+++ /dev/null
diff --git a/makefile b/makefile
index e869bee..bc8c5de 100644
--- a/makefile
+++ b/makefile
@@ -1,49 +1,39 @@
-PATH := scripts/:${PATH}
+IMAGES = $(wildcard img/*.png)
+COMPRESSED_IMAGES = $(foreach img, ${IMAGES}, site/${img})
+HIGHLIGHT_THEME = github
-ARTIFACTS := logs/*.stderr logs/*.stdout
-CONFIGURE := .emacs.d
+.PHONY: default
+default: build
-PROCS := $(wildcard *.mk)
-PROCS_DEPS := $(foreach proc,$(PROCS:.mk=.deps),.${proc})
+.PHONY: build-deps
+build-deps: build-ocaml-deps build-node-deps
-CMD ?= postbuild
+.PHONY: build-node-deps
+build-node-deps: package-lock.json
-EMACS_NAME=cleopatra
+.PHONY: build-ocaml-deps
+build-ocaml-deps: _opam/.init
+ @opam pin dependencies . --no-action --yes
+ @opam install dependencies --deps-only --yes
-EMACS := emacsclient -s ${EMACS_NAME}
+_opam/.init:
+ @opam switch create . ocaml-system --yes --no-install --deps-only || true
+ @touch $@
-init : ${PROCS_DEPS} needs-emacs
- @make ${CMD}
+package-lock.json: package.json
+ @npm install
-needs-emacs :
- @pretty-echo.sh "Starting" "emacs daemon"
- @${EMACS} -s ${EMACS_NAME} --eval "(kill-emacs)" 2> /dev/null || true
- @ROOT=$(shell pwd) capture.sh "start-server" emacs --daemon=${EMACS_NAME} -Q --load="scripts/init.el"
+style.min.css: style.css package-lock.json
+ @./scripts/css.sh
-.PHONY : needs-emacs
+site/styles/highlight.css: package-lock.json .FORCE
+ @cp $(shell npm root)/highlight.js/styles/${HIGHLIGHT_THEME}.css $@
-.%.deps : %.mk makefile
- @gen-deps.sh $< $@
+site/img/%.png: img/%.png
+ @pngcrush -q $^ $@
--include ${PROCS_DEPS}
+.PHONY:build
+build: style.min.css site/styles/highlight.css ${COMPRESSED_IMAGES}
+ @soupault
-prebuild :
-build : prebuild
-postbuild : build
-
-postbuild :
- @pretty-echo.sh "Updating" ".gitignore"
- @update-gitignore.sh $(sort ${CONFIGURE} ${ARTIFACTS} ${PROCS_DEPS})
- @pretty-echo.sh "Killing" "emacs daemon"
- @${EMACS} -s ${EMACS_NAME} --eval "(kill-emacs)"
- @rm -f $(wildcard .*.deps)
-
-.PHONY: prebuild build postbuild
-
-clean :
- @rm -rf ${ARTIFACTS}
-
-cleanall : clean
- @rm -rf ${CONFIGURE}
-
-.PHONY : clean cleanall
+.FORCE:
diff --git a/org.mk b/org.mk
deleted file mode 100644
index b66c94b..0000000
--- a/org.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-ORG_IN := $(shell find site/ -name "*.org")
-ORG_OUT := $(ORG_IN:.org=.html)
-
-org-build : ${ORG_OUT}
-
-soupault-build : org-build
-
-ARTIFACTS += ${ORG_OUT} .emacs.d/cache
-
-site/posts/index.html : site/posts/haskell.org site/posts/miscellaneous.org site/posts/meta.org site/posts/coq.org
-
-%.html : %.org org.mk
- @pretty-echo.sh Exporting "$*.org"
- @capture.sh "$@" ${EMACS} --eval "(cleopatra:export-org \"$<\")"
diff --git a/package.json b/package.json
index d6d0609..d4b799f 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,14 @@
{
"dependencies": {
- "katex": "^0.16.4",
"minify": "^9.2.0",
- "normalize.css": "^8.0.1"
+ "normalize.css": "^8.0.1",
+ "markdown-it": "^13.0.1",
+ "markdown-it-footnote": "^3.0.3",
+ "markdown-it-figure": "^0.2.0",
+ "markdown-it-highlightjs": "^4.0.1",
+ "markdown-it-meta": "^0.0.1",
+ "markdown-it-custom-block": "^0.1.2",
+ "markdown-it-mermaid": "^0.2.5",
+ "@ryanxcharles/markdown-it-katex": "^4.0.1"
}
}
diff --git a/plugins/archives-index.lua b/plugins/archives-index.lua
new file mode 100644
index 0000000..aceb7a9
--- /dev/null
+++ b/plugins/archives-index.lua
@@ -0,0 +1,22 @@
+env = {}
+
+container = HTML.select_one(page, config["index_selector"])
+container_content = HTML.inner_html(container)
+
+if Value.is_string(container_content) then
+ container_content = String.to_number(container_content)
+end
+
+template = nil
+
+if container_content then
+ env['contents'] = Table.take(site_index, container_content)
+ template = "index_short_template_file"
+else
+ env['contents'] = site_index
+ template = "index_full_template_file"
+end
+
+template = Sys.read_file(config[template])
+rendered_entries = HTML.parse(String.render_template(template, env))
+HTML.replace_content(container, rendered_entries)
diff --git a/plugins/clean-up.lua b/plugins/clean-up.lua
deleted file mode 100644
index 6325e59..0000000
--- a/plugins/clean-up.lua
+++ /dev/null
@@ -1,19 +0,0 @@
-function remove_if_empty(html)
- if String.trim(HTML.inner_html(html)) == "" then
- HTML.delete(html)
- end
-end
-
-function remove_all_if_empty(cls)
- local elements = HTML.select(page, cls)
-
- local i = 1
- while elements[i] do
- local element = elements[i]
- remove_if_empty(element)
- i = i + 1
- end
-end
-
-remove_all_if_empty("p") -- introduced by org-mode
-remove_all_if_empty("div.code") -- introduced by coqdoc
diff --git a/plugins/external-urls.lua b/plugins/external-urls.lua
index afd8a1a..7ab72da 100644
--- a/plugins/external-urls.lua
+++ b/plugins/external-urls.lua
@@ -1,5 +1,5 @@
function mark(name)
- return '<span class="icon"><svg><use href="/img/icons.svg#'
+ return '&nbsp;<span class="icon"><svg><use href="/img/icons.svg#'
.. name ..
'"></use></svg></span>'
end
@@ -10,15 +10,18 @@ index, link = next(links)
while index do
href = HTML.get_attribute(link, "href")
+ todo = not HTML.get_attribute(link, "marked")
- if href then
+ if href and todo then
if Regex.match(href, "^https?://github.com") then
icon = HTML.parse(mark("github"))
- HTML.append_child(link, icon)
+ HTML.insert_after(link, icon)
elseif Regex.match(href, "^https?://") then
icon = HTML.parse(mark("external-link"))
- HTML.append_child(link, icon)
+ HTML.insert_after(link, icon)
end
+
+ HTML.set_attribute(link, "marked", "")
end
index, link = next(links, index)
diff --git a/plugins/footnote-postprocess.lua b/plugins/footnote-postprocess.lua
new file mode 100644
index 0000000..5daaa65
--- /dev/null
+++ b/plugins/footnote-postprocess.lua
@@ -0,0 +1,48 @@
+-- Turn `markdown-it-footnote` output into Tufte-compatible sidenotes.
+
+footnotes = HTML.select(page, ".footnote-ref")
+
+index, footnote = next(footnotes)
+
+while index do
+ ahref = HTML.select_one(footnote, "a")
+ href = HTML.get_attribute(ahref, "href")
+ href = Regex.replace(href, "^\#", "")
+ footnote_contents = HTML.select_one(page, "li#" .. href)
+ HTML.delete(HTML.select_one(footnote_contents, ".footnote-backref"))
+
+ paragraphs = HTML.select(footnote_contents, "p")
+ i, p = next(paragraphs)
+
+ while i do
+ HTML.set_tag_name(p, "span")
+ HTML.add_class(p, "footnote-p")
+ i, p = next(paragraphs, i)
+ end
+
+ footnote_contents = HTML.inner_html(footnote_contents)
+
+ label = HTML.create_element("label")
+ HTML.add_class(label, "margin-toggle")
+ HTML.add_class(label, "sidenote-number")
+ HTML.set_attribute(label, "for", href)
+ HTML.insert_after(footnote, label)
+
+ input = HTML.create_element("input")
+ HTML.add_class(input, "margin-toggle")
+ HTML.set_attribute(input, "type", "checkbox")
+ HTML.set_attribute(input, "id", href)
+ HTML.insert_after(label, input)
+
+ contents_node = HTML.create_element("span")
+ HTML.add_class(contents_node, "note")
+ HTML.add_class(contents_node, "sidenote")
+ HTML.append_child(contents_node, HTML.parse(footnote_contents))
+ HTML.insert_after(input, contents_node)
+
+ HTML.delete(footnote)
+ index, footnote = next(footnotes, index)
+end
+
+HTML.delete(HTML.select_one(page, "hr.footnotes-sep"))
+HTML.delete(HTML.select_one(page, "section.footnotes"))
diff --git a/plugins/meta.lua b/plugins/meta.lua
new file mode 100644
index 0000000..23013ca
--- /dev/null
+++ b/plugins/meta.lua
@@ -0,0 +1,19 @@
+description = HTML.select_one(page, "#meta-tags .description")
+
+if description then
+ description_contents = HTML.strip_tags(description)
+ head = HTML.select_one(page, "head")
+ HTML.append_child(head, HTML.parse('<meta type="description" content="' .. description_contents .. '">'))
+else
+ Log.warning("Missing description in " .. page_file)
+end
+
+HTML.delete(description)
+
+timestamps = HTML.select_one(page, "#timestamps")
+
+if timestamps then
+ HTML.delete(timestamps)
+ target = HTML.select_one(page, "#whoami")
+ HTML.insert_after(target, timestamps)
+end
diff --git a/plugins/move-tags.lua b/plugins/move-tags.lua
new file mode 100644
index 0000000..a4eb025
--- /dev/null
+++ b/plugins/move-tags.lua
@@ -0,0 +1,7 @@
+tags = HTML.select_one(page, "#tags-list")
+title = HTML.select_one(page, "h1")
+
+if tags and title then
+ HTML.delete(tags)
+ HTML.insert_after(title, tags)
+end
diff --git a/plugins/notes.lua b/plugins/notes.lua
index bfaa3e2..861ca64 100644
--- a/plugins/notes.lua
+++ b/plugins/notes.lua
@@ -1,4 +1,4 @@
-notes = HTML.select_all_of(page, {".marginnote", ".sidenote"})
+notes = HTML.select_all_of(page, {".marginblock", ".sidenote"})
local index = 1
while notes[index] do
@@ -9,16 +9,24 @@ while notes[index] do
index = index + 1
end
+ofs = 0
notes = HTML.select(page, ".note")
index = 1
while notes[index] do
local note = notes[index]
- if index % 2 == 0 then
+ if (index + ofs) % 2 == 0 then
HTML.add_class(note, "note-right")
else
HTML.add_class(note, "note-left")
end
index = index + 1
-end \ No newline at end of file
+
+ -- the first margin note component (the avatar) takes a lot more space than
+ -- the second one (update dates and tags), so it's interesting that the first
+ -- note after these ones is also on the right.
+ if index == 3 then
+ ofs = 1
+ end
+end
diff --git a/plugins/series-index.lua b/plugins/series-index.lua
new file mode 100644
index 0000000..12b693a
--- /dev/null
+++ b/plugins/series-index.lua
@@ -0,0 +1,39 @@
+env = {}
+current_entry = {}
+
+function append_entry(entry)
+ if entry['series_url'] then
+ if build_dir .. entry['series_url'] == target_file then
+ if not entry['series_prev_url'] then
+ current_entry = entry['url']
+ end
+
+ env[entry['url']] = entry
+ end
+ end
+end
+
+if site_index and site_index[1] then
+
+ index = HTML.select_one(page, config['index_selector'])
+
+ if index then
+ Table.iter_values_ordered(append_entry, site_index)
+
+ res = {}
+ res['entries'] = {}
+ res_count = 1
+
+ while current_entry do
+ res['entries'][res_count] = env[current_entry]
+ res_count = res_count + 1
+
+ current_entry = env[current_entry]['series_next_url']
+ end
+
+ template = Sys.read_file(config["index_template_file"])
+ rendered_entries = HTML.parse(String.render_template(template, res))
+
+ HTML.replace_content(index, rendered_entries)
+ end
+end
diff --git a/plugins/series.lua b/plugins/series.lua
index 37d0e05..b94f136 100644
--- a/plugins/series.lua
+++ b/plugins/series.lua
@@ -10,7 +10,7 @@ function get_title_from_path (path)
Plugin.fail(path .. ' has no <h1> tag')
end
else
- Plugin.fail(path .. ' is not a file')
+ Log.warning('Missing file: ' .. path)
end
end
@@ -41,10 +41,10 @@ function generate_nav_items (cwd, cls, template)
end
end
-cwd = Sys.dirname(page_file)
+cwd = build_dir
-home_template = 'This article is part of the series “<a href="{{ url }}">{{ title }}</a>.”'
-nav_template = '<a href="{{ url }}">{{ title }}</a>'
+home_template = 'This page is part of the series “<a href="/{{ url }}">{{ title }}</a>.”'
+nav_template = '<a href="/{{ url }}">{{ title }}</a>'
generate_nav_items(cwd, ".series", home_template)
generate_nav_items(cwd, ".series-prev", nav_template)
diff --git a/plugins/tags-index.lua b/plugins/tags-index.lua
new file mode 100644
index 0000000..75c7f6b
--- /dev/null
+++ b/plugins/tags-index.lua
@@ -0,0 +1,72 @@
+counts = {}
+env = {}
+
+function append_entry(entry)
+ if entry["tags"] then
+ i, tag = next(entry["tags"])
+
+ while i do
+ if not counts[tag] then
+ env[tag] = {}
+ counts[tag] = 1
+ else
+ counts[tag] = counts[tag] + 1
+ end
+
+ env[tag][counts[tag]] = entry
+
+ i, tag = next(entry["tags"], i)
+ end
+
+ index, entry = next(site_index, index)
+ end
+end
+
+Table.iter_values_ordered(append_entry, site_index)
+
+tags = {}
+tag_count = 1
+
+function append_template_value (tag, entry_list)
+ entry = {}
+ entry['name'] = tag
+ entry['contents'] = env[tag]
+ tags[tag_count] = entry
+ tag_count = tag_count + 1
+end
+
+Table.iter_ordered(append_template_value, env)
+
+res = {}
+res['tags'] = tags
+
+template = Sys.read_file(config["index_template_file"])
+rendered_entries = HTML.parse(String.render_template(template, res))
+
+container = HTML.select_one(page, config["index_selector"])
+HTML.replace_content(container, rendered_entries)
+
+pages = {}
+i = 1
+current = res['tags'][i]
+
+page_template = [[
+<h1><span class="icon"><svg><use href="/img/icons.svg#tag"></use></svg></span> <code>{{ name }}</code></h2>
+<article class="index" id="tags-index">
+ {{ html }}
+</article>
+]]
+
+while current do
+ pages[i] = {}
+ pages[i]['page_file'] = Sys.join_path(Sys.dirname(page_file), current['name'] .. ".html")
+
+ template = Sys.read_file("templates/index_archives_full.html")
+ current['html'] = String.render_template(template, current)
+ page_html = String.render_template(page_template, current)
+
+ pages[i]['page_content'] = page_html
+
+ i = i + 1
+ current = res['tags'][i]
+end
diff --git a/plugins/urls-rewriting.lua b/plugins/urls-rewriting.lua
index 82e4692..192781e 100644
--- a/plugins/urls-rewriting.lua
+++ b/plugins/urls-rewriting.lua
@@ -19,13 +19,22 @@ function prefix_urls (links, attr, prefix_url)
href = HTML.get_attribute(link, attr)
if href then
- if Regex.match(href, "^/") then
- href = Regex.replace(href, "^/*", "")
- href = prefix_url .. href
- end
+ todo = not Regex.match(href, "^" .. prefix_url .. "*")
+
+ if todo then
+ if Regex.match(href, "^/") then
+ href = Regex.replace(href, "^/*", "")
+ href = prefix_url .. href
+ end
+
+ if Regex.match(href, "index.html$") then
+ href = Regex.replace(href, "index.html$", "")
+ end
- HTML.set_attribute(link, attr, href)
+ HTML.set_attribute(link, attr, href)
+ end
end
+
index, link = next(links, index)
end
end
diff --git a/scripts/capture.sh b/scripts/capture.sh
deleted file mode 100755
index f343cbb..0000000
--- a/scripts/capture.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh
-
-logfile=logs/$(echo ${1} | sed -e 's/\//--/g')
-
-shift 1
-
-"$@" 1> ${logfile}.stdout 2> ${logfile}.stderr
-exitcode=$?
-
-if [[ ! ${exitcode} -eq 0 ]]; then
- echo -e "\033[0;31mError:\033[0m '"$@"' returned ${exitcode}"
- echo -e "See \033[0;33m${logfile}.stdout\033[0m and \033[0;33m${logfile}.stderr\033[0m"
- exit ${exitcode}
-fi
diff --git a/scripts/gen-deps.sh b/scripts/gen-deps.sh
deleted file mode 100755
index 2283f9b..0000000
--- a/scripts/gen-deps.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh
-
-input=${1}
-output=${2}
-
-proc="$(basename ${input} | cut -f 1 -d '.')"
-
-echo "include ${input}" > ${output}
-echo "prebuild : ${proc}-prebuild" >> ${output}
-echo "build : ${proc}-build" >> ${output}
-echo "postbuild : ${proc}-postbuild" >> ${output}
-echo "${proc}-build : ${proc}-prebuild" >> ${output}
-echo "${proc}-postbuild : ${proc}-build" >> ${output}
-echo ".PHONY : ${proc}-prebuild ${proc}-build ${proc}-postbuild" >> ${output}
diff --git a/scripts/history.sh b/scripts/history.sh
deleted file mode 100755
index 7e6da02..0000000
--- a/scripts/history.sh
+++ /dev/null
@@ -1,107 +0,0 @@
-#!/usr/bin/bash
-
-function main () {
- local file="${1}"
- local template="${2}"
-
- tmp_file=$(mktemp)
- generate_json ${file} > ${tmp_file}
- mustache ${tmp_file} ${template} | tail -n +2
- rm ${tmp_file}
-}
-
-function gitlog () {
- local file="${1}"
- git --no-pager log \
- --follow \
- --stat=10000 \
- --pretty=format:'%s%n%h%n%H%n%cs%n' \
- "${file}"
-}
-
-function parse_filename () {
- local line="${1}"
- local shrink='s/ *\(.*\) \+|.*/\1/'
- local unfold='s/\(.*\){\(.*\) => \(.*\)}/\1\3/'
-
- echo ${line} | sed -e "${shrink}" | sed -e "${unfold}"
-}
-
-function generate_json () {
- local input="${1}"
- local logs="$(gitlog ${input})"
-
- if [ ! $? -eq 0 ]; then
- exit 1
- fi
-
- let "idx=0"
- let "last_entry=$(echo "${logs}" | wc -l) / 8"
-
- local subject=""
- local abbr_hash=""
- local hash=""
- local date=""
- local file=""
- local created="true"
- local modified="false"
-
- echo -n "{"
- echo -n "\"file\": \"${input}\""
- echo -n ",\"history\": ["
-
- while read -r subject; do
- read -r abbr_hash
- read -r hash
- read -r date
- read -r # empty line
- read -r file
- read -r # short log
- read -r # empty line
-
- if [ ${idx} -ne 0 ]; then
- echo -n ","
- fi
-
- if [ ${idx} -eq ${last_entry} ]; then
- created="true"
- modified="false"
- else
- created="false"
- modified="true"
- fi
-
- output_json_entry "${subject}" \
- "${abbr_hash}" \
- "${hash}" \
- "${date}" \
- "$(parse_filename "${file}")" \
- "${created}" \
- "${modified}"
-
- let idx++
- done < <(echo "${logs}")
-
- echo -n "]}"
-}
-
-function output_json_entry () {
- local subject="${1}"
- local abbr_hash="${2}"
- local hash="${3}"
- local date="${4}"
- local file="${5}"
- local created="${6}"
- local last_entry="${7}"
-
- echo -n "{\"subject\": \"${subject}\""
- echo -n ",\"created\":${created}"
- echo -n ",\"modified\":${modified}"
- echo -n ",\"abbr_hash\":\"${abbr_hash}\""
- echo -n ",\"hash\":\"${hash}\""
- echo -n ",\"date\":\"${date}\""
- echo -n ",\"filename\":\"${file}\""
- echo -n "}"
-}
-
-main "$(cat)" "${1}"
diff --git a/scripts/init.el b/scripts/init.el
deleted file mode 100644
index 6c63344..0000000
--- a/scripts/init.el
+++ /dev/null
@@ -1,183 +0,0 @@
-;;; cleopatra.el --- The cleopatra Emacs Library
-;;; Commentary:
-;;; Code:
-(require 'package)
-
-(defun cleopatra:ensure-package-installed (&rest packages)
- "Ensure every PACKAGES is installed."
- (mapcar
- (lambda (package)
- (if (package-installed-p package)
- nil
- (package-install package))
- package)
- packages))
-
-(defvar cleopatra:*emacs-dir* (concat (getenv "ROOT") "/.emacs.d/"))
-
-(setq user-emacs-directory cleopatra:*emacs-dir*)
-(setq package-user-dir (concat cleopatra:*emacs-dir* "packages"))
-
-(setq package-archives
- '(("gnu" . "https://elpa.gnu.org/packages/")
- ("melpa" . "https://melpa.org/packages/")))
-
-(package-initialize)
-
-(or (file-exists-p package-user-dir)
- (package-refresh-contents))
-
-(cleopatra:ensure-package-installed 'use-package)
-
-(require 'use-package)
-
-;; -----------------------------------------------------------------------------
-
-(use-package htmlize :ensure t)
-(use-package ox-tufte :ensure t)
-(use-package tuareg :ensure t
- :config
- (require 'ob-ocaml))
-
-;; -----------------------------------------------------------------------------
-
-(org-babel-do-load-languages
- 'org-babel-load-languages
- '((dot . t)
- (shell . t)
- (ocaml . t)))
-
-(setq backup-inhibited t
- org-export-with-toc nil
- org-html-htmlize-output-type nil
- org-export-with-section-numbers nil
- org-html-doctype "html5"
- org-html-html5-fancy t
- org-src-fontify-natively t
- org-export-with-sub-superscripts nil
- org-confirm-babel-evaluate nil
- org-publish-timestamp-directory (concat cleopatra:*emacs-dir* "cache/")
- org-confirm-babel-evaluate nil
- org-src-preserve-indentation t)
-
-(add-to-list 'org-babel-default-header-args
- '(:mkdirp . "yes")
-(add-to-list 'org-babel-default-header-args
- '(:noweb-sep . "\n\n")))
-
-(add-to-list 'org-entities-user
- '("im" "\\(" nil "<span class=\"imath\">" "" "" ""))
-(add-to-list 'org-entities-user
- '("mi" "\\)" nil "</span>" "" "" ""))
-
-;; Borrowed from Stack Exchange
-;; https://emacs.stackexchange.com/questions/9807/org-mode-dont-change-relative-urls
-(defun export-rel-url (path desc format)
- (cl-case format
- (html (format "<a href=\"%s\">%s</a>" path (or desc path)))
- (latex (format "\\href{%s}{%s}" path (or desc path)))
- (otherwise path)))
-
-(eval-after-load "org"
- '(org-link-set-parameters "rel" :follow #'browse-url :export #'export-rel-url))
-;; --
-
-
-(defun with-keyword (keyword k)
- "Look-up for keyword KEYWORD, and call continuation K with its value."
- (pcase (org-collect-keywords `(,keyword))
- (`((,keyword . ,kw))
- (when kw (funcall k (string-join kw " "))))))
-
-(defun get-keyword (keyword)
- "Look-up for keyword KEYWORD, and returns its value"
- (with-keyword keyword (lambda (x) x)))
-
-(defun get-org-title (path)
- "Fetch the title of an Org file whose path is PATH."
- (with-temp-buffer
- (find-file-read-only path)
- (get-keyword "TITLE")))
-
-(defun insert-title ()
- "Insert the title of the article."
- (with-keyword
- "TITLE"
- (lambda (title)
- (insert
- (format "\n\n@@html:<h1>@@ %s @@html:</h1>@@\n\n" title)))))
-
-(defun insert-series ()
- "Insert the series root link."
- (with-keyword
- "SERIES"
- (lambda (series)
- (insert "\n\n#+attr_html: :class series\n")
- (insert series))))
-
-(defun insert-series-prev ()
- "Insert the series previous article link."
- (with-keyword
- "SERIES_PREV"
- (lambda (series-prev)
- (insert "\n\n#+attr_html: :class series-prev\n")
- (insert series-prev))))
-
-(defun insert-series-next ()
- "Insert the series next article link."
- (with-keyword
- "SERIES_NEXT"
- (lambda (series-next)
- (insert "\n\n#+attr_html: :class series-next\n")
- (insert series-next))))
-
-(defun insert-nav ()
- "Insert the navigation links."
- (when (get-keyword "SERIES")
- (insert "\n\n#+begin_nav\n")
- (insert-series)
- (insert-series-prev)
- (insert-series-next)
- (insert "\n\n#+end_nav\n")))
-
-(defun cleopatra:export-org (input)
- (with-temp-buffer
- (insert-file-contents input)
- (cd (file-name-directory input))
- (beginning-of-buffer)
- (insert-nav)
- (insert-title)
-
- (let ((outfile (concat (file-name-base input) ".html"))
- (org-html-footnotes-section "<!-- %s --><!-- %s -->"))
- (org-export-to-file 'tufte-html outfile nil nil nil t))))
-
-;; -----------------------------------------------------------------------------
-
-(defun cleopatra:tangle-publish (conf filename _pub-dir)
- (let ((pub-dir (plist-get conf :publishing-directory)))
- (if pub-dir
- (with-temp-buffer
- (find-file-read-only filename)
- (cd (getenv "ROOT"))
- (unless (file-exists-p pub-dir)
- (make-directory pub-dir))
- (cd pub-dir)
- (org-babel-tangle))
- (error "cleopatra: missing :publishing-directory option"))))
-
-
-(defun cleopatra:export-lp ()
- (setq-local org-publish-project-alist
- '(("lp"
- :base-directory "site/posts"
- :publishing-directory "lp"
- ;; hand-pick which files to tangle (this save a lots of time)
- :exclude ".*"
- :include ("CoqffiEcho.org")
- :publishing-function cleopatra:tangle-publish)))
-
- (org-publish-all))
-
-(provide 'cleopatra)
-;;; cleopatra.el ends here
diff --git a/scripts/md-render.js b/scripts/md-render.js
new file mode 100755
index 0000000..2be0c32
--- /dev/null
+++ b/scripts/md-render.js
@@ -0,0 +1,112 @@
+#!/usr/bin/node
+
+function renderer() {
+ return require('markdown-it')()
+ .use(require('@ryanxcharles/markdown-it-katex'))
+ .use(require('markdown-it-meta'))
+ .use(require('markdown-it-footnote'))
+ .use(require('markdown-it-figure'))
+ .use(require('markdown-it-custom-block'), {
+ allSeries (placeholder) {
+ return `<div class="index" id="all-series-index"></div>`
+ },
+ series (placeholder) {
+ return `<div class="index" id="series-index"></div>`
+ },
+ tags (placeholder) {
+ return `<div class="index" id="tags-index"></div>`
+ },
+ archives (placeholder) {
+ if (placeholder == 'all') {
+ return `<div class="index" id="archives-index"></div>`
+ } else {
+ return `<div id="archives-index">${placeholder}</div>`
+ }
+ }
+ })
+ .use(require('markdown-it-highlightjs'), { inline: true, auto: false });
+}
+
+function icon(name) {
+ return `<span class="icon"><svg><use href="/img/icons.svg#${name}"></use></svg></span>`;
+}
+
+const fs = require('fs');
+const md = renderer();
+
+const path = process.argv[2];
+
+function calendar(date) {
+ return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
+}
+
+function datetime(date, id) {
+ const options = { year: 'numeric', month: 'long', day: 'numeric' };
+
+ return `<time datetime="${calendar(date)}" id="${id}">${date.toLocaleDateString('en-US', options)}</time>`;
+}
+
+function span(str, cls) {
+ return `<span class="${cls}">${str}</span>`;
+}
+
+fs.readFile(path, 'utf8', (err, data) => {
+ if (err) {
+ process.exit(1);
+ }
+
+ const document = md.render(data);
+ const series = md.meta.series;
+ const published = md.meta.published;
+ const modified = md.meta.modified;
+ const tags = md.meta.tags;
+ const abstract = md.meta.abstract;
+
+ if (abstract) {
+ const abstract_rendered = renderer().render(abstract);
+ process.stdout.write('<div id="meta-tags">');
+ process.stdout.write(`<div class="description">${abstract_rendered}</div>`);
+ process.stdout.write('</div>');
+ }
+
+ process.stdout.write('<span id="timestamps" class="marginblock">');
+
+ if (published || tags) {
+ if (published) {
+ process.stdout.write(span(`${icon('clock')}&nbsp;Published on ${datetime(published, 'published')}`, 'footnote-p full-only narrow'));
+
+ if (modified) {
+ process.stdout.write(span(`${icon('edit')}&nbsp;Modified on ${datetime(modified, 'modified')}`, 'footnote-p full-only narrow'));
+ }
+ }
+ }
+
+ const now = new Date();
+ process.stdout.write(span(`${icon('circle-arrow')}&nbsp;Generated on ${datetime(now, 'generated')}`, 'footnote-p full-only narrow'));
+
+ process.stdout.write('</span>');
+
+ if (series) {
+ process.stdout.write(`<nav id="series-nav"><p class="series">${series.parent}</p>`);
+
+ if (series.prev) {
+ process.stdout.write(`<p class="series-prev">${series.prev}</p>`);
+ }
+
+ if (series.next) {
+ process.stdout.write(`<p class="series-next">${series.next}</p>`);
+ }
+
+ process.stdout.write('</nav>');
+ }
+
+ if (tags) {
+ process.stdout.write('<div id="tags-list">');
+ tags.forEach(tag => {
+ process.stdout.write(`${icon('tag')}&nbsp;<a class="tag" href="/tags/${tag}.html">${tag}</a> `);
+ });
+ process.stdout.write('</div>');
+ }
+
+ process.stdout.write(`<article>${document}</article>`);
+})
diff --git a/scripts/pretty-echo.sh b/scripts/pretty-echo.sh
deleted file mode 100755
index 8b4e149..0000000
--- a/scripts/pretty-echo.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-
-title=${1}
-message=${2}
-
-printf "\033[0;32m%12s \e[0m%s\n" "${title}" "${message}"
diff --git a/scripts/render-equations.js b/scripts/render-equations.js
deleted file mode 100644
index cae348a..0000000
--- a/scripts/render-equations.js
+++ /dev/null
@@ -1,11 +0,0 @@
-var katex = require("katex");
-var fs = require("fs");
-var input = fs.readFileSync(0);
-var displayMode = process.env.DISPLAY != undefined;
-
-var html = katex.renderToString(String.raw`${input}`, {
- throwOnError : false,
- displayModed : displayMode
-});
-
-console.log(html)
diff --git a/scripts/update-gitignore.sh b/scripts/update-gitignore.sh
deleted file mode 100755
index 7b9fafe..0000000
--- a/scripts/update-gitignore.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#+/bin/bash
-BEGIN_MARKER="# begin generated files"
-END_MARKER="# end generated files"
-
-# remove the previous list of generated files to ignore
-sed -i -e "/${BEGIN_MARKER}/,/${END_MARKER}/d" .gitignore
-# remove trailing empty lines
-sed -i -e :a -e '/^\n*$/{$d;N;};/\n$/ba' .gitignore
-
-# output the list of files to ignore
-echo "" >> .gitignore
-echo ${BEGIN_MARKER} >> .gitignore
-for f in $@; do
- echo "${f}" >> .gitignore
-done
-echo ${END_MARKER} >> .gitignore
diff --git a/site/files/coqffi-tutorial.tar.gz b/site/files/coqffi-tutorial.tar.gz
new file mode 100644
index 0000000..ce74cc3
--- /dev/null
+++ b/site/files/coqffi-tutorial.tar.gz
Binary files differ
diff --git a/site/files/coqffi_jfla21.pdf b/site/files/coqffi_jfla21.pdf
deleted file mode 100644
index 4a57127..0000000
--- a/site/files/coqffi_jfla21.pdf
+++ /dev/null
Binary files differ
diff --git a/site/img/echo-deps.svg b/site/img/echo-deps.svg
new file mode 100644
index 0000000..b99a4a6
--- /dev/null
+++ b/site/img/echo-deps.svg
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 8.0.5 (0)
+ -->
+<!-- Title: dependencies Pages: 1 -->
+<svg width="272pt" height="138pt"
+ viewBox="0.00 0.00 271.75 138.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 134)">
+<title>dependencies</title>
+<polygon fill="white" stroke="none" points="-4,4 -4,-134 267.75,-134 267.75,4 -4,4"/>
+<!-- FFI -->
+<g id="node1" class="node">
+<title>FFI</title>
+<polygon fill="none" stroke="black" stroke-dasharray="5,2" points="68.12,-36 4.88,-36 4.88,0 68.12,0 68.12,-36"/>
+<text text-anchor="middle" x="36.5" y="-12.95" font-family="Times,serif" font-size="14.00">Socket.v</text>
+</g>
+<!-- Echo -->
+<g id="node3" class="node">
+<title>Echo</title>
+<polygon fill="none" stroke="black" points="164.25,-36 110,-36 110,0 164.25,0 164.25,-36"/>
+<text text-anchor="middle" x="137.12" y="-12.95" font-family="Times,serif" font-size="14.00">Echo.v</text>
+</g>
+<!-- FFI&#45;&gt;Echo -->
+<g id="edge3" class="edge">
+<title>FFI&#45;&gt;Echo</title>
+<path fill="none" stroke="black" d="M68.43,-18C78.04,-18 88.73,-18 98.72,-18"/>
+<polygon fill="black" stroke="black" points="98.62,-21.5 108.62,-18 98.62,-14.5 98.62,-21.5"/>
+</g>
+<!-- ffi -->
+<g id="node2" class="node">
+<title>ffi</title>
+<polygon fill="none" stroke="black" points="73,-130 0,-130 0,-94 73,-94 73,-130"/>
+<text text-anchor="middle" x="36.5" y="-106.95" font-family="Times,serif" font-size="14.00">socket.mli</text>
+</g>
+<!-- ffi&#45;&gt;FFI -->
+<g id="edge1" class="edge">
+<title>ffi&#45;&gt;FFI</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M36.5,-93.61C36.5,-80.37 36.5,-62.23 36.5,-47.04"/>
+<polygon fill="black" stroke="black" points="40,-47.12 36.5,-37.12 33,-47.12 40,-47.12"/>
+<text text-anchor="middle" x="25.25" y="-59.95" font-family="Times,serif" font-size="14.00">coqffi &#160;&#160;&#160;&#160;&#160;</text>
+</g>
+<!-- echo_ml -->
+<g id="node5" class="node">
+<title>echo_ml</title>
+<polygon fill="none" stroke="black" stroke-dasharray="5,2" points="263.75,-130 201.25,-130 201.25,-94 263.75,-94 263.75,-130"/>
+<text text-anchor="middle" x="232.5" y="-106.95" font-family="Times,serif" font-size="14.00">main.ml</text>
+</g>
+<!-- ffi&#45;&gt;echo_ml -->
+<g id="edge5" class="edge">
+<title>ffi&#45;&gt;echo_ml</title>
+<path fill="none" stroke="black" d="M73.08,-112C106.15,-112 155.39,-112 190.18,-112"/>
+<polygon fill="black" stroke="black" points="189.93,-115.5 199.93,-112 189.93,-108.5 189.93,-115.5"/>
+</g>
+<!-- echo_v -->
+<g id="node4" class="node">
+<title>echo_v</title>
+<polygon fill="none" stroke="black" points="259.62,-36 205.38,-36 205.38,0 259.62,0 259.62,-36"/>
+<text text-anchor="middle" x="232.5" y="-12.95" font-family="Times,serif" font-size="14.00">main.v</text>
+</g>
+<!-- Echo&#45;&gt;echo_v -->
+<g id="edge4" class="edge">
+<title>Echo&#45;&gt;echo_v</title>
+<path fill="none" stroke="black" d="M164.63,-18C173.8,-18 184.25,-18 194.12,-18"/>
+<polygon fill="black" stroke="black" points="193.93,-21.5 203.93,-18 193.93,-14.5 193.93,-21.5"/>
+</g>
+<!-- echo_ml&#45;&gt;echo_v -->
+<g id="edge2" class="edge">
+<title>echo_ml&#45;&gt;echo_v</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M232.5,-82.66C232.5,-67.43 232.5,-49.3 232.5,-36.12"/>
+<polygon fill="black" stroke="black" points="229,-82.61 232.5,-92.61 236,-82.61 229,-82.61"/>
+<text text-anchor="middle" x="221.25" y="-59.95" font-family="Times,serif" font-size="14.00">coqc &#160;&#160;&#160;&#160;</text>
+</g>
+</g>
+</svg>
diff --git a/site/img/eldoc-overlay.png b/site/img/eldoc-overlay.png
deleted file mode 100644
index 4b5a3fe..0000000
--- a/site/img/eldoc-overlay.png
+++ /dev/null
Binary files differ
diff --git a/site/img/flycheck-inline.png b/site/img/flycheck-inline.png
index 5c84fa2..7acc42a 100644
--- a/site/img/flycheck-inline.png
+++ b/site/img/flycheck-inline.png
Binary files differ
diff --git a/site/img/good-highlighting.png b/site/img/good-highlighting.png
index d05dbd5..aa99337 100644
--- a/site/img/good-highlighting.png
+++ b/site/img/good-highlighting.png
Binary files differ
diff --git a/site/img/icons.svg b/site/img/icons.svg
index 917a417..918fd9d 100644
--- a/site/img/icons.svg
+++ b/site/img/icons.svg
@@ -36,4 +36,49 @@
c44.163-14.653,80.185-41.062,108.068-79.226c27.88-38.161,41.825-81.126,41.825-128.906
C438.536,184.851,428.728,148.168,409.132,114.573z"/>
</symbol>
+ <symbol id="clock" viewBox="0 0 512 512">
+ <path d="M464 256A208 208 0 1 1 48 256a208 208 0 1 1 416 0zM0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM232 120V256c0 8 4
+ 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2V120c0-13.3-10.7-24-24-24s-24 10.7-24 24z"/>
+ </symbol>
+ <symbol id="edit" viewBox="0 0 512 512">
+ <path d="M441 58.9L453.1 71c9.4 9.4 9.4 24.6 0 33.9L424 134.1 377.9 88 407 58.9c9.4-9.4 24.6-9.4 33.9 0zM209.8 256.2L344 121.9
+ 390.1 168 255.8 302.2c-2.9 2.9-6.5 5-10.4 6.1l-58.5 16.7 16.7-58.5c1.1-3.9 3.2-7.5 6.1-10.4zM373.1 25L175.8 222.2c-8.7
+ 8.7-15 19.4-18.3 31.1l-28.6 100c-2.4 8.4-.1 17.4 6.1 23.6s15.2 8.5 23.6 6.1l100-28.6c11.8-3.4 22.5-9.7 31.1-18.3L487
+ 138.9c28.1-28.1 28.1-73.7 0-101.8L474.9 25C446.8-3.1 401.2-3.1 373.1 25zM88 64C39.4 64 0 103.4 0 152V424c0 48.6 39.4 88
+ 88 88H360c48.6 0 88-39.4 88-88V312c0-13.3-10.7-24-24-24s-24 10.7-24 24V424c0 22.1-17.9 40-40 40H88c-22.1
+ 0-40-17.9-40-40V152c0-22.1 17.9-40 40-40H200c13.3 0 24-10.7 24-24s-10.7-24-24-24H88z"/>
+ </symbol>
+ <symbol id="circle-arrow" viewBox="0 0 512 512">
+ <path d="M386.3 160H336c-17.7 0-32 14.3-32 32s14.3 32 32 32H464c17.7 0 32-14.3 32-32V64c0-17.7-14.3-32-32-32s-32 14.3-32
+ 32v51.2L414.4 97.6c-87.5-87.5-229.3-87.5-316.8 0s-87.5 229.3 0 316.8s229.3 87.5 316.8 0c12.5-12.5 12.5-32.8
+ 0-45.3s-32.8-12.5-45.3 0c-62.5 62.5-163.8 62.5-226.3 0s-62.5-163.8 0-226.3s163.8-62.5 226.3 0L386.3 160z"/>
+ </symbol>
+ <symbol id="tag" viewBox="0 0 448 512">
+ <path d="M0 80V229.5c0 17 6.7 33.3 18.7 45.3l176 176c25 25 65.5 25 90.5 0L418.7 317.3c25-25 25-65.5
+ 0-90.5l-176-176c-12-12-28.3-18.7-45.3-18.7H48C21.5 32 0 53.5 0 80zm112 32a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/>
+ </symbol>
+ <symbol id="book" viewBox="0 0 448 512">
+ <path d="M96 0C43 0 0 43 0 96V416c0 53 43 96 96 96H384h32c17.7 0 32-14.3 32-32s-14.3-32-32-32V384c17.7 0 32-14.3
+ 32-32V32c0-17.7-14.3-32-32-32H384 96zm0 384H352v64H96c-17.7 0-32-14.3-32-32s14.3-32 32-32zm32-240c0-8.8
+ 7.2-16 16-16H336c8.8 0 16 7.2 16 16s-7.2 16-16 16H144c-8.8 0-16-7.2-16-16zm16 48H336c8.8 0 16 7.2 16 16s-7.2 16-16
+ 16H144c-8.8 0-16-7.2-16-16s7.2-16 16-16z"/>
+ </symbol>
+ <symbol id="home" viewBox="0 0 576 512">
+ <path d="M575.8 255.5c0 18-15 32.1-32 32.1h-32l.7 160.2c0 2.7-.2 5.4-.5 8.1V472c0 22.1-17.9 40-40 40H456c-1.1
+ 0-2.2 0-3.3-.1c-1.4 .1-2.8 .1-4.2 .1H416 392c-22.1 0-40-17.9-40-40V448 384c0-17.7-14.3-32-32-32H256c-17.7 0-32
+ 14.3-32 32v64 24c0 22.1-17.9 40-40 40H160 128.1c-1.5 0-3-.1-4.5-.2c-1.2 .1-2.4 .2-3.6 .2H104c-22.1
+ 0-40-17.9-40-40V360c0-.9 0-1.9 .1-2.8V287.6H32c-18 0-32-14-32-32.1c0-9 3-17 10-24L266.4 8c7-7 15-8 22-8s15 2 21
+ 7L564.8 231.5c8 7 12 15 11 24z"/>
+ </symbol>
+ <symbol id="tags" viewBox="0 0 512 512">
+ <path d="M345 39.1L472.8 168.4c52.4 53 52.4 138.2 0 191.2L360.8 472.9c-9.3 9.4-24.5 9.5-33.9 .2s-9.5-24.5-.2-33.9L438.6
+ 325.9c33.9-34.3 33.9-89.4 0-123.7L310.9 72.9c-9.3-9.4-9.2-24.6 .2-33.9s24.6-9.2 33.9 .2zM0 229.5V80C0 53.5 21.5
+ 32 48 32H197.5c17 0 33.3 6.7 45.3 18.7l168 168c25 25 25 65.5 0 90.5L277.3 442.7c-25 25-65.5 25-90.5
+ 0l-168-168C6.7 262.7 0 246.5 0 229.5zM144 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/>
+ </symbol>
+ <symbol id="scroll" viewBox="0 0 576 512">
+ <path d="M0 80v48c0 17.7 14.3 32 32 32H48 96V80c0-26.5-21.5-48-48-48S0 53.5 0 80zM112 32c10 13.4 16 30 16 48V384c0 35.3
+ 28.7 64 64 64s64-28.7 64-64v-5.3c0-32.4 26.3-58.7 58.7-58.7H480V128c0-53-43-96-96-96H112zM464 480c61.9 0 112-50.1
+ 112-112c0-8.8-7.2-16-16-16H314.7c-14.7 0-26.7 11.9-26.7 26.7V384c0 53-43 96-96 96H368h96z"/>
+ </symbol>
</svg>
diff --git a/site/img/knitting-20200901.jpeg b/site/img/knitting-20200901.jpeg
deleted file mode 100644
index ebf7239..0000000
--- a/site/img/knitting-20200901.jpeg
+++ /dev/null
Binary files differ
diff --git a/site/img/pixel.png b/site/img/pixel.png
deleted file mode 100644
index e9f12bb..0000000
--- a/site/img/pixel.png
+++ /dev/null
Binary files differ
diff --git a/site/img/select-theme.png b/site/img/select-theme.png
index 4467c36..f8701be 100644
--- a/site/img/select-theme.png
+++ b/site/img/select-theme.png
Binary files differ
diff --git a/site/img/spatial-shell-example.png b/site/img/spatial-shell-example.png
index 4e03e10..7ccbf97 100644
--- a/site/img/spatial-shell-example.png
+++ b/site/img/spatial-shell-example.png
Binary files differ
diff --git a/site/img/spatial-shell.png b/site/img/spatial-shell.png
index cb348c2..fb025de 100644
--- a/site/img/spatial-shell.png
+++ b/site/img/spatial-shell.png
Binary files differ
diff --git a/site/img/spatial-sway-preview.png b/site/img/spatial-sway-preview.png
index b579587..09ab5a7 100644
--- a/site/img/spatial-sway-preview.png
+++ b/site/img/spatial-sway-preview.png
Binary files differ
diff --git a/site/img/thinking.png b/site/img/thinking.png
index a47f2c4..590cbaf 100644
--- a/site/img/thinking.png
+++ b/site/img/thinking.png
Binary files differ
diff --git a/site/img/vampy.jpg b/site/img/vampy.jpg
deleted file mode 100644
index e51beca..0000000
--- a/site/img/vampy.jpg
+++ /dev/null
Binary files differ
diff --git a/site/img/wrong-highlighting.png b/site/img/wrong-highlighting.png
index 3926ead..15da0ea 100644
--- a/site/img/wrong-highlighting.png
+++ b/site/img/wrong-highlighting.png
Binary files differ
diff --git a/site/index.md b/site/index.md
new file mode 100644
index 0000000..7b43d35
--- /dev/null
+++ b/site/index.md
@@ -0,0 +1,42 @@
+---
+abstract: |
+ Welcome to my little corner of the Internel. You will find it dusty more
+ often than not, but it’s been there for quite a while now, and its contents
+ might be of interest for you. If that is the case, I’m thrilled, or I would
+ be if I knew
+---
+
+# Hi 👋
+
+My name is Thomas Letan, and this is my little corner of the Internet.
+
+You will find it dusty more often than not, but it’s been there for quite a
+while now, and its contents might be of interest for you. If that is the case,
+I’m thrilled.
+
+## Latest Posts
+
+@[archives](5)
+
+[See more →](/posts)
+
+## Projects
+
+Here is a list of the free software projects I have most enjoyed writing, and
+that made an impact on my daily life.
+
+- [Spatial Shell](https://github.com/lthms/spatial-shell) (MPL-2.0) enables a
+ dynamic tiling management a la Material Shell and PaperWM.
+- [**keyr**](https://sr.ht/~lthms/keyr) (GPL-3) lets you know how many
+ keystrokes you have made since you have started using it.
+
+Additionally, you might be interested in the following software projects. I
+don’t work on them actively anymore, but they reach a state where they could be
+useful to you, and they work pretty well.
+
+- [`coqffi`](https://github.com/coq-community/coqffi) (MIT) generates the
+ necessary boilerplate to use OCaml functions in a Coq development, and
+ configure the Coq extraction mechanism accordingly.
+- [ogam](https://src.soap.coffee/crates/ogam.git/about) (MPL-2.0) is a
+ domain-specific markup language intended to free fiction writers from the
+ pain to enforce typographic rules themselves.
diff --git a/site/index.org b/site/index.org
deleted file mode 100644
index a473ddf..0000000
--- a/site/index.org
+++ /dev/null
@@ -1,35 +0,0 @@
-Welcome to my little corner of the Internet.
-
-You will find it dusty more often than not, but it’s been there for
-quite a while now, and its contents might be of interest for you. If
-that is the case, I’m thrilled, or I would be if I knew[fn::Please,
-let me know!].
-
-* Last Contents
- All categories combined, here is the last pieces of contents I have
- published on this website.
-
- - [[./posts/NeoVimOcamlInterfacesAndLSP.org][Neovim, OCaml Interfaces, Tree-Sitter and LSP]] /(May 1, 2023)/
- - [[./news/CFTSpatialShell.org][Call for Testers: Spatial Shell]] /(April 27, 2023)/
- - [[./opinions/StackedGitPatchTheory.org][Patch Dependencies for Stacked Git]] /(January 25, 2023)/
- - [[./opinions/StackedGit2.org][How I Keep Using Stacked Git at ~$WORK~ in 2023]] /(January 15, 2023)/
- - [[./news/November2022.org][Monthly Retrospective: October and November 2022]] /(November 19, 2022)/
-
- I had an RSS feed once, but it’s long gone, mostly because the
- toolchain I ended up writing for generating this website does not
- allow me to generate one easily. I hope it might come back at some
- point.
-
-* Projects
- Here is a curated list of the free software projects I have most
- enjoyed writing, and that made an impact on my daily life.
-
- - [[https://src.soap.coffee/crates/ogam.git/about][ogam]] is a domain-specific markup language intended to free fiction
- writers from the pain to enforce typographic rules themselves.
- - [[https://sr.ht/~lthms/keyr][*keyr*]] is a collection of software to know how many keystrokes you
- have made since you have started using it.
- - [[https://github.com/coq-community/coqffi][~coqffi~]] is a tool that generates the necessary boilerplate to use
- OCaml functions in a Coq development, and configure the Coq
- extraction mechanism accordingly.
- - [[https://github.com/lthms/spatial-shell][Spatial Shell]] is a daemon that enables a dynamic tiling management
- workflow a la Material Shell and PaperWM.
diff --git a/site/news/August2022.org b/site/news/August2022.org
deleted file mode 100644
index 32cf7b5..0000000
--- a/site/news/August2022.org
+++ /dev/null
@@ -1,126 +0,0 @@
-#+TITLE: Monthly Retrospective: August 2022
-
-#+SERIES: ./MonthlyRetrospectives.html
-#+SERIES_NEXT: ./September2022.html
-
-Without further ado, let’s take a look at what was achieved
-for the last thirty days or so.
-
-#+begin_export html
-<nav id="generate-toc"></nav>
-#+end_export
-
-* Emacs
- I have started tweaking and improving my Emacs [[https://src.soap.coffee/dotfiles/emacs.d.git][configuration]]
- again. [[mn:minimalism][After having used Emacs for seven years now, I am nowhere
- close to consider my configuration as a done project. I really envy
- developers who are using their editor with little to no
- customization.]]
-
-** Theme Selection Menu
- The change I am the most excited about is that I have /finally/
- reduced the boilerplate in need to write to use a new theme. I am
- very indecisive when it comes to theming. I like to have my
- choices, and I get tired of any colorscheme pretty quickly. As a
- consequence, I introduced a customizable variable to let me select
- a theme dynamically, and have this choice persist across Emacs
- session.
-
- I have a Hydra menu that allows me to select which theme I want to
- use for the time being. It looks like this.
-
- #+CAPTION: A Hydra menu for selecting a theme.
- #+NAME: fig:hydra-theme-menu
- [[../img/select-theme.png]]
-
- But adding new entries to this menu was very cumbersome, and mostly
- boilerplate that I know a good macro could abstract away. And I can
- finally report that I was right all along. I have my macros now,
- and they allow me to have the Hydra menu above generated with these
- simple lines of code.
-
- #+begin_src elisp
-(use-theme ancientless "a" :straight nil :load-path "~/.emacs.d/lisp")
-(use-theme darkless "d" :straight nil :load-path "~/.emacs.d/lisp")
-(use-theme brightless "b" :straight nil :load-path "~/.emacs.d/lisp")
-(use-theme monotropic "m")
-(use-theme monokai "M")
-(use-theme nothing "n")
-(use-theme eink "e")
-(use-theme dracula "D")
-(use-theme chocolate "c")
-(use-themes-from tao-theme
- '(("tl" . tao-yang)
- ("td" . tao-yin)))
- #+end_src
-
-
-** Eldoc and Flycheck Popups
- I have been experimenting with several combinations of packages to
- have Eldoc and Flycheck using pop-up-like mechanisms to report
- things to me, instead of the echo area.
-
- The winning setup for now is the one that uses the [[https://github.com/cpitclaudel/quick-peek][quick-peek
- package]]. That is, [[https://github.com/flycheck/flycheck-inline][flycheck-inline]] (customized to use quick-peek, as
- suggested in their README), and [[https://melpa.org/#/eldoc-overlay][eldoc-overlay]]. This works well
- enough, so the pop-ups of eldoc are maybe a bit too distracting.
-
- #+CAPTION: flycheck-inline in action with an OCaml compilation error.
- #+NAME: fig:flycheck-inline
- [[../img/flycheck-inline.png]]
-
- #+CAPTION: eldoc-overlay in action, displaying an OCaml type.
- #+NAME: fig:eldoc-overlay
- [[../img/eldoc-overlay.png]]
-
- In my quest for pop-ups, I ran into several issues with the
- packages I tried out. For instance, [[https://github.com/casouri/eldoc-box][eldoc-box]] was very nice, but
- also very slow for some reason. It turns out there were an issue
- about that slowness, wherein the culprit was identified. This
- allowed me to [[https://github.com/casouri/eldoc-box/pull/48][submit a pull request that got merged rather quickly]].
- Similarly, after a packages update, I discovered [[https://github.com/flycheck/flycheck-ocaml][flycheck-ocaml]] was
- no longer working, and [[https://github.com/flycheck/flycheck-ocaml/pull/14][submit a patch to fix the issue]].
-
-* This Website
- I have not been investing a lot of time in this website for the past
- six years or so. This month, things change a bit on that side too.
-
-** New Contents
- First, I have published a (short) article on [[rel:/posts/RankNTypesInOCaml.html][higher-order
- polymorphism in OCaml]]. The goal was for me to log somewhere the
- solution for an odd problem I was confronted to at ~$WORK~, but the
- resulting article was not doing a great job as conveying this. In
- particular, two comments on Reddit motivated me to rework it, and I
- am glad I did. I hope you enjoy the retake.
-
- Once this was out of the way, I decided that generating this website
- was taking way too much time for no good enough reason. The culprit
- was *~cleopatra~*, a toolchain I had developed in 2020 to integrate
- the build process of this website as additional contents that I
- thought might interest people. The sad things were: *~cleopatra~*
- was adding a significant overhead, and I never take the time to
- actually document them properly.
-
-** Under the Hood
- Overall, the cost of using *~cleopatra~* was not worth the burden,
- and so I got ride of it. Fortunately, it was not very difficult,
- since the job of *~cleopatra~* was to extracting the generation
- processes from org files; I just add to implement a small
- ~makefile~ to make use of these files, without having to rely on
- *~cleopatra~* anymore.
-
- This was something I was pondering to do for a long time, and as
- often in these circumstances, this gave me the extra motivation I
- needed to tackle other ideas I had in mind for this website. This
- is why now, rather than starting one Emacs process per Org file I
- have to process, my build toolchain starts one Emacs server, and
- later uses ~emacsclient~.
-
- Now, most of the build time is spent by [[https:soupault.app][soupault]]. I guess I will
- have to spend some time on the Lua plugins I have developed for it
- at some point.
-
-** A New Mailing List
- Finally, I have created [[https://lists.sr.ht/~lthms/public-inbox][a public mailing]] list that is available if
- you want to start a discussion on one of my article. Don’t hesitate
- to use it, or to register to it!
diff --git a/site/news/ColorlessThemes-0.2.org b/site/news/ColorlessThemes-0.2.org
deleted file mode 100644
index 50abddd..0000000
--- a/site/news/ColorlessThemes-0.2.org
+++ /dev/null
@@ -1,23 +0,0 @@
-#+BEGIN_EXPORT html
-<h1>Release of <code>colorless-themes-0.2</code>,
-<code>nordless-theme-0.2</code>, and <code>lavenderless-theme-0.2</code></h1>
-
-<p><span class="time">February 15, 2020</span></p>
-#+END_EXPORT
-
-[[https://code.soap.coffee/colorless-themes.git/tag/?h=0.2][I have tagged and released a new version of ~colorless-themes~]]. The motivation
-behind modifying the version number is an important breaking change regarding
-how the =colorless-themes-make= macro shall be used. Before ~0.2~, the macro was
-calling =deftheme= and =provide-theme= itself. In practices, it meant the actual
-themes were not using these two functions themselves. It was working great in
-isolation, but it turns out many tools such as ~use-package~ or [[https://peach-melpa.org][Peach Melpa]] (an
-auto-generated Emacs themes gallery) are relying on the presence of these
-functions to decide whether a file provides a theme or not. As of now,
-~nordless-theme~ and ~lavenderless-theme~ have been updated accordingly, and
-[[https://peach-melpa.org/themes/lavenderless-theme/variants/lavenderless][they appear on Peach Melpa]]. Their loading can also be defered using the =:defer=
-keyword of the =use-package= macro.
-
-Maybe now is a good time to get in touch with ~melpa-stable~, and see if we can
-add ~colorless-themes~ and its companion themes there. Also, if you happen to
-be a user of ~colorless-themes~, feel free to shoot an email! I would love to
-hear about your experience using a mostly colorless theme.
diff --git a/site/news/MonthlyRetrospectives.org b/site/news/MonthlyRetrospectives.org
deleted file mode 100644
index 54af833..0000000
--- a/site/news/MonthlyRetrospectives.org
+++ /dev/null
@@ -1,16 +0,0 @@
-#+TITLE: Monthly Retrospectives
-
-For a year or two now, I have been aware of a trend consisting in
-writing monthly recap in certain developers communities. I have
-enjoyed reading these regular posts, and from time to time, I have
-entertained the idea of writing my own. Never did I actually act on
-this thought. That is, until today. I anticipate the main audience of
-this post —and hopefully the ones that will come afterwards— will be
-future me, curious to remember what he was up to at that time.
-
-- [[./November2022.html][October and November 2022]], when two months pass by way too quickly
- for me to progress on any side projects.
-- [[./September2022.html][September 2022]], when I talked about Spatial Sway for the first time
- on this website (but not on Twitter/the Fediverse to be honest).
-- [[./August2022.html][August 2022]], when I gave up on *~cleopatra~* and was able to
- generate this website in a few seconds again.
diff --git a/site/news/November2022.org b/site/news/November2022.org
deleted file mode 100644
index 3fc831c..0000000
--- a/site/news/November2022.org
+++ /dev/null
@@ -1,33 +0,0 @@
-#+TITLE: Monthly Retrospective: October and November 2022
-
-#+SERIES: ./MonthlyRetrospectives.html
-#+SERIES_PREV: ./September2022.html
-
-It is November 19 today, and I’m one month and 4 days late for the
-October Retrospective! Truth is, ~$WORK~ has been intense lately, to a
-point where I have not made much progress on my side projects. Anyway.
-
-I have implemented the last feature I was really missing in my daily
-use of *Spatial Sway*: moving windows to adjacent workspaces. As a
-result, I think I can say that Spatial Sway has really reached the
-“Minimum Viable Product” stage, with a convenient UX, and a nice
-enough UI. It is still lacking when it comes to configurability,
-though. It is the next item of my TODO list, but I have no idea when I
-will implement the support for a configuration file.
-
-Another highlight of the past two months was the [[https://nanowrimo.org][NaNoWriMo]]. I took the
-last week of October and the first week of November off to plan and
-start writing a fiction project for it. Writing again was really nice,
-and I even gave writing fiction in English a shot. That made me
-uncover a bug in the English support of [[https://crates.io/crates/ogam][ogam]], my markup language for
-fiction writers, which led me to publish a fix on Crates.io. However,
-as soon as I came back to ~$WORK~, my writing spree ended. That’s
-Okay, though. It gave me plenty of ideas for future sessions. Thanks,
-NaNoWriMo! Sorry to quit so soon, and see you next year, maybe.
-
-Finally, a nice surprise of the past month is that [[https://github.com/ocaml/dune/pull/6489][someone has started
-working on adding proper support for ~coqffi~ to ~dune~]], the build
-system for OCaml and Coq! I’m thrilled by this. Thanks, [[https://github.com/Alizter][*@Alizter*]]!
-
-This wraps-up this retrospective. I hope I will have more interesting,
-concrete news to share next month.
diff --git a/site/news/September2022.org b/site/news/September2022.org
deleted file mode 100644
index e66ba74..0000000
--- a/site/news/September2022.org
+++ /dev/null
@@ -1,96 +0,0 @@
-#+TITLE: Monthly Retrospective: September 2022
-
-#+SERIES: ./MonthlyRetrospectives.html
-#+SERIES_PREV: ./August2022.html
-#+SERIES_NEXT: ./November2022.html
-
-It is September 18 today, and it has already been a month since I
-decided to start these retrospectives. This means it is time to take a
-step back and reflect of what happened these past few thirty days or
-so[fn::There is the shocking news that I have started to use syntax
-highlighting again. But let’s not dwelve too much into it just yet.].
-
-* Spatial Sway
- A few days after publishing my August Retrospective, I have learnt
- the existence of [[https://material-shell.com][Material Shell]], an extension for Gnome that
- provides a very interesting user experience.
-
- I tried it for a few hours, but the thing kept crashing (it’s
- probably on me, I did not even remember I had Gnome installed on my
- machine, and I would not be surprised the culprit was my dusty setup
- rather than Material Shell itself). The experience remained very
- promising, though. Their “spatial model” especially felt like a very
- good fit for me. Basically, the main idea is that you have a grid of
- windows, with your workspaces acting as the rows. You can navigate
- horizontally (from one workspace to another), or horizontally, and
- you choose how many windows you want to see at once on your screen.
-
- And so for a few hours, I was a bit frustrated by the situation…
- until I learnt about how one can actually manage and extend Sway
- (the wayland compositor I use for several years now) thanks to its
- IPC protocol. I spend like three days experimenting, first in Rust,
- then in OCaml,[[mn:ocaml][This was actually an interesting thought process. I am
- using OCaml at ~$WORK~ for about more than a year now. I have
- curated a setup that works pretty well, and I am familiar with the
- development tools. On the contrary, I had not written a line of Rust
- for at least a year, my Emacs configuration for this language was
- broken, and I had lost all my fluancy in this language. Still, I was
- not expecting to pick OCaml when I started this project.]] and by the
- end of the week, I had a first working prototype I called [[https://github.com/lthms/spatial-sway][Spatial
- Sway]]. It works pretty well, enough at least that I am using it daily
- for several weeks now. It feels clunky at time, but it works well,
- and I have been able to write a [[https://github.com/Alexays/Waybar][Waybar]] configuration heavily
- inspired on Material Shell UI.
-
- Overall, I am pretty satisfied with this turnout. Writing a hobbyist
- software project is always nice, but the one you can integrate in
- your daily workflow are the best one. The last time I did that was
- [[https://sr.ht/~lthms/keyrd][*keyrd*]], my little keystrokes counting daemon[fn::19,970,965 since I
- started using it at the time of writing this article].
-
- Anyway, lots remain to be said about Spatial Sway, but I might save
- it for a bit later. I still have some key features to implement
- (notably, moving a window to another workspaces), then I will
- probably try to advertise it a bit. I am under the impression this
- project could be of interest for other, and I would love to see it
- used by folks willing to give a Material Shell-like experience a
- try, without having to deal with Gnome Shell. By the way,
- considering Sway is a drop-in replacement for i3, and that it
- implements the exact same IPC protocol, there is no reason why
- Spatial Sway is actually Sway specific, and I will rename it Spatial
- Shell at some point.
-
- #+CAPTION: Mandatory screenshot of Spatial Sway.
- #+NAME: fig:hydra-theme-menu
- [[../img/spatial-sway-preview.png]]
-
-* This Website
- On a side note, I have started to refine the layout of this website
- a bit. Similarly, I have written a new, curated home page where I
- want to highlight the most recent things I have published on the
- Internet.
-
- I have been experimenting with [[https://github.com/cpitclaudel/alectryon/][Alectryon]] as a way to replace
- ~coqdoc~, to improve the readability of my Coq-related
- articles. Unfortunately, it looks like this tool is missing [[https://github.com/cpitclaudel/alectryon/issues/86][a key
- feature I need]]. I might try to get my hand dirty and implement it my
- self, if I find the time and the motivation in the following weeks.
-
- Finally, reading about how [[https://xeiaso.net/talks/how-my-website-works][Xe Iaso’s talk about how she generates
- her blog]] was very inspiring too me. I can only suggest you to have a
- look.
-
- Though not to the same extend, I too think I have spent way too much
- effort in my website. Most of my Coq-related articles are actual Coq
- program, expect the articles about ~coqffi~ which are actual
- literate programs. Hell, this website itself used to be a literate
- program of sort, until I stopped using my homegrown literate
- programming toolchain *~cleopatra~* last month. At some point, I
- have even spent a bit of time to ensure most of the pages of this
- website were granted a 100/100 on websites like PageSpeed
- Insight[fn::Good news, I’ve just checked, and it still is!]. I had
- almost forgot.
-
- A lot remains to be done, but watching this talk made me reflect on
- the job done. And opened my eyes to new perspective, too. We will
- see what translates into reality.
diff --git a/site/news/index.html b/site/news/index.html
deleted file mode 100644
index 78b017a..0000000
--- a/site/news/index.html
+++ /dev/null
@@ -1,143 +0,0 @@
-<h1>News</h1>
-
-<h2>2023</h1>
-
-<ul>
- <li>
- On <strong>April 29, 2022</strong>, I have tagged
- <a href="https://github.com/lthms/spatial-shell/releases/tag/1">the
- first release of Spatial Shell</a>, and created a
- <a href="https://aur.archlinux.org/packages/spatial-shell">package
- in the Archlinux User Repository</a>.
- </li>
- <li>
- On <strong>April 27, 2022</strong>, Spatial Shell is ready for being
- tested. It works on my machine, so it will most definitely breaks on yours!
- See the <a href="CFTSpatialShell.html">Call for Testers post for more
- details</a>.
- </li>
- <li>
- On <strong>April 27, 2022</strong>, <code>coq-coqffi~beta8</code>
- has been tagged.
- </li>
-</ul>
-
-<h2>2022</h2>
-
-<ul>
- <li>
- On <strong>August 21, 2022</strong>, I have
- <a href="https://github.com/lthms/spatial-sway">published on Github</a>
- a MVP version of <code>spatial-sway</code>, a dynamic tiling
- management daemon for Sway.
- </li>
- <li>
- On <strong>August 15, 2022</strong>, I have
- published <a href="August2022.html">my very first monthly
- retrospective</a>.
- </li>
- <li>
- On <strong>August 13, 2022</strong>, after more than two years of good services,
- <a href="https://cleopatra.soap.coffee"><strong><code>cleopatra</code></strong></a>
- is no longer used to build this website.
- </li>
-</ul>
-
-<h2>2021</h2>
-
-<ul>
- <li>
- On <strong>August 20, 2021</strong>, <code>coq-coqffi.1.0.0~beta7</code>
- has been released.
- </li>
- <li>
- On <strong>June 21, 2021</strong>, <code>coq-coqffi.1.0.0~beta6</code>
- has been released.
- </li>
- <li>
- On <strong>March 4, 2021</strong>,
- FreeSpec packages
- (<code>coq-freespec-core.0.3</code>, <code>coq-freespec-ffi.0.3</code>
- and
- <code>coq-freespec-exec.0.3</code>) have finally been released.
- </li>
- <li>
- On <strong>March 1st, 2021</strong>, <code>coq-coqffi.1.0.0~beta5</code>
- has been released.
- </li>
- <li>
- On <strong>February 24, 2021</strong>, <code>coq-coqffi.1.0.0~beta4</code>
- has been released.
- </li>
- <li>
- On <strong>January 27, 2021</strong>,
- <a href="https://github.com/lthms/FreeSpec">FreeSpec</a> has been
- relicensed under the terms of the
- <a href="https://www.mozilla.org/en-US/MPL/2.0/">Mozilla Public
- License version 2</a>.
- </li>
- <li>
- On <strong>January 24, 2021</strong>, <code>coq-coqffi.1.0.0~beta3</code>
- has been released.
- </li>
-</ul>
-
-<h2>2020</h2>
-
-<ul>
- <li>
- On <strong>December 16, 2020</strong>, FreeSpec packages have been
- published to the <code>extra-dev</code> repository of the Opam Coq
- archive.
- </li>
- <li>
- On <strong>December 10, 2020</strong>, <code>coq-coqffi.1.0.0~beta2</code>
- has been published to the Opam Coq archive.
- </li>
- <li>
- On <strong>December 7, 2020</strong>, <code>coq-coqffi.dev</code>
- and <code>coq-coqffi.1.0.0~beta1</code> have been published to the
- Opam Coq archive.
- </li>
- <li>
- On <strong>November 16, 2020</strong>, we have been notified that
- our tool paper on <code>coqffi</code> has been accepted
- to <a href="http://jfla.inria.fr/jfla2021.html">JFLA'21</a>! You
- can find the paper (2 pages, in
- French) <a href="/files/coqffi_jfla21.pdf">here</a>!
- </li>
- <li>
- On <strong>October 8, 2020</strong>, <code>coqffi</code> has officially been
- published as
- a <a href="https://github.com/coq-community/coqffi"><code>coq-community</code>’s
- project</a>.
- </li>
- <li>
- On <strong>April 17, 2020</strong>, my first contribution to
- Dune <a href="https://github.com/ocaml/dune/pull/3384">has been merged
- to <code>master</code></a>.
- </li>
- <li>
- On <strong>April 2, 2020</strong>, I started using the rewriting of
- <a href="https://cleopatra.soap.coffee"><strong><code>cleopatra</code></strong></a>
- to build this website.
- </li>
- <li>
- On <strong>April 1, 2020</strong>, my first contribution ever to
- Coq <a href="https://github.com/coq/coq/pull/10592">has been merged
- to <code>master</code></a>.
- </li>
- <li>
- On <strong>February 23, 2020</strong>,
- <strong><code>cleopatra</code></strong> has been completely
- bootstrapped, in that it generates itself!
- </li>
- <li>
- On <strong>February 15, 2020</strong>, I have tagged and
- released <code>colorless-themes-0.2</code>,
- <code>nordless-theme-0.2</code>,
- and <code>lavenderless-theme-0.2</code>.
- <a href="./ColorlessThemes-0.2.html">Learn more.</a>
- </li>
-</ul>
-</ul>
diff --git a/site/opinions/StackedGit.org b/site/opinions/StackedGit.org
deleted file mode 100644
index a53fd98..0000000
--- a/site/opinions/StackedGit.org
+++ /dev/null
@@ -1,344 +0,0 @@
-#+TITLE: How I Use Stacked Git at ~$WORK~
-
-#+SERIES: index.html
-#+SERIES_PREV: MonadTransformers.html
-#+SERIES_NEXT: StackedGit2.html
-
-According to [[https://lobste.rs/s/s6quvg/stacked_git][my Lobste.rs history]], I have run into [[https://stacked-git.github.io][Stacked Git]] in
-early April, 2021, and I remember its promises hit a soft spot.
-A few weeks later, I was submitting [[https://github.com/stacked-git/stgit/pull/100][a /pull request/ to teach Stacked
-Git to sign commits]].
-It was all I needed to start using it at ~$WORK~, and now it has
-become a cornerstone of my development workflow.
-
-#+BEGIN_EXPORT html
-<div id="history">site/opinions/StackedGit.org</div>
-#+END_EXPORT
-
-* What is Stacked Git?
-
- Before going any further, it is probably a good idea to take a
- moment and present Stacked Git.
- The website introduces the tool as follows:
-
- #+begin_quote
- Stacked Git, *StGit* for short, is an application for managing Git
- commits as a stack of patches.
- #+end_quote
-
- There is a few things to unpack here.
- First and as its name suggests, Stacked Git is a tool built on top
- of Git.
- [[mn:pijul][My main takeaway from my Pijul adventure is connected to
- this. Git is not limited to the ~git~ binary.
- Git comes with a collection of powerful forges, nice editor plugins,
- and years of good practices.
- To this day, it’s neither the bugs nor the breaking changes that
- made me quite Pijul.
- Those were expected.
- What I naively did not anticipate is the dry feeling that Pijul was
- just the ~pijul~ binary, which left me with a lot of tasks to do
- manually.]]
- It is *not* a brand new VCS, and as a consequence you keep to use
- all your existing tools and plugins[fn::I am looking at you,
- Magit.].
- Secondly, Stacked Git helps you curate your Git history, by turning
- your commits into patches, and your branches into stacks of patches.
- This speaks to me, maybe because I have been fascinated by
- email-based workflows for quite some time.
-
- To me, the two core features of Stacked Git are (1) allowing you to
- name your commits, and (2) to navigate among them.
- Together, they create a wonderful companion to help you keep your
- history clean.
-
-* My Subset of Stacked Git
-
- I do not want this article to be a Stacked Git tutorial.
- Fortunately, I don’t really use the tool at its full potential.
- I only care about a relatively small subset of commands I feel
- comfortable with and use daily.
-
- First, to decide which commits are part of my “stack of patches,” I
- can count of these commands:
-
- - ~stg new NAME~ creates an empty commit, and gives it the name
- ~NAME~.
- Having a way to identify a patch with a meaningful name that is
- resistant to rebase and amend is very nice.
- These are two properties commit hashes do not have.
- - ~stg uncommit NAME~ names the most recent commit under my
- stack with ~NAME~ and integrates it into it. I do this when I am
- tasked to work on a merge request made by a colleague, for
- instance.
- - ~stg commit~ removes from my stack its last patch. I do this when
- said commit has been merged into ~master~.
-
- Once my stack of patches is ready, the fun begins.
-
- At a given time, a patch can either be (1) applied, (2) unapplied,
- or (3) hidden.
- On the one hand, if a patch is applied it is part of the Git history.
- On the other hand, unapplying a patch means removing it from the
- working branch (but not from the stack of patches of Stacked Git).
- If a patch becomes unrelevant, but you don’t want to remove it
- entierely because it can become handy later, you can hide it.
- A hidden patch sits beside the stack of patches, and can be
- reintegrated if need be.
-
- Analoguous to ~git log~ ---which allows you to visualize your Git
- history---, ~stg series~ gives you a view the state of your stack of
- patches.
- Patches prefixed with ~+~ (or ~>~) are applied, while ~-~ means the
- patch is unapplied.
-
- Then,
-
- - ~stg pop~ unapplies the patch on top of the list of applied
- patches.
- - ~stg push~ applies the patch on the bottom of the list of unapplied
- patches.
- - ~stg goto NAME~ unapplies or applies the necessary patches so that
- ~NAME~ becomes the top patch of the list of applied patches.
-
- ~HEAD~ and the worktree are updated accordingly.
-
- In addition, ~stg sink~ and ~stg float~ allow to reorganize your
- stack of patches, moving patches around.
- Basically, they are like ~git rebase -i~, but without having to use
- ~$EDITOR~.
-
- Modifying patches is done with ~stg refresh~.
- It’s akin to ~git commit --amend~, except it is more powerful because
- you can modify any applied patches with the ~-p~ option.
- I’d always encourage you to ~stg goto~ first, because ~stg refresh
- -p~ remains unfortunately error prone (nothing prevents you to target
- the wrong patch).
- But when used carefully, it can be very handy.
-
- [[mn:rebase][Stacked Git is supposedly able to detect, during a rebase,
- which of your patches have been applied to your target branch.
- I’d rather use ~stg uncommit~ before do the rebase, though.]]
- Finally, ~stg rebase REF~ moves your stack of patches on top of
- ~REF~.
- It is akin to ~git rebase --onto~, but more straightforward.
- What happens is Stacked Git pop all the patches of my stack, reset
- the ~HEAD~ of the current branch to ~REF~, and tries applying the
- patches one by one
- In case of conflicts, the process stop, and I am left with an empty
- patch, and a dirty worktree with conflicts to solve.
- The hidden gem is that, contrary to ~git rebase~, the repository is
- not “in the middle of a rebase.”
- Suppos there are many conflicting patches still waiting in my stack
- of patches, and an urgent task I need to take care of first.
- I can just leave them here.
- I can switch to another branch, and when I come back, I get my
- patches back.
- I call this feature “incremental rebases.”
-
- And that is basically it.
- In a nutshell, Stacked Git equips commits with the same features as
- branches.
-
-* My Stacked Git Workflow
-
- As mentioned in the introduction of this article, Stacked Git has
- become a cornerstone of my workflow.
- I’ve been asked a few times what this workflow is, and why Magit is
- not enough[fn::It’s always about Magit ;).].
- So let’s try to do that.
- But first, a warning.
- Yes, because Stacked Git is only a wrapper above Git, everything I
- will explain can be achieved using Git alone, especially if you are
- a Magit wizard.
-
- Stacked Git makes just everything so more convenient to me.
-
-** Planning My Commits Ahead Of Time
-
- I’ve been introduced to Git with a pretty simple workflow: I am
- supposed to start working on a feature, and once it’s ready, I
- can commit, and move on to the next task on my todo list.
-
- To me, this approach is backward.
- It makes you set your intent after the fact.
- With Stacked Git, I often try to plan my final history /before
- writing the very first line of code/.
- Using ~stack new~, I create my patches, and take the time to write
- their description.
- It helps me visualizing where I want to go.
- Then, I use ~stack goto~ to go back to the beginning of my stack,
- and start working.
-
- It is not, and cannot be, an exact science. I often have to refine
- them as my work progresses.
- Yet, I think my Git history is cleaner, more focused, since I have
- started this exercise.
-
-** Getting My Fixup Commits Right
-
- Reviews are a fundamental aspect of a software developer job.
- At ~$WORK~, we use Gitlab and their merge requests workflow,
- which I find very annoying, because it does not provide meaningful
- ways to compare two versions of your submission[fn::There is a
- notion of “versions” in Gitlab, but its ergonomics fall short of my
- expectations for such tool.].
-
- What we end up doing is creating “fixup commits”, and we push them
- to Gitlab so that reviewers can easily verify that their feedback
- have correctly been taken into account.
-
- A fixup commit is a commit that will eventually be squashed into
- another.
- You can understand it as a delayed ~git commit --amend~.
- Git has some built-in features to manipulate them.
- You create them with ~git commit --fixup=<HASH>~, and they are
- interpreted in a specific manner by ~git rebase -i~.
- But they have always felt to me like a sordid hack.
- It is way too easy to create a fixup commit that targets the wrong
- commit, and you can end up with strange conflicts when you finally
- squash them.
- That being said, if used carefully, they are a powerful tool to
- keep a Git history clean.
-
- I am not sure we are using them carefully, though.
-
- Some reviews can be excruciating, with dozens of comments to
- address, and theoretically as many fixup commits to create.
- Then you push all of them on Gitlab, and days later, after the
- green light from the reviewer, you get to call ~git rebase~
- and discover your history is broken, you have tones of conflicts
- to fix, and you’re good for a long afternoon of untangling.
-
- The main reason behind this mess is that you end up fixing a commit
- from the ~HEAD~ of your working branch, not the commit itself.
- But with Stacked Git, things are different.
- With ~stg goto~, I put my working tree in the best state possible
- to fix a commit: the commit itself.
- I can use ~stg new~ to create a fixup commit, with a meaningful
- name.
- Then, I am forced to deal with the potential conflicts it brings
- when I call ~stg push~.
-
- Once my reviewer is happy with my work, I can call ~stg squash~.
- It is less automated than ~git rebase -i~, but the comfort I gained
- during the development is worth this little annoyance.
-
-** Managing Stacked Merge Requests
-
- At ~$WORK~, we are trying to change how we deliver new features to
- our ~master~ branch.
- More precisely, we want to merge smaller contributions more
- frequently.
- We have had our fair share of large and complex merge requests that
- were a nightmare to review in the past, and it’s really not a fun
- position to be put in.
-
- For a few months, I have been involved in a project wherein we
- decided /not/ to fall in the same trap again.
- We agreed on a “planning of merge requests” and started working.
- The first merge request was soon opened.
- We’ve nominated a “owner” to take care of the review, and the rest
- of the team carried on.
- Before the first merge request was merged, the second one was
- declared ready, and another owner was appointed.
- Then, the owner of the first merge request had a baby, and yours
- truly ended up having to manage two interdependent merge requests.
-
- It turns out Stacked Git is a wonderful tool to help me keep this
- under control.
-
- I only have one branch, and I use the same workflow to deal with
- feedbacks, even if they are coming from more than one one merge
- request.
- To remember the structure of everything, I just prefix the name of
- my patches with a merge request nickname.
- So my stack will look something like this:
-
- #+begin_src
- + mr1-base
- + mr1-tests
- + mr1-doc
- > mr2-command
- - mr2-tests
- #+end_src
-
- A reviewer leaves a hard-truth comment that requires a significant
- rework of the oldest merge request?
- ~stg goto~ reverts my worktree in the appropriate state, and ~stg
- push~ allows me to deal with conflicts one patch at a time.
- If at some point I need to spend more time on the oldest merge
- request, I can continue my work, knowing the patches related to the
- newest one are awaiting in my stack.
-
- The most annoying part is when the time comes to push everything.
- I need to ~stg goto~ at the last patch of each merge request, and
- ~git push HEAD:the-branch~.
- It’s not horrible.
- But I will probably try to automate it at some point.
-
-* Grievances
-
- Stacked Git have changed how I contribute to ~$SOFTWARE~ at ~$WORK~.
- It makes my life so much easier, especially now that I am dealing
- with stacked merge requests.
- That being said, I still have some grievances I’d like to address at
- some point, hopefully by contributing upstream.
-
-
-** Stacked Git Feels Slow
-
- I suspect this is due to the conjunction of (1) ~$WORK~ repository
- is large, and (2) Stacked Git is implemented in Python.
- Maybe I am unfair, and the real causes lie somewhere else.
- But the measurable fact I am witnessing is that ~stg series~ and
- ~stg top~ (which prints the top patch name of the applied patches)
- take 0.1s each.
-
- It’s not an issue when you call them from the shell, but it is when
- you use them in your prompt.
- Which I do.
- This brings an annoying latency to my every interaction with the
- repository.
-
-** I’d Like ~stg abort~ Please
-
- In this article, I have praised how Stacked Git allows for
- its so-called ---by me--- incremental rebases.
- However, the other side of the coin is that Stacked Git does not
- have something analoguous to the ~--abort~ command-line argument
- that you can pass to ~git cherry-pick~ and ~git rebase~.
- Not really.
-
- [[mn:doubts][I don’t want to be unfair to Stacked Git here. Maybe the
- documentation of Stacked Git provides useful tips to deal with this
- issue, and I have just overlooked it.]]
- Stacked Git has a command called ~stg undo~, which can achieve this
- to some extent.
- But ~stg undo~ does not like conflicts.
- When called after a conflicting ~stg push~, its output is not
- really helpful.
-
- #+begin_src
- Error: Need to resolve conflicts first
- stg undo: Command aborted (all changes rolled back)
- #+end_src
-
- The only way out that I am aware of is:
-
- - ~git add~ the files with conflicts.
- - ~stg refresh~ to fix recover.
- - ~stg undo~, twice.
-
-I’d argue we have seen better UXs.
-
-* Conclusion
-
- Overall, I am really thankful to Stacked Git’s authors!
- Thank you!
- You are making my interactions with Git fun and carefree.
- You provide me some of the convenience of patch-based VCS like [[http://darcs.net][Darcs]]
- and [[https://pijul.org][Pijul]], but without sacrificing the power of Git.
-
- I encourage anyone to at least give it a try, and I really hope I
- will be able to contribute back to Stacked Git in the near future.
diff --git a/site/opinions/StackedGit2.org b/site/opinions/StackedGit2.org
deleted file mode 100644
index 16eb974..0000000
--- a/site/opinions/StackedGit2.org
+++ /dev/null
@@ -1,121 +0,0 @@
-#+SERIES: index.html
-#+SERIES_PREV: StackedGit.html
-#+SERIES_NEXT: StackedGitPatchTheory.html
-
-#+TITLE: How I Keep Using Stacked Git at ~$WORK~
-
-One year ago, I have published an article summarizing
-[[./StackedGit.html][my experience using Stacked Git at ~$WORK~]]. Twelve months later,
-enough has changed to motivate a spin-off piece.
-
-* Stacked Git is /Fast/
- Firstly, it is important to state that my main complain about
- Stacked Git is now a thing of the past! Stacked Git does not feel
- slow anymore, and far from it. This is because
- [[https://github.com/stacked-git/stgit/discussions/185][Stacked Git 2.0 has been rewritten in Rust]]. While RiiR (/Rewrite it
- in Rust/) is a running meme on the Internet, in this particular
- case, the result is very exciting.
-
- Thanks to the performance boost, my Zsh prompt does not take 0.1s to
- appear!
-
- Speaking of Zsh prompt, basically what I ended up displaying is
- ~(<TOP PATCH NAME> <APPLIED PATCHES COUNT>/<PATCHSET SIZE> <HIDDEN
- PATCHES COUNT)~. For instance, ~(fix-1337 1/2 3)~.
-
- In case you want to take inspiration in my somewhat working
- configuration, here is the snippet of interest.
-
- #+begin_src sh
-local series_top="$(stg top 2> /dev/null)"
-local total="$(stg series 2> /dev/null | wc -l)"
-local hidden="$(stg series --hidden 2> /dev/null | wc -l)"
-
-if [[ "${total}" -gt 0 ]]; then
- local not_applied="$(stg series | grep -E '^-' | wc -l)"
- local applied="$(($total - $not_applied))"
-
- if [[ -z "${series_top}" ]]; then
- series_top="·"
- fi
-
- echo -n "(${status_color}${series_top} ${applied}/${total} ${hidden})"
- echo -n " ($(current_branch))"
-fi
- #+end_src
-
-* Branchless Workflow
- Last year, I was using Stacked Git on top of git branches. More
- precisely, I had one branch for each (stack of) Merge Request. It
- worked well, because my typical MR counted 3 to 4 commits in
- average.
-
- Fast forward today, and things have changed on this front too. In a
- nutshell, I have become a “one commit per MR” maximalist of
- sort[[mn:onecommit][It goes without saying that this approach comes
- with its set of drawbacks too. During the past year, I’ve pushed
- fairly large commits which could have been splitted into several
- smaller ones, for the sake of keeping my “one commit per MR”
- policy. I have also had to manage large stacks of MRs.]]. I find
- this approach very effective to get more focused reviews, and to
- reduce the time it takes for a given MR to be integrated into the
- main branch.
-
- My previous approach based on git branches did not scale well with
- this new mindset, and during the course of the year, I stopped using
- branches altogether[fn::I did not invent the branchless workflow, of
- course. After it was first published, someone posted a link to my
- Stacked Git article on Hacker News, and [[https://news.ycombinator.com/item?id=29959224][*@arxanas* posted a comment
- about ~git-branchless~]]. I tried the tool, and even if it never
- clicked for me, I was really compelled by its core ideas. Similarly,
- [[https://drewdevault.com/2020/04/06/My-weird-branchless-git-workflow.html][Drew
- DeVault has published a complete article on its own branchless
- workflow in 2020]].].
-
- These days, I proceed as follows.
-
- 1. I name each patch after the branch to which I will push it on our
- upstream Git remote.
- 2. 99% of the time, I push my work using ~git push -f upstream @:$(stg top)~
- 3. I created a small git plugin I called ~git-prepare~ which allows
- me to select one of the patch of my current patchset using ~fzf~,
- and which pops all other patches that are currently applied.
-
- ~git-prepare~ is really straightforward:
-
- #+begin_src sh
-#!/bin/sh
-patch=$(stg series -P | fzf)
-
-if [[ ! $? -eq 0 ]] ; then
- exit $?
-fi
-
-if [ -n "$(stg series -A)" ]; then
- stg pop -a
-fi
-
-stg push ${patch}
- #+end_src
-
- The main hurdle which I still need to figure out is how to deal with
- stacked MRs. Currently, this is very manual. I need to remember
- which commit belongs to the stack, the order and dependencies of
- these commits, and I need to publish each commit individually using
- ~stg push; git push @:$(stg top)~.
-
- The pragmatic answer is definitely to come back to git branches /for
- this particular use case/, but it's not the /fun/ answer. So from
- time to time, I try to experiment alternative approaches. My current
- intuition is that, by adopting a naming convention for my patches, I
- could probably implement a thin tooling on top of Stacked Git to
- deal with dependents commits.
-
-* Conclusion
- Putting aside stacked MRs for now, I am really satisfied with my
- workflow. It’s very lightweight and intuitive, and working without
- Stacked Git now feels backward and clunky.
-
- So I will take this opportunity to thank one more time Stacked Git’s
- authors and contributors. You all are making my professional like
- easier with your project.
diff --git a/site/opinions/StackedGitPatchTheory.org b/site/opinions/StackedGitPatchTheory.org
deleted file mode 100644
index c3099f2..0000000
--- a/site/opinions/StackedGitPatchTheory.org
+++ /dev/null
@@ -1,112 +0,0 @@
-#+SERIES: index.html
-#+SERIES_PREV: StackedGit2.html
-
-#+TITLE: Patch Dependencies for Stacked Git
-
-Every time I catch myself thinking about dependencies between
-changeset of a software project, the fascinating field of patch
-theories comes to my mind.
-
-A “patch theory” usually refers to the mathematical foundation behind
-the data model of so-called Patch-based DVCS like Darcs and
-Pijul. More precisely, a patch theory is an encoding of the state of a
-repository, equipped with operations (gathered in so-called patches,
-not to be confused with ~GNU diff~ patches) one can do to this
-state. For instance, my rough understanding of Pijul’s patch theory is
-that a repository is an oriented graph of lines, and a patch is a set
-of operations onto this graph.
-
-An interesting aspect of patch theory is that it requires a partial
-order for its patches, from which a Patch-based DVCS derives a
-dependency graph. In a nutshell, a patch /P/ depends on the patches
-which are responsible for the presence of the lines that /P/
-modifies.
-
-I have always found this idea of a dependency graph for the patches
-of a repository fascinating, because I though it would be a very
-valuable tool in the context of software development.
-
-I wanted to slightly change the definition of what a patch
-dependency is, though. See, the partial order of Darcs and Pijul
-focus on syntactic dependencies: the relation between lines in text
-files. They need that to reconstruct these text files in the file
-system of their users. As a software developers writing these text
-files, I quickly realized these dependencies were of little interest
-to me, though. What I wanted to be able to express was that a
-feature introduced by a patch /P/ relied on a fix introduced by a
-patch /P'/.
-
-I have experimented with Darcs and Pijul quite a bit, with this idea
-stuck in the back of my mind. At the end of this journey, I
-convinced myself[fn::I am not trying to convince you, per say. This is
-a very personal and subjective feedback, it does not mean someone else
-couldn't reach a different conclusion.] (1) this beautiful idea I
-had simply could not scale, and (2) neither I nor our industry is
-ready to give up on the extensive ecosystem that has been built on top
-of ~git~ just yet. As a consequence, my interest in Patch-based DVCS
-decreased sensibly.
-
-Until very recently, that is. I got reminded of the appeal of a
-dependency graph for changesets when I started adopted a Gitlab
-workflow centered around Stacked Git and smaller, sometimes
-interdependent MRs.
-
-A manually curated graph dependency for a whole repository is not
-practical, but what about my local queue of patches, patiently
-waiting to be integrated into the upstream repository I am
-contributing too? Not only does it look like a more approachable
-task, it could make synchronizing my stacked MRs a lot easier.
-
-The workflow I have in mind would proceed as follows.
-
-- Stacked Git’s ~new~ and ~edit~ commands could be extended to let
- developers declare dependencies between their patches. It would be
- the commands’ responsibility to enforce the wellfoundness of the
- dependency graph (/e.g./, prevent the introduction of cycles in the
- graph, and maybe diamonds too[fn::At least in a first version. There
- is definitely value in being able to work with two independent
- patches in conjunction with a third one that needs them both. That
- being said, our goal here is to organize our work locally, and if it
- is made easier by declaring artificial dependency, this is a
- pragmatic sacrifice I am personally willing to make.]).
-- The ~series~ command could be improved to display the resulting
- dependency graph.
-- ~push~ and ~pop~ would automatically take care (pushing or popping)
- of the selected patch(es) dependencies.
-- Ideally, Stacked Git would get a new command ~prepare <PATCH NAME>~
- which would pop every patches applied, then only only push ~<PATCH
- NAME>~ and its dependencies (in the reverse order). Developers could
- fix conflicts if need be. That is, Stacked Git would not be
- responsible for the consistency or correctness of the dependency
- graph.
-- Stacked Git could get commands to detect potential issues with the
- dependency graph specified by the developer (mostly consisting in
- dry-run of ~prepare~ to check if it would lead to conflicts).
-
-Because what we want is semantic dependencies, not syntactic
-dependencies between patches, I really think it makes a lot of sense
-to completely delegate the dependencies declaration to the
-developer[fn::Further versions of Stacked Git could explore computing
-the dependency graph automatically, similarly to what Git does. But I
-think that if Darcs and Pijul told us anything, it's that this
-computation is far from being trivial.]. The very mundane example that
-convinced me is the ~CHANGELOG~ file any mature software project ends
-up maintaining. If the contribution guidelines require to modify the
-~CHANGELOG~ file in the same commit as a feature is introduced, then
-the patches to two independent features will systematically
-conflict. This does not mean, from my patch queue perspective, I
-should be forced to ~pop~ the first commit before starting to work on
-the second one. It just means that when I call ~stg prepare~, I can
-have to fix a conflict, but fixing Git conflicts is part of the
-job after all[fn::And we have tools to help us. I wonder to which extends
-~git rerere~ could save the day in some cases, for instance.]. If for
-some reasons solving a conflict proves to be too cumbersome, I can
-always acknowledge that, and declare a new dependency to the
-appropriate patch. It only means I and my reviewers will be
-constrained a bit more than expected when dealing with my stack of
-MRs.
-
-I am under the impression that this model extends quite nicely the
-current way Stacked Git is working. To its core, it extends its data
-model to constraint a bit ~push~ and ~pop~, and empowers developers to
-organize a bit its local mess.
diff --git a/site/opinions/index.org b/site/opinions/index.org
deleted file mode 100644
index 2fe3e39..0000000
--- a/site/opinions/index.org
+++ /dev/null
@@ -1,31 +0,0 @@
-#+TITLE: Opinions
-
-I may have some opinions on some topics, and sometimes I may want to share
-them. However, I strongly believe facts and opinions are two differents things,
-and even though I do not intend to write plain wrong facts, I prefer to keep two
-separate indexes.
-
-I would encourage you to use your critical mind while reading these write-ups.
-
-** 2023
-
- - [[./StackedGitPatchTheory.org][Patch Dependencies for Stacked Git]] ::
- Could patch dependencies could help reduce the friction my
- branchless workflow suffers from when it comes to stacked MRs?
-
- - [[./StackedGit2.org][How I Keep Using Stacked Git at ~$WORK~ in 2023]] ::
- One year has passed, and I keep using Stacked Git almost
- daily. /How/ I am using it has slightly changed, though.
-
-** 2022
-
- - [[./StackedGit.org][How I Use Stacked Git at ~$WORK~]] ::
- I’ve been using Stacked Git at ~work~ since early 2021, and as
- of January 2022, it has become a cornerstone of my daily
- workflow.
-
-** 2017
-
-- [[./MonadTransformers.org][Monad Transformers are a Great Abstraction]] ::
- Monads are hard to get right, monad transformers are harder. Yet, they remain
- a very powerful abstraction.
diff --git a/site/posts/AlgebraicDatatypes.md b/site/posts/AlgebraicDatatypes.md
new file mode 100644
index 0000000..e52306d
--- /dev/null
+++ b/site/posts/AlgebraicDatatypes.md
@@ -0,0 +1,711 @@
+---
+published: 2020-07-12
+modified: 2023-05-07
+tags: ['coq']
+abstract: |
+ The set of types which can be defined in a language together with $+$ and
+ $*$ form an “algebraic structure” in the mathematical sense, hence the
+ name. It means the definitions of $+$ and $*$ have to satisfy properties
+ such as commutativity or the existence of neutral elements. In this
+ article, we prove the `sum`{.coq} and `prod`{.coq} Coq types satisfy these
+ properties.
+---
+
+# Proving Algebraic Datatypes are “Algebraic”
+
+Several programming languages allow programmers to define (potentially
+recursive) custom types, by composing together existing ones. For instance, in
+OCaml, one can define lists as follows:
+
+```ocaml
+type 'a list =
+| Cons of 'a * 'a list
+| Nil
+```
+
+This translates in Haskell as
+
+```haskell
+data List a =
+ Cons a (List a)
+| Nil
+```
+
+In Rust as
+
+```rust
+enum List<A> {
+ Cons(A, Box<List<a>>),
+ Nil,
+}
+```
+
+Or in Coq as
+
+```coq
+Inductive list a :=
+| cons : a -> list a -> list a
+| nil
+```
+
+And so forth.
+
+Each language will have its own specific constructions, and the type systems
+of OCaml, Haskell, Rust and Coq —to only cite them— are far from being
+equivalent. That being said, they often share a common “base formalism,”
+usually (and sometimes abusively) referred to as *algebraic datatypes*. This
+expression is used because under the hood any datatype can be encoded as a
+composition of types using two operators: sum ($+$) and product ($*$) for
+types.
+
+ - $a + b$ is the disjoint union of types $a$ and $b$. Any term of $a$
+ can be injected into $a + b$, and the same goes for $b$. Conversely,
+ a term of $a + b$ can be projected into either $a$ or $b$.
+ - $a * b$ is the Cartesian product of types $a$ and $b$. Any term of $a *
+ b$ is made of one term of $a$ and one term of $b$ (think tuples).
+
+For an algebraic datatype, one constructor allows for defining “named
+tuples”, that is ad-hoc product types. Besides, constructors are mutually
+exclusive: you cannot define the same term using two different constructors.
+Therefore, a datatype with several constructors is reminescent of a disjoint
+union. Coming back to the $\mathrm{list}$ type, under the syntactic sugar of
+algebraic datatypes, we can define it as
+
+$$\mathrm{list}_\alpha \equiv \mathrm{unit} + \alpha * \mathrm{list}_\alpha$$
+
+where $\mathrm{unit}$ models the `nil`{.coq} case, and $α * \mathrm{list}_\alpha$
+models the `cons`{.coq} case.
+
+The set of types which can be defined in a language together with $+$ and
+$*$ form an “algebraic structure” in the mathematical sense, hence the
+name. It means the definitions of $+$ and $*$ have to satisfy properties
+such as commutativity or the existence of neutral elements. In this article,
+we will prove some of them in Coq. More precisely,
+
+ - $+$ is commutative, that is $\forall (x, y),\ x + y = y + x$
+ - $+$ is associative, that is $\forall (x, y, z),\ (x + y) + z = x + (y + z)$
+ - $+$ has a neutral element, that is $\exists e_s,\ \forall x,\ x + e_s = x$
+ - $*$ is commutative, that is $\forall (x, y),\ x * y = y * x$
+ - $*$ is associative, that is $\forall (x, y, z),\ (x * y) * z = x * (y * z)$
+ - $*$ has a neutral element, that is $\exists e_p,\ \forall x,\ x * e_p = x$
+ - The distributivity of $+$ and $*$, that is $\forall
+ (x, y, z),\ x * (y + z) = x * y + x * z$
+ - $*$ has an absorbing element, that is $\exists e_a,\ \forall x, \ x * e_a = e_a$
+
+For the record, the `sum`{.coq} ($+$) and `prod`{.coq} ($*$) types are defined
+in Coq as follows:
+
+```coq
+Inductive sum (A B : Type) : Type :=
+| inl : A -> sum A B
+| inr : B -> sum A B
+
+Inductive prod (A B : Type) : Type :=
+| pair : A -> B -> prod A B
+```
+
+## An Equivalence for `Type`{.coq}
+
+Algebraic structures come with *equations* expected to be true. This means
+there is an implicit dependency which is —to my opinion— too easily overlooked:
+the definition of $=$. In Coq, `=`{.coq} is a built-in relation that states
+that two terms are “equal” if they can be reduced to the same “hierarchy” of
+constructors. This is too strong in the general case, and in particular for our
+study of algebraic structures of `Type`{.coq}. It is clear that, to Coq’s
+opinion, $\alpha + \beta$ is not structurally *equal* to $\beta + \alpha$, yet
+we will have to prove they are “equivalent.”
+
+### Introducing `type_equiv`{.coq}
+
+Since `=`{.coq} for `Type`{.coq} is not suitable for reasoning about algebraic
+datatypes, we introduce our own equivalence relation, denoted `==`{.coq}. We
+say two types $\alpha$ and $\beta$ are equivalent up to an isomorphism
+when for any term of type $\alpha$, there exists a counter-part term of type
+$\beta$ and vice versa. In other words, $\alpha$ and $\beta$ are equivalent if
+we can exhibit two functions $f$ and $g$ such that:
+
+$$\forall (x : α),\ x = g(f(x))$$
+
+$$\forall (y : β),\ y = f(g(y))$$
+
+This translates into the following inductive type.
+
+```coq
+Reserved Notation "x == y" (at level 72).
+
+Inductive type_equiv (α β : Type) : Prop :=
+| mk_type_equiv (f : α -> β) (g : β -> α)
+ (equ1 : forall (x : α), x = g (f x))
+ (equ2 : forall (y : β), y = f (g y))
+ : α == β
+where "x == y" := (type_equiv x y).
+```
+
+As mentioned earlier, we prove two types are equivalent by exhibiting
+two functions, and proving these functions satisfy two properties. We
+introduce a `Ltac` notation to that end.
+
+```coq
+Tactic Notation "equiv" "with" uconstr(f) "and" uconstr(g)
+ := apply (mk_type_equiv f g).
+```
+
+The tactic `equiv with f and g`{.coq} will turn a goal of the form `α ==
+β`{.coq} into two subgoals to prove `f` and `g` form an isomorphism.
+
+### `type_equiv`{.coq} is an Equivalence
+
+We can prove it by demonstrating it is
+
+1. Reflexive,
+2. Symmetric, and
+3. Transitive.
+
+#### `type_equiv`{.coq} is reflexive
+
+This proof is straightforward. A type $\alpha$ is equivalent to itself because:
+
+$$\forall (x : α),\ x = id(id(x))$$
+
+```coq
+Lemma type_equiv_refl (α : Type) : α == α.
+Proof.
+ now equiv with (@id α) and (@id α).
+Qed.
+```
+#### `type_equiv`{.coq} is symmetric
+
+If $\alpha = \beta$, then we know there exists two functions $f$ and $g$ which
+satisfy the expected properties. We can “swap” them to prove that $\beta ==
+\alpha$.
+
+```coq
+Lemma type_equiv_sym {α β} (equ : α == β) : β == α.
+Proof.
+ destruct equ as [f g equ1 equ2].
+ now equiv with g and f.
+Qed.
+```
+
+#### `type_equiv`{.coq} is transitive
+
+If $\alpha = \beta$, we know there exists two functions $f_\alpha$ and
+$g_\beta$ which satisfy the expected properties of `type_equiv`{.coq}.
+Similarly, because $\beta = \gamma$,
+we know there exists two additional functions $f_\beta$ and $g_\gamma$. We can
+compose these functions together to prove $\alpha = \gamma$.
+
+As a reminder, composing two functions $f$ and $g$ (denoted by `f >>> g`{.coq}
+thereafter) consists in using the result of $f$ as the input of $g$.
+
+```coq
+Infix ">>>" := (fun f g x => g (f x)) (at level 70).
+
+Lemma type_equiv_trans {α β γ} (equ1 : α == β) (equ2 : β == γ)
+ : α == γ.
+Proof.
+ destruct equ1 as [fα gβ equαβ equβα],
+ equ2 as [fβ gγ equβγ equγβ].
+ equiv with (fα >>> fβ) and (gγ >>> gβ).
+ + intros x.
+ rewrite <- equβγ.
+ now rewrite <- equαβ.
+ + intros x.
+ rewrite <- equβα.
+ now rewrite <- equγβ.
+Qed.
+```
+#### Conclusion
+
+The Coq standard library introduces the `Equivalence`{.coq} type class. We can
+provide an instance of this type class for `type_equiv`{.coq}, using the three
+lemmas we have proven in this section.
+
+```coq
+#[refine]
+Instance type_equiv_Equivalence : Equivalence type_equiv :=
+ {}.
+
+Proof.
+ + intros x.
+ apply type_equiv_refl.
+ + intros x y.
+ apply type_equiv_sym.
+ + intros x y z.
+ apply type_equiv_trans.
+Qed.
+```
+
+### Examples
+
+#### `list`{.coq}’s canonical form
+
+We now come back to our initial example, given in the introduction of this
+write-up. We can prove our assertion, that is `list α == unit + α * list
+α`{.coq}.
+
+```coq
+Lemma list_equiv (α : Type)
+ : list α == unit + α * list α.
+
+Proof.
+ equiv with (fun x => match x with
+ | [] => inl tt
+ | x :: rst => inr (x, rst)
+ end)
+ and (fun x => match x with
+ | inl _ => []
+ | inr (x, rst) => x :: rst
+ end).
+ + now intros [| x rst].
+ + now intros [[] | [x rst]].
+Qed.
+```
+
+#### `list`{.coq} is a morphism
+
+This means that if `α == β`{.coq}, then `list α == list β`{.coq}. We prove this
+by defining an instance of the `Proper`{.coq} type class.
+
+```coq
+Instance list_Proper
+ : Proper (type_equiv ==> type_equiv) list.
+
+Proof.
+ add_morphism_tactic.
+ intros α β [f g equαβ equβα].
+ equiv with (map f) and (map g).
+ all: setoid_rewrite map_map; intros l.
+ + replace (fun x : α => g (f x))
+ with (@id α).
+ ++ symmetry; apply map_id.
+ ++ apply functional_extensionality.
+ apply equαβ.
+ + replace (fun x : β => f (g x))
+ with (@id β).
+ ++ symmetry; apply map_id.
+ ++ apply functional_extensionality.
+ apply equβα.
+Qed.
+```
+
+The use of the `Proper`{.coq} type class allows for leveraging hypotheses of
+the form `α == β`{.coq} with the `rewrite` tactic. I personally consider
+providing instances of `Proper`{.coq} whenever it is possible to be a good
+practice, and would encourage any Coq programmers to do so.
+
+#### `nat`{.coq} is a special-purpose `list`
+
+Did you notice? Now, using `type_equiv`{.coq}, we can prove it!
+
+```coq
+Lemma nat_and_list : nat == list unit.
+Proof.
+ equiv with (fix to_list n :=
+ match n with
+ | S m => tt :: to_list m
+ | _ => []
+ end)
+ and (fix of_list l :=
+ match l with
+ | _ :: rst => S (of_list rst)
+ | _ => 0
+ end).
+ + induction x; auto.
+ + induction y; auto.
+ rewrite <- IHy.
+ now destruct a.
+Qed.
+```
+
+#### Non-empty lists
+
+We can introduce a variant of `list`{.coq} which contains at least one element
+by modifying the `nil`{.coq} constructor so that it takes one argument instead
+of none.
+
+```coq
+Inductive non_empty_list (α : Type) :=
+| ne_cons : α -> non_empty_list α -> non_empty_list α
+| ne_singleton : α -> non_empty_list α.
+```
+
+We can demonstrate the relation between `list`{.coq} and
+`non_empty_list`{.coq}, which reveals an alternative implementation of
+`non_empty_list`{.coq}. More precisely, we can prove that `forall (α : Type),
+non_empty_list α == α * list α`{.coq}. It is a bit more cumbersome, but not that
+much. We first define the conversion functions, then prove they satisfy the
+properties expected by `type_equiv`{.coq}.
+
+```coq
+Fixpoint non_empty_list_of_list {α} (x : α) (l : list α)
+ : non_empty_list α :=
+ match l with
+ | y :: rst => ne_cons x (non_empty_list_of_list y rst)
+ | [] => ne_singleton x
+ end.
+
+#[local]
+Fixpoint list_of_non_empty_list {α} (l : non_empty_list α)
+ : list α :=
+ match l with
+ | ne_cons x rst => x :: list_of_non_empty_list rst
+ | ne_singleton x => [x]
+ end.
+
+Definition list_of_non_empty_list {α} (l : non_empty_list α)
+ : α * list α :=
+ match l with
+ | ne_singleton x => (x, [])
+ | ne_cons x rst => (x, list_of_non_empty_list rst)
+ end.
+
+Lemma ne_list_list_equiv (α : Type)
+ : non_empty_list α == α * list α.
+
+Proof.
+ equiv with list_of_non_empty_list
+ and (prod_curry non_empty_list_of_list).
+ + intros [x rst|x]; auto.
+ cbn.
+ revert x.
+ induction rst; intros x; auto.
+ cbn; now rewrite IHrst.
+ + intros [x rst].
+ cbn.
+ destruct rst; auto.
+ change (non_empty_list_of_list x (α0 :: rst))
+ with (ne_cons x (non_empty_list_of_list α0 rst)).
+ replace (α0 :: rst)
+ with (list_of_non_empty_list
+ (non_empty_list_of_list α0 rst)); auto.
+ revert α0.
+ induction rst; intros y; [ reflexivity | cbn ].
+ now rewrite IHrst.
+Qed.
+```
+
+## The `sum`{.coq} Operator
+
+### `sum`{.coq} Is A Morphism
+
+To prove this, we compose together the functions whose existence is implied by
+$\alpha = \alpha'$ and $\beta = \beta'$. To that end, we introduce the
+auxiliary function `lr_map`{.coq}.
+
+```coq
+Definition lr_map_sum {α β α' β'} (f : α -> α') (g : β -> β')
+ (x : α + β)
+ : α' + β' :=
+ match x with
+ | inl x => inl (f x)
+ | inr y => inr (g y)
+ end.
+```
+
+Then, we prove `sum`{.coq} is a morphism by defining a `Proper`{.coq} instance.
+
+```coq
+Instance sum_Proper
+ : Proper (type_equiv ==> type_equiv ==> type_equiv) sum.
+Proof.
+ add_morphism_tactic.
+ intros α α' [fα gα' equαα' equα'α]
+ β β' [fβ gβ' equββ' equβ'β].
+ equiv with (lr_map_sum fα fβ)
+ and (lr_map_sum gα' gβ').
+ + intros [x|y]; cbn.
+ ++ now rewrite <- equαα'.
+ ++ now rewrite <- equββ'.
+ + intros [x|y]; cbn.
+ ++ now rewrite <- equα'α.
+ ++ now rewrite <- equβ'β.
+Qed.
+```
+
+### `sum`{.coq} Is Commutative
+
+```coq
+Definition sum_invert {α β} (x : α + β) : β + α :=
+ match x with
+ | inl x => inr x
+ | inr x => inl x
+ end.
+
+Lemma sum_com {α β} : α + β == β + α.
+Proof.
+ equiv with sum_invert and sum_invert;
+ now intros [x|x].
+Qed.
+```
+
+### `sum`{.coq} Is Associative
+
+The associativity of `sum`{.coq} is straightforward to prove, and should not
+pose a particular challenge to perspective readers[^joke].
+
+[^joke]: If we assume that this article is well-written, that is.
+
+```coq
+Lemma sum_assoc {α β γ} : α + β + γ == α + (β + γ).
+Proof.
+ equiv with (fun x =>
+ match x with
+ | inl (inl x) => inl x
+ | inl (inr x) => inr (inl x)
+ | inr x => inr (inr x)
+ end)
+ and (fun x =>
+ match x with
+ | inl x => inl (inl x)
+ | inr (inl x) => inl (inr x)
+ | inr (inr x) => inr x
+ end).
+ + now intros [[x|x]|x].
+ + now intros [x|[x|x]].
+Qed.
+```
+
+### `sum`{.coq} Has A Neutral Element
+
+We need to find a type $e$ such that $\alpha + e = \alpha$ for any type
+$\alpha$ (similarly to $x + 0 = x$ for any natural number $x$).
+
+Any empty type (that is, a type with no term such as `False`{.coq}) can act as
+the natural element of `Type`{.coq}. As a reminder, empty types in Coq are
+defined with the following syntax[^empty]:
+
+```coq
+Inductive empty := .
+```
+
+[^empty]: Note that `Inductive empty.`{.coq} is erroneous.
+
+ When the `:=`{.coq} is omitted, Coq defines an inductive type with one
+ constructor, making such a type a type equivalent to `unit`{.coq}, not
+ `False`{.coq}.
+
+From a high-level perspective, `empty`{.coq} being the neutral element of
+`sum`{.coq} makes sense. Because we cannot construct a term of type `empty`{.coq},
+then `α + empty`{.coq} contains exactly the same numbers of terms as `α`{.coq}.
+This is the intuition. Now, how can we convince Coq that our intuition is
+correct? Just like before, by providing two functions of types:
+
+ - `α -> α + empty`{.coq}
+ - `α + empty -> α`{.coq}
+
+The first function is `inl`{.coq}, that is one of the constructor of
+`sum`{.coq}.
+
+The second function is more tricky to write in Coq, because it comes down to
+writing a function of type is `empty -> α`{.coq}.
+
+```coq
+Definition from_empty {α} : empty -> α :=
+ fun x => match x with end.
+```
+
+It is the exact same trick that allows Coq to encode proofs by
+contradiction.
+
+If we combine `from_empty`{.coq} with the generic function
+
+```coq
+Definition unwrap_left_or {α β}
+ (f : β -> α) (x : α + β)
+ : α :=
+ match x with
+ | inl x => x
+ | inr x => f x
+ end.
+```
+
+Then, we have everything to prove that `α == α + empty`{.coq}.
+
+```coq
+Lemma sum_neutral (α : Type) : α == α + empty.
+Proof.
+ equiv with inl and (unwrap_left_or from_empty);
+ auto.
+ now intros [x|x].
+Qed.
+```
+
+## The `prod`{.coq} Operator
+
+This is very similar to what we have just proven for `sum`{.coq}, so expect
+less text for this section.
+
+### `prod`{.coq} Is A Morphism
+
+```coq
+Definition lr_map_prod {α α' β β'}
+ (f : α -> α') (g : β -> β')
+ : α * β -> α' * β' :=
+ fun x => match x with (x, y) => (f x, g y) end.
+
+Instance prod_Proper
+ : Proper (type_equiv ==> type_equiv ==> type_equiv) prod.
+
+Proof.
+ add_morphism_tactic.
+ intros α α' [fα gα' equαα' equα'α]
+ β β' [fβ gβ' equββ' equβ'β].
+ equiv with (lr_map_prod fα fβ)
+ and (lr_map_prod gα' gβ').
+ + intros [x y]; cbn.
+ rewrite <- equαα'.
+ now rewrite <- equββ'.
+ + intros [x y]; cbn.
+ rewrite <- equα'α.
+ now rewrite <- equβ'β.
+Qed.
+```
+
+### `prod`{.coq} Is Commutative
+
+```coq
+Definition prod_invert {α β} (x : α * β) : β * α :=
+ (snd x, fst x).
+
+Lemma prod_com {α β} : α * β == β * α.
+
+Proof.
+ equiv with prod_invert and prod_invert;
+ now intros [x y].
+Qed.
+```
+
+### `prod`{.coq} Is Associative
+
+```coq
+Lemma prod_assoc {α β γ}
+ : α * β * γ == α * (β * γ).
+
+Proof.
+ equiv with (fun x =>
+ match x with
+ | ((x, y), z) => (x, (y, z))
+ end)
+ and (fun x =>
+ match x with
+ | (x, (y, z)) => ((x, y), z)
+ end).
+ + now intros [[x y] z].
+ + now intros [x [y z]].
+Qed.
+```
+
+### `prod`{.coq} Has A Neutral Element
+
+```coq
+Lemma prod_neutral (α : Type) : α * unit == α.
+
+Proof.
+ equiv with fst and ((flip pair) tt).
+ + now intros [x []].
+ + now intros.
+Qed.
+```
+
+### `prod`{.coq} Has An Absorbing Element *)
+
+And this absorbing element is `empty`{.coq}, just like the absorbing element of
+the multiplication of natural number is $0$ (that is, the neutral element of
+the addition).
+
+```coq
+Lemma prod_absord (α : Type) : α * empty == empty.
+Proof.
+ equiv with (snd >>> from_empty)
+ and (from_empty).
+ + intros [_ []].
+ + intros [].
+Qed.
+```
+
+## `prod`{.coq} And `sum`{.coq} Distributivity
+
+Finally, we can prove the distributivity property of `prod`{.coq} and
+`sum`{.coq} using a similar approach to prove the associativity of `prod`{.coq}
+and `sum`{.coq}.
+
+```coq
+Lemma prod_sum_distr (α β γ : Type)
+ : α * (β + γ) == α * β + α * γ.
+Proof.
+ equiv with (fun x => match x with
+ | (x, inr y) => inr (x, y)
+ | (x, inl y) => inl (x, y)
+ end)
+ and (fun x => match x with
+ | inr (x, y) => (x, inr y)
+ | inl (x, y) => (x, inl y)
+ end).
+ + now intros [x [y | y]].
+ + now intros [[x y] | [x y]].
+Qed.
+```
+
+## Bonus: Algebraic Datatypes and Metaprogramming
+
+Algebraic datatypes are very suitable for generating functions, as demonstrated
+by the automatic deriving of typeclass in Haskell or trait in Rust. Because a
+datatype can be expressed in terms of `sum`{.coq} and `prod`{.coq}, you just
+have to know how to deal with these two constructions to start metaprogramming.
+
+We can take the example of the `fold`{.coq} functions. A `fold`{.coq} function
+is a function which takes a container as its argument, and iterates over the
+values of that container in order to compute a result.
+
+We introduce `fold_type INPUT CANON_FORM OUTPUT`{.coq}, a tactic to compute the
+type of the fold function of the type `INPUT`, whose “canonical form” (in terms
+of `prod`{.coq} and `sum`{.coq}) is `CANON_FORM` and whose result type is
+`OUTPUT`. Interested readers have to be familiar with `Ltac`.
+
+```coq
+Ltac fold_args b a r :=
+ lazymatch a with
+ | unit =>
+ exact r
+ | b =>
+ exact (r -> r)
+ | (?c + ?d)%type =>
+ exact (ltac:(fold_args b c r) * ltac:(fold_args b d r))%type
+ | (b * ?c)%type =>
+ exact (r -> ltac:(fold_args b c r))
+ | (?c * ?d)%type =>
+ exact (c -> ltac:(fold_args b d r))
+ | ?a =>
+ exact (a -> r)
+ end.
+
+Ltac currying a :=
+ match a with
+ | ?a * ?b -> ?c => exact (a -> ltac:(currying (b -> c)))
+ | ?a => exact a
+ end.
+
+Ltac fold_type b a r :=
+ exact (ltac:(currying (ltac:(fold_args b a r) -> b -> r))).
+```
+
+We use it to compute the type of a `fold`{.coq} function for `list`{.coq}.
+
+```coq
+Definition fold_list_type (α β : Type) : Type :=
+ ltac:(fold_type (list α) (unit + α * list α)%type β).
+```
+
+```coq
+fold_list_type =
+ fun α β : Type => β -> (α -> β -> β) -> list α -> β
+ : Type -> Type -> Type
+```
+
+It is exactly what you could have expected (as match the type of
+`fold_right`{.coq}).
+
+Generating the body of the function is possible in theory, but probably not in
+`Ltac` without modifying a bit `type_equiv`{.coq}. This could be a nice
+use-case for [MetaCoq](https://github.com/MetaCoq/metacoq) though.
diff --git a/site/posts/AlgebraicDatatypes.v b/site/posts/AlgebraicDatatypes.v
deleted file mode 100644
index 77ecc3c..0000000
--- a/site/posts/AlgebraicDatatypes.v
+++ /dev/null
@@ -1,682 +0,0 @@
-(** #<nav><p class="series">./coq.html</p>
- <p class="series-prev">./ClightIntroduction.html</p>
- <p class="series-next">./Coqffi.html</p></nav># *)
-
-(** * Proving Algebraic Datatypes are “Algebraic” *)
-
-(** Several programming languages allow programmers to define (potentially
- recursive) custom types, by composing together existing ones. For instance,
- in OCaml, one can define lists as follows:
-
-<<
-type 'a list =
-| Cons of 'a * 'a list
-| Nil
->>
-
- This translates in Haskell as
-
-<<
-data List a =
- Cons a (List a)
-| Nil
->>
-
- In Rust:
-
-<<
-enum List<A> {
- Cons(A, Box< List<a> >),
- Nil,
-}
->>
-
- In Coq:
-
-<<
-Inductive list a :=
-| cons : a -> list a -> list a
-| nil
->>
-
- And so forth.
-
- Each language will have its own specific constructions, and the type systems
- of OCaml, Haskell, Rust and Coq —to only cite them— are far from being
- equivalent. That being said, they often share a common “base formalism,”
- usually (and sometimes abusively) referred to as _algebraic datatypes_. This
- expression is used because under the hood any datatype can be encoded as a
- composition of types using two operators: sum ([+]) and product ([*]) for
- types.
-
- - [a + b] is the disjoint union of types [a] and [b]. Any term of [a]
- can be injected into [a + b], and the same goes for [b]. Conversely,
- a term of [a + b] can be projected into either [a] or [b].
- - [a * b] is the Cartesian product of types [a] and [b]. Any term of [a *
- b] is made of one term of [a] and one term of [b] (remember tuples?).
-
- For an algebraic datatype, one constructor allows for defining “named
- tuples”, that is ad-hoc product types. Besides, constructors are mutually
- exclusive: you cannot define the same term using two different constructors.
- Therefore, a datatype with several constructors is reminescent of a disjoint
- union. Coming back to the [list] type, under the syntactic sugar of
- algebraic datatypes, the [list α] type is equivalent to [unit + α * list α],
- where [unit] models the [nil] case, and [α * list α] models the [cons] case.
-
- The set of types which can be defined in a language together with [+] and
- [*] form an “algebraic structure” in the mathematical sense, hence the
- name. It means the definitions of [+] and [*] have to satisfy properties
- such as commutativity or the existence of neutral elements. In this article,
- we will prove some of them in Coq. More precisely,
-
- - [+] is commutative, that is #<span class="imath">#\forall (x, y),\ x + y
- = y + x#</span>#
- - [+] is associative, that is #<span class="imath">#\forall (x, y, z),\ (x
- + y) + z = x + (y + z)#</span>#
- - [+] has a neutral element, that is #<span class="imath">#\exists e_s,
- \ \forall x,\ x + e_s = x#</span>#
- - [*] is commutative, that is #<span class="imath">#\forall (x, y),\ x * y
- = y * x#</span>#
- - [*] is associative, that is #<span class="imath">#\forall (x, y, z),\ (x
- * y) * z = x * (y * z)#</span>#
- - [*] has a neutral element, that is #<span class="imath">#\exists e_p,
- \ \forall x,\ x * e_p = x#</span>#
- - The distributivity of [+] and [*], that is #<span class="imath">#\forall
- (x, y, z),\ x * (y + z) = x * y + x * z#</span>#
- - [*] has an absorbing element, that is #<span class="imath">#\exists e_a,
- \ \forall x, \ x * e_a = e_a#</span>#
-
- For the record, the [sum] and [prod] types are defined in Coq as follows:
-
-<<
-Inductive sum (A B : Type) : Type :=
-| inl : A -> sum A B
-| inr : B -> sum A B
-
-Inductive prod (A B : Type) : Type :=
-| pair : A -> B -> prod A B
->>
-
- #<nav id="generate-toc"></nav>#
-
- #<div id="history">site/posts/AlgebraicDatatypes.v</div># *)
-
-From Coq Require Import
- Basics Setoid Equivalence Morphisms
- List FunctionalExtensionality.
-Import ListNotations.
-
-Set Implicit Arguments.
-
-(** ** An Equivalence for [Type] *)
-
-(** Algebraic structures come with _equations_ expected to be true. This means
- there is an implicit dependency which is —to my opinion— too easily
- overlooked: the definition of [=]. In Coq, [=] is a built-in relation that
- states that two terms are “equal” if they can be reduced to the same
- “hierarchy” of constructors. This is too strong in the general case, and in
- particular for our study of algebraic structures of [Type]. It is clear
- that, to Coq’s opinion, [α + β] is not structurally _equal_ to [β + α], yet
- we will have to prove they are “equivalent.” *)
-
-(** *** Introducing [type_equiv] *)
-
-(** Since [=] for [Type] is not suitable for reasoning about algebraic
- datatypes, we introduce our own equivalence relation, denoted [==]. We say
- two types [α] and [β] are equivalent up to an isomorphism (denoted by [α ==
- β]) when for any term of type [α], there exists a counter-part term of type
- [β] and vice versa. In other words, [α] and [β] are equivalent if we can
- exhibit two functions [f] and [g] such that:
-
- #<span class="dmath">#\forall (x : α),\ x = g(f(x))#</span>#
-
- #<span class="dmath">#\forall (y : β),\ y = f(g(y))#</span>#
-
- In Coq, this translates into the following inductive types. *)
-
-Reserved Notation "x == y" (at level 72).
-
-Inductive type_equiv (α β : Type) : Prop :=
-| mk_type_equiv (f : α -> β) (g : β -> α)
- (equ1 : forall (x : α), x = g (f x))
- (equ2 : forall (y : β), y = f (g y))
- : α == β
-where "x == y" := (type_equiv x y).
-
-(** As mentioned earlier, we prove two types are equivalent by exhibiting
- two functions, and proving these functions satisfy two properties. We
- introduce a <<Ltac>> notation to that end. *)
-
-Tactic Notation "equiv" "with" uconstr(f) "and" uconstr(g)
- := apply (mk_type_equiv f g).
-
-(** The tactic [equiv with f and g] will turn a goal of the form [α == β] into
- two subgoals to prove [f] and [g] form an isomorphism. *)
-
-(** *** [type_equiv] is an Equivalence *)
-
-(** [type_equiv] is an equivalence, and we can prove it by demonstrating it is
- (1) reflexive, (2) symmetric, and (3) transitive.
-
- [type_equiv] is reflexive. *)
-
-Lemma type_equiv_refl (α : Type) : α == α.
-
-(** This proof is straightforward. A type [α] is equivalent to itself because:
-
- #<span class="imath">#\forall (x : α),\ x = id(id(x))#</span># *)
-
-Proof.
- now equiv with (@id α) and (@id α).
-Qed.
-
-(** [type_equiv] is symmetric. *)
-
-Lemma type_equiv_sym {α β} (equ : α == β) : β == α.
-
-(** If [α == β], then we know there exists two functions [f] and [g] which
- satisfy the expected properties. We can “swap” them to prove that [β == α].
- *)
-
-Proof.
- destruct equ as [f g equ1 equ2].
- now equiv with g and f.
-Qed.
-
-(** [type_equiv] is transitive *)
-
-Lemma type_equiv_trans {α β γ} (equ1 : α == β) (equ2 : β == γ)
- : α == γ.
-
-(** If [α == β], we know there exists two functions [fα] and [gβ] which satisfy
- the expected properties of [type_equiv]. Similarly, because [β == γ], we
- know there exists two additional functions [fβ] and [gγ]. We can compose
- these functions together to prove [α == γ].
-
- As a reminder, composing two functions [f] and [g] (denoted by [f >>> g]
- thereafter) consists in using the result of [f] as the input of [g]: *)
-
-Infix ">>>" := (fun f g x => g (f x)) (at level 70).
-
-(** Then comes the proof. *)
-
-Proof.
- destruct equ1 as [fα gβ equαβ equβα],
- equ2 as [fβ gγ equβγ equγβ].
- equiv with (fα >>> fβ) and (gγ >>> gβ).
- + intros x.
- rewrite <- equβγ.
- now rewrite <- equαβ.
- + intros x.
- rewrite <- equβα.
- now rewrite <- equγβ.
-Qed.
-
-(** The Coq standard library introduces the [Equivalence] type class. We can
- provide an instance of this type class for [type_equiv], using the three
- lemmas we have proven in this section. *)
-
-#[refine]
-Instance type_equiv_Equivalence : Equivalence type_equiv :=
- {}.
-
-Proof.
- + intros x.
- apply type_equiv_refl.
- + intros x y.
- apply type_equiv_sym.
- + intros x y z.
- apply type_equiv_trans.
-Qed.
-
-(** *** Examples *)
-
-(** **** [list]’s Canonical Form *)
-
-(** We now come back to our initial example, given in the Introduction of this
- write-up. We can prove our assertion, that is [list α == unit + α * list
- α]. *)
-
-Lemma list_equiv (α : Type)
- : list α == unit + α * list α.
-
-Proof.
- equiv with (fun x => match x with
- | [] => inl tt
- | x :: rst => inr (x, rst)
- end)
- and (fun x => match x with
- | inl _ => []
- | inr (x, rst) => x :: rst
- end).
- + now intros [| x rst].
- + now intros [[] | [x rst]].
-Qed.
-
-(** **** [list] is a Morphism *)
-
-(** This means that if [α == β], then [list α == list β]. We prove this by
- defining an instance of the [Proper] type class. *)
-
-Instance list_Proper
- : Proper (type_equiv ==> type_equiv) list.
-
-Proof.
- add_morphism_tactic.
- intros α β [f g equαβ equβα].
- equiv with (map f) and (map g).
- all: setoid_rewrite map_map; intros l.
- + replace (fun x : α => g (f x))
- with (@id α).
- ++ symmetry; apply map_id.
- ++ apply functional_extensionality.
- apply equαβ.
- + replace (fun x : β => f (g x))
- with (@id β).
- ++ symmetry; apply map_id.
- ++ apply functional_extensionality.
- apply equβα.
-Qed.
-
-(** The use of the [Proper] type class allows for leveraging hypotheses of the
- form [α == β] with the [rewrite] tactic. I personally consider providing
- instances of [Proper] whenever it is possible to be a good practice, and
- would encourage any Coq programmers to do so. *)
-
-(** **** [nat] is a Special-Purpose [list] *)
-
-(** Did you notice? Now, using [type_equiv], we can prove it! *)
-
-Lemma nat_and_list : nat == list unit.
-
-Proof.
- equiv with (fix to_list n :=
- match n with
- | S m => tt :: to_list m
- | _ => []
- end)
- and (fix of_list l :=
- match l with
- | _ :: rst => S (of_list rst)
- | _ => 0
- end).
- + induction x; auto.
- + induction y; auto.
- rewrite <- IHy.
- now destruct a.
-Qed.
-
-(** **** Non-empty Lists *)
-
-(** We can introduce a variant of [list] which contains at least one element by
- modifying the [nil] constructor so that it takes one argument instead of
- none. *)
-
-Inductive non_empty_list (α : Type) :=
-| ne_cons : α -> non_empty_list α -> non_empty_list α
-| ne_singleton : α -> non_empty_list α.
-
-(** We can demonstrate the relation between [list] and [non_empty_list], which
- reveals an alternative implementation of [non_empty_list]. More precisely,
- we can prove that [forall (α : Type), non_empty_list α == α * list α]. It
- is a bit more cumbersome, but not that much. We first define the conversion
- functions, then prove they satisfy the properties expected by
- [type_equiv]. *)
-
-Fixpoint non_empty_list_of_list {α} (x : α) (l : list α)
- : non_empty_list α :=
- match l with
- | y :: rst => ne_cons x (non_empty_list_of_list y rst)
- | [] => ne_singleton x
- end.
-
-#[local]
-Fixpoint list_of_non_empty_list {α} (l : non_empty_list α)
- : list α :=
- match l with
- | ne_cons x rst => x :: list_of_non_empty_list rst
- | ne_singleton x => [x]
- end.
-
-Definition prod_list_of_non_empty_list {α} (l : non_empty_list α)
- : α * list α :=
- match l with
- | ne_singleton x => (x, [])
- | ne_cons x rst => (x, list_of_non_empty_list rst)
- end.
-
-Lemma ne_list_list_equiv (α : Type)
- : non_empty_list α == α * list α.
-
-Proof.
- equiv with prod_list_of_non_empty_list
- and (prod_curry non_empty_list_of_list).
- + intros [x rst|x]; auto.
- cbn.
- revert x.
- induction rst; intros x; auto.
- cbn; now rewrite IHrst.
- + intros [x rst].
- cbn.
- destruct rst; auto.
- change (non_empty_list_of_list x (α0 :: rst))
- with (ne_cons x (non_empty_list_of_list α0 rst)).
- replace (α0 :: rst)
- with (list_of_non_empty_list
- (non_empty_list_of_list α0 rst)); auto.
- revert α0.
- induction rst; intros y; [ reflexivity | cbn ].
- now rewrite IHrst.
-Qed.
-
-(** ** The [sum] Operator *)
-
-(** *** [sum] is a Morphism *)
-
-(** This means that if [α == α'] and [β == β'], then [α + β == α' + β']. To
- prove this, we compose together the functions whose existence is implied by
- [α == α'] and [β == β']. To that end, we introduce the auxiliary function
- [lr_map]. *)
-
-Definition lr_map_sum {α β α' β'} (f : α -> α') (g : β -> β')
- (x : α + β)
- : α' + β' :=
- match x with
- | inl x => inl (f x)
- | inr y => inr (g y)
- end.
-
-(** Then, we prove [sum] is a morphism by defining a [Proper] instance. *)
-
-Instance sum_Proper
- : Proper (type_equiv ==> type_equiv ==> type_equiv) sum.
-
-Proof.
- add_morphism_tactic.
- intros α α' [fα gα' equαα' equα'α]
- β β' [fβ gβ' equββ' equβ'β].
- equiv with (lr_map_sum fα fβ)
- and (lr_map_sum gα' gβ').
- + intros [x|y]; cbn.
- ++ now rewrite <- equαα'.
- ++ now rewrite <- equββ'.
- + intros [x|y]; cbn.
- ++ now rewrite <- equα'α.
- ++ now rewrite <- equβ'β.
-Qed.
-
-(** *** [sum] is Commutative *)
-
-Definition sum_invert {α β} (x : α + β) : β + α :=
- match x with
- | inl x => inr x
- | inr x => inl x
- end.
-
-Lemma sum_com {α β} : α + β == β + α.
-
-Proof.
- equiv with sum_invert and sum_invert;
- now intros [x|x].
-Qed.
-
-(** *** [sum] is Associative *)
-
-(** The associativity of [sum] is straightforward to prove, and should not pose
- a particular challenge to perspective readers; if we assume that this
- article is well-written, that is! *)
-
-Lemma sum_assoc {α β γ} : α + β + γ == α + (β + γ).
-
-Proof.
- equiv with (fun x =>
- match x with
- | inl (inl x) => inl x
- | inl (inr x) => inr (inl x)
- | inr x => inr (inr x)
- end)
- and (fun x =>
- match x with
- | inl x => inl (inl x)
- | inr (inl x) => inl (inr x)
- | inr (inr x) => inr x
- end).
- + now intros [[x|x]|x].
- + now intros [x|[x|x]].
-Qed.
-
-(** *** [sum] has a Neutral Element *)
-
-(** We need to find a type [e] such that [α + e == α] for any type [α]
- (similarly to #<span class="imath">#x~+~0~=~x#</span># for any natural
- number #<span class="imath">#x#</span># that is).
-
- Any empty type (that is, a type with no term such as [False]) can act as the
- natural element of [Type]. As a reminder, empty types in Coq are defined
- with the following syntax: *)
-
-Inductive empty := .
-
-(** Note that the following definition is erroneous.
-
-<<
-Inductive empty.
->>
-
- Using [Print] on such a type illustrates the issue.
-
-<<
-Inductive empty : Prop := Build_empty { }
->>
-
- That is, when the [:=] is omitted, Coq defines an inductive type with one
- constructor.
-
- Coming back to [empty] being the neutral element of [sum]. From a high-level
- perspective, this makes sense. Because we cannot construct a term of type
- [empty], then [α + empty] contains exactly the same numbers of terms as [α].
- This is the intuition. Now, how can we convince Coq that our intuition is
- correct? Just like before, by providing two functions of types:
-
- - [α -> α + empty]
- - [α + empty -> α]
-
- The first function is [inl], that is one of the constructor of [sum].
-
- The second function is more tricky to write in Coq, because it comes down to
- writing a function of type is [empty -> α]. *)
-
-Definition from_empty {α} : empty -> α :=
- fun x => match x with end.
-
-(** It is the exact same trick that allows Coq to encode proofs by
- contradiction.
-
- If we combine [from_empty] with the generic function *)
-
-Definition unwrap_left_or {α β}
- (f : β -> α) (x : α + β)
- : α :=
- match x with
- | inl x => x
- | inr x => f x
- end.
-
-(** Then, we have everything to prove that [α == α + empty]. *)
-
-Lemma sum_neutral (α : Type) : α == α + empty.
-
-Proof.
- equiv with inl and (unwrap_left_or from_empty);
- auto.
- now intros [x|x].
-Qed.
-
-(** ** The [prod] Operator *)
-
-(** This is very similar to what we have just proven for [sum], so expect less
- text for this section. *)
-
-(** *** [prod] is a Morphism *)
-
-Definition lr_map_prod {α α' β β'}
- (f : α -> α') (g : β -> β')
- : α * β -> α' * β' :=
- fun x => match x with (x, y) => (f x, g y) end.
-
-Instance prod_Proper
- : Proper (type_equiv ==> type_equiv ==> type_equiv) prod.
-
-Proof.
- add_morphism_tactic.
- intros α α' [fα gα' equαα' equα'α]
- β β' [fβ gβ' equββ' equβ'β].
- equiv with (lr_map_prod fα fβ)
- and (lr_map_prod gα' gβ').
- + intros [x y]; cbn.
- rewrite <- equαα'.
- now rewrite <- equββ'.
- + intros [x y]; cbn.
- rewrite <- equα'α.
- now rewrite <- equβ'β.
-Qed.
-
-(** *** [prod] is Commutative *)
-
-Definition prod_invert {α β} (x : α * β) : β * α :=
- (snd x, fst x).
-
-Lemma prod_com {α β} : α * β == β * α.
-
-Proof.
- equiv with prod_invert and prod_invert;
- now intros [x y].
-Qed.
-
-(** *** [prod] is Associative *)
-
-Lemma prod_assoc {α β γ}
- : α * β * γ == α * (β * γ).
-
-Proof.
- equiv with (fun x =>
- match x with
- | ((x, y), z) => (x, (y, z))
- end)
- and (fun x =>
- match x with
- | (x, (y, z)) => ((x, y), z)
- end).
- + now intros [[x y] z].
- + now intros [x [y z]].
-Qed.
-
-(** *** [prod] has a Neutral Element *)
-
-Lemma prod_neutral (α : Type) : α * unit == α.
-
-Proof.
- equiv with fst and ((flip pair) tt).
- + now intros [x []].
- + now intros.
-Qed.
-
-(** ** [prod] has an Absorbing Element *)
-
-(** And this absorbing element is [empty], just like the absorbing element of
- the multiplication of natural number is #<span class="imath">#0#</span>#
- (the neutral element of the addition). *)
-
-Lemma prod_absord (α : Type) : α * empty == empty.
-
-Proof.
- equiv with (snd >>> from_empty)
- and (from_empty).
- + intros [_ []].
- + intros [].
-Qed.
-
-(** ** [prod] and [sum] Distributivity *)
-
-(** Finally, we can prove the distributivity property of [prod] and [sum] using
- a similar approach to prove the associativity of [prod] and [sum]. *)
-
-Lemma prod_sum_distr (α β γ : Type)
- : α * (β + γ) == α * β + α * γ.
-
-Proof.
- equiv with (fun x => match x with
- | (x, inr y) => inr (x, y)
- | (x, inl y) => inl (x, y)
- end)
- and (fun x => match x with
- | inr (x, y) => (x, inr y)
- | inl (x, y) => (x, inl y)
- end).
- + now intros [x [y | y]].
- + now intros [[x y] | [x y]].
-Qed.
-
-(** ** Bonus: Algebraic Datatypes and Metaprogramming *)
-
-(** Algebraic datatypes are very suitable for generating functions, as
- demonstrated by the automatic deriving of typeclass in Haskell or trait in
- Rust. Because a datatype can be expressed in terms of [sum] and [prod], you
- just have to know how to deal with these two constructions to start
- metaprogramming.
-
- We can take the example of the [fold] functions. A [fold] function is a
- function which takes a container as its argument, and iterates over the
- values of that container in order to compute a result.
-
- We introduce [fold_type INPUT CANON_FORM OUTPUT], a tactic to compute the
- type of the fold function of the type <<INPUT>>, whose “canonical form” (in
- terms of [prod] and [sum]) is <<CANON_FORM>> and whose result type is
- #<code>#OUTPUT#</code>#. Interested readers have to be familiar with
- [Ltac]. *)
-
-Ltac fold_args b a r :=
- lazymatch a with
- | unit =>
- exact r
- | b =>
- exact (r -> r)
- | (?c + ?d)%type =>
- exact (ltac:(fold_args b c r) * ltac:(fold_args b d r))%type
- | (b * ?c)%type =>
- exact (r -> ltac:(fold_args b c r))
- | (?c * ?d)%type =>
- exact (c -> ltac:(fold_args b d r))
- | ?a =>
- exact (a -> r)
- end.
-
-Ltac currying a :=
- match a with
- | ?a * ?b -> ?c => exact (a -> ltac:(currying (b -> c)))
- | ?a => exact a
- end.
-
-Ltac fold_type b a r :=
- exact (ltac:(currying (ltac:(fold_args b a r) -> b -> r))).
-
-(** We use it to compute the type of a [fold] function for [list]. *)
-
-Definition fold_list_type (α β : Type) : Type :=
- ltac:(fold_type (list α) (unit + α * list α)%type β).
-
-(** Here is the definition of [fold_list_type], as outputed by [Print].
-
-<<
-fold_list_type =
- fun α β : Type => β -> (α -> β -> β) -> list α -> β
- : Type -> Type -> Type
->>
-
- It is exactly what you could have expected (as match the type of
- [fold_right]).
-
- Generating the body of the function is possible in theory, but probably not
- in [Ltac] without modifying a bit [type_equiv]. This could be a nice
- use-case for #<a href="https://github.com/MetaCoq/metacoq">#MetaCoq#</a>#,
- though. *)
diff --git a/site/posts/August2022.md b/site/posts/August2022.md
new file mode 100644
index 0000000..c0ff61b
--- /dev/null
+++ b/site/posts/August2022.md
@@ -0,0 +1,136 @@
+---
+published: 2022-08-15
+modified: 2023-05-09
+series:
+ parent: series/Retrospectives.html
+ next: posts/September2022.html
+tags: ['emacs', 'meta']
+abstract: |
+ In an attempt to start a regular blogging habbits, I am giving a try to the
+ monthly “status updates” format. This month: some Emacs config hacking, and
+ some changes on how this website is generated.
+---
+
+# What happened in August 2022?
+
+Without further ado, let’s take a look at what was achieved
+for the last thirty days or so.
+
+## Emacs
+
+I have started tweaking and improving my Emacs
+[configuration](https://src.soap.coffee/dotfiles/emacs.d.git)
+again[^minimalism].
+
+[^minimalism]: After having used Emacs for seven years now, I am nowhere close
+ to consider my configuration as a done project. I really envy developers
+ who are using their editor with little to no customization.
+
+### Theme Selection Menu
+
+The change I am the most excited about is that I have *finally* reduced the
+boilerplate in need to write to use a new theme. I am very indecisive when
+it comes to theming. I like to have my choices, and I get tired of any
+colorscheme pretty quickly. As a consequence, I introduced a customizable
+variable to let me select a theme dynamically, and have this choice persist
+across Emacs session.
+
+I have a Hydra menu that allows me to select which theme I want to
+use for the time being. It looks like this.
+
+#[A Hydra menu for selecting a theme.](/img/select-theme.png)
+
+But adding new entries to this menu was very cumbersome, and mostly
+boilerplate that I know a good macro could abstract away. And I can
+finally report that I was right all along. I have my macros now,
+and they allow me to have the Hydra menu above generated with these
+simple lines of code.
+
+```lisp
+(use-theme ancientless "a" :straight nil :load-path "~/.emacs.d/lisp")
+(use-theme darkless "d" :straight nil :load-path "~/.emacs.d/lisp")
+(use-theme brightless "b" :straight nil :load-path "~/.emacs.d/lisp")
+(use-theme monotropic "m")
+(use-theme monokai "M")
+(use-theme nothing "n")
+(use-theme eink "e")
+(use-theme dracula "D")
+(use-theme chocolate "c")
+(use-themes-from tao-theme
+ '(("tl" . tao-yang)
+ ("td" . tao-yin)))
+```
+
+
+### Eldoc and Flycheck Popups
+
+I have been experimenting with several combinations of packages to
+have Eldoc and Flycheck using pop-up-like mechanisms to report
+things to me, instead of the echo area.
+
+The winning setup for now is the one that uses the [`quick-peek`
+package](https://github.com/cpitclaudel/quick-peek). That is,
+[`flycheck-inline`](https://github.com/flycheck/flycheck-inline) (customized to
+use quick-peek, as suggested in their README), and
+[`eldoc-overlay`](https://melpa.org/#/eldoc-overlay). This works well enough,
+so the pop-ups of eldoc are maybe a bit too distracting.
+
+#[`flycheck-inline` in action with an OCaml compilation error.](/img/flycheck-inline.png)
+
+In my quest for pop-ups, I ran into several issues with the packages I tried
+out. For instance, [`eldoc-box`](https://github.com/casouri/eldoc-box) was very
+nice, but also very slow for some reason. It turns out there were an issue
+about that slowness, wherein the culprit was identified. This allowed me to
+[submit a pull request that got merged rather
+quickly](https://github.com/casouri/eldoc-box/pull/48).
+
+Similarly, after a packages update, I discovered
+[`flycheck-ocaml`](https://github.com/flycheck/flycheck-ocaml) was no longer
+working, and [submit a patch to fix the
+issue](https://github.com/flycheck/flycheck-ocaml/pull/14).
+
+## This Website
+ I have not been investing a lot of time in this website for the past
+ six years or so. This month, things change a bit on that side too.
+
+### New Contents
+
+First, I have published a (short) article on [higher-order
+polymorphism in OCaml](/posts/RankNTypesInOCaml.html). The goal was for me to
+log somewhere the solution for an odd problem I was confronted to at
+`$WORK`{.bash}, but the resulting article was not doing a great job as
+conveying this. In particular, two comments on Reddit motivated me to rework
+it, and I am glad I did. I hope you enjoy the retake.
+
+Once this was out of the way, I decided that generating this website was taking
+way too much time for no good enough reason. The culprit was **`cleopatra`**, a
+toolchain I had developed in 2020 to integrate the build process of this
+website as additional contents that I thought might interest people. The sad
+things were: **`cleopatra`** was adding a significant overhead, and I never take
+the time to actually document them properly.
+
+### Under the Hood
+
+Overall, the cost of using **`cleopatra`** was not worth the burden, and so I
+got ride of it. Fortunately, it was not very difficult, since the job of
+**`cleopatra`** was to extracting the generation processes from org files; I
+just add to implement a small `makefile` to make use of these files, without
+having to rely on **`cleopatra`** anymore.
+
+This was something I was pondering to do for a long time, and as
+often in these circumstances, this gave me the extra motivation I
+needed to tackle other ideas I had in mind for this website. This
+is why now, rather than starting one Emacs process per Org file I
+have to process, my build toolchain starts one Emacs server, and
+later uses `emacsclient`.
+
+Now, most of the build time is spent by [soupault](https://soupault.app). I guess
+I will have to spend some time on the Lua plugins I have developed for it at
+some point.
+
+## A New Mailing List
+
+Finally, I have created [a public
+mailing](https://lists.sr.ht/~lthms/public-inbox) list that is available if you
+want to start a discussion on one of my article. Don’t hesitate to use it, or
+to register to it!
diff --git a/site/news/CFTSpatialShell.org b/site/posts/CFTSpatialShell.md
index 4ed4832..14de1cd 100644
--- a/site/news/CFTSpatialShell.org
+++ b/site/posts/CFTSpatialShell.md
@@ -1,91 +1,91 @@
-#+TITLE: Call For Testers: Spatial Shell
-
-In August, 2022, I discovered [[https://material-shell.com][Material Shell]]. A
-few weeks later, I had pieced together a working prototype of a dynamic tiling
-management “a la Material Shell” for [[https://swaywm.org][sway]]. By October,
-the project was basically fulfilling my needs, and I had already started to use
-it on my workstation[fn::I tried so you do not have to: having my graphical
-session going crazy during a work meeting because of a software I had
-written.]. The project sat there for a while, until I rediscovered this thing
-called /holidays/.
+---
+published: 2023-04-27
+tags: ['spatial-shell']
+abstract: |
+ In August, 2022, I have discovered Material Shell, and decided to implement
+ a dynamic tiling management “a la Material Shell” for sway I called Spatial
+ Shell. Spatial Shell works on my machine, which means it will definitely
+ break on yours, and I would love to know how.
+---
+
+# Spatial Shell: Call For Testers
+
+In August, 2022, I have discovered [Material
+Shell](https://material-shell.com). A few weeks later, I had pieced together a
+working prototype of a dynamic tiling management “a la Material Shell” for
+[sway](https://swaywm.org). By October, the project was basically fulfilling my
+needs, and I had already started to use it on my workstation[^1]. The project
+sat there for a while, until I rediscovered this thing called *holidays*.
+
+[^1]: I tried so you do not have to: having my graphical session going crazy
+ during a work meeting because of a software I had written.
For a short week, I tried to address at many of the remaining issues and
missing features that I was aware of. Then, I started to write
-[[https://lthms.github.io/spatial-shell/spatial.1.html][man pages]], which
+[man pages](https://lthms.github.io/spatial-shell/spatial.1.html), which
turned out to be the perfect opportunity to clean-up every clunkiness I could
possibly found.
I can’t help but finding the result rather nice and satisfying, and I hope you
-will enjoy it too! [[https://github.com/lthms/spatial-shell][Spatial Shell]]
+will enjoy it too! [Spatial Shell](https://github.com/lthms/spatial-shell)
works on my machine, which means it will definitely break on yours. But this is
where the fun lies, right? At this point, I definitely think the project is
ready to fall into the hands of (motivated) alpha testers.
-It would be fair to say that Spatial Shell does not bring any novel ideas to
-the table (neither from a user perspective, nor a developer one). Hopefully, it
-does implement well established ideas in a nice and reliable way, so sway users
-can enjoy Material Shell key ideas without having to install GNOME 3.
-
Anyway, let me give a tour!
-#+BEGIN_EXPORT html
-<nav id="generate-toc"></nav>
-<div id="history">site/news/CFTSpatialShell.org</div>
-#+END_EXPORT
-
-* Spatial Model
+## Spatial Model
At its core, Spatial Shell allows you to navigate a grid of windows.
Workspace are rows which can be individually configured to determine how many
windows (cells) you can see at once. More precisely, workspaces in Spatial
Shell can use two layouts:
-- *Maximize:* One window is displayed at a time
-- *Column:* Several windows are displayed side by side, to your convenience
+- **Maximize:** One window is displayed at a time
+- **Column:** Several windows are displayed side by side, to your convenience
-The reason why *Maximize* is not a particular case of *Column*, but instead a
+The reason why **Maximize** is not a particular case of **Column**, but instead a
layout on its own, is to easily allow you to switch to and back from maximizing
-the focused window. The following picture[fn::Created using
-[[https://excalidraw.com/][Excalidraw]].] summarizes one particular setup with
+the focused window. The following picture[^2] summarizes one particular setup with
tree workspaces, each configured differently.
-#+CAPTION: Spatial Shell allows users to configure the layout of each workspace individually.
-#+NAME: fig:spatial-shell-example
-[[../img/spatial-shell-example.png]]
+[^2]: Created using [Excalidraw](https://excalidraw.com/).
+
+#[Spatial Shell allows users to configure the layout of each workspace individually.](/img/spatial-shell-example.png)
-- Workspace 1 contains three windows, and uses the *Column* layout to display
+- Workspace 1 contains three windows, and uses the **Column** layout to display
at most three windows, so every windows are visible, with the focus being on
the leftmost one.
-- Workspace 2 contains four windows, and uses the *Column* layout to display at
+- Workspace 2 contains four windows, and uses the **Column** layout to display at
most two windows. As a consequence, two windows are not visible.
-- Workspace 3 contains two windows, and uses the *Maximize* layout so only the
+- Workspace 3 contains two windows, and uses the **Maximize** layout so only the
focused window is visible.
To help users know which window is currently holding the focus, Spatial Shell
slightly reduces the opacity of unfocused windows (as poorly hinted by the gray
backgrounds in the figure). Finally, Spatial Shell can also set a background
-for empty workspaces (using ~swaybg~ under the hood).
+for empty workspaces (using `swaybg`{.bash} under the hood).
And this is basically it. There is not much more to say about Spatial Shell
features.
-* Configuration
+## Configuration
From an implementation and user experience perspective, Spatial Shell is taking
inspiration from i3 and sway.
More precisely, Spatial Shell comprises two executables:
-- [[https://lthms.github.io/spatial-shell/spatial.1.html][spatial(1)]], the
+- [**spatial**(1)](https://lthms.github.io/spatial-shell/spatial.1.html), the
daemon implementing the spatial model described in the previous section, and
-- [[https://lthms.github.io/spatial-shell/spatialmsg.1.html][spatialmsg(1)]], a
+- [**spatialmsg**(1)](https://lthms.github.io/spatial-shell/spatialmsg.1.html), a
client used to control the current instance of spatial.
-Assuming ~$spatial~ and ~$spatialmsg~ contains the pathes to spatial and
-spatialmsg binaries respectively, then the simplest sway configuration to start
-using Spatial Shell is the following
+Assuming `$spatial`{.bash} and `$spatialmsg`{.bash} contains the pathes to
+spatial and spatialmsg binaries respectively, then the simplest sway
+configuration to start using Spatial Shell is the following
-#+begin_src
+```bash
exec $spatial
# moving the focus
@@ -109,61 +109,65 @@ bindsym $mod+o exec $spatialmsg "column count increment"
# start waybar, spatial will take care of the rest
exec waybar
-#+end_src
+```
By default, Spatial Shell sets the initial configuration of a workspace to
the Column layout with two columns at most, and sets the opacity of the
unfocused windows to 75%. This can be customized, either globally or per
workspace, by creating a configuration file in
-~$XDG_CONFIG_HOME/spatial/config~[fn::If unset, ~XDG_CONFIG_HOME~ defaults to
-~$HOME/.config~.].
+`$XDG_CONFIG_HOME/spatial/config`{.bash}[^3].
+
+[^3]: If unset, `$XDG_CONFIG_HOME`{.bash} defaults to
+ `$HOME/.config`{.bash}.
For instance, my config file looks like that.
-#+begin_src
+```bash
[workspace=3] default layout maximize
background "~/.config/spatial/wallpaper.jpg"
unfocus opacity 85
-#+end_src
+```
-See [[https://lthms.github.io/spatial-shell/spatial.5.html][spatial(5)]] for
+See [**spatial**(5)](https://lthms.github.io/spatial-shell/spatial.5.html) for
the list of commands supported by Spatial Shell.
As a sidenote, readers familiar with sway will definetely pick the resemblance
with sway and swaymsg, and it actually goes pretty deep. In a nutshell, swaymsg
connects to a UNIX socket created by sway at startup time, to sends it commands
-(see [[https://lthms.github.io/spatial-shell/spatial.5.html][spatial(5)]])
+(see [**spatial**(5)](https://lthms.github.io/spatial-shell/spatial.5.html))
using a dedicated IPC protocol inherited from i3 (see
-[[https://lthms.github.io/spatial-shell/sway-ipc.7.html][sway-ipc(5)]]). Not
+[**sway-ipc**(7)](https://lthms.github.io/spatial-shell/sway-ipc.7.html)). Not
only spatial also relies on sway IPC protocol to interact with sway and
implement its spatial model, it creates a UNIX of its own, and supports its own
protocol
-([[https://lthms.github.io/spatial-shell/spatial-ipc.7.html][spatial-ipc.7.html]]).
+([**spatial-ipc**(7)](https://lthms.github.io/spatial-shell/spatial-ipc.7.html][spatial-ipc.7.html)).
-* Waybar Integration
+## Waybar Integration
It is a common practice to use a so-called “bar” with sway, to display some
-useful information about the current state of the system. In the ~contrib/~
-directory of [[https://github.com/lthms/spatial-shell][Spatial Shell
-repository]], interested readers will find a configuration for
-[[https://github.com/Alexays/Waybar][Waybar]][fn::Readers familiar with
-Material Shell design will not be surprised by the general look and feel of
-the screenshot below.]. This configuration is somewhat clunky at the moment,
-due to the limitations of the custom widget of Waybar which does not allow to
-have one widget defines several “buttons.” I am interested in investing a bit
-of time to see if I could write a native widget, similarly to sway’s one.
+useful information about the current state of the system. In the `contrib/`
+directory of [Spatial Shell repository](https://github.com/lthms/spatial-shell),
+interested readers will find a configuration for
+[Waybar](https://github.com/Alexays/Waybar)[^design]. This configuration is
+somewhat clunky at the moment, due to the limitations of the custom widget of
+Waybar which does not allow to have one widget defines several “buttons.” I am
+interested in investing a bit of time to see if I could write a native widget,
+similarly to sway’s one.
+
+[^design]: Readers familiar with Material Shell design will not be surprised by
+ the general look and feel of the screenshot below.
+
+#[Mandatory screenshot of Spatial Shell, with the Waybar configuration.](/img/spatial-shell.png)
That being said, the user experience with this integration is already pretty
neat. As long as you don’t need more than 6 workspaces and 8 windows per
-workspaces[fn::These constants are totally arbitrary and can be increased by
-modifying the Waybar config, but the issue will remain that a limit will
-exist.], you are good to go!
+workspaces[^constants], you are good to go!
-#+CAPTION: Mandatory screenshot of Spatial Shell, with the Waybar configuration.
-#+NAME: fig:spatial-shell
-[[../img/spatial-shell.png]]
+[^constants]: These constants are totally arbitrary and can be increased by
+ modifying the Waybar config, but the issue will remain that a
+ limit will exist.
-* Building from Source
+## Building from Source
As of April 2023, the only way to get Spatial Shell is to build it from source.
@@ -182,15 +186,15 @@ You will need the following build dependencies:
Then, building and installing Spatial Shell is as simple as using the two
following commands.
-#+begin_src
+```bash
make build-deps
make install
-#+end_src
+```
-The latter command will install Spatial Shell’s binaries in ~/usr/local/bin~,
-and the man pages in ~/usr/local/man~. You can remove them with ~make
-uninstall~.
+The latter command will install Spatial Shell’s binaries in
+`/usr/local/bin`{.bash}, and the man pages in `/usr/local/man`{.bash}. You can
+remove them with `make uninstall`{.bash}.
-To install Waybar theme, copy ~contrib/waybar/spatialbar.py~ to
-~/usr/local/bin/spatialbar~ for instance, and the Waybar style and config file
-to ~$HOME/.config/waybar~.
+To install Waybar theme, copy `contrib/waybar/spatialbar.py`{.bash} to
+`/usr/local/bin/spatialbar`{.bash} for instance, and the Waybar style and
+config file to `$HOME/.config/waybar`{.bash}.
diff --git a/site/posts/CleopatraV1.md b/site/posts/CleopatraV1.md
new file mode 100644
index 0000000..55a850a
--- /dev/null
+++ b/site/posts/CleopatraV1.md
@@ -0,0 +1,402 @@
+---
+published: 2020-02-04
+modified: 2023-05-12
+tags: ['meta', 'literate-programming', 'emacs']
+abstract: |
+ Our literate programming toolchain cannot live solely inside Org files,
+ waiting to be turned into actual code by Babel. Otherwise there we would
+ not have any code to execute the first time we try to generate the website.
+---
+
+# A Literate Toolchain To Build This Website
+
+A literate program is a particular type of software program where code is not
+directly written in source files, but rather in text document as code
+snippets. In essence, literate programming allows for writing in the same place
+both the software program and its technical documentation.
+
+**`cleopatra`** is a “literate toolchain” I have implemented to build this
+website, and you are currently reading it[^past]. That is, **`cleopatra`** is
+both the build system and an article of this website! To acheive this,
+**`cleopatra`** has been written as a collection of org files which can be
+either “tangled” using [Babel](https://orgmode.org/worg/org-contrib/babel/) or
+“exported” as a HTML document. Tangling here refers to extracted marked code
+blocks into files.
+
+[^past]: This sentence was true when this article was published, but things
+ have changed since then.
+
+ What you are reading is actually the rendered version of a Markdown
+ document that was manually “translated” from the Org original document,
+ named `Bootstrap.org`. Interested readers can [have a look at the original
+ version
+ here](https://src.soap.coffee/soap.coffee/lthms.git/tree/site/cleopatra?id=9329e9883a52eb95c0803a46560c396d149ef2c6).
+
+ Truth be told, said version is probably complete gibberish for anyone who
+ isn’t me. For this reason, this version was actually heavily reworked…
+ Because I have too much free time, probably.
+
+The page you are currently reading is **`cleopatra`** entry point. Its
+primilarly purpose is to define two Makefiles —`makefile` and `bootstrap.mk`—
+and the necessary emacs-lisp script to tangle this document.
+
+On the one hand, `makefile` is the main entrypoint of **`cleopatra`**. It
+serves two purposes:
+
+1. It initiates a few global variables, and
+2. It provides a rule to tangle this document, that is to update itself and
+ `bootstrap.mk`.
+
+On the other hand, `bootstrap.mk` is used to declare the various “generation
+processes” used to generate this website.
+
+`makefile` and the emacs-lisp scripts are versioned, because they are necessary
+to bootstrap **`cleopatra`**; but since they are also define in this document,
+it means **`cleopatra`** can update itself, in some sense. This is to be kept in mind
+when modifying this document to hastly.
+
+## Global Constants and Variables
+
+First, `makefile` defines several global “constants”[^constants]. In a nutshell,
+
+- `$ROOT`{.bash} tells Emacs where the root of your website sources is, so
+ that tangled output filenames can be given relative to it rather than the org
+ files. So for instance, the `block_src` tangle parameter for `Makefile`
+ looks like `:tangle Makefile`{.lisp}, instead of `:tangle
+ ../../Makefile`{.lisp}.
+- `$CLEODIR`{.bash} tells **`cleopatra`** where its sources live. If you place
+ it inside the `site/` directory (as it is intended), and you enable the use
+ of `org` files to author your contents, then **`cleopatra`** documents will
+ be part of your website. If you don’t want that, just move the directory
+ outside the `site/` directory, and update the `$CLEODIR`{.bash} variable
+ accordingly.
+
+[^constants]: As far as I know, `make` does not support true *constant* values,
+ It is assumed generation processes will not modify them.
+
+For this website, these constants are defined as follows[^comments].
+
+[^comments]: I will use a comment in the first line to recall to which file a
+ given block code is expected to be tangled.
+
+```makefile
+# makefile:
+ROOT := $(shell pwd)
+CLEODIR := site/cleopatra
+```
+
+We then introduce two variables to list the output of the generation processes,
+with two purposes in mind: keeping the `.gitignore` up-to-date automatically,
+and providing rules to remove them.
+
+- `$ARTIFACTS`{.bash} lists the short-term artifacts which can be removed
+ frequently without too much hassle. They will be removed by `make clean`.
+- `$CONFIGURE`{.bash} lists the long-term artifacts whose generation can be
+ time consuming. They will only be removed by`~make cleanall`.
+
+```makefile
+# makefile:
+ARTIFACTS := build.log
+CONFIGURE :=
+
+clean :
+ @rm -rf ${ARTIFACTS}
+
+cleanall : clean
+ @rm -rf ${CONFIGURE}
+```
+
+Generation processes can declare new build outputs using the `+=` assignement
+operators. Using another operator will likely provent an underisable result.
+
+## Tangling Org Documents
+
+**`cleopatra`** is a literate program implemented with Org mode, an Emacs major
+editing mode. We provide the necessary bits to easily tangle Org documents.
+
+The configuration of Babel is done using an emacs lisp script called
+`tangle-org.el` whose status is similar to `Makefile`. It is part of the
+bootstrap process, and therefore lives “outside” of **`cleopatra`** (it is not
+deleted with `make clean` for instance). However, it is overwritten when this
+file is tangled. If you try to modify it and find that **`cleopatra`** does not
+work properly, you should restore it.
+
+```lisp
+;;; tangle-org.el:
+(require 'org)
+(cd (getenv "ROOT"))
+(setq org-confirm-babel-evaluate nil)
+(setq org-src-preserve-indentation t)
+(add-to-list 'org-babel-default-header-args
+ '(:mkdirp . "yes"))
+(org-babel-do-load-languages
+ 'org-babel-load-languages
+ '((shell . t)))
+(org-babel-tangle)
+```
+
+We define variables that ensure that the `$ROOT`{.bash} environment variable is
+set and `tangle-org.el` is loaded when using Emacs.
+
+```makefile
+# makefile:
+EMACSBIN := emacs
+EMACS := ROOT="${ROOT}" ${EMACSBIN}
+TANGLE := --batch \
+ --load="${ROOT}/scripts/tangle-org.el" \
+ 2>> build.log
+```
+
+Finally, we introduce a [canned
+recipe](https://www.gnu.org/software/make/manual/html_node/Canned-Recipes.html#Canned-Recipes)
+to seamlessly tangle a given file[^canned].
+
+[^canned]: It was the first time I had used canned recipes, and I don’t think I
+ had the opportunity to re-use it ever again.
+
+```makefile
+# makefile:
+define emacs-tangle =
+echo " tangle $<"
+${EMACS} $< ${TANGLE}
+endef
+```
+
+## Updating `.gitignore` Automatically
+
+Assuming each generation process correctly defines its `$ARTIFACTS`{.bash}
+and `$CONFIGURE`{.bash} variables, we have all the information we need to
+update `.gitignore` automatically.
+
+This is done by adding markers in `.gitignore` to define a region under the
+control of **`cleopatra`**, and writing a script to update said region after
+each build.
+
+```bash
+# update-gitignore.sh:
+BEGIN_MARKER="# begin generated files"
+END_MARKER="# begin generated files"
+
+# remove the previous list of generated files to ignore
+sed -i -e "/${BEGIN_MARKER}/,/${END_MARKER}/d" .gitignore
+# remove trailing empty lines
+sed -i -e :a -e '/^\n*$/{$d;N;};/\n$/ba' .gitignore
+
+# output the list of files to ignore
+echo "" >> .gitignore
+echo ${BEGIN_MARKER} >> .gitignore
+for f in $@; do
+ echo "${f}" >> .gitignore
+done
+echo ${END_MARKER} >> .gitignore
+```
+
+The `ignore` rule of `makefile` is defined as follows.
+
+```makefile
+# makefile:
+ignore :
+ @echo " update gitignore"
+ @scripts/update-gitignore.sh \
+ ${ARTIFACTS} \
+ ${CONFIGURE}
+```
+
+## Bootstrapping
+
+The core purpose of `makefile` remains to bootstrap the chain of generation
+processes. This chain is divided into three stages: `prebuild`, `build`, and
+`postbuild`.
+
+This translates as follows in `makefile`.
+
+```makefile
+# makefile:
+default : postbuild ignore
+
+init :
+ @rm -f build.log
+
+prebuild : init
+
+build : prebuild
+
+postbuild : build
+
+.PHONY : init prebuild build postbuild ignore
+```
+
+A *generation process* in **`cleopatra`** is a Makefile which provides rules for
+these three stages, along with the utilities used by these rules. More
+precisely, a generation process `proc` is defined in `proc.mk`. The rules of
+`proc.mk` for each stage are expected to be prefixed by `proc-`, *e.g.*,
+`proc-prebuild` for the `prebuild` stage.
+
+Eventually, the following dependencies are expected between within the chain of
+generation processes for every generation processes.
+
+```makefile
+prebuild : proc-prebuild
+build : proc-build
+postbuild : proc-postbuild
+
+proc-build : proc-prebuild
+proc-postbuild : proc build
+```
+
+**`cleopatra`** is a literate toolchain whose main purpose is to allow me to
+turn the scripts I wrote to generate my website into blogposts of said website.
+As such, it allows me to implement the generation processes using Org mode,
+which means that before being able to start generating HTML files,
+**`cleopatra`** has to tangle the generation processes.
+
+To achieve this, **`cleopatra`** relies on a particular behavior of `make`
+regarding the `include` directive. If there exists a rule to generate a
+Makefile used as an operand of `include`, `make` will use this rule to update
+(if necessary) said Makefile before actually including it.
+
+Therefore, rules of the following form achieve our ambition of extensibility.
+
+```makefile
+include ${PROC}.mk
+
+prebuild : ${PROC}-prebuild
+build : ${PROC}-build
+postbuild : ${PROC}-postbuild
+
+${PROC}-prebuild : ${PROC}.mk ${AUX}
+${PROC}-build : ${PROC}-prebuild
+${PROC}-postbuild : ${PROC}-build
+
+${PROC}.mk ${AUX} &:\
+ ${CLEODIR}/${IN}
+ @$(emacs-tangle)
+
+CONFIGURE += ${PROC}.mk ${AUX}
+
+.PHONY : ${PROC}-prebuild \
+ ${PROC}-build \
+ ${PROC}-postbuild
+```
+
+where
+
+- `$IN`{.bash} is the Org document which contains the generation process code
+- `$PROC`{.bash} is the name of the generation process
+- `$AUX`{.bash} lists the utilities of the generation process tangled from
+ `$IN`{.bash} with `$PROC.mk`{.bash}
+
+We use `&:` is used in place of `:` to separate the target from its
+dependencies in the “tangle rule.”[^obscure] This tells `make` that the recipe of this
+rule generates all these files.
+
+[^obscure]: Yet another obscure Makefile trick I have never encountered again.
+
+Rather than writing these rules manually for each generation process we want to
+define, we rely on to [noweb of
+Babel](https://orgmode.org/worg/org-tutorials/org-latex-export.html). We call
+`extends` the primitive to generate new generation processes.
+
+We derive the rule to tangle `bootstrap.mk` using `extends`, using the following Org mode syntax.
+
+```org
+#+BEGIN_SRC makefile :noweb yes
+# makefile:
+<<extends(IN="Bootstrap.org", PROC="bootstrap", AUX="scripts/update-gitignore.sh")>>
+#+END_SRC
+```
+
+which gives the following result.
+
+```makefile
+include bootstrap.mk
+
+prebuild : bootstrap-prebuild
+build : bootstrap-build
+postbuild : bootstrap-postbuild
+
+bootstrap-prebuild : bootstrap.mk scripts/update-gitignore.sh
+bootstrap-build : bootstrap-prebuild
+bootstrap-postbuild : bootstrap-build
+
+bootstrap.mk scripts/update-gitignore.sh &:\
+ ${CLEODIR}/Bootstrap.org
+ @$(emacs-tangle)
+
+CONFIGURE += bootstrap.mk scripts/update-gitignore.sh
+
+.PHONY : bootstrap-prebuild \
+ bootstrap-build \
+ bootstrap-postbuild
+```
+
+These are the last lines of `makefile`. The rest of the generation processes will be
+declared in `bootstrap.mk`.
+
+## Generation Processes
+
+In this section, we construct `bootstrap.mk` by enumerating the generation
+processes that are currently used to generate the website you are reading.
+
+We recall that each generation process shall
+
+1. Define `proc-prebuild`, `proc-build`, and `proc-postbuild`
+2. Declare dependencies between stages of generation processes
+3. Declare build outputs (see `ARTIFACTS` and `CONFIGURE`)
+
+### Theming and Templating
+
+The
+[`theme`](https://src.soap.coffee/soap.coffee/lthms.git/tree/site/cleopatra/Theme.org?id=9329e9883a52eb95c0803a46560c396d149ef2c6)
+generation process controls the general appearance of the website. More
+precisely, it introduces the main template used by `soupault`
+(`main/templates.html`), and the main SASS sheet used by this template.
+
+If a generation process produces a set of styles within a specific SASS files,
+the current approach is
+
+1. To make this file a dependency of `theme-build`
+2. To modify `style/main.sass` in `theme`
+ to import this file
+
+Eventually, the second step will be automated, but in the meantime
+this customization is mandatory.
+
+### Configuring Soupault
+
+The
+[`soupault`](https://src.soap.coffee/soap.coffee/lthms.git/tree/site/cleopatra/Soupault.org?id=9329e9883a52eb95c0803a46560c396d149ef2c6)
+generation configures and run `soupault`, in order to generate a static
+website.
+
+If a generation process `proc` produces files that will eventually be
+integrated to your website, its `proc-build` recipe needs to be executed
+*before* the `soupault-build` recipe. This can be enforced by making the
+dependency explicit to `make`, *i.e.*,
+
+```makefile
+soupault-build : proc-build
+```
+
+Eventually, generation processes shall be allowed to produce specific `soupault`
+widgets to be integrated into `soupault.conf`.
+
+### Authoring Contents
+
+The fact that **`cleopatra`** is a literate program which gradually generates
+itself was not intended: it is a consequence of my desire to be able to easily
+use whatever format I so desire for writing my contents, and Org documents in
+particular.
+
+In the present website, contents can be written in the following format:
+
+- **HTML Files:** This requires no particular set-up, since HTML is the *lingua
+ franca* of `soupault`.
+- **Regular Coq files:** Coq is a system which allows to write machine-checked
+ proofs, and it comes with a source “prettifier” called `coqdoc`. [Learn more
+ about the generation process for Coq
+ files](https://src.soap.coffee/soap.coffee/lthms.git/tree/site/cleopatra/Contents/Coq.org?id=9329e9883a52eb95c0803a46560c396d149ef2c6).
+- **Org documents:** Emacs comes with a powerful editing mode called [Org
+ mode](https://orgmode.org/), and Org documents are really pleasant to work
+ with. [Learn more about the generation process for Org
+ documents](https://src.soap.coffee/soap.coffee/lthms.git/tree/site/cleopatra/Contents/Org.org?id=9329e9883a52eb95c0803a46560c396d149ef2c6)
diff --git a/site/posts/CleopatraV1.org b/site/posts/CleopatraV1.org
deleted file mode 100644
index ee5789d..0000000
--- a/site/posts/CleopatraV1.org
+++ /dev/null
@@ -1,324 +0,0 @@
-#+BEGIN_EXPORT html
-<h1><strong><code>cleopatra</code></strong>: Bootstrapping an Extensible Toolchain</h1>
-#+END_EXPORT
-
-#+BEGIN_TODO
-You are about to read the first version of *~cleopatra~*, the toolchain
-initially implemented to build the website you are reading. Since then,
-*~cleopatra~* has been completely rewritten as a
-[[https://cleopatra.soap.coffee][independant, more generic command-line
-program]]. That being said, the build process described in this write-up remains
-the one implemented in *~cleopatra~* the Second.
-#+END_TODO
-
-A literate program is a particular type of software program where code is not
-directly written in source files, but rather in text document as code
-snippets. In some sense, literate programming allows for writing in the same
-place both the software program and its technical documentation.
-
-That being said, *~cleopatra~* is a toolchain to build a website before being a
-literate program, and one of its objective is to be /part of this very website
-it is used to generate/. To acheive this, *~cleopatra~* has been written as a
-collection of org files which can be either “tangled” using
-[[https://orgmode.org/worg/org-contrib/babel/][Babel]] or “exported” as a HTML
-document. Tangling here refers to extracted marked code blocks into files.
-
-The page you are currently reading is *~cleopatra~* entry point. Its primilarly
-purpose is to introduce two Makefiles: ~Makefile~ and ~bootstrap.mk~.
-
-#+TOC: headlines 2
-
-#+BEGIN_EXPORT html
-<div id="history">site/posts/CleopatraV1.org</div>
-#+END_EXPORT
-
-~Makefile~ serves two purposes: it initiates a few global variables, and it
-provides a rule to generate ~bootstrap.mk~. At this point, some readers may
-wonder /why/ we need ~Makefile~ in this context, and the motivation behind this
-choice is really reminescent of a boot sequence. The rationale is that we need a
-“starting point” for *~cleopatra~*. The toolchain cannot live solely inside
-org-files, otherwise there would not have any code to execute the first time we
-tried to generate the website. We need an initial Makefile, one that has little
-chance to change, so that we can almost consider it read-only. Contrary to the
-other Makefiles that we will generate, this one will not be deleted by ~make
-clean~.
-
-This is similar to your computer: it requires a firmware to boot, whose purpose
-—in a nutshell— is to find and load an operating system.
-
-Modifying the content of ~Makefile~ in this document /will/ modify
-~Makefile~. This means one can easily put *~cleopatra~* into an inconsistent
-state, which would prevent further generation. This is why the generated
-~Makefile~ should be versioned, so that you can restore it using ~git~ if you
-made a mistake when you modified it.
-
-For readers interested in using *~cleopatra~* for their own websites, this
-documents tries to highlight the potential modifications they would have to
-make.
-
-* Global Constants and Variables
-
-First, ~Makefile~ defines several global “constants” (although as far as I know
-~make~ does not support true constant values, it is expected further generation
-process will not modify them).
-
-In a nutshell,
-
-- ~ROOT~ ::
- Tell Emacs where the root of your website sources is, so that tangled output
- filenames can be given relative to it rather than the org files. So for
- instance, the ~BLOCK_SRC~ tangle parameter for ~Makefile~ looks like ~:tangle
- Makefile~, instead of ~:tangle ../../Makefile~.
-- ~CLEODIR~ ::
- Tell *~cleopatra~* where its sources live. If you place it inside the ~site/~
- directory (as it is intended), and you enable the use of ~org~ files to author
- your contents, then *~cleopatra~* documents will be part of your website. If
- you don’t want that, just move the directory outside the ~site/~ directory,
- and update the ~CLEODIR~ variable accordingly.
-
-For this website, these constants are defined as follows.
-
-#+BEGIN_SRC makefile :noweb no-export
-ROOT := $(shell pwd)
-CLEODIR := site/cleopatra
-#+END_SRC
-
-We then introduce two variables to list the output of the generation processes,
-with two purposes in mind: keeping the ~.gitignore~ up-to-date automatically,
-and providing rules to remove them.
-
-- ~ARTIFACTS~ ::
- Short-term artifacts which can be removed frequently without too much
- hassle. They will be removed by ~make clean~.
-- ~CONFIGURE~ ::
- Long-term artifacts whose generation can be time consuming. They will only be
- removed by ~make cleanall~.
-
-#+BEGIN_SRC makefile
-ARTIFACTS := build.log
-CONFIGURE :=
-#+END_SRC
-
-Generation processes shall declare new build outputs using the ~+=~ assignement
-operators. Using another operator will likely provent an underisable result.
-
-* Easy Tangling of Org Documents
-
-*~cleopatra~* is a literate program implemented with Org mode, an Emacs major
-editing mode. We provide the necessary bits to easily tangle Org documents.
-
-The configuration of Babel is done using an emacs lisp script called
-~tangle-org.el~ whose status is similar to ~Makefile~. It is part of the
-bootstrap process, and therefore lives “outside” of *~cleopatra~* (it is not
-deleted with ~make clean~ for instance). However, it is overwritten. If you try
-to modify it and find that *~cleopatra~* does not work properly, you should
-restore it using ~git~.
-
-#+BEGIN_SRC emacs-lisp
-(require 'org)
-(cd (getenv "ROOT"))
-(setq org-confirm-babel-evaluate nil)
-(setq org-src-preserve-indentation t)
-(add-to-list 'org-babel-default-header-args
- '(:mkdirp . "yes"))
-(org-babel-do-load-languages
- 'org-babel-load-languages
- '((shell . t)))
-(org-babel-tangle)
-#+END_SRC
-
-We define variables that ensure that the ~ROOT~ environment variable is set and
-~tangle-org.el~ is loaded when using Emacs.
-
-#+BEGIN_SRC makefile
-EMACSBIN := emacs
-EMACS := ROOT="${ROOT}" ${EMACSBIN}
-TANGLE := --batch \
- --load="${ROOT}/scripts/tangle-org.el" \
- 2>> build.log
-#+END_SRC
-
-Finally, we introduce a
-[[https://www.gnu.org/software/make/manual/html_node/Canned-Recipes.html#Canned-Recipes][canned
-recipe]] to seamlessly tangle a given file.
-
-#+BEGIN_SRC makefile
-define emacs-tangle =
-echo " tangle $<"
-${EMACS} $< ${TANGLE}
-endef
-#+END_SRC
-
-* Bootstrapping
-
-The core purpose of ~Makefile~ remains to bootstrap the chain of generation
-processes. This chain is divided into three stages: ~prebuild~, ~build~, and
-~postbuild~.
-
-This translates as follows in ~Makefile~.
-
-#+BEGIN_SRC makefile
-default : postbuild ignore
-
-init :
- @rm -f build.log
-
-prebuild : init
-
-build : prebuild
-
-postbuild : build
-
-.PHONY : init prebuild build postbuild ignore
-#+END_SRC
-
-A *generation process* in *~cleopatra~* is a Makefile which provides rules for
-these three stages, along with the utilities used by these rules. More
-precisely, a generation process ~proc~ is defined in ~proc.mk~. The rules of
-~proc.mk~ for each stage are expected to be prefixed by ~proc-~, /e.g./,
-~proc-prebuild~ for the ~prebuild~ stage.
-
-Eventually, the following dependencies are expected between within the chain of
-generation processes.
-
-#+BEGIN_SRC makefile
-prebuild : proc-prebuild
-build : proc-build
-postbuild : proc-postbuild
-
-proc-build : proc-prebuild
-proc-postbuild : proc build
-#+END_SRC
-
-Because *~cleopatra~* is a literate program, generation processes are defined in
-Org documents –which may contains additional utilities like scripts or
-templates—, and therefore need to be tangled prior to be effectively
-useful. *~cleopatra~ relies on a particular behavior of ~make~ regarding the
-~include~ directive. If there exists a rule to generate a Makefile used as an
-operand of ~include~, ~make~ will use this rule to update (if necessary) said
-Makefile before actually including it.
-
-Therefore, rules of the following form achieve our ambition of extensibility.
-
-#+BEGIN_SRC makefile :noweb yes
-<<extends(PROC="${PROC}", IN="${IN}", AUX="${AUX}")>>
-#+END_SRC
-
-where
-
-- ~${IN}~ is the Org document which contains the generation process code
-- ~${PROC}~ is the name of the generation process
-- ~${AUX}~ lists the utilities of the generation process tangled from ~${IN}~
- with ~${PROC}.mk~
-
-We use ~&:~ is used in place of ~:~ to separate the target from its dependencies
-in the “tangle rule.” This tells ~make~ that the recipe of this rule generates
-all these files.
-
-Writing these rules manually —has yours truly had to do in the early days of his
-website— has proven to be error-prone.
-
-One desirable feature for *~cleopatra~* would be to generate them automatically,
-by looking for relevant ~:tangle~ directives inside the input Org document. The
-challenge lies in the “relevant” part: the risk exists that we have false
-posivite. However and as a first steps towards a fully automated solution, we
-can leverage the evaluation features of Babel here.
-
-Here is a bash script which, given the proper variables, would generate the
-expected Makefile rule.
-
-#+NAME: extends
-#+BEGIN_SRC bash :var PROC="" :var AUX="" :var IN="" :results output
-cat <<EOF
-include ${PROC}.mk
-
-prebuild : ${PROC}-prebuild
-build : ${PROC}-build
-postbuild : ${PROC}-postbuild
-
-${PROC}-prebuild : ${PROC}.mk ${AUX}
-${PROC}-build : ${PROC}-prebuild
-${PROC}-postbuild : ${PROC}-build
-
-${PROC}.mk ${AUX} &:\\
- \${CLEODIR}/${IN}
- @\$(emacs-tangle)
-
-CONFIGURE += ${PROC}.mk ${AUX}
-
-.PHONY : ${PROC}-prebuild \\
- ${PROC}-build \\
- ${PROC}-postbuild
-EOF
-#+END_SRC
-
-The previous source block is given a name (=extends=), and an explicit lists of
-variables (~IN~, ~PROC~, and ~AUX~). Thanks to the
-[[https://orgmode.org/worg/org-tutorials/org-latex-export.html][noweb syntax of
-Babel]], we can insert the result of the evaluation of =extends= inside another
-source block when the latter is tangled.
-
-We derive the rule to tangle ~bootstrap.mk~ using =extends=, which gives us the
-following Makefile snippet.
-
-#+BEGIN_SRC makefile :noweb yes
-<<extends(IN="Bootstrap.org", PROC="bootstrap", AUX="scripts/update-gitignore.sh")>>
-#+END_SRC
-
-Beware that, as a consequence, modifying code block of =extends= is as
-“dangerous” as modifying ~Makefile~ itself. Keep that in mind if you start
-hacking *~cleopatra~*!
-
-Additional customizations of *~cleopatra~* will be parth ~bootstrap.mk~, rather
-than ~Makefile~.
-
-* Generation Processes
-
-Using the =extends= noweb reference, *~cleopatra~* is easily extensible. In
-this section, we first detail the structure of a typical generation process.
-Then, we construct ~bootstrap.mk~ by enumerating the generation processes that
-are currently used to generate the website you are reading.
-
-Each generation process shall
-
-1. Define ~proc-prebuild~, ~proc-build~, and ~proc-postbuild~
-2. Declare dependencies between stages of generation processes
-3. Declare build outputs (see ~ARTIFACTS~ and ~CONFIGURE~)
-
-* Wrapping-up
-
-#+BEGIN_SRC bash :shebang "#+/bin/bash"
-BEGIN_MARKER="# begin generated files"
-END_MARKER="# begin generated files"
-
-# remove the previous list of generated files to ignore
-sed -i -e "/${BEGIN_MARKER}/,/${END_MARKER}/d" .gitignore
-# remove trailing empty lines
-sed -i -e :a -e '/^\n*$/{$d;N;};/\n$/ba' .gitignore
-
-# output the list of files to ignore
-echo "" >> .gitignore
-echo ${BEGIN_MARKER} >> .gitignore
-for f in $@; do
- echo "${f}" >> .gitignore
-done
-echo ${END_MARKER} >> .gitignore
-#+END_SRC
-
-#+BEGIN_SRC makefile
-ignore :
- @echo " update gitignore"
- @scripts/update-gitignore.sh \
- ${ARTIFACTS} \
- ${CONFIGURE}
-
-clean :
- @rm -rf ${ARTIFACTS}
-
-cleanall : clean
- @rm -rf ${CONFIGURE}
-#+END_SRC
-
-# Local Variables:
-# org-src-preserve-indentation: t
-# End:
diff --git a/site/posts/ClightIntroduction.md b/site/posts/ClightIntroduction.md
new file mode 100644
index 0000000..6598316
--- /dev/null
+++ b/site/posts/ClightIntroduction.md
@@ -0,0 +1,386 @@
+---
+tags: ['coq']
+published: 2020-03-20
+modified: 2020-12-08
+abstract: |
+ Clight is a “simplified” C AST used by CompCert, the certified C compiler.
+ In this write-up, we prove a straighforward functional property of a small
+ C function, as an exercise to discover the Clight semantics.
+---
+
+# A Study of Clight and its Semantics
+
+CompCert is a certified C compiler which comes with a proof of semantics
+preservation. What this means is the following: the semantics of the C code you
+write is preserved by CompCert compilation passes up to the generated machine
+code.
+
+I had been interested in CompCert for quite some times, and ultimately
+challenged myself to study Clight and its semantics. This write-up is the
+result of this challenge, written as I was progressing.
+
+## Installing CompCert
+
+CompCert has been added to `opam`, and as a consequence can be very easily
+used as a library for other Coq developments. A typical use case is for a
+project to produce Clight (the high-level AST of CompCert), and to benefit
+from CompCert proofs after that.
+
+Installing CompCert is as easy as
+
+```bash
+opam install coq-compcert
+```
+
+More precisely, this article uses `coq-compcert.3.8`.
+
+Once `opam` terminates, the `compcert` namespace becomes available. In
+addition, several binaries are now available if you have correctly set your
+`$PATH`{.bash} environment variable. For instance, `clightgen` takes a C file
+as an argument, and generates a Coq file which contains the Clight generated by
+CompCert.
+
+## Problem Statement
+
+Our goal for this first write-up is to prove that the C function
+
+```c
+int add (int x, int y) {
+ return x + y;
+}
+```
+
+returns the expected result, that is `x + y`{.c}. The `clightgen` tool
+generates (among other things) the following AST[^read].
+
+[^read]: It has been modified in order to improve its readability.
+
+```coq
+From compcert Require Import Clight Ctypes Clightdefs AST
+ Coqlib Cop.
+
+Definition _x : ident := 1%positive.
+Definition _y : ident := 2%positive.
+
+Definition f_add : function :=
+ {| fn_return := tint
+ ; fn_callconv := cc_default
+ ; fn_params := [(_x, tint); (_y, tint)]
+ ; fn_vars := []
+ ; fn_temps := []
+ ; fn_body := Sreturn
+ (Some (Ebinop Oadd
+ (Etempvar _x tint)
+ (Etempvar _y tint)
+ tint))
+ |}.
+```
+
+The fields of the `function`{.coq} type are pretty self-explanatory (as it is
+often the case in CompCert’s ASTs as far as I can tell for now).
+
+Identifiers in Clight are (`positive`{.coq}) indices. The `fn_body` field is of
+type `statement`{.coq}, with the particular constructor `Sreturn`{.coq} whose argument
+is of type `option expr`{.coq}, and `statement`{.coq} and `expr`{.coq} look like the two main
+types to study. The predicates `step1`{.coq} and `step2`{.coq} allow for reasoning
+about the execution of a `function`{.coq}, step by step (hence the name). It
+appears that `clightgen` generates Clight terms using the function call
+convention encoded by `step2`{.coq}. To reason about a complete execution, it
+appears that we can use `star`{.coq} (from the `Smallstep`{.coq} module) which is
+basically a trace of `step`{.coq}. These semantics are defined as predicates (that
+is, they live in `Prop`{.coq}). They allow for reasoning about
+state-transformation, where a state is either
+
+- A function call, with a given list of arguments and a continuation
+- A function return, with a result and a continuation
+- A `statement`{.coq} execution within a `function`{.coq}
+
+We import several CompCert modules to manipulate *values* (in our case,
+bounded integers).
+
+```coq
+From compcert Require Import Values Integers.
+Import Int.
+```
+
+Putting everything together, the lemma we want to prove about `f_add`{.coq} is
+the following.
+
+```coq
+Lemma f_add_spec (env : genv)
+ (t : Events.trace)
+ (m m' : Memory.Mem.mem)
+ (v : val) (x y z : int)
+ (trace : Smallstep.star step2 env
+ (Callstate (Ctypes.Internal f_add)
+ [Vint x; Vint y]
+ Kstop
+ m)
+ t
+ (Returnstate (Vint z) Kstop m'))
+ : z = add x y.
+```
+
+## Proof Walkthrough
+
+We introduce a custom `inversion`{.coq} tactic which does some clean-up in
+addition to just perform the inversion.
+
+```coq
+Ltac smart_inv H :=
+ inversion H; subst; cbn in *; clear H.
+```
+
+We can now try to prove our lemma.
+
+```coq
+Proof.
+```
+
+We first destruct `trace`{.coq}, and we rename the generated hypothesis in order
+to improve the readability of these notes.
+
+```coq
+ smart_inv trace.
+ rename H into Hstep.
+ rename H0 into Hstar.
+```
+
+This generates two hypotheses.
+
+```
+Hstep : step1
+ env
+ (Callstate (Ctypes.Internal f_add)
+ [Vint x; Vint y]
+ Kstop
+ m)
+ t1
+ s2
+Hstar : Smallstep.star
+ step2
+ env
+ s2
+ t2
+ (Returnstate (Vint z) Kstop m')
+```
+
+In other words, to “go” from a `Callstate`{.coq} of `f_add`{.coq} to a
+`Returnstate`{.coq}, there is a first step from a `Callstate`{.coq} to a state
+`s2`{.coq}, then a succession of steps to go from `s2`{.coq} to a
+`Returnstate`{.coq}.
+
+We consider the single `step`{.coq}, in order to determine the actual value of
+`s2`{.coq} (among other things). To do that, we use `smart_inv`{.coq} on
+`Hstep`{.coq}, and again perform some renaming.
+
+```coq
+ smart_inv Hstep.
+ rename le into tmp_env.
+ rename e into c_env.
+ rename H5 into f_entry.
+```
+
+This produces two effects. First, a new hypothesis is added to the context.
+
+```
+f_entry : function_entry1
+ env
+ f_add
+ [Vint x; Vint y]
+ m
+ c_env
+ tmp_env
+ m1
+```
+
+Then, the `Hstar`{.coq} hypothesis has been updated, because we now have a more
+precise value of `s2`{.coq}. More precisely, `s2`{.coq} has become
+
+```
+State
+ f_add
+ (Sreturn
+ (Some (Ebinop Oadd
+ (Etempvar _x tint)
+ (Etempvar _y tint)
+ tint)))
+ Kstop
+ c_env
+ tmp_env
+ m1
+```
+
+Using the same approach as before, we can uncover the next step.
+
+```coq
+ smart_inv Hstar.
+ rename H into Hstep.
+ rename H0 into Hstar.
+```
+
+The resulting hypotheses are
+
+```
+Hstep : step2 env
+ (State
+ f_add
+ (Sreturn
+ (Some
+ (Ebinop Oadd
+ (Etempvar _x tint)
+ (Etempvar _y tint)
+ tint)))
+ Kstop c_env tmp_env m1) t1 s2
+Hstar : Smallstep.star
+ step2
+ env
+ s2
+ t0
+ (Returnstate (Vint z) Kstop m')
+```
+
+An inversion of `Hstep`{.coq} can be used to learn more about its resulting
+state… So let’s do just that.
+
+```coq
+ smart_inv Hstep.
+ rename H7 into ev.
+ rename v0 into res.
+ rename H8 into res_equ.
+ rename H9 into mem_equ.
+```
+
+The generated hypotheses have become
+
+```
+res : val
+ev : eval_expr env c_env tmp_env m1
+ (Ebinop Oadd
+ (Etempvar _x tint)
+ (Etempvar _y tint)
+ tint)
+ res
+res_equ : sem_cast res tint tint m1 = Some v'
+mem_equ : Memory.Mem.free_list m1
+ (blocks_of_env env c_env)
+ = Some m'0
+```
+
+Our understanding of these hypotheses is the following
+
+- The expression `_x + _y`{.coq} is evaluated using the `c_env`{.coq}
+ environment (and we know thanks to `binding`{.coq} the value of `_x`{.coq}
+ and `_y`{.coq}), and its result is stored in `res`{.coq}
+- `res`{.coq} is cast into a `tint`{.coq} value, and acts as the result of
+ `f_add`{.coq}
+
+The `Hstar`{.coq} hypothesis is now interesting
+
+```
+Hstar : Smallstep.star
+ step2 env
+ (Returnstate v' Kstop m'0) t0
+ (Returnstate (Vint z) Kstop m')
+```
+
+It is clear that we are at the end of the execution of `f_add`{.coq} (even if
+Coq generates two subgoals, the second one is not relevant and easy to
+discard).
+
+```coq
+ smart_inv Hstar; [| smart_inv H ].
+```
+
+We are making good progress here, and we can focus our attention on the `ev`{.coq}
+hypothesis, which concerns the evaluation of the `_x + _y`{.coq} expression. We can
+simplify it a bit further.
+
+```coq
+ smart_inv ev; [| smart_inv H].
+ rename H4 into fetch_x.
+ rename H5 into fetch_y.
+ rename H6 into add_op.
+```
+
+In a short-term, the hypotheses `fetch_x`{.coq} and `fetch_y`{.coq} are the
+most important.
+
+```
+fetch_x : eval_expr env c_env tmp_env m1 (Etempvar _x tint) v1
+fetch_y : eval_expr env c_env tmp_env m1 (Etempvar _y tint) v2
+```
+
+The current challenge we face is to prove that we know their value. At this
+point, we can have a look at `f_entry`{.coq}. This is starting to look
+familiar: `smart_inv`{.coq}, then renaming, etc.
+
+```coq
+ smart_inv f_entry.
+ clear H.
+ clear H0.
+ clear H1.
+ smart_inv H3; subst.
+ rename H2 into allocs.
+```
+
+We are almost done. Let’s simplify as much as possible `fetch_x`{.coq} and
+`fetch_y`{.coq}. Each time, the `smart_inv`{.coq} tactic generates two suboals,
+but only the first one is relevant. The second one is not, and can be
+discarded.
+
+```coq
+ smart_inv fetch_x; [| inversion H].
+ smart_inv H2.
+ smart_inv fetch_y; [| inversion H].
+ smart_inv H2.
+```
+
+We now know the values of the operands of `add`{.coq}. The two relevant
+hypotheses that we need to consider next are `add_op`{.coq} and
+`res_equ`{.coq}. They are easy to read.
+
+```
+add_op : sem_binarith
+ (fun (_ : signedness) (n1 n2 : Integers.int)
+ => Some (Vint (add n1 n2)))
+ (fun (_ : signedness) (n1 n2 : int64)
+ => Some (Vlong (Int64.add n1 n2)))
+ (fun n1 n2 : Floats.float
+ => Some (Vfloat (Floats.Float.add n1 n2)))
+ (fun n1 n2 : Floats.float32
+ => Some (Vsingle (Floats.Float32.add n1 n2)))
+ v1 tint v2 tint m1 = Some res
+```
+
+- `add_op`{.coq} is the addition of `Vint x`{.coq} and `Vint y`{.coq}, and its
+ result is `res`{.coq}.
+
+ ```
+ res_equ : sem_cast res tint tint m1 = Some (Vint z)
+ ```
+
+- `res_equ`{.coq} is the equation which says that the result of `f_add`{.coq} is `res`{.coq},
+ after it has been cast as a `tint`{.coq} value.
+
+We can simplify `add_op`{.coq} and `res_equ`{.coq}, and this allows us to
+conclude.
+
+```coq
+ smart_inv add_op.
+ smart_inv res_equ.
+ reflexivity.
+Qed.
+```
+
+## Conclusion
+
+The definitions of Clight are straightforward, and the [CompCert
+documentation](http://compcert.inria.fr/doc/index.html) is very pleasant to
+read. Understanding Clight and its semantics can be very interesting if you
+are working on a language that you want to translate into machine code.
+However, proving functional properties of a given C snippet using only CompCert
+can quickly become cumbersome. From this perspective, the
+[VST](https://github.com/PrincetonUniversity/VST) project is very interesting,
+as its main purpose is to provide tools to reason about Clight programs more
+easily.
diff --git a/site/posts/ClightIntroduction.v b/site/posts/ClightIntroduction.v
deleted file mode 100644
index 755d505..0000000
--- a/site/posts/ClightIntroduction.v
+++ /dev/null
@@ -1,357 +0,0 @@
-(** #<nav><p class="series">./coq.html</p>
- <p class="series-prev">./RewritingInCoq.html</p>
- <p class="series-next">./AlgebraicDatatypes.html</p></nav># *)
-
-(** * A Study of Clight and its Semantics *)
-(* begin hide *)
-From Coq Require Import List.
-Import ListNotations.
-(* end hide *)
-(** CompCert is a certified C compiler which comes with a proof of semantics
- preservation. What this means is the following: the semantics of the C code
- you write is preserved by CompCert compilation passes up to the generated
- machine code.
-
- I had been interested in CompCert for quite some times, and ultimately
- challenged myself to study Clight and its semantics. This write-up is the
- result of this challenge, written as I was progressing.
-
- #<nav id="generate-toc"></nav>#
-
- #<div id="history">site/posts/ClightIntroduction.v</div># *)
-
-(** ** Installing CompCert *)
-
-(** CompCert has been added to <<opam>>, and as a consequence can be very easily
- used as a library for other Coq developments. A typical use case is for a
- project to produce Clight (the high-level AST of CompCert), and to benefit
- from CompCert proofs after that.
-
- Installing CompCert is as easy as
-
-<<
-opam install coq-compcert
->>
-
- More precisely, this article uses #<code>coq-compcert.3.8</code>#.
-
- Once <<opam>> terminates, the <<compcert>> namespace becomes available. In
- addition, several binaries are now available if you have correctly set your
- <<PATH>> environment variable. For instance, <<clightgen>> takes a C file as
- an argument, and generates a Coq file which contains the Clight generated by
- CompCert. *)
-
-(** ** Problem Statement *)
-
-(** Our goal for this first write-up is to prove that the C function
-
-<<
-int add (int x, int y) {
- return x + y;
-}
->>
-
- returns the expected result, that is <<x + y>>. The <<clightgen>> tool
- generates (among other things) the following AST (note: I have modified it
- in order to improve its readability). *)
-
-From compcert Require Import Clight Ctypes Clightdefs AST
- Coqlib Cop.
-
-Definition _x : ident := 1%positive.
-Definition _y : ident := 2%positive.
-
-Definition f_add : function :=
- {| fn_return := tint
- ; fn_callconv := cc_default
- ; fn_params := [(_x, tint); (_y, tint)]
- ; fn_vars := []
- ; fn_temps := []
- ; fn_body := Sreturn
- (Some (Ebinop Oadd
- (Etempvar _x tint)
- (Etempvar _y tint)
- tint))
- |}.
-
-(** The fields of the [function] type are pretty self-explanatory (as it is
- often the case in CompCert’s ASTs as far as I can tell for now).
-
- Identifiers in Clight are ([positive]) indices. The [fn_body] field is of
- type [statement], with the particular constructor [Sreturn] whose argument
- is of type [option expr], and [statement] and [expr] look like the two main
- types to study. The predicates [step1] and [step2] allow for reasoning
- about the execution of a [function], step by step (hence the name). It
- appears that <<clightgen>> generates Clight terms using the function call
- convention encoded by [step2]. To reason about a complete execution, it
- appears that we can use [star] (from the [Smallstep] module) which is
- basically a trace of [step]. These semantics are defined as predicates (that
- is, they live in [Prop]). They allow for reasoning about
- state-transformation, where a state is either
-
- - A function call, with a given list of arguments and a continuation
- - A function return, with a result and a continuation
- - A [statement] execution within a [function]
-
- We import several CompCert modules to manipulate _values_ (in our case,
- bounded integers). *)
-
-From compcert Require Import Values Integers.
-Import Int.
-
-(** Putting everything together, the lemma we want to prove about [f_add] is
- the following. *)
-
-Lemma f_add_spec (env : genv)
- (t : Events.trace)
- (m m' : Memory.Mem.mem)
- (v : val) (x y z : int)
- (trace : Smallstep.star step2 env
- (Callstate (Ctypes.Internal f_add)
- [Vint x; Vint y]
- Kstop
- m)
- t
- (Returnstate (Vint z) Kstop m'))
- : z = add x y.
-
-(** ** Proof Walkthrough *)
-
-(** We introduce a custom [inversion] tactic which does some clean-up in
- addition to just perform the inversion. *)
-
-Ltac smart_inv H :=
- inversion H; subst; cbn in *; clear H.
-
-(** We can now try to prove our lemma. *)
-
-Proof.
-
-(** We first destruct [trace], and we rename the generated hypothesis in order
- to improve the readability of these notes. *)
-
- smart_inv trace.
- rename H into Hstep.
- rename H0 into Hstar.
-
-(** This generates two hypotheses.
-
-<<
-Hstep : step1
- env
- (Callstate (Ctypes.Internal f_add)
- [Vint x; Vint y]
- Kstop
- m)
- t1
- s2
-Hstar : Smallstep.star
- step2
- env
- s2
- t2
- (Returnstate (Vint z) Kstop m')
->>
-
- In other words, to “go” from a [Callstate] of [f_add] to a [Returnstate],
- there is a first step from a [Callstate] to a state [s2], then a succession
- of steps to go from [s2] to a [Returnstate].
-
- We consider the single [step], in order to determine the actual value of
- [s2] (among other things). To do that, we use [smart_inv] on [Hstep], and
- again perform some renaming. *)
-
- smart_inv Hstep.
- rename le into tmp_env.
- rename e into c_env.
- rename H5 into f_entry.
-
-(** This produces two effects. First, a new hypothesis is added to the context.
-
-<<
-f_entry : function_entry1
- env
- f_add
- [Vint x; Vint y]
- m
- c_env
- tmp_env
- m1
->>
-
- Then, the [Hstar] hypothesis has been updated, because we now have a more
- precise value of [s2]. More precisely, [s2] has become
-
-<<
-State
- f_add
- (Sreturn
- (Some (Ebinop Oadd
- (Etempvar _x tint)
- (Etempvar _y tint)
- tint)))
- Kstop
- c_env
- tmp_env
- m1
->>
-
- Using the same approach as before, we can uncover the next step. *)
-
- smart_inv Hstar.
- rename H into Hstep.
- rename H0 into Hstar.
-
-(** The resulting hypotheses are
-
-<<
-Hstep : step2 env
- (State
- f_add
- (Sreturn
- (Some
- (Ebinop Oadd
- (Etempvar _x tint)
- (Etempvar _y tint)
- tint)))
- Kstop c_env tmp_env m1) t1 s2
-Hstar : Smallstep.star
- step2
- env
- s2
- t0
- (Returnstate (Vint z) Kstop m')
->>
-
- An inversion of [Hstep] can be used to learn more about its resulting
- state… So let’s do just that. *)
-
- smart_inv Hstep.
- rename H7 into ev.
- rename v0 into res.
- rename H8 into res_equ.
- rename H9 into mem_equ.
-
-(** The generated hypotheses have become
-
-<<
-res : val
-ev : eval_expr env c_env tmp_env m1
- (Ebinop Oadd
- (Etempvar _x tint)
- (Etempvar _y tint)
- tint)
- res
-res_equ : sem_cast res tint tint m1 = Some v'
-mem_equ : Memory.Mem.free_list m1
- (blocks_of_env env c_env)
- = Some m'0
->>
-
- Our understanding of these hypotheses is the following
-
- - The expression [_x + _y] is evaluated using the [c_env] environment (and
- we know thanks to [binding] the value of [_x] and [_y]), and its result
- is stored in [res]
- - [res] is cast into a [tint] value, and acts as the result of [f_add]
-
- The [Hstar] hypothesis is now interesting
-
-<<
-Hstar : Smallstep.star
- step2 env
- (Returnstate v' Kstop m'0) t0
- (Returnstate (Vint z) Kstop m')
->>
-
- It is clear that we are at the end of the execution of [f_add] (even if Coq
- generates two subgoals, the second one is not relevant and easy to
- discard). *)
-
- smart_inv Hstar; [| smart_inv H ].
-
-(** We are making good progress here, and we can focus our attention on the [ev]
- hypothesis, which concerns the evaluation of the [_x + _y] expression. We
- can simplify it a bit further. *)
-
- smart_inv ev; [| smart_inv H].
- rename H4 into fetch_x.
- rename H5 into fetch_y.
- rename H6 into add_op.
-
-(** In a short-term, the hypotheses [fetch_x] and [fetch_y] are the most
- important.
-
-<<
-fetch_x : eval_expr env c_env tmp_env m1 (Etempvar _x tint) v1
-fetch_y : eval_expr env c_env tmp_env m1 (Etempvar _y tint) v2
->>
-
- The current challenge we face is to prove that we know their value. At this
- point, we can have a look at [f_entry]. This is starting to look familiar:
- [smart_inv], then renaming, etc. *)
-
- smart_inv f_entry.
- clear H.
- clear H0.
- clear H1.
- smart_inv H3; subst.
- rename H2 into allocs.
-
-(** We are almost done. Let’s simplify as much as possible [fetch_x] and
- [fetch_y]. Each time, the [smart_inv] tactic generates two suboals, but only
- the first one is relevant. The second one is not, and can be discarded. *)
-
- smart_inv fetch_x; [| inversion H].
- smart_inv H2.
- smart_inv fetch_y; [| inversion H].
- smart_inv H2.
-
-(** We now know the values of the operands of [add]. The two relevant hypotheses
- that we need to consider next are [add_op] and [res_equ]. They are easy to
- read.
-
-<<
-add_op : sem_binarith
- (fun (_ : signedness) (n1 n2 : Integers.int)
- => Some (Vint (add n1 n2)))
- (fun (_ : signedness) (n1 n2 : int64)
- => Some (Vlong (Int64.add n1 n2)))
- (fun n1 n2 : Floats.float
- => Some (Vfloat (Floats.Float.add n1 n2)))
- (fun n1 n2 : Floats.float32
- => Some (Vsingle (Floats.Float32.add n1 n2)))
- v1 tint v2 tint m1 = Some res
->>
-
- - [add_op] is the addition of [Vint x] and [Vint y], and its result is
- [res].
-
-<<
-res_equ : sem_cast res tint tint m1 = Some (Vint z)
->>
-
- - [res_equ] is the equation which says that the result of [f_add] is
- [res], after it has been cast as a [tint] value.
-
- We can simplify [add_op] and [res_equ], and this allows us to
- conclude. *)
-
- smart_inv add_op.
- smart_inv res_equ.
- reflexivity.
-Qed.
-
-(** ** Conclusion *)
-
-(** The definitions of Clight are easy to understand, and the #<a
- href="http://compcert.inria.fr/doc/index.html">#CompCert
- documentation#</a># is very pleasant to read. Understanding
- Clight and its semantics can be very interesting if you are
- working on a language that you want to translate into machine
- code. However, proving functional properties of a given C snippet
- using only CompCert can quickly become cumbersome. From this
- perspective, the #<a
- href="https://github.com/PrincetonUniversity/VST">#VST#</a>#
- project is very interesting, as its main purpose is to provide
- tools to reason about Clight programs more easily. *)
diff --git a/site/posts/ColorlessThemes-0.2.md b/site/posts/ColorlessThemes-0.2.md
new file mode 100644
index 0000000..625b604
--- /dev/null
+++ b/site/posts/ColorlessThemes-0.2.md
@@ -0,0 +1,29 @@
+---
+published: 2020-02-15
+tags: ['releases']
+abstract: |
+ Introducing a new macro, more friendly with themes gallery like Peach
+ Melpa.
+---
+
+# Release of `colorless-themes-0.2`
+
+[I have tagged and released a new version of
+`colorless-themes`](https://git.sr.ht/~lthms/colorless-themes.el/refs/0.2).
+The motivation behind modifying the version number is an important breaking
+change regarding how the `colorless-themes-make`{.lisp} macro shall be used.
+
+Before `0.2`, the macro was calling `deftheme`{.lisp} and
+`provide-theme`{.lisp} itself. In practices, it meant the actual themes were
+not using these two functions themselves. It was working great in isolation,
+but it turns out many tools such as `use-package`{.lisp} or [Peach
+Melpa](https://peach-melpa.org) (an auto-generated Emacs themes gallery) are
+relying on the presence of these functions to decide whether a file provides a
+theme or not. As of now, `nordless-theme` and `lavenderless-theme` have been
+updated accordingly, and [they appear on Peach
+Melpa](https://peach-melpa.org/themes/lavenderless-theme/variants/lavenderless).
+Their loading can also be defered using the `:defer`{.lisp} keyword of the
+`use-package`{.lisp} macro.
+
+if you happen to be a user of `colorless-themes`, feel free to shoot an email!
+I would love to hear about your experience using a mostly colorless theme.
diff --git a/site/posts/Coqffi-1-0-0.md b/site/posts/Coqffi-1-0-0.md
new file mode 100644
index 0000000..e8fce8c
--- /dev/null
+++ b/site/posts/Coqffi-1-0-0.md
@@ -0,0 +1,522 @@
+---
+published: 2020-12-10
+modified: 2023-04-29
+tags: ['coq', 'ocaml', 'coqffi']
+abstract: |
+ For each entry of a cmi file, coqffi tries to generate an equivalent (from
+ the extraction mechanism perspective) Coq definition. In this article, we
+ walk through how coqffi works.
+---
+
+# `coqffi.1.0.0` In A Nutshell
+
+For each entry of a `cmi` file (a *compiled* `mli` file), `coqffi`
+tries to generate an equivalent (from the extraction mechanism
+perspective) Coq definition. In this article, we walk through how
+`coqffi` works.
+
+Note that we do not dive into the vernacular commands `coqffi`
+generates. They are of no concern for users of `coqffi`.
+
+## Getting Started
+
+### Requirements
+
+The latest version of `coqffi` (`1.0.0~beta8`)
+is compatible with OCaml `4.08` up to `4.14`, and Coq `8.12` up top
+`8.13`. If you want to use `coqffi`, but have incompatible
+requirements of your own, feel free to
+[submit an issue](https://github.com/coq-community/coqffi/issues).
+
+### Installing `coqffi`
+
+The recommended way to install `coqffi` is through the
+[Opam Coq Archive](https://coq.inria.fr/opam/www), in the `released`
+repository. If you haven’t activated this repository yet, you can use the
+following bash command.
+
+```bash
+opam repo add coq-released https://coq.inria.fr/opam/released
+```
+
+Then, installing `coqffi` is as simple as
+
+```bash
+opam install coq-coqffi
+```
+
+You can also get the source from [the upstream `git`
+repository](https://github.com/coq-community/coqffi). The `README` provides the
+necessary pieces of information to build it from source.
+
+### Additional Dependencies
+
+One major difference between Coq and OCaml is that the former is pure,
+while the latter is not. Impurity can be modeled in pure languages,
+and Coq does not lack of frameworks in this respect. `coqffi` currently
+supports two of them:
+[`coq-simple-io`](https://github.com/Lysxia/coq-simple-io) and
+[FreeSpec](https://github.com/ANSSI-FR/FreeSpec). It is also possible to use it
+with [Interaction Trees](https://github.com/DeepSpec/InteractionTrees), albeit
+in a less direct manner.
+
+### Primitive Types
+
+`coqffi` supports a set of primitive types, *i.e.*, a set of OCaml
+types for which it knows an equivalent type in Coq. The list is the
+following (the Coq types are fully qualified in the table, but not in
+the generated Coq module as the necessary `Import` statement are
+generated too).
+
+| OCaml type | Coq type |
+| ----------------- | ----------------------------- |
+| `bool`{.ocaml} | `Coq.Init.Datatypes.bool` |
+| `char`{.ocaml} | `Coq.Strings.Ascii.ascii` |
+| `int`{.ocaml} | `CoqFFI.Data.Int.i63` |
+| `'a list`{.ocaml} | `Coq.Init.Datatypes.list a` |
+| `'a Seq.t`{.ocaml} | `CoqFFI.Data.Seq.t` |
+| `'a option`{.ocaml} | `Coq.Init.Datatypes.option a` |
+| `('a, 'e) result`{.ocaml} | `Coq.Init.Datatypes.sum` |
+| `string`{.ocaml} | `Coq.Strings.String.string` |
+| `unit`{.ocaml} | `Coq.Init.Datatypes.unit` |
+| `exn`{.ocaml} | `CoqFFI.Exn` |
+
+The `i63`{.coq} type is introduced by the `CoqFFI`{.coq} theory to provide
+signed primitive integers to Coq users. They are implemented on top of the
+(unsigned) Coq native integers introduced in Coq `8.13`. The `i63` type will be
+deprecated once the support for [signed primitive
+integers](https://github.com/coq/coq/pull/13559) is implemented[^compat].
+
+[^compat]: This is actually one of the sources of incompatibilities of `coqffi`
+ with most recent versions of Coq.
+
+When processing the entries of a given interface model, `coqffi` will
+check that they only use these types, or types introduced by the
+interface module itself.
+
+Sometimes, you may encounter a situation where you have two interface
+modules `b.mli` and `b.mli`, such that `b.mli` uses a type introduced
+in `a.mli`. To deal with this scenario, you can use the `--witness`
+flag to generate `A.v`. This will tell `coqffi` to also generate
+`A.ffi`; this file can then be used when generating `B.v` thanks to
+the `-I` option. Furthermore, for `B.v` to compile the `--require`
+option needs to be used to ensure the `A` Coq library (`A.v`) is
+required.
+
+To give a more concrete example, given ~a.mli~
+
+```ocaml
+type t
+```
+
+and `b.mli`
+
+```ocaml
+type a = A.t
+```
+
+To generate `A.v`, we can use the following commands:
+
+```bash
+ocamlc a.mli
+coqffi --witness -o A.v a.cmi
+```
+
+Which would generate the following axiom for `t`.
+
+```coq
+Axiom t : Type.
+```
+
+Then, generating `B.v` can be achieved as follows:
+
+```bash
+ocamlc b.mli
+coqffi -I A.ffi -ftransparent-types -r A -o B.v b.cmi
+```
+
+which results in the following output for `v`:
+
+```coq
+Require A.
+
+Definition u : Type := A.t.
+```
+
+## Code Generation
+
+`coqffi` distinguishes five types of entries: types, pure values,
+impure primitives, asynchronous primitives, exceptions, and
+modules. We now discuss how each one of them is handled.
+
+### Types
+
+By default, `coqffi` generates axiomatized definitions for each type defined in
+a `.cmi` file. This means that `type t`{.ocaml} becomes `Axiom t : Type`{.coq}.
+Polymorphism is supported, *i.e.*, `type 'a t`{.ocaml} becomes `Axiom t :
+forall (a : Type), Type`{.coq}.
+
+It is possible to provide a “model” for a type using the `coq_model`
+annotation, for instance for reasoning purposes. For instance, we can specify
+that a type is equivalent to a `list`.
+
+```ocaml
+type 'a t [@@coq_model "list"]
+```
+
+This generates the following Coq definition.
+
+```coq
+Definition t : forall (a : Type), Type := list.
+```
+
+It is important to be careful when using the =coq_model= annotation. More
+precisely, the fact that `t` is a `list` in the “Coq universe” shall not be
+used while the implementation phase, only the verification phase.
+
+Unamed polymorphic type parameters are also supported. In presence of
+such parameters, `coqffi` will find it a name that is not already
+used. For instance,
+
+```ocaml
+type (_, 'a) ast
+```
+
+becomes
+
+```coq
+Axiom ast : forall (b : Type) (a : Type), Type.
+```
+
+Finally, `coqffi` has got an experimental feature called `transparent-types`
+(enabled by using the `-ftransparent-types` command-line argument). If the type
+definition is given in the module interface, then `coqffi` tries to generates
+an equivalent definition in Coq. For instance,
+
+```ocaml
+type 'a llist =
+ | LCons of 'a * (unit -> 'a llist)
+ | LNil
+```
+
+becomes
+
+```coq
+Inductive llist (a : Type) : Type :=
+| LCons (x0 : a) (x1 : unit -> llist a) : llist a
+| LNil : llist a.
+```
+
+Mutually recursive types are supported, so
+
+```ocaml
+type even = Zero | ESucc of odd
+and odd = OSucc of even
+```
+
+becomes
+
+```coq
+Inductive odd : Type :=
+| OSucc (x0 : even) : odd
+with even : Type :=
+| Zero : even
+| ESucc (x0 : odd) : even.
+```
+
+Besides, `coqffi` supports alias types, as suggested in this write-up
+when we discuss witness files.
+
+The `transparent-types` feature is **experimental**, and is currently
+limited to variant types. It notably does not support records. Besides, it may
+generate incorrect Coq types, because it does not check whether or not the
+[positivity
+condition](https://coq.inria.fr/refman/language/core/inductive.html#positivity-condition)
+is satisfied.
+
+### Pure values
+
+`coqffi` decides whether or not a given OCaml values is pure or impure
+with the following heuristics:
+
+- Constants are pure
+- Functions are impure by default
+- Functions with a `coq_model` annotation are pure
+- Functions marked with the `pure` annotation are pure
+- If the `pure-module` feature is enabled (`-fpure-module`), then synchronous
+ functions (which do not live inside the
+ [~Lwt~](https://ocsigen.org/lwt/5.3.0/manual/manual) monad) are pure
+
+Similarly to types, `coqffi` generates axioms (or definitions, if the
+`coq_model` annotation is used) for pure values. Then,
+
+```ocaml
+val unpack : string -> (char * string) option [@@pure]
+```
+
+becomes
+
+```ocaml
+Axiom unpack : string -> option (ascii * string).
+```
+
+Polymorphic values are supported.
+
+```ocaml
+val map : ('a -> 'b) -> 'a list -> 'b list [@@pure]
+```
+
+becomes
+
+```coq
+Axiom map : forall (a : Type) (b : Type), (a -> b) -> list a -> list b.
+```
+
+Again, unamed polymorphic type are supported, so
+
+```ocaml
+val ast_to_string : _ ast -> string [@@pure]
+```
+
+becomes
+
+```coq
+Axiom ast_to_string : forall (a : Type), string.
+```
+
+### Impure Primitives
+
+`coqffi` reserves a special treatment for /impure/ OCaml functions.
+Impurity is usually handled in pure programming languages by means of
+monads, and `coqffi` is no exception to the rule.
+
+Given the set of impure primitives declared in an interface module,
+`coqffi` will (1) generate a typeclass which gathers these primitives,
+and (2) generate instances of this typeclass for supported backends.
+
+We illustrate the rest of this section with the following impure
+primitives.
+
+```ocaml
+val echo : string -> unit
+val scan : unit -> string
+```
+
+where `echo` allows writing something the standard output, and `scan`
+to read the standard input.
+
+Assuming the processed module interface is named `console.mli`, the
+following Coq typeclass is generated.
+
+```coq
+Class MonadConsole (m : Type -> Type) := { echo : string -> m unit
+ ; scan : unit -> m string
+ }.
+```
+
+Using this typeclass and with the additional support of an additional
+`Monad` typeclass, we can specify impure computations which interacts
+with the console. For instance, with the support of `ExtLib`, one can
+write.
+
+```coq
+Definition pipe `{Monad m, MonadConsole m} : m unit :=
+ let* msg := scan () in
+ echo msg.
+```
+
+There is no canonical way to model impurity in Coq, but over the years
+several frameworks have been released to tackle this challenge.
+
+`coqffi` provides three features related to impure primitives.
+
+#### `simple-io`
+
+When this feature is enabled, `coqffi` generates an instance of the
+typeclass for the =IO= monad introduced in the `coq-simple-io` package
+
+```coq
+Axiom io_echo : string -> IO unit.
+Axiom io_scan : unit -> IO string.
+
+Instance IO_MonadConsole : MonadConsole IO := { echo := io_echo
+ ; scan := io_scan
+ }.
+```
+
+It is enabled by default, but can be disabled using the
+`-fno-simple-io` command-line argument.
+
+#### `interface`
+
+When this feature is enabled, `coqffi` generates an inductive type which
+describes the set of primitives available, to be used with frameworks like
+[FreeSpec](https://github.com/lthms/FreeSpec) or [Interactions
+Trees](https://github.com/DeepSpec/InteractionTrees).
+
+```coq
+Inductive CONSOLE : Type -> Type :=
+| Echo : string -> CONSOLE unit
+| Scan : unit -> CONSOLE string.
+
+Definition inj_echo `{Inject CONSOLE m} (x0 : string) : m unit :=
+ inject (Echo x0).
+
+Definition inj_scan `{Inject CONSOLE m} (x0 : unit) : m string :=
+ inject (Scan x0).
+
+Instance Inject_MonadConsole `{Inject CONSOLE m} : MonadConsole m :=
+ { echo := inj_echo
+ ; scan := inj_scan
+ }.
+```
+
+Providing an instance of the form `forall i, Inject i M`{.coq} is enough for
+your monad `M` to be compatible with this feature[^example].
+
+[^example]: See for instance [how FreeSpec implements
+ it](https://github.com/lthms/FreeSpec/blob/master/theories/FFI/FFI.v)).
+
+#### `freespec`
+
+When this feature in enabled, `coqffi` generates a semantics for the
+inductive type generated by the `interface` feature.
+
+```coq
+Axiom unsafe_echo : string -> unit.
+Axiom unsafe_scan : uint -> string.
+
+Definition console_unsafe_semantics : semantics CONSOLE :=
+ bootstrap (fun a e =>
+ local match e in CONSOLE a return a with
+ | Echo x0 => unsafe_echo x0
+ | Scan x0 => unsafe_scan x0
+ end).
+```
+
+### Asynchronous Primitives
+
+`coqffi` also reserves a special treatment for *asynchronous*
+primitives —*i.e.*, functions which live inside the `Lwt` monad— when
+the `lwt` feature is enabled.
+
+The treatment is very analoguous to the one for impure primitives: (1)
+a typeclass is generated (with the `_Async` suffix), and (2) an
+instance for the `Lwt` monad is generated. Besides, an instance for
+the “synchronous” primitives is also generated for `Lwt`. If the
+`interface` feature is enabled, an interface datatype is generated,
+which means you can potentially use Coq to reason about your
+asynchronous programs (using FreeSpec and alike, although the
+interleaving of asynchronous programs in not yet supported in
+FreeSpec).
+
+By default, the type of the `Lwt` monad is `Lwt.t`. You can override
+this setting using the `--lwt-alias` option. This can be useful when
+you are using an alias type in place of `Lwt.t`.
+
+### Exceptions
+
+OCaml features an exception mechanism. Developers can define their
+own exceptions using the `exception` keyword, whose syntax is similar
+to constructors definition. For instance,
+
+```ocaml
+exception Foo of int * bool
+```
+
+introduces a new exception `Foo` which takes two parameters of type `int`{.ocaml} and
+`bool`{.ocaml}. `Foo (x, y)` constructs of value of type `exn`{.ocaml}.
+
+For each new exceptions introduced in an OCaml module, `coqffi`
+generates (1) a so-called “proxy type,” and (2) conversion functions
+to and from this type.
+
+Coming back to our example, the “proxy type” generates by `coqffi` is
+
+```coq
+Inductive FooExn : Type :=
+| MakeFooExn (x0 : i63) (x1 : bool) : FooExn.
+```
+
+Then, `coqffi` generates conversion functions.
+
+```coq
+Axiom exn_of_foo : FooExn -> exn.
+Axiom foo_of_exn : exn -> option FooExn.
+```
+
+Besides, `coqffi` also generates an instance for the `Exn` typeclass
+provided by the `CoqFFI` theory:
+
+```coq
+Instance FooExn_Exn : Exn FooExn :=
+ { to_exn := exn_of_foo
+ ; of_exn := foo_of_exn
+ }.
+```
+
+Under the hood, `exn`{.ocaml} is an
+[extensible
+datatype](https://caml.inria.fr/pub/docs/manual-ocaml/extensiblevariants.html),
+and how `coqffi` supports it will probably be generalized in future releases.
+
+Finally, `coqffi` has a minimal support for functions which may raise
+exceptions. Since OCaml type system does not allow to identify such
+functions, they need to be annotated explicitely, using the
+=may_raise= annotation. In such a case, `coqffi` will change the
+return type of the function to use the =sum= Coq inductive type.
+
+For instance,
+
+```ocaml
+val from_option : 'a option -> 'a [@@may_raise] [@@pure]
+```
+
+becomes
+
+```coq
+Axiom from_option : forall (a : Type), option a -> sum a exn.
+```
+
+### Modules
+
+Lastly, `coqffi` supports OCaml modules described within `mli` files,
+when they are specify as `module T : sig ... end`{.ocaml}. For instance,
+
+```ocaml
+module T : sig
+ type t
+
+ val to_string : t -> string [@@pure]
+end
+```
+
+becomes
+
+```coq
+Module T.
+ Axiom t : Type.
+
+ Axiom to_string : t -> string.
+End T.
+```
+
+As of now, the following construction is unfortunately *not*
+supported, and will be ignored by `coqffi`:
+
+```ocaml
+module S = sig
+ type t
+
+ val to_string : t -> string [@@pure]
+end
+
+module T : S
+```
+
+## Moving Forward
+
+`coqffi` comes with a comprehensive man page. In addition, the
+interested reader can proceed to the next article of this series,
+which explains how [`coqffi` can be used to easily implement an echo
+server in Coq](/posts/CoqffiEcho.html).
diff --git a/site/posts/Coqffi.org b/site/posts/Coqffi.org
deleted file mode 100644
index 6116612..0000000
--- a/site/posts/Coqffi.org
+++ /dev/null
@@ -1,18 +0,0 @@
-#+TITLE: A series on ~coqffi~
-
-#+SERIES: ./coq.html
-#+SERIES_PREV: AlgebraicDatatypes.html
-
-~coqffi~ generates Coq FFI modules from compiled OCaml interface
-modules (~.cmi~). In practice, it greatly reduces the hassle to mix
-OCaml and Coq modules within the same codebase, especially when used
-together with the ~dune~ build system.
-
-- [[./CoqffiIntro.org][~coqffi~ in a Nutshell]] ::
- For each entry of a ~cmi~ file, ~coqffi~ tries to generate an
- equivalent (from the extraction mechanism perspective) Coq
- definition. In this article, we walk through how ~coqffi~ works.
-- [[./CoqffiEcho.org][Implementing an Echo Server in Coq with ~coqffi~]] ::
- In this tutorial, we will demonstrate how ~coqffi~ can be used to
- implement an echo server, /i.e./, a TCP server which sends back
- any input it receives from its clients.
diff --git a/site/posts/CoqffiEcho.md b/site/posts/CoqffiEcho.md
new file mode 100644
index 0000000..3dc87c9
--- /dev/null
+++ b/site/posts/CoqffiEcho.md
@@ -0,0 +1,519 @@
+---
+published: 2020-12-10
+modified: 2021-08-20
+tags: ['coq', 'ocaml', 'coqffi']
+abstract: |
+ In this article, we will demonstrate how `coqffi` can be used to implement
+ an echo server, *i.e.*, a TCP server which sends back any input it receives
+ from its clients.
+---
+
+# Implementing an Echo Server in Coq with `coqffi.1.0.0`
+
+In this article, we will demonstrate how `coqffi` can be used to
+implement an echo server, *i.e.*, a TCP server which sends back any
+input it receives from its clients. In addition to `coqffi`, you will need to
+install `coq-simple-io`. The latter is available in the [`released` repository
+of the Opam Coq Archive](https://github.com/coq/opam-coq-archive).
+
+```bash
+opam install coq-coqffi coq-simple-io
+```
+
+Besides, you can download [the source tree presented in this
+article](/files/coqffi-tutorial.tar.gz) if you want to try to read the source
+directly, or modify it to your taste.
+
+## Project Layout
+
+Before diving too much into the implementation of our echo server, we
+first give an overview of the resulting project’s layout. Since we aim
+at implementing a program, we draw our inspiration from the idiomatic
+way of organizing a OCaml project.
+
+We have three directories at the root of the project.
+
+- **`ffi/` contains the low-level OCaml code:**
+ It provides an OCaml library (`ffi`), and a Coq theory (`FFI`{.coq}) which
+ gathers the FFI modules generated by `coqffi`.
+- **`src/` contains the Coq implementation of our echo server:** It provides a
+ Coq theory (`Echo`{.coq}) which depends on the `FFI`{.coq} theory the
+ `SimpleIO`{.coq} theory of `coq-simple~io`. This theory provides the
+ implementation of our echo server in Coq.
+- **`bin/` contains the pieces of code to get an executable program:** It
+ contains a Coq module (`echo.v`) which configures and uses the extraction
+ mechanism to generate an OCaml module (`echo.ml`). This OCaml module can be
+ compiled to get an executable program.
+
+Note that we could have decided to only have one Coq theory. We could
+also have added a fourth directory (`theories/`) for formal
+verification specific code, but this is out of the scope of this
+tutorial.
+
+Overall, we use `dune` to compile and compose the different parts of
+the echo server. `dune` has a native —yet unstable at the time of
+writing— support for building Coq projects, with very convenient
+stanzas like `coq.theory` and `coq.extraction`.
+
+The following graph summarizes the dependencies between each component
+(plain arrows symbolize software dependencies).
+
+#[The echo server dependency graph. Dashed boxes are generated.](/img/echo-deps.svg)
+
+We enable Coq-related stanza with `(using coq 0.2)`{.lisp} in the
+`dune-project`{.dune}. file.
+
+```lisp
+(lang dune 2.7)
+(using coq 0.2)
+```
+
+The rest of this tutorial proceeds by diving into each directory.
+
+## FFI Bindings
+
+Our objective is to implement an echo server, *i.e.*, a server which
+(1) accepts incoming connections, and (2) sends back any incoming
+messages. We will consider two classes of effects. One is related to
+creating and manipulating TCP sockets. The other is dedicated to
+process management, more precisely to be able to fork when receiving
+incoming connections.
+
+Therefore, the `ffi` library will provide two modules. Likewise, the
+`FFI`{.coq} theory will provide two analogous modules generated by `coqffi`.
+
+In the `ffi/` directory, we add the following stanza to the `dune` file.
+
+```lisp
+(library
+ (name ffi)
+ (libraries unix))
+```
+
+`dune` will look for any `.ml` and `.mli` files within the directory and will
+consider they belong to the `ffi` library. We use the
+[`unix`](https://caml.inria.fr/pub/docs/manual-ocaml/libref/Unix.html) library
+to implement the features we are looking for.
+
+Then, we add the following stanza to the `dune` file of the `ffi/`
+directory.
+
+```lisp
+(coq.theory
+ (name FFI))
+```
+
+This tells `dune` to look for `.v` file within the `ffi/` directory,
+in order to build them with Coq. A nice feature of `dune` is that if we
+automatically generate Coq files, they will be automatically “attached” to this
+theory.
+
+### Sockets
+
+Sockets are boring. The following OCaml module interface provides the
+necessary type and functions to manipulate them.
+
+```ocaml
+type socket_descr
+
+val open_socket : string -> int -> socket_descr
+val listen : socket_descr -> unit
+val recv : socket_descr -> string
+val send : socket_descr -> string -> int
+val accept_connection : socket_descr -> socket_descr
+val close_socket : socket_descr -> unit
+```
+
+Our focus is how to write the interface modules for `coqffi`. Since the object
+of this tutorial is not the implementation of an echo server in itself, the
+implementation details of the `ffi` library will not be discussed, but is
+provided at the end of this article.
+
+`dune` generates `.cmi` files for the `.mli` files of our library, and
+provides the necessary bits to easily locate them. Besides, the
+`action` stanza can be used here to tell to `dune` how to generate the
+module `Socket.v` from `file.cmi`. We add the following entry to
+`ffi/dune`.
+
+```lisp
+(rule
+ (target Socket.v)
+ (action (run coqffi %{cmi:socket} -o %{target})))
+```
+
+We call `coqffi` without any feature-related command-line argument,
+which means only the `simple-io` feature is enabled. As a consequence,
+the `socket_descr` type is axiomatized in Coq, and in addition to a
+`MonadSocket` monad, `coqffi` will generate an instance for this monad
+for the `IO` monad of `coq-simple-io`.
+
+The stanza generates the following Coq module.
+
+```coq
+(* This file has been generated by coqffi. *)
+
+Set Implicit Arguments.
+Unset Strict Implicit.
+Set Contextual Implicit.
+Generalizable All Variables.
+Close Scope nat_scope.
+
+From CoqFFI Require Export Extraction.
+From SimpleIO Require Import IO_Monad.
+
+Axiom socket_descr : Type.
+
+Extract Constant socket_descr => "Ffi.Socket.socket_descr".
+
+(** * Impure Primitives *)
+
+(** ** Monad Definition *)
+
+Class MonadSocket (m : Type -> Type) : Type :=
+ { open_socket : string -> i63 -> m socket_descr
+ ; listen : socket_descr -> m unit
+ ; recv : socket_descr -> m string
+ ; send : socket_descr -> string -> m i63
+ ; accept_connection : socket_descr -> m socket_descr
+ ; close_socket : socket_descr -> m unit
+ }.
+
+(** ** [IO] Instance *)
+
+Axiom io_open_socket : string -> i63 -> IO socket_descr.
+Axiom io_listen : socket_descr -> IO unit.
+Axiom io_recv : socket_descr -> IO string.
+Axiom io_send : socket_descr -> string -> IO i63.
+Axiom io_accept_connection : socket_descr -> IO socket_descr.
+Axiom io_close_socket : socket_descr -> IO unit.
+
+Extract Constant io_open_socket
+ => "(fun x1 x2 k__ -> k__ ((Ffi.Socket.open_socket x1 x2)))".
+Extract Constant io_listen => "(fun x1 k__ -> k__ ((Ffi.Socket.listen x1)))".
+Extract Constant io_recv => "(fun x1 k__ -> k__ ((Ffi.Socket.recv x1)))".
+Extract Constant io_send
+ => "(fun x1 x2 k__ -> k__ ((Ffi.Socket.send x1 x2)))".
+Extract Constant io_accept_connection
+ => "(fun x1 k__ -> k__ ((Ffi.Socket.accept_connection x1)))".
+Extract Constant io_close_socket
+ => "(fun x1 k__ -> k__ ((Ffi.Socket.close_socket x1)))".
+
+Instance IO_MonadSocket : MonadSocket IO :=
+ { open_socket := io_open_socket
+ ; listen := io_listen
+ ; recv := io_recv
+ ; send := io_send
+ ; accept_connection := io_accept_connection
+ ; close_socket := io_close_socket
+ }.
+
+(* The generated file ends here. *)
+```
+
+### Process Management
+
+In order to avoid a client to block the server by connecting to it
+without sending anything, we can fork a new process for each client.
+
+```ocaml
+type identity = Parent of int | Child
+
+val fork : unit -> identity
+```
+
+This time, the `proc.mli` module interface introduces a transparent
+type, /i.e./, it also provides its definition. This is a good use case
+for the `transparent-types` feature of `coqffi`. In the stanza for
+generating `Proc.v`, we enable it with the `-ftransparent-types`
+command-line argument, like this.
+
+```lisp
+(rule
+ (target Proc.v)
+ (action (run coqffi -ftransparent-types %{cmi:proc} -o %{target})))
+```
+
+which generates the following Coq module.
+
+```coq
+(* This file has been generated by coqffi. *)
+
+Set Implicit Arguments.
+Unset Strict Implicit.
+Set Contextual Implicit.
+Generalizable All Variables.
+Close Scope nat_scope.
+
+From CoqFFI Require Export Extraction.
+From SimpleIO Require Import IO_Monad.
+
+Inductive identity : Type :=
+| Parent (x0 : i63) : identity
+| Child : identity.
+
+Extract Inductive identity => "Ffi.Proc.identity"
+ [ "Ffi.Proc.Parent" "Ffi.Proc.Child" ].
+
+(** * Impure Primitives *)
+
+(** ** Monad Definition *)
+
+Class MonadProc (m : Type -> Type) : Type := { fork : unit -> m identity
+ }.
+
+(** ** [IO] Instance *)
+
+Axiom io_fork : unit -> IO identity.
+
+Extract Constant io_fork => "(fun x1 k__ -> k__ ((Ffi.Proc.fork x1)))".
+
+Instance IO_MonadProc : MonadProc IO := { fork := io_fork
+ }.
+
+(* The generated file ends here. *)
+```
+
+We now have everything we need to implement an echo server in Coq.
+
+## Implementing an Echo Server
+
+Our implementation will be part of a dedicated Coq theory, called `Echo`{.coq}.
+This is done easily a `dune`{.coq} file in the `src/` directory, with the
+following content.
+
+```lisp
+(coq.theory
+ (name Echo)
+ (theories FFI))
+```
+
+In the rest of this section, we will discuss the content of the unique
+module of this theory. Hopefully, readers familiar with programming
+impurity by means of monads will not find anything particularly
+surprising here.
+
+Let us start with the inevitable sequence of import commands. We use
+the `Monad`{.coq} and `MonadFix`{.coq} typeclasses of `ExtLib`{.coq}, and our
+FFI modules from the `FFI`{.coq} theory we have previously defined.
+
+```coq
+From ExtLib Require Import Monad MonadFix.
+From FFI Require Import Proc Socket.
+```
+
+Letting Coq guess the type of unintroduced variables using the `` ` ``
+annotation (*e.g.*, in presence of`` `{Monad m}``{.coq}, Coq understands `m`
+is of type `Type -> Type`) is always nice, so we enable it.
+
+```coq
+Generalizable All Variables.
+```
+
+We enable the monad notation provided by `ExtLib`. In this article, we
+prefer the `let*` notation (as recently introduced by OCaml) over the
+`<-` notation of Haskell, but both are available.
+
+```coq
+Import MonadLetNotation.
+Open Scope monad_scope.
+```
+
+Then, we define a notation to be able to define local, monadic
+recursive functions using the `mfix` combinator of the `MonadFix`
+typeclass.
+
+```coq
+Notation "'let_rec*' f x ':`' p 'in' q" :`
+ (let f :` mfix (fun f x `> p) in q)
+ (at level 61, x pattern, f name, q at next level, right associativity).
+```
+
+Note that `mfix` does /not/ check whether or not the defined function
+will terminate (contrary to the `fix` keyword of Coq). This is
+fortunate because in our case, we do not want our echo server to
+converge, but rather to accept an infinite number of connections.
+
+We can demonstrate how this notation can be leveraged by defining a
+generic TCP server, parameterized by a handler to deal with incoming
+connections.
+
+```coq
+Definition tcp_srv `{Monad m, MonadFix m, MonadProc m, MonadSocket m}
+ (handler : socket_descr -> m unit)
+ : m unit :=
+ let* srv := open_socket "127.0.0.1" 8888 in
+ listen srv;;
+
+ let_rec* tcp_aux _ :=
+ let* client := accept_connection srv in
+ let* res := fork tt in
+ match res with
+ | Parent _ => close_socket client >>= tcp_aux
+ | Child => handler client
+ end
+ in
+
+ tcp_aux tt.
+```
+
+The handler for the echo server is straightforward: it just reads
+incoming bytes from the socket, sends it back, and closes the socket.
+
+```coq
+Definition echo_handler `{Monad m, MonadSocket m} (sock : socket_descr)
+ : m unit :=
+ let* msg := recv sock in
+ send sock msg;;
+ close_socket sock.
+```
+
+Composing our generic TCP server with our echo handler gives us an
+echo server.
+
+```coq
+Definition echo_server `{Monad m, MonadFix m, MonadProc m, MonadSocket m}
+ : m unit :=
+ tcp_srv echo_handler.
+```
+
+Because `coqffi` has generated typeclasses for the impure primitives
+of `proc.mli` and `socket.mli`, `echo_server` is polymorphic, and can
+be instantiated for different monads. When it comes to extracting our
+program, we will generally prefer the `IO` monad of `coq-simple-io`.
+But we could also imagine verifying the client handler with FreeSpec,
+or the generic TCP server with Interaction Trees (which support
+diverging computations). Overall, we can have different verification
+strategies for different parts of our program, by leveraging the most
+relevant framework for each part, yet being able to extract it in an
+efficient form.
+
+The next section shows how this last part is achieved using, once
+again, a convenient stanza of dune.
+
+## Extracting and Building an Executable
+
+The `0.2` version of the Coq-related stanzas of `dune` provides the
+`coq.extraction` stanza, which can be used to build a Coq module
+expected to generate `ml` files.
+
+In our case, we will write `bin/echo.v` to extract the `echo_server`
+in a `echo.ml` module, and uses the `executable` stanza of `dune` to
+get an executable from this file. To achieve this, the `bin/dune`
+file simply requires these two stanzas.
+
+```lisp
+(coq.extraction
+ (prelude echo)
+ (theories Echo)
+ (extracted_modules echo))
+
+(executable
+ (name echo)
+ (libraries ffi))
+```
+
+We are almost done. We now need to write the `echo.v` module, which
+mostly consists of (1) providing a `MonadFix` instance for the `IO`
+monad, (2) using the `IO.unsafe_run` function to escape the `IO`
+monad, (3) calling the `Extraction`{.coq} command to wrap it up.
+
+```coq
+From Coq Require Extraction.
+From ExtLib Require Import MonadFix.
+From SimpleIO Require Import SimpleIO.
+From Echo Require Import Server.
+
+Instance MonadFix_IO : MonadFix IO :=
+ { mfix := @IO.fix_io }.
+
+Definition main : io_unit :=
+ IO.unsafe_run echo_server.
+
+Extraction "echo.ml" main.
+```
+
+Since we are using the `i63`{.coq} type (signed 63bits integers) of the
+`CoqFFI` theory, and since `i63`{.coq} is implemented under the hood with Coq
+primitive integers, we *also* need to provide a `Uint63`{.ocaml} module with a
+`of_int`{.ocaml} function. Fortunately, this module is straightforward to
+write.
+
+```ocaml
+let of_int x = x
+```
+
+And *voilà*. A call to `dune` at the root of the repository will
+build everything (Coq and OCaml alike). Starting the echo server
+is as simple as
+
+```bash
+dune exec bin/echo.exe
+```
+
+And connecting to it can be achieved with a program like `telnet`.
+
+```console
+$ telnet 127.0.0.1 8888
+Trying 127.0.0.1...
+Connected to 127.0.0.1.
+Escape character is '^]'.
+hello, echo server!
+hello, echo server!
+Connection closed by foreign host.
+```
+
+## Appendix
+
+### The `Socket` OCaml Module
+
+There is not much to say, except that (as already stated) we use the
+`Unix`{.ocaml} module to manipulate sockets, and we attach to each socket a
+buffer to store incoming bytes.
+
+```ocaml
+let buffer_size = 1024
+
+type socket_descr = {
+ fd : Unix.file_descr;
+ recv_buffer : bytes;
+}
+
+let from_fd fd =
+ let rbuff = Bytes.create buffer_size in
+ { fd ` fd; recv_buffer ` rbuff }
+
+let open_socket hostname port =
+ let open Unix in
+ let addr = inet_addr_of_string hostname in
+ let fd = socket PF_INET SOCK_STREAM 0 in
+ setsockopt fd SO_REUSEADDR true;
+ bind fd (ADDR_INET (addr, port));
+ from_fd fd
+
+let listen sock = Unix.listen sock.fd 1
+
+let recv sock =
+ let s = Unix.read sock.fd sock.recv_buffer 0 buffer_size in
+ Bytes.sub_string sock.recv_buffer 0 s
+
+let send sock msg =
+ Unix.write_substring sock.fd msg 0 (String.length msg)
+
+let accept_connection sock =
+ Unix.accept sock.fd |> fst |> from_fd
+
+let close_socket sock = Unix.close sock.fd
+```
+
+### The `Proc` OCaml Module
+
+Thanks to the `Unix` module, the implementation is pretty straightforward.
+
+```ocaml
+type identity = Parent of int | Child
+
+let fork x =
+ match Unix.fork x with
+ | 0 -> Child
+ | x -> Parent x
+```
diff --git a/site/posts/CoqffiEcho.org b/site/posts/CoqffiEcho.org
deleted file mode 100644
index f2b03b1..0000000
--- a/site/posts/CoqffiEcho.org
+++ /dev/null
@@ -1,471 +0,0 @@
-#+TITLE: Implementing an Echo Server in Coq with ~coqffi~
-
-#+SERIES: ./Coqffi.html
-#+SERIES_PREV: ./CoqffiIntro.html
-
-#+NAME: coqffi_output
-#+BEGIN_SRC sh :results output :exports none :var mod=""
-cat ${ROOT}/lp/coqffi-tutorial/_build/default/ffi/${mod}
-#+END_SRC
-
-In this article, we will demonstrate how ~coqffi~ can be used to
-implement an echo server, /i.e./, a TCP server which sends back any
-input it receives from its clients. In addition to ~coqffi~, you will
-need to install ~coq-simple-io~. The latter is available in the
-~released~ repository of the Opam Coq Archive.
-
-#+BEGIN_SRC sh
-opam install coq-coqffi coq-simple-io
-#+END_SRC
-
-Besides, this article is a literate program, and you can download
-[[rel:/files/coqffi-tutorial.tar.gz][the resulting source tree]] if you
-want to try to read the source directly, or modify it to your taste.
-
-#+BEGIN_EXPORT html
-<nav id="generate-toc"></nav>
-<div id="history">site/posts/CoqffiEcho.org</div>
-#+END_EXPORT
-
-* Project Layout
-
-Before diving too much into the implementation of our echo server, we
-first give an overview of the resulting project’s layout. Since we aim
-at implementing a program, we draw our inspiration from the idiomatic
-way of organizing a OCaml project.
-
-#+BEGIN_SRC sh :results output :exports results
-cd ${ROOT}/lp/coqffi-tutorial/ ; tree --noreport -I "_build"
-#+END_SRC
-
-We have three directories at the root of the project.
-
-- ~ffi/~ contains the low-level OCaml code ::
- It provides an OCaml library (~ffi~), and a Coq theory (~FFI~) which
- gathers the FFI modules generated by ~coqffi~.
-- ~src/~ contains the Coq implementation of our echo server ::
- It provides a Coq theory (~Echo~) which depends on the ~FFI~ theory
- the ~SimpleIO~ theory of ~coq-simple~io~. This theory provides the
- implementation of our echo server in Coq.
-- ~bin/~ contains the pieces of code to get an executable program ::
- It contains a Coq module (~echo.v~) which configures and uses the
- extraction mechanism to generate an OCaml module (~echo.ml~). This
- OCaml module can be compiled to get an executable program.
-
-Note that we could have decided to only have one Coq theory. We could
-also have added a fourth directory (~theories/~) for formal
-verification specific code, but this is out of the scope of this
-tutorial.
-
-Overall, we use ~dune~ to compile and compose the different parts of
-the echo server. ~dune~ has a native —yet unstable at the time of
-writing— support for building Coq projects, with very convenient
-stanzas like =coq.theory= and =coq.extraction=.
-
-The following graph summarizes the dependencies between each component
-(plain arrows symbolize software dependencies).
-
-#+BEGIN_SRC dot :file deps.svg :exports results
-digraph dependencies {
- graph [nodesep="0.4"];
- rankdir="LR";
- node [shape=box];
- subgraph {
- rank=same;
- FFI [label="Socket.v" style="dashed"];
- ffi [label="socket.mli"];
- }
- subgraph {
- Echo [label="Echo.v"];
- }
-
- subgraph {
- rank=same;
- echo_v [label="main.v"];
- echo_ml [label="main.ml" style="dashed"];
- }
-
- ffi -> FFI [style="dashed" label="coqffi "];
- echo_ml -> echo_v [dir=back style="dashed" label="coqc "];
- FFI -> Echo -> echo_v;
- ffi -> echo_ml;
-}
-#+END_SRC
-
-We enable Coq-related stanza with ~(using coq 0.2)~ in the
-~dune-project~.
-file.
-
-#+BEGIN_SRC lisp :tangle coqffi-tutorial/dune-project
-(lang dune 2.7)
-(using coq 0.2)
-#+END_SRC
-
-The rest of this tutorial proceeds by diving into each directory.
-
-* FFI Bindings
-
-Our objective is to implement an echo server, /i.e./, a server which
-(1) accepts incoming connections, and (2) sends back any incoming
-messages. We will consider two classes of effects. One is related to
-creating and manipulating TCP sockets. The other is dedicated to
-process management, more precisely to be able to fork when receiving
-incoming connections.
-
-Therefore, the ~ffi~ library will provide two modules. Likewise, the
-~FFI~ theory will provide two analogous modules generated by ~coqffi~.
-
-In the ~ffi/~ directory, we add the following stanza to the ~dune~
-file.
-
-#+BEGIN_SRC lisp :tangle coqffi-tutorial/ffi/dune
-(library
- (name ffi)
- (libraries unix))
-#+END_SRC
-
-~dune~ will look for any ~.ml~ and ~.mli~ files within the directory
-and will consider they belong to the ~ffi~ library. We use the
-[[https://caml.inria.fr/pub/docs/manual-ocaml/libref/Unix.html][~unix~]]
-library to implement the features we are looking for.
-
-Then, we add the following stanza to the ~dune~ file of the ~ffi/~
-directory.
-
-#+BEGIN_SRC lisp :tangle coqffi-tutorial/ffi/dune
-(coq.theory
- (name FFI))
-#+END_SRC
-
-This tells ~dune~ to look for ~.v~ file within the ~ffi/~ directory,
-in order to build them with Coq. A nice feature of ~dune~ is that if
-we automatically generate Coq files, they will be automatically
-“attached” to this theory.
-
-** Sockets
-
-Sockets are boring. The following OCaml module interface provides the
-necessary type and functions to manipulate them.
-
-#+BEGIN_SRC ocaml :tangle coqffi-tutorial/ffi/socket.mli
-type socket_descr
-
-val open_socket : string -> int -> socket_descr
-val listen : socket_descr -> unit
-val recv : socket_descr -> string
-val send : socket_descr -> string -> int
-val accept_connection : socket_descr -> socket_descr
-val close_socket : socket_descr -> unit
-#+END_SRC
-
-Our focus is how to write the interface modules for ~coqffi~. Since
-the object of this tutorial is not the implementation of an echo
-server in itself, the implementation details of the ~ffi~ library will
-not be discussed.
-
-#+BEGIN_details
-#+HTML: <summary>Implementation for <code>socket.mli</code></summary>
-
-There is not much to say, except that (as already stated) we use the
-~Unix~ module to manipulate sockets, and we attach to each socket a
-buffer to store incoming bytes.
-
-#+BEGIN_SRC ocaml :tangle coqffi-tutorial/ffi/socket.ml
-let buffer_size = 1024
-
-type socket_descr = {
- fd : Unix.file_descr;
- recv_buffer : bytes;
-}
-
-let from_fd fd =
- let rbuff = Bytes.create buffer_size in
- { fd = fd; recv_buffer = rbuff }
-
-let open_socket hostname port =
- let open Unix in
- let addr = inet_addr_of_string hostname in
- let fd = socket PF_INET SOCK_STREAM 0 in
- setsockopt fd SO_REUSEADDR true;
- bind fd (ADDR_INET (addr, port));
- from_fd fd
-
-let listen sock = Unix.listen sock.fd 1
-
-let recv sock =
- let s = Unix.read sock.fd sock.recv_buffer 0 buffer_size in
- Bytes.sub_string sock.recv_buffer 0 s
-
-let send sock msg =
- Unix.write_substring sock.fd msg 0 (String.length msg)
-
-let accept_connection sock =
- Unix.accept sock.fd |> fst |> from_fd
-
-let close_socket sock = Unix.close sock.fd
-#+END_SRC
-#+END_details
-
-~dune~ generates ~.cmi~ files for the ~.mli~ files of our library, and
-provides the necessary bits to easily locate them. Besides, the
-=action= stanza can be used here to tell to ~dune~ how to generate the
-module ~Socket.v~ from ~file.cmi~. We add the following entry to
-~ffi/dune~.
-
-#+BEGIN_SRC lisp :tangle coqffi-tutorial/ffi/dune
-(rule
- (target Socket.v)
- (action (run coqffi %{cmi:socket} -o %{target})))
-#+END_SRC
-
-We call ~coqffi~ without any feature-related command-line argument,
-which means only the ~simple-io~ feature is enabled. As a consequence,
-the ~socket_descr~ type is axiomatized in Coq, and in addition to a
-=MonadSocket= monad, ~coqffi~ will generate an instance for this monad
-for the =IO= monad of ~coq-simple-io~.
-
-Interested readers can have a look at the generated Coq module below.
-
-#+BEGIN_details
-#+HTML: <summary><code>Socket.v</code> as generated by <code>coqffi</code></summary>
-
-#+BEGIN_SRC coq :noweb yes
-<<coqffi_output(mod="Socket.v")>>
-#+END_SRC
-#+END_details
-
-** Process Management
-
-In order to avoid a client to block the server by connecting to it
-without sending anything, we can fork a new process for each client.
-
-#+BEGIN_SRC ocaml :tangle coqffi-tutorial/ffi/proc.mli
-type identity = Parent of int | Child
-
-val fork : unit -> identity
-#+END_SRC
-
-#+BEGIN_details
-#+HTML: <summary>Implementation for <code>proc.mli</code></summary>
-
-Again, thanks to the ~Unix~ module, the implementation is pretty
-straightforward.
-
-#+BEGIN_SRC ocaml :tangle coqffi-tutorial/ffi/proc.ml
-type identity = Parent of int | Child
-
-let fork x =
- match Unix.fork x with
- | 0 -> Child
- | x -> Parent x
-#+END_SRC
-#+END_details
-
-This time, the ~proc.mli~ module interface introduces a transparent
-type, /i.e./, it also provides its definition. This is a good use case
-for the ~transparent-types~ feature of ~coqffi~. In the stanza for
-generating ~Proc.v~, we enable it with the ~-ftransparent-types~
-command-line argument, like this.
-
-#+BEGIN_SRC lisp :tangle coqffi-tutorial/ffi/dune
-(rule
- (target Proc.v)
- (action (run coqffi -ftransparent-types %{cmi:proc} -o %{target})))
-#+END_SRC
-
-#+BEGIN_details
-#+HTML: <summary><code>Proc.v</code> as generated by <code>coqffi</code></summary>
-#+BEGIN_SRC coq :noweb yes
-<<coqffi_output(mod="Proc.v")>>
-#+END_SRC
-#+END_details
-
-We now have everything we need to implement an echo server in Coq.
-
-* Implementing an Echo Server
-
-Our implementation will be part of a dedicated Coq theory, called
-~Echo~. This is done easily a ~dune~ file in the ~src/~ directory,
-with the following content.
-
-#+BEGIN_SRC lisp :tangle coqffi-tutorial/src/dune
-(coq.theory
- (name Echo)
- (theories FFI))
-#+END_SRC
-
-In the rest of this section, we will discuss the content of the unique
-module of this theory. Hopefully, readers familiar with programming
-impurity by means of monads will not find anything particularly
-surprising here.
-
-Let us start with the inevitable sequence of import commands. We use
-the =Monad= and =MonadFix= typeclasses of =ExtLib=, and our FFI
-modules from the =FFI= theory we have previously defined.
-
-#+BEGIN_SRC coq :tangle coqffi-tutorial/src/Server.v
-From ExtLib Require Import Monad MonadFix.
-From FFI Require Import Proc Socket.
-#+END_SRC
-
-Letting Coq guess the type of unintroduced variables using the ~`~
-annotation (/e.g./, in presence of ~`{Monad m}~, Coq understands ~m~
-is of type ~Type -> Type~) is always nice, so we enable it.
-
-#+BEGIN_SRC coq :tangle coqffi-tutorial/src/Server.v
-Generalizable All Variables.
-#+END_SRC
-
-We enable the monad notation provided by =ExtLib=. In this article, we
-prefer the ~let*~ notation (as recently introduced by OCaml) over the
-~<-~ notation of Haskell, but both are available.
-
-#+BEGIN_SRC coq :tangle coqffi-tutorial/src/Server.v
-Import MonadLetNotation.
-Open Scope monad_scope.
-#+END_SRC
-
-Then, we define a notation to be able to define local, monadic
-recursive functions using the =mfix= combinator of the =MonadFix=
-typeclass.
-
-#+BEGIN_SRC coq :tangle coqffi-tutorial/src/Server.v
-Notation "'let_rec*' f x ':=' p 'in' q" :=
- (let f := mfix (fun f x => p) in q)
- (at level 61, x pattern, f name, q at next level, right associativity).
-#+END_SRC
-
-Note that ~mfix~ does /not/ check whether or not the defined function
-will terminate (contrary to the ~fix~ keyword of Coq). This is
-fortunate because in our case, we do not want our echo server to
-converge, but rather to accept an infinite number of connections.
-
-We can demonstrate how this notation can be leveraged by defining a
-generic TCP server, parameterized by a handler to deal with incoming
-connections.
-
-#+BEGIN_SRC coq :tangle coqffi-tutorial/src/Server.v
-Definition tcp_srv `{Monad m, MonadFix m, MonadProc m, MonadSocket m}
- (handler : socket_descr -> m unit)
- : m unit :=
- let* srv := open_socket "127.0.0.1" 8888 in
- listen srv;;
-
- let_rec* tcp_aux _ :=
- let* client := accept_connection srv in
- let* res := fork tt in
- match res with
- | Parent _ => close_socket client >>= tcp_aux
- | Child => handler client
- end
- in
-
- tcp_aux tt.
-#+END_SRC
-
-The handler for the echo server is straightforward: it just reads
-incoming bytes from the socket, sends it back, and closes the socket.
-
-#+BEGIN_SRC coq :tangle coqffi-tutorial/src/Server.v
-Definition echo_handler `{Monad m, MonadSocket m} (sock : socket_descr)
- : m unit :=
- let* msg := recv sock in
- send sock msg;;
- close_socket sock.
-#+END_SRC
-
-Composing our generic TCP server with our echo handler gives us an
-echo server.
-
-#+BEGIN_SRC coq :tangle coqffi-tutorial/src/Server.v
-Definition echo_server `{Monad m, MonadFix m, MonadProc m, MonadSocket m}
- : m unit :=
- tcp_srv echo_handler.
-#+END_SRC
-
-Because ~coqffi~ has generated typeclasses for the impure primitives
-of ~proc.mli~ and ~socket.mli~, =echo_server= is polymorphic, and can
-be instantiated for different monads. When it comes to extracting our
-program, we will generally prefer the =IO= monad of ~coq-simple-io~.
-But we could also imagine verifying the client handler with FreeSpec,
-or the generic TCP server with Interaction Trees (which support
-diverging computations). Overall, we can have different verification
-strategies for different parts of our program, by leveraging the most
-relevant framework for each part, yet being able to extract it in an
-efficient form.
-
-The next section shows how this last part is achieved using, once
-again, a convenient stanza of dune.
-
-* Extracting and Building an Executable
-
-The ~0.2~ version of the Coq-related stanzas of ~dune~ provides the
-~coq.extraction~ stanza, which can be used to build a Coq module
-expected to generate ~ml~ files.
-
-In our case, we will write ~bin/echo.v~ to extract the ~echo_server~
-in a ~echo.ml~ module, and uses the =executable= stanza of ~dune~ to
-get an executable from this file. To achieve this, the ~bin/dune~
-file simply requires these two stanzas.
-
-#+BEGIN_SRC lisp :tangle coqffi-tutorial/bin/dune
-(coq.extraction
- (prelude echo)
- (theories Echo)
- (extracted_modules echo))
-
-(executable
- (name echo)
- (libraries ffi))
-#+END_SRC
-
-We are almost done. We now need to write the ~echo.v~ module, which
-mostly consists of (1) providing a =MonadFix= instance for the =IO=
-monad, (2) using the =IO.unsafe_run= function to escape the =IO=
-monad, (3) calling the src_coq[:exports code]{Extraction} command to
-wrap it up.
-
-#+BEGIN_SRC coq :tangle coqffi-tutorial/bin/echo.v
-From Coq Require Extraction.
-From ExtLib Require Import MonadFix.
-From SimpleIO Require Import SimpleIO.
-From Echo Require Import Server.
-
-Instance MonadFix_IO : MonadFix IO :=
- { mfix := @IO.fix_io }.
-
-Definition main : io_unit :=
- IO.unsafe_run echo_server.
-
-Extraction "echo.ml" main.
-#+END_SRC
-
-Since we are using the =i63= type (signed 63bits integers) of the
-~CoqFFI~ theory, and since =i63= is implemented under the hood with
-Coq primitive integers, we /also/ need to provide a =Uint63= module
-with a =of_int= function. Fortunately, this module is straightforward
-to write.
-
-#+BEGIN_SRC ocaml :tangle coqffi-tutorial/bin/uint63.ml
-let of_int x = x
-#+END_SRC
-
-And /voilà/. A call to ~dune~ at the root of the repository will
-build everything (Coq and OCaml alike). Starting the echo server
-is as simple as
-
-#+BEGIN_SRC sh
-dune exec bin/echo.exe
-#+END_SRC
-
-And connecting to it can be achieved with a program like =telnet=.
-
-#+BEGIN_SRC console
-$ telnet 127.0.0.1 8888
-Trying 127.0.0.1...
-Connected to 127.0.0.1.
-Escape character is '^]'.
-hello, echo server!
-hello, echo server!
-Connection closed by foreign host.
-#+END_SRC
diff --git a/site/posts/CoqffiIntro.org b/site/posts/CoqffiIntro.org
deleted file mode 100644
index 677283a..0000000
--- a/site/posts/CoqffiIntro.org
+++ /dev/null
@@ -1,516 +0,0 @@
-#+TITLE: ~coqffi~ in a Nutshell
-
-#+SERIES: ./Coqffi.html
-#+SERIES_NEXT: ./CoqffiEcho.html
-
-For each entry of a ~cmi~ file (a /compiled/ ~mli~ file), ~coqffi~
-tries to generate an equivalent (from the extraction mechanism
-perspective) Coq definition. In this article, we walk through how
-~coqffi~ works.
-
-Note that we do not dive into the vernacular commands ~coqffi~
-generates. They are of no concern for users of ~coqffi~.
-
-#+BEGIN_EXPORT html
-<nav id="generate-toc"></nav>
-<div id="history">site/posts/CoqffiIntro.org</div>
-#+END_EXPORT
-
-* Getting Started
-
-** Requirements
-
-The latest version of ~coqffi~ (~1.0.0~beta8~ at the time of writing)
-is compatible with OCaml ~4.08~ up to ~4.14~, and Coq ~8.12~ up top
-~8.13~. If you want to use ~coqffi~, but have incompatible
-requirements of your own, feel free to
-[[https://github.com/coq-community/coqffi/issues][submit an issue]].
-
-** Installing ~coqffi~
-
-The recommended way to install ~coqffi~ is through the
-[[https://coq.inria.fr/opam/www][Opam Coq Archive]], in the ~released~
-repository. If you haven’t activated this repository yet, you can use
-the following bash command.
-
-#+BEGIN_SRC sh
-opam repo add coq-released https://coq.inria.fr/opam/released
-#+END_SRC
-
-Then, installing ~coqffi~ is as simple as
-
-#+BEGIN_SRC sh
-opam install coq-coqffi
-#+END_SRC
-
-You can also get the source from
-[[https://github.com/coq-community/coqffi][the upstream ~git~
-repository]]. The ~README~ provides the necessary pieces of
-information to build it from source.
-
-** Additional Dependencies
-
-One major difference between Coq and OCaml is that the former is pure,
-while the latter is not. Impurity can be modeled in pure languages,
-and Coq does not lack of frameworks in this respect. ~coqffi~
-currently supports two of them: [[https://github.com/Lysxia/coq-simple-io][~coq-simple-io~]] and [[https://github.com/ANSSI-FR/FreeSpec][FreeSpec]]. It is
-also possible to use it with [[https://github.com/DeepSpec/InteractionTrees][Interaction Trees]], albeit in a less
-direct manner.
-
-* Primitive Types
-
-~coqffi~ supports a set of primitive types, /i.e./, a set of OCaml
-types for which it knows an equivalent type in Coq. The list is the
-following (the Coq types are fully qualified in the table, but not in
-the generated Coq module as the necessary ~Import~ statement are
-generated too).
-
-| OCaml type | Coq type |
-|-------------------+-------------------------------|
-| =bool= | =Coq.Init.Datatypes.bool= |
-| =char= | =Coq.Strings.Ascii.ascii= |
-| =int= | =CoqFFI.Data.Int.i63= |
-| ='a list= | =Coq.Init.Datatypes.list a= |
-| ='a Seq.t= | =CoqFFI.Data.Seq.t= |
-| ='a option= | =Coq.Init.Datatypes.option a= |
-| =('a, 'e) result= | =Coq.Init.Datatypes.sum= |
-| =string= | =Coq.Strings.String.string= |
-| =unit= | =Coq.Init.Datatypes.unit= |
-| =exn= | =CoqFFI.Exn= |
-
-The =i63= type is introduced by the =CoqFFI= theory to provide signed
-primitive integers to Coq users. They are implemented on top of the
-(sadly unsigned) Coq native integers introduced in Coq
-~8.10~. Hopefully, the =i63= type will be deprecated once [[https://github.com/coq/coq/pull/13559][signed
-primitive integers find their way to Coq upstream]].
-
-When processing the entries of a given interface model, ~coqffi~ will
-check that they only use these types, or types introduced by the
-interface module itself.
-
-Sometimes, you may encounter a situation where you have two interface
-modules ~b.mli~ and ~b.mli~, such that ~b.mli~ uses a type introduced
-in ~a.mli~. To deal with this scenario, you can use the ~--witness~
-flag to generate ~A.v~. This will tell ~coqffi~ to also generate
-~A.ffi~; this file can then be used when generating ~B.v~ thanks to
-the ~-I~ option. Furthermore, for ~B.v~ to compile the ~--require~
-option needs to be used to ensure the ~A~ Coq library (~A.v~) is
-required.
-
-To give a more concrete example, given ~a.mli~
-
-#+BEGIN_SRC ocaml
-type t
-#+END_SRC
-
-and ~b.mli~
-
-#+BEGIN_SRC ocaml
-type a = A.t
-#+END_SRC
-
-To generate ~A.v~, we can use the following commands:
-
-#+BEGIN_SRC bash
-ocamlc a.mli
-coqffi --witness -o A.v a.cmi
-#+END_SRC
-
-Which would generate the following axiom for =t=.
-
-#+BEGIN_SRC coq
-Axiom t : Type.
-#+END_SRC
-
-Then, generating ~B.v~ can be achieved as follows:
-
-#+BEGIN_SRC bash
-ocamlc b.mli
-coqffi -I A.ffi -ftransparent-types -r A -o B.v b.cmi
-#+END_SRC
-
-which results in the following output for =v=:
-
-#+BEGIN_SRC coq
-Require A.
-
-Definition u : Type := A.t.
-#+END_SRC
-
-* Code Generation
-
-~coqffi~ distinguishes five types of entries: types, pure values,
-impure primitives, asynchronous primitives, exceptions, and
-modules. We now discuss how each one of them is handled.
-
-** Types
-
-By default, ~coqffi~ generates axiomatized definitions for each type
-defined in a ~.cmi~ file. This means that src_ocaml[:exports
-code]{type t} becomes src_coq[:exports code]{Axiom t : Type}.
-Polymorphism is supported, /i.e./, src_ocaml[:exports code]{type 'a t}
-becomes src_coq[:exports code]{Axiom t : forall (a : Type), Type}.
-
-It is possible to provide a “model” for a type using the =coq_model=
-annotation, for instance for reasoning purposes. For instance,
-we can specify that a type is equivalent to a =list=.
-
-#+BEGIN_SRC ocaml
-type 'a t [@@coq_model "list"]
-#+END_SRC
-
-This generates the following Coq definition.
-
-#+BEGIN_SRC coq
-Definition t : forall (a : Type), Type := list.
-#+END_SRC
-
-It is important to be careful when using the =coq_model= annotation.
-More precisely, the fact that =t= is a =list= in the “Coq universe”
-shall not be used while the implementation phase, only the
-verification phase.
-
-Unamed polymorphic type parameters are also supported. In presence of
-such parameters, ~coqffi~ will find it a name that is not already
-used. For instance,
-
-#+BEGIN_SRC ocaml
-type (_, 'a) ast
-#+END_SRC
-
-becomes
-
-#+BEGIN_SRC ocaml
-Axiom ast : forall (b : Type) (a : Type), Type.
-#+END_SRC
-
-Finally, ~coqffi~ has got an experimental feature called
-~transparent-types~ (enabled by using the ~-ftransparent-types~
-command-line argument). If the type definition is given in the module
-interface, then ~coqffi~ tries to generates an equivalent definition
-in Coq. For instance,
-
-#+BEGIN_SRC ocaml
-type 'a llist =
- | LCons of 'a * (unit -> 'a llist)
- | LNil
-#+END_SRC
-
-becomes
-
-#+BEGIN_SRC coq
-Inductive llist (a : Type) : Type :=
-| LCons (x0 : a) (x1 : unit -> llist a) : llist a
-| LNil : llist a.
-#+END_SRC
-
-Mutually recursive types are supported, so
-
-#+BEGIN_SRC ocaml
-type even = Zero | ESucc of odd
-and odd = OSucc of even
-#+END_SRC
-
-becomes
-
-#+BEGIN_SRC coq
-Inductive odd : Type :=
-| OSucc (x0 : even) : odd
-with even : Type :=
-| Zero : even
-| ESucc (x0 : odd) : even.
-#+END_SRC
-
-Besides, ~coqffi~ supports alias types, as suggested in this write-up
-when we discuss witness files.
-
-The ~transparent-types~ feature is *experimental*, and is currently
-limited to variant types. It notably does not support
-records. Besides, it may generate incorrect Coq types, because it does
-not check whether or not the [[https://coq.inria.fr/refman/language/core/inductive.html#positivity-condition][positivity condition]] is
-satisfied.
-
-** Pure values
-
-~coqffi~ decides whether or not a given OCaml values is pure or impure
-with the following heuristics:
-
-- Constants are pure
-- Functions are impure by default
-- Functions with a =coq_model= annotation are pure
-- Functions marked with the =pure= annotation are pure
-- If the ~pure-module~ feature is enabled (~-fpure-module~),
- then synchronous functions (which do not live inside the [[https://ocsigen.org/lwt/5.3.0/manual/manual][~Lwt~]]
- monad) are pure
-
-Similarly to types, ~coqffi~ generates axioms (or definitions, if the
-~coq_model~ annotation is used) for pure values. Then,
-
-#+BEGIN_SRC ocaml
-val unpack : string -> (char * string) option [@@pure]
-#+END_SRC
-
-becomes
-
-#+BEGIN_SRC coq
-Axiom unpack : string -> option (ascii * string).
-#+END_SRC
-
-Polymorphic values are supported.
-
-#+BEGIN_SRC ocaml
-val map : ('a -> 'b) -> 'a list -> 'b list [@@pure]
-#+END_SRC
-
-becomes
-
-#+BEGIN_SRC coq
-Axiom map : forall (a : Type) (b : Type), (a -> b) -> list a -> list b.
-#+END_SRC
-
-Again, unamed polymorphic type are supported, so
-
-#+BEGIN_SRC ocaml
-val ast_to_string : _ ast -> string [@@pure]
-#+END_SRC
-
-becomes
-
-#+BEGIN_SRC coq
-Axiom ast_to_string : forall (a : Type), string.
-#+END_SRC
-
-** Impure Primitives
-
-~coqffi~ reserves a special treatment for /impure/ OCaml functions.
-Impurity is usually handled in pure programming languages by means of
-monads, and ~coqffi~ is no exception to the rule.
-
-Given the set of impure primitives declared in an interface module,
-~coqffi~ will (1) generate a typeclass which gathers these primitives,
-and (2) generate instances of this typeclass for supported backends.
-
-We illustrate the rest of this section with the following impure
-primitives.
-
-#+BEGIN_SRC ocaml
-val echo : string -> unit
-val scan : unit -> string
-#+END_SRC
-
-where =echo= allows writing something the standard output, and =scan=
-to read the standard input.
-
-Assuming the processed module interface is named ~console.mli~, the
-following Coq typeclass is generated.
-
-#+BEGIN_SRC coq
-Class MonadConsole (m : Type -> Type) := { echo : string -> m unit
- ; scan : unit -> m string
- }.
-#+END_SRC
-
-Using this typeclass and with the additional support of an additional
-=Monad= typeclass, we can specify impure computations which interacts
-with the console. For instance, with the support of ~ExtLib~, one can
-write.
-
-#+BEGIN_SRC coq
-Definition pipe `{Monad m, MonadConsole m} : m unit :=
- let* msg := scan () in
- echo msg.
-#+END_SRC
-
-There is no canonical way to model impurity in Coq, but over the years
-several frameworks have been released to tackle this challenge.
-
-~coqffi~ provides three features related to impure primitives.
-
-*** ~simple-io~
-
-When this feature is enabled, ~coqffi~ generates an instance of the
-typeclass for the =IO= monad introduced in the ~coq-simple-io~ package
-
-#+BEGIN_SRC coq
-Axiom io_echo : string -> IO unit.
-Axiom io_scan : unit -> IO string.
-
-Instance IO_MonadConsole : MonadConsole IO := { echo := io_echo
- ; scan := io_scan
- }.
-#+END_SRC
-
-It is enabled by default, but can be disabled using the
-~-fno-simple-io~ command-line argument.
-
-*** ~interface~
-
-When this feature is enabled, ~coqffi~ generates an inductive type
-which describes the set of primitives available, to be used with
-frameworks like [[https://github.com/ANSSI-FR/FreeSpec][FreeSpec]] or
-[[https://github.com/DeepSpec/InteractionTrees][Interactions Trees]]
-
-#+BEGIN_SRC coq
-Inductive CONSOLE : Type -> Type :=
-| Echo : string -> CONSOLE unit
-| Scan : unit -> CONSOLE string.
-
-Definition inj_echo `{Inject CONSOLE m} (x0 : string) : m unit :=
- inject (Echo x0).
-
-Definition inj_scan `{Inject CONSOLE m} (x0 : unit) : m string :=
- inject (Scan x0).
-
-Instance Inject_MonadConsole `{Inject CONSOLE m} : MonadConsole m :=
- { echo := inj_echo
- ; scan := inj_scan
- }.
-#+END_SRC
-
-Providing an instance of the form src_coq[:exports code]{forall i,
-Inject i M} is enough for your monad =M= to be compatible with this
-feature (see for instance
-[[https://github.com/ANSSI-FR/FreeSpec/blob/master/theories/FFI/FFI.v][how
-FreeSpec implements it]]).
-
-*** ~freespec~
-
-When this feature in enabled, ~coqffi~ generates a semantics for the
-inductive type generated by the ~interface~ feature.
-
-#+BEGIN_SRC coq
-Axiom unsafe_echo : string -> unit.
-Axiom unsafe_scan : uint -> string.
-
-Definition console_unsafe_semantics : semantics CONSOLE :=
- bootstrap (fun a e =>
- local match e in CONSOLE a return a with
- | Echo x0 => unsafe_echo x0
- | Scan x0 => unsafe_scan x0
- end).
-#+END_SRC
-
-** Asynchronous Primitives
-
-~coqffi~ also reserves a special treatment for /asynchronous/
-primitives —/i.e./, functions which live inside the ~Lwt~ monad— when
-the ~lwt~ feature is enabled.
-
-The treatment is very analoguous to the one for impure primitives: (1)
-a typeclass is generated (with the ~_Async~ suffix), and (2) an
-instance for the ~Lwt~ monad is generated. Besides, an instance for
-the “synchronous” primitives is also generated for ~Lwt~. If the
-~interface~ feature is enabled, an interface datatype is generated,
-which means you can potentially use Coq to reason about your
-asynchronous programs (using FreeSpec and alike, although the
-interleaving of asynchronous programs in not yet supported in
-FreeSpec).
-
-By default, the type of the ~Lwt~ monad is ~Lwt.t~. You can override
-this setting using the ~--lwt-alias~ option. This can be useful when
-you are using an alias type in place of ~Lwt.t~.
-
-** Exceptions
-
-OCaml features an exception mechanism. Developers can define their
-own exceptions using the ~exception~ keyword, whose syntax is similar
-to constructors definition. For instance,
-
-#+BEGIN_SRC ocaml
-exception Foo of int * bool
-#+END_SRC
-
-introduces a new exception =Foo= which takes two parameters of type
-=int= and =bool=. =Foo (x, y)= constructs of value of type =exn=.
-
-For each new exceptions introduced in an OCaml module, ~coqffi~
-generates (1) a so-called “proxy type,” and (2) conversion functions
-to and from this type.
-
-Coming back to our example, the “proxy type” generates by ~coqffi~ is
-
-#+BEGIN_SRC coq
-Inductive FooExn : Type :=
-| MakeFooExn (x0 : i63) (x1 : bool) : FooExn.
-#+END_SRC
-
-Then, ~coqffi~ generates conversion functions.
-
-#+BEGIN_SRC coq
-Axiom exn_of_foo : FooExn -> exn.
-Axiom foo_of_exn : exn -> option FooExn.
-#+END_SRC
-
-Besides, ~coqffi~ also generates an instance for the =Exn= typeclass
-provided by the =CoqFFI= theory:
-
-#+BEGIN_SRC coq
-Instance FooExn_Exn : Exn FooExn :=
- { to_exn := exn_of_foo
- ; of_exn := foo_of_exn
- }.
-#+END_SRC
-
-Under the hood, =exn= is an [[https://caml.inria.fr/pub/docs/manual-ocaml/extensiblevariants.html][extensible datatype]], and how ~coqffi~
-supports it will probably be generalized in future releases.
-
-Finally, ~coqffi~ has a minimal support for functions which may raise
-exceptions. Since OCaml type system does not allow to identify such
-functions, they need to be annotated explicitely, using the
-=may_raise= annotation. In such a case, ~coqffi~ will change the
-return type of the function to use the =sum= Coq inductive type.
-
-For instance,
-
-#+BEGIN_SRC ocaml
-val from_option : 'a option -> 'a [@@may_raise] [@@pure]
-#+END_SRC
-
-becomes
-
-#+BEGIN_SRC coq
-Axiom from_option : forall (a : Type), option a -> sum a exn.
-#+END_SRC
-
-** Modules
-
-Lastly, ~coqffi~ supports OCaml modules described within ~mli~ files,
-when they are specify as ~module T : sig ... end~. For instance,
-
-#+BEGIN_SRC ocaml
-module T : sig
- type t
-
- val to_string : t -> string [@@pure]
-end
-#+END_SRC
-
-becomes
-
-#+BEGIN_SRC coq
-Module T.
- Axiom t : Type.
-
- Axiom to_string : t -> string.
-End T.
-#+END_SRC
-
-As of now, the following construction is unfortunately *not*
-supported, and will be ignored by ~coqffi~:
-
-#+BEGIN_SRC coq
-module S = sig
- type t
-
- val to_string : t -> string [@@pure]
-end
-
-module T : S
-#+END_SRC
-
-* Moving Forward
-
-~coqffi~ comes with a comprehensive man page. In addition, the
-interested reader can proceed to the next article of this series,
-which explains how [[./CoqffiEcho.org][~coqffi~ can be used to easily implement an echo
-server in Coq]].
diff --git a/site/posts/DiscoveringCommonLisp.md b/site/posts/DiscoveringCommonLisp.md
new file mode 100644
index 0000000..a947110
--- /dev/null
+++ b/site/posts/DiscoveringCommonLisp.md
@@ -0,0 +1,252 @@
+---
+published: 2018-06-17
+tags: ['lisp']
+abstract: |
+ Common Lisp is a venerable programming languages like no other I know. From
+ the creation of a Lisp package up to the creation of a standalone
+ executable, we explore the shore of this strange beast.
+---
+
+# Discovering Common Lisp with `trivial-gamekit`
+
+I always wanted to learn some Lisp dialect. In the meantime,
+[lykan](https://github.com/lkn-org/lykan) —my Slayers Online clone— begins to
+take shape. So, of course, my brain got an idea: *why not writing a client for
+lykan in some Lisp dialect?*[^why] I asked on
+[Mastodon](https://mastodon.social/@lthms/100135240390747697) if there were
+good game engine for Lisp, and someone told me about
+[`trivial-gamekit`](https://github.com/borodust/trivial-gamekit).
+
+[^why]: Spoiler alert: this wasn’t the most efficient approach for the lykan
+ project. But it was fun.
+
+I have no idea if I will manage to implement a decent client using
+trivial-gamekit, but why not trying? This article is the first of a series
+about my experiments, discoveries and difficulties. The complete project
+detailed in this article is available [as a
+gist](https://gist.github.com/lthms/9833f4851843119c966917775b4c4180).
+
+## Common Lisp, Quicklisp and `trivial-gamekit`
+
+The trivial-gamekit
+[website](https://borodust.github.io/projects/trivial-gamekit/) lists several
+requirements. Two are related to Lisp:
+
+1. Quicklisp
+2. SBCL or CCL
+
+[Quicklisp](https://quicklisp.org/beta) is an experimental package manager for
+Lisp project, while SBCL and CCL are two Lisp implementations. I had already
+installed [Clisp](https://www.archlinux.org/packages/?name=clisp), and it took
+me quite some times to understand my mistake. Fortunately,
+[SBCL](https://www.archlinux.org/packages/?name=sbcl) is also packaged in
+ArchLinux.
+
+With a compatible Lisp implementation, installing Quicklisp as a user is
+straightforward. Following the website instructions is enough. At the end of
+the process, you will have a new directory `${HOME}/quicklisp`{.bash}[^go].
+
+[^go]: The purpose of this directory is similar to the [Go
+ workspace](https://github.com/golang/go/wiki/SettingGOPATH).
+
+Quicklisp is not a native feature of SBCL, and requires a small bit of
+configuration to be made available automatically. You have to create a file
+`${HOME}/.sbclrc`{.bash}, with the following content:
+
+```lisp
+(load "~/quicklisp/setup")
+```
+
+There is one final step to be able to use `trivial-gamekit`.
+
+```bash
+sbcl --eval '(ql-dist:install-dist "http://bodge.borodust.org/dist/org.borodust.bodge.txt")' \
+ --quit
+```
+
+As of June 2018, Quicklisp [does not support
+HTTPS](https://github.com/quicklisp/quicklisp-client/issues/167).
+
+## Introducing Lysk
+
+### Packaging
+
+The first thing I search for when I learn a new language is how projects are
+organized. From this perspective, `trivial-gamekit` pointed me directly to
+Quicklisp
+
+Creating a new Quicklisp project is straightforward. From my understanding, new
+Quicklisp projects have to be located inside
+`${HOME}/quicklisp/local-projects`{.bash}. I am not particularly happy with
+this, but it is not really important.
+
+The current code name of my Lisp game client is lysk.
+
+```bash
+mkdir ~/quicklisp/local-projects/lysk
+```
+
+Quicklisp packages (systems?) are defined through `asd` files.
+I have firstly created `lysk.asd` as follows:
+
+```lisp
+(asdf:defsystem lysk
+ :description "Lykan Game Client"
+ :author "lthms"
+ :license "GPLv3"
+ :version "0.0.1"
+ :serial t
+ :depends-on (trivial-gamekit)
+ :components ((:file "package")
+ (:file "lysk")))
+```
+
+`:serial t`{.lisp} means that the files detailed in the `components`{.lisp}
+field depends on the previous ones. That is, `lysk.lisp` depends on
+`package.lisp` in this case. It is possible to manage files dependencies
+manually, with the following syntax:
+
+```lisp
+(:file "seconds" :depends-on "first")
+```
+
+I have declared only one dependency: `trivial-gamekit`. That way, Quicklisp
+will load it for us.
+
+The first “true” Lisp file we define in our skeleton is `package.lisp`.
+Here is its content:
+
+```lisp
+(defpackage :lysk
+ (:use :cl)
+ (:export run app))
+```
+
+Basically, this means we use two symbols, `run`{.lisp} and `app`{.lisp}.
+
+### A Game Client
+
+The `lysk.lisp` file contains the program in itself. My first goal was to
+obtain the following program: at startup, it shall creates a new window in
+fullscreen, and exit when users release the left button of their mouse. It is
+worth mentioning that I had to report [an issue to the `trivial-gamekit`
+upstream](https://github.com/borodust/trivial-gamekit/issues/30) in order to
+make my program work as expected.
+
+While it may sounds scary —it suggests `trivial-gamekit` is a relatively young
+project— the author has implemented a fix in less than an hour! He also took
+the time to answer many questions I had when I joined the `#lispgames` Freenode
+channel.
+
+Before going any further, lets have a look at the complete file.
+
+```lisp
+(cl:in-package :lysk)
+
+(gamekit:defgame app () ()
+ (:fullscreen-p 't))
+
+(defmethod gamekit:post-initialize ((app app))
+ (gamekit:bind-button :mouse-left :released
+ (lambda () (gamekit:stop))))
+
+(defun run ()
+ (gamekit:start 'app))
+```
+
+The first line is some kind of header, to tell Lisp the owner of the file.
+
+The `gamekit:defgame`{.lisp} function allows for creating a new game
+application (called `app`{.lisp} in our case). I ask for a fullscreen window
+with `:fullscreen-p`{.lisp}. Then, we use the `gamekit:post-initialize`{.lisp}
+hook to bind a handler to the release of the left button of our mouse. This
+handler is a simple call to `gamekit:stop`{.lisp}. Finally, we define a new
+function `run`{.lisp} which only starts our application.
+
+Pretty straightforward!
+
+### Running our Program
+
+To “play” our game, we can start the SBCL REPL.
+
+```bash
+sbcl --eval '(ql:quickload :lysk)' --eval '(lysk:run)'
+```
+
+### A Standalone Executable
+
+It looks like empower a REPL-driven development. That being said, once the
+development is finished, I don't think I will have a lot of success if I ask my
+future players to start sbcl to enjoy my game. Fortunately, `trivial-gamekit`
+provides a dedicated function to bundle the game as a standalone executable.
+
+Following the advises of the [**@borodust**](https://github.com/borodust) —the
+`trivial-gamekit` author— I created a second package to that end. First, we
+need to edit the `lysk.asd` file to detail a second package:
+
+```lisp
+(asdf:defsystem lysk/bundle
+ :description "Bundle the Lykan Game Client"
+ :author "lthms"
+ :license "GPLv3"
+ :version "0.0.1"
+ :serial t
+ :depends-on (trivial-gamekit/distribution lysk)
+ :components ((:file "bundle")))
+ ```
+
+This second package depends on lysk (our game client) and
+trivial-gamekit/distribution. The latter provides the `deliver`{.lisp}
+function, and we use it in the `bundle.lisp` file:
+
+```lisp
+(cl:defpackage :lysk.bundle
+ (:use :cl)
+ (:export deliver))
+
+(cl:in-package :lysk.bundle)
+
+(defun deliver ()
+ (gamekit.distribution:deliver :lysk 'lysk:app))
+```
+
+To bundle the game, we can use SBCL from our command line interface.
+
+```bash
+sbcl --eval "(ql:quickload :lysk/bundle)" \
+ --eval "(lysk.bundle:deliver)" \
+ --quit
+```
+
+## Conclusion
+
+Objectively, there is not much in this article. However, because I am totally
+new to Lisp, it took me quite some time to get these few lines of code to work
+together. All being told I think this constitutes a good `trivial-gamekit`
+skeleton. Do not hesitate to us it this way.
+
+Thanks again to [**@borodust**](https://github.com/borodust), for your time and
+all your answers!
+
+## Appendix: a Makefile
+
+I like Makefile, so here is one to `run`{.lisp} the game directly, or
+`bundle`{.lisp} it.
+
+```makefile
+run:
+ @sbcl --eval "(ql:quickload :lysk)" \
+ --eval "(lysk:run)"
+
+bundle:
+ @echo -en "[ ] Remove old build"
+ @rm -rf build/
+ @echo -e "\r[*] Remove old build"
+ @echo "[ ] Building"
+ @sbcl --eval "(ql:quickload :lysk/bundle)" \
+ --eval "(lysk.bundle:deliver)" \
+ --quit
+ @echo "[*] Building"
+
+.PHONY: bundle run
+```
diff --git a/site/posts/DiscoveringCommonLisp.org b/site/posts/DiscoveringCommonLisp.org
deleted file mode 100644
index f31f280..0000000
--- a/site/posts/DiscoveringCommonLisp.org
+++ /dev/null
@@ -1,264 +0,0 @@
-#+TITLE: Discovering Common Lisp with ~trivial-gamekit~
-
-#+SERIES: ./miscellaneous.html
-#+SERIES_NEXT: ./RankNTypesInOCaml.html
-
-#+BEGIN_EXPORT html
-<p>This article has originally been published on <span class="time">June 17,
-2018</span>.</p>
-#+END_EXPORT
-
-I always wanted to learn some Lisp dialect.
-In the meantime, [[https://github.com/lkn-org/lykan][lykan]] —my Slayers Online clone— begins to take shape.
-So, of course, my brain got an idea: /why not writing a client for lykan in some
-Lisp dialect?/
-I asked on [[https://mastodon.social/@lthms/100135240390747697][Mastodon]] if there were good game engine for Lisp, and someone told me
-about [[https://github.com/borodust/trivial-gamekit][trivial-gamekit]].
-
-I have no idea if I will manage to implement a decent client using
-trivial-gamekit, but why not trying?
-This article is the first of a series about my experiments, discoveries and
-difficulties.
-
-The code of my client is hosted on my server, using the pijul vcs.
-If you have pijul installed, you can clone the repository:
-
-#+BEGIN_SRC bash
-pijul clone "https://pijul.lthms.xyz/lkn/lysk"
-#+END_SRC
-
-In addition, the complete project detailed in this article is available [[https://gist.github.com/lthms/9833f4851843119c966917775b4c4180][as a
-gist]].
-
-#+TOC: headlines 2
-
-#+BEGIN_EXPORT html
-<div id="history">site/posts/DiscoveringCommonLisp.org</div>
-#+END_EXPORT
-
-* Common Lisp, Quicklisp and trivial-gamekit
-
-The trivial-gamekit [[https://borodust.github.io/projects/trivial-gamekit/][website]] lists several requirements.
-Two are related to Lisp:
-
-1. Quicklisp
-2. SBCL or CCL
-
-Quicklisp is an experimental package manager for Lisp project (it was easy to
-guess, because there is a link to [[https://quicklisp.org/beta][quicklisp website]] in the trivial-gamekit
-documentation).
-As for SBCL and CCL, it turns out they are two Lisp implementations.
-I had already installed [[https://www.archlinux.org/packages/?name=clisp][clisp]], and it took me quite some times to understand my
-mistake.
-Fortunately, [[https://www.archlinux.org/packages/?name=sbcl][sbcl]] is also packaged in ArchLinux.
-
-With a compatible Lisp implementation, installing Quicklisp as a user is
-straightforward.
-Following the website instructions is enough.
-At the end of the process, you will have a new directory ~${HOME}/quicklisp~,
-whose purpose is similar to the [[https://github.com/golang/go/wiki/SettingGOPATH][go workspace]].
-
-Quicklisp is not a native feature of sbcl, and has to be loaded to be available.
-To do it automatically, you have to create a file ~${HOME}/.sbclrc~, with the
-following content:
-
-#+BEGIN_SRC common-lisp
-(load "~/quicklisp/setup")
-#+END_SRC
-
-There is one final step to be able to use trivial-gamekit.
-
-#+BEGIN_SRC bash
-sbcl --eval '(ql-dist:install-dist "http://bodge.borodust.org/dist/org.borodust.bodge.txt")' \
- --quit
-#+END_SRC
-
-As for now[fn::June 2018], Quicklisp [[https://github.com/quicklisp/quicklisp-client/issues/167][does not support HTTPS]].
-
-* Introducing Lysk
-
-** Packaging
-
-The first thing I search for when I learn a new language is how projects are
-organized.
-From this perspective, trivial-gamekit pointed me directly to Quicklisp
-
-Creating a new Quicklisp project is very simple, and this is a very good thing.
-As I said, the ~${HOME}/quicklisp~ directory acts like the go workspace.
-As far as I can tell, new Quicklisp projects have to be located inside
-~${HOME}/quicklisp/local-projects~.
-I am not particularly happy with it, but it is not really important.
-
-The current code name of my Lisp game client is lysk.
-
-#+BEGIN_SRC bash
-mkdir ~/quicklisp/local-projects/lysk
-#+END_SRC
-
-Quicklisp packages (systems?) are defined through ~asd~ files.
-I have firstly created ~lysk.asd~ as follows:
-
-#+BEGIN_SRC common-lisp
-(asdf:defsystem lysk
- :description "Lykan Game Client"
- :author "lthms"
- :license "GPLv3"
- :version "0.0.1"
- :serial t
- :depends-on (trivial-gamekit)
- :components ((:file "package")
- (:file "lysk")))
-#+END_SRC
-
-~:serial t~ means that the files detailed in the ~components~ field depends on
-the previous ones.
-That is, ~lysk.lisp~ depends on ~package.lisp~ in this case.
-It is possible to manage files dependencies manually, with the following syntax:
-
-#+BEGIN_SRC common-lisp
-(:file "seconds" :depends-on "first")
-#+END_SRC
-
-I have declared only one dependency: trivial-gamekit.
-That way, Quicklisp will load it for us.
-
-The first “true” Lisp file we define in our skeleton is ~package.lisp~.
-Here is its content:
-
-#+BEGIN_SRC common-lisp
-(defpackage :lysk
- (:use :cl)
- (:export run app))
-#+END_SRC
-
-Basically, this means we use two symbols, ~run~ and ~app~.
-
-** A Game Client
-
-The ~lysk.lisp~ file contains the program in itself.
-My first goal was to obtain the following program: at startup, it shall creates
-a new window in fullscreen, and exit when users release the left button of their
-mouse.
-It is worth mentioning that I had to report [[https://github.com/borodust/trivial-gamekit/issues/30][an issue to the trivial-gamekit
-upstream]] in order to make my program work as expected.
-While it may sounds scary —it definitely shows trivial-gamekit is a relatively
-young project— the author has implemented a fix in less than an hour!
-He also took the time to answer many questions I had when I joined the
-~#lispgames~ Freenode channel.
-
-Before going any further, lets have a look at the complete file.
-
-#+BEGIN_SRC common-lisp
-(cl:in-package :lysk)
-
-(gamekit:defgame app () ()
- (:fullscreen-p 't))
-
-(defmethod gamekit:post-initialize ((app app))
- (gamekit:bind-button :mouse-left :released
- (lambda () (gamekit:stop))))
-
-(defun run ()
- (gamekit:start 'app))
-#+END_SRC
-
-The first line is some kind of header, to tell Lisp the owner of the file.
-
-The ~gamekit:defgame~ function allows for creating a new game application
-(called ~app~ in our case).
-I ask for a fullscreen window with ~:fullscreen-p~.
-Then, we use the ~gamekit:post-initialize~ hook to bind a handler to the release
-of the left button of our mouse.
-This handler is a simple call to ~gamekit:stop~.
-Finally, we define a new function ~run~ which only starts our application.
-
-Pretty straightforward, right?
-
-** Running our Program
-
-To “play” our game, we can start the sbcl REPL.
-
-#+BEGIN_SRC bash
-sbcl --eval '(ql:quickload :lysk)' --eval '(lysk:run)'
-#+END_SRC
-
-And it works!
-
-** A Standalone Executable
-
-It looks like empower a REPL-driven development.
-That being said, once the development is finished, I don't think I will have a
-lot of success if I ask my future players to start sbcl to enjoy my game.
-Fortunately, trivial-gamekit provides a dedicated function to bundle the game as
-a standalone executable.
-
-Following the advises of the borodust —the trivial-gamekit author— I created a
-second package to that end.
-First, we need to edit the ~lysk.asd~ file to detail a second package:
-
-#+BEGIN_SRC common-lisp
-(asdf:defsystem lysk/bundle
- :description "Bundle the Lykan Game Client"
- :author "lthms"
- :license "GPLv3"
- :version "0.0.1"
- :serial t
- :depends-on (trivial-gamekit/distribution lysk)
- :components ((:file "bundle")))
-#+END_SRC
-
-This second package depends on lysk (our game client) and and
-trivial-gamekit/distribution.
-The latter provides the ~deliver~ function, and we use it in the ~bundle.lisp~
-file:
-
-#+BEGIN_SRC common-lisp
-(cl:defpackage :lysk.bundle
- (:use :cl)
- (:export deliver))
-
-(cl:in-package :lysk.bundle)
-
-(defun deliver ()
- (gamekit.distribution:deliver :lysk 'lysk:app))
-#+END_SRC
-
-To bundle the game, we can use ~sbcl~ from our command line interface.
-
-#+BEGIN_SRC bash
-sbcl --eval "(ql:quickload :lysk/bundle)" \
- --eval "(lysk.bundle:deliver)" \
- --quit
-#+END_SRC
-
-* Conclusion
-
-Objectively, there is not much in this article.
-However, because I am totally new to Lisp, it took me quite some time to get
-these few lines of code to work together.
-All being told I think this constitutes a good trivial-gamekit skeleton.
-Do not hesitate to us it this way.
-
-Thanks again to borodust, for your time and all your answers!
-
-* Appendix: a Makefile
-
-I like Makefile, so here is one to ~run~ the game directly, or ~bundle~ it.
-
-#+BEGIN_SRC makefile
-run:
- @sbcl --eval "(ql:quickload :lysk)" \
- --eval "(lysk:run)"
-
-bundle:
- @echo -en "[ ] Remove old build"
- @rm -rf build/
- @echo -e "\r[*] Remove old build"
- @echo "[ ] Building"
- @sbcl --eval "(ql:quickload :lysk/bundle)" \
- --eval "(lysk.bundle:deliver)" \
- --quit
- @echo "[*] Building"
-
-.PHONY: bundle run
-#+END_SRC
diff --git a/site/posts/EndOfPhd.md b/site/posts/EndOfPhd.md
new file mode 100644
index 0000000..0b5a445
--- /dev/null
+++ b/site/posts/EndOfPhd.md
@@ -0,0 +1,81 @@
+---
+published: 2019-01-15
+tags: ['research']
+abstract: |
+ It has been a long journey —4 years, 10 days— but I have completed my PhD
+ on October 25, 2018.
+
+---
+
+# I am no longer a PhD. student
+
+It has been a long journey —4 years, 10 days— but I have completed my PhD on
+October 25, 2018. The exact title of my PhD thesis is “[*Specifying and
+Verifying Hardware-based Security Enforcement
+Mechanisms*](https://inria.hal.science/tel-01989940v2/file/2018_LETAN_archivage.pdf)”.
+
+## Abstract
+
+In this thesis, we consider a class of security enforcement mechanisms we
+called *Hardware-based Security Enforcement* (HSE). In such mechanisms, some
+trusted software components rely on the underlying hardware architecture to
+constrain the execution of untrusted software components with respect to
+targeted security policies. For instance, an operating system which configures
+page tables to isolate userland applications implements a HSE mechanism.
+
+For a HSE mechanism to correctly enforce a targeted security policy, it
+requires both hardware and trusted software components to play their parts.
+During the past decades, several vulnerability disclosures have defeated HSE
+mechanisms. We focus on the vulnerabilities that are the result of errors at
+the specification level, rather than implementation errors. In some critical
+vulnerabilities, the attacker makes a legitimate use of one hardware component
+to circumvent the HSE mechanism provided by another one. For instance, cache
+poisoning attacks leverage inconsistencies between cache and DRAM’s access
+control mechanisms. We call this class of attacks, where an attacker leverages
+inconsistencies in hardware specifications, *compositional attacks*.
+
+Our goal is to explore approaches to specify and verify HSE mechanisms using
+formal methods that would benefit both hardware designers and software
+developers. Firstly, a formal specification of HSE mechanisms can be leveraged
+as a foundation for a systematic approach to verify hardware specifications, in
+the hope of uncovering potential compositional attacks ahead of time. Secondly,
+it provides unambiguous specifications to software developers, in the form of a
+list of requirements.
+
+Our contribution is two-fold:
+
+1. We propose a theory of HSE mechanisms against hardware architecture models.
+ This theory can be used to specify and verify such mechanisms. To evaluate
+ our approach, we propose a minimal model for a single core x86-based
+ computing platform. We use it to specify and verify the HSE mechanism
+ provided by Intel to isolate the code executed while the CPU is in System
+ Management Mode (SMM), a highly privileged execution mode of x86
+ microprocessors. We have written machine-checked proofs in the Coq proof
+ assistant to that end.
+2. We propose a novel approach inspired by algebraic effects to enable modular
+ verification of complex systems made of interconnected components as a first
+ step towards addressing the challenge posed by the scale of the x86 hardware
+ architecture. This approach is not specific to hardware models, and could
+ also be leveraged to reason about composition of software components as
+ well. In addition, we have implemented our approach in the Coq theorem
+ prover, and the resulting framework takes advantages of Coq proof automation
+ features to provide general-purpose facilities to reason about components
+ interactions.
+
+## Publicatinos
+
+If you are interested, you can have a look at the paper I wrote during my PhD:
+
+- [SpecCert: Specifying and Verifying Hardware-based Security Enforcement
+ Mechanisms](https://inria.hal.science/hal-01361422v1/file/speccert-fm2016.pdf),
+ with Pierre Chifflier, Guillame Hiet and Benjamin Morin, at Formal Methods
+ 2016
+- [Modular Verification of Programs with Effects and Effect Handlers in
+ Coq](https://inria.hal.science/hal-01799712v1/file/main.pdf), with Yann
+ Régis-Gianas, Pierre Chifflier and Guillaume Hiet, at Formal Methods 2018
+
+You can also have a look at the Coq frameworks I have published:
+
+- [SpecCert on Github](https://github.com/lthms/speccert) (CeCILL-B)
+- [FreeSpec on Github](https://github.com/lthms/FreeSpec) (MPL-2.0)
+
diff --git a/site/posts/ExtensibleTypeSafeErrorHandling.md b/site/posts/ExtensibleTypeSafeErrorHandling.md
new file mode 100644
index 0000000..7e6e747
--- /dev/null
+++ b/site/posts/ExtensibleTypeSafeErrorHandling.md
@@ -0,0 +1,420 @@
+---
+published: 2018-02-04
+modified: 2023-05-08
+tags: ['haskell']
+abstract: |
+ Ever heard of “extensible effects?” By applying the same principle, but for
+ error handling, the result is nice, type-safe API for Haskell, with a lot
+ of GHC magic under the hood.
+---
+
+# Extensible Type-Safe Error Handling in Haskell
+
+A colleague of mine introduced me to the benefits of
+[`error-chain`](https://crates.io/crates/error-chain), a crate which aims to
+implement “*consistent error handling*” for Rust. I found the overall design
+pretty convincing, and in his use case, the crate really makes error handling
+clearer and flexible. I knew [Pijul](https://pijul.org) was also using
+`error-chain` at that time, but I never had the opportunity to dig more into it.
+
+At the same time, I have read quite a lot about *extensible effects* in
+Functional Programming, for an academic article I have submitted to [Formal
+Methods 2018](http://www.fm2018.org)[^fm2018]. In particular, the
+[freer](https://hackage.haskell.org/package/freer) package provides a very nice
+API to define monadic functions which may use well-identified effects. For
+instance, we can imagine that `Console`{.haskell} identifies the functions
+which may print to and read from the standard output. A function
+`askPassword`{.haskell} which displays a prompt and get the user password would
+have this type signature:
+
+[^fm2018]: The odds were in my favour: the aforementioned academic article has
+ been accepted.
+
+```haskell
+askPassword :: Member Console r => Eff r ()
+```
+
+Compared to `IO`{.haskell}, `Eff`{.haskell} allows for meaningful type
+signatures. It becomes easier to reason about function composition, and you
+know that a given function which lacks a given effect in its type signature
+will not be able to use them. As a predictable drawback, `Eff`{.haskell} can
+become burdensome to use.
+
+Basically, when my colleague showed me his Rust project and how he was using
+`error-chain`, the question popped out. *Can we use an approach similar to
+`Eff`{.haskell} to implement a Haskell-flavoured `error-chain`?*
+
+Spoiler alert: the answer is yes. In this post, I will dive into the resulting
+API, leaving for another time the details of the underlying implementation[^api].
+Believe me, there is plenty to say. If you want to have a look already, the
+current implementation can be found on
+[GitHub](https://github.com/lthms/chain).
+
+[^api]: For once, I wanted to write about the *result* of a project, instead of
+ *how it is implemented*.
+
+In this article, I will use several “advanced” GHC pragmas. I will not explain
+each of them, but I will *try* to give some pointers for the reader who wants
+to learn more.
+
+
+## State of the Art
+
+This is not an academic publication, and my goal was primarily to explore the
+arcane of the Haskell type system, so I might have skipped the proper study of
+the state of the art. That being said, I have written programs in Rust and
+Haskell before.
+
+### Starting Point
+
+In Rust, `Result<T, E>`{.rust} is the counterpart of `Either E T`{.haskell} in
+Haskell[^either]. You can use it to model to wrap either the result of a
+function (`T`) or an error encountered during this computation (~E~). Both
+`Either`{.haskell} and `Result`{.rust} are used in order to achieve the same
+end, that is writing functions which might fail.
+
+[^either]: I wonder if they deliberately choose to swap the two type arguments.
+
+On the one hand, `Either E`{.haskell} is a monad. It works exactly as
+`Maybe`{.haskell} (returning an error acts as a shortcut for the rest of the
+function), but gives you the ability to specify *why* the function has failed.
+To deal with effects, the `mtl` package provides `EitherT`{.haskell}, a
+transformer version of `Either`{.haskell} to be used in a monad stack.
+
+On the other hand, the Rust language provides the `?`{.rust} syntactic sugar,
+to achieve the same thing. That is, both languages provide you the means to
+write potentially failing functions without the need to care locally about
+failure. If your function `f` uses a function `g` which might fail, and want to
+fail yourself if `f` fails, it becomes trivial.
+
+Out of the box, neither `EitherT`{.haskell} nor `Result`{.rust} is extensible.
+The functions must use the exact same `E`, or errors must be converted
+manually.
+
+### Handling Errors in Rust
+
+Rust and the `error-chain` crate provide several means to overcome this
+limitation. In particular, it has the `Into`{.rust} and `From`{.rust} traits to
+ease the conversion from one error to another. Among other things, the
+`error-chain` crate provides a macro to easily define a wrapper around many
+errors types, basically your own and the one defined by the crates you are
+using.
+
+I see several drawbacks to this approach. First, it is extensible if you take
+the time to modify the wrapper type each time you want to consider a new error
+type. Second, either you can either use one error type or every error
+type.
+
+However, the `error-chain` package provides a way to solve a very annoying
+limitation of `Result`{.rust} and `Either`{.haskell}. When you “catch” an
+error, after a given function returns its result, it can be hard to determine
+from where the error is coming from. Imagine you are parsing a very complicated
+source file, and the error you get is `SyntaxError`{.rust} with no additional
+context. How would you feel?
+
+`error-chain` solves this by providing an API to construct a chain of errors,
+rather than a single value.
+
+```rust
+my_function().chain_err(|| "a message with some context")?;
+```
+
+The `chain_err` function makes it easier to replace a given error in its
+context, leading to be able to write more meaningful error messages for
+instance.
+
+## The `ResultT`{.haskell} Monad
+
+The `ResultT`{.haskell} is an attempt to bring together the extensible power of
+`Eff`{.haskell} and the chaining of errors of `chain_err`. I will admit that,
+for the latter, the current implementation of `ResultT`{.haskell} is probably
+less powerful, but to be honest I mostly cared about the “extensible” thing, so
+it is not very surprising.
+
+This monad is not an alternative to neither Monad Stacks a la mtl nor to the
+`Eff`{.haskell} monad. In its current state, it aims to be a more powerful and
+flexible version of `EitherT`{.haskell}.
+
+### Parameters
+
+As often in Haskell, the `ResultT`{.haskell} monad can be parameterised in
+several ways.
+
+```haskell
+data ResultT msg (err :: [*]) m a
+```
+
+- `msg`{.haskell} is the type of messages you can stack to provide more context
+ to error handling
+- `err`{.haskell} is a *row of errors*[^row], it basically describes the set of
+ errors you will eventually have to handle
+- `m`{.haskell} is the underlying monad stack of your application, knowing that
+ `ResultT`{.haskell} is not intended to be stacked itself
+- `a`{.haskell} is the expected type of the computation result
+
+[^row]: You might have notice `err`{.haskell} is of kind `[*]`{.haskell}. To write such a thing,
+ you will need the
+ [`DataKinds`{.haskell}](https://www.schoolofhaskell.com/user/konn/prove-your-haskell-for-great-safety/dependent-types-in-haskell)
+ GHC pragmas.
+
+### `achieve`{.haskell} and `abort`{.haskell}
+
+The two main monadic operations which comes with ~ResultT~ are ~achieve~ and
+~abort~. The former allows for building the context, by stacking so-called
+messages which describe what you want to do. The latter allows for bailing on a
+computation and explaining why.
+
+```haskell
+achieve :: (Monad m)
+ => msg
+ -> ResultT msg err m a
+ -> ResultT msg err m a
+```
+
+`achieve`{.haskell} should be used for `do`{.haskell} blocks. You can use
+`<?>`{.haskell} to attach a contextual message to a given computation.
+
+The type signature of `abort`{.haskell} is also interesting, because it
+introduces the `Contains`{.haskell} typeclass (e.g., it is equivalent to
+`Member`{.haskell} for `Eff`{.haskell}).
+
+```haskell
+abort :: (Contains err e, Monad m)
+ => e
+ -> ResultT msg err m a
+```
+
+This reads as follows: “*you can abort with an error of type `e`{.haskell} if
+and only if the row of errors `err`{.haskell} contains the type
+`e`{.haskell}.*”
+
+For instance, imagine we have an error type `FileError`{.haskell} to describe
+filesystem-related errors. Then, we can imagine the following function:
+
+```haskell
+readContent :: (Contains err FileError, MonadIO m)
+ => FilePath
+ -> ResultT msg err m String
+```
+
+We could leverage this function in a given project, for instance to read its
+configuration files (for the sake of the example, it has several configuration
+files). This function can use its own type to describe ill-formed description
+(`ConfigurationError`{.haskell}).
+
+```haskell
+parseConfiguration :: (Contains err ConfigurationError, MonadIO m)
+ => String
+ -> String
+ -> ResultT msg err m Configuration
+```
+
+To avoid repeating `Contains`{.haskell} when the row of errors needs to
+contains several elements, we introduce `:<`{.haskell}[^top] (read *subset or
+equal*):
+
+```haskell
+getConfig :: ( '[FileError, ConfigurationError] :< err
+ , MonadIO m)
+ => ResultT String err m Configuration
+getConfig = do
+ achieve "get configuration from ~/.myapp directory" $ do
+ f1 <- readContent "~/.myapp/init.conf"
+ <?> "fetch the main configuration"
+ f2 <- readContent "~/.myapp/net.conf"
+ <?> "fetch the net-related configuration"
+
+ parseConfiguration f1 f2
+```
+
+You might see, now, why I say ~ResultT~ is extensible. You can use two functions
+with totally unrelated errors, as long as the caller advertises that with
+~Contains~ or ~:<~.
+
+[^top]: If you are confused by `:<`{.haskell}, it is probably because you were
+ not aware that the
+ [`TypeOperators`{.haskell}](https://ocharles.org.uk/blog/posts/2014-12-08-type-operators.html)
+ GHC pragma was a thing.
+
+### Recovering by Handling Errors
+
+Monads are traps, you can only escape them by playing with their
+rules. `ResultT`{.haskell} comes with `runResultT`{.haskell}.
+
+```haskell
+runResultT :: Monad m => ResultT msg '[] m a -> m a
+```
+
+This might be surprising: we can only escape out from the `ResultT`{.haskell}
+if we do not use *any errors at all*. That is, `ResultT`{.haskell} forces us to
+handle errors before calling `runResultT`{.haskell}.
+
+`ResultT`{.haskell} provides several functions prefixed by `recover`{.haskell}.
+Their type signatures can be a little confusing, so we will dive into the
+simpler one:
+
+```haskell
+recover :: forall e m msg err a.
+ (Monad m)
+ => ResultT msg (e ': err) m a
+ -> (e -> [msg] -> ResultT msg err m a)
+ -> ResultT msg err m a
+```
+
+`recover`{.haskell} allows for *removing* an error type from the row of errors,
+To do that, it requires to provide an error handler to determine what to do
+with the error raised during the computation and the stack of messages at that
+time. Using `recover`{.haskell}, a function may use more errors than advertised
+in its type signature, but we know by construction that in such a case, it
+handles these errors so that it is transparent for the function user. The type
+of the handler is `e -> [msg] -> ResultT msg err m a`{.haskell}, which means
+the handler *can raise errors if required*.
+
+`recoverWhile msg`{.haskell} is basically a synonym for `achieve msg $
+recover`{.haskell}. `recoverMany`{.haskell} allows for doing the same with a
+row of errors, by providing as many functions as required. Finally,
+`recoverManyWith`{.haskell} simplifies `recoverMany`{.haskell}: you can provide
+only one function tied to a given typeclass, on the condition that the handling
+errors implement this typeclass.
+
+Using `recover`{.haskell} and its siblings often requires to help a bit the
+Haskell type system, especially if we use lambdas to define the error handlers.
+Doing that is usually achieved with the `Proxy a`{.haskell} dataype (where
+`a`{.haskell} is a phantom type). I would rather use the
+`TypeApplications`{.haskell} pragma[^tap].
+
+```haskell
+recoverManyWith @[FileError, NetworkError] @DescriptiveError
+ (do x <- readFromFile f
+ y <- readFromNetwork socket
+ printToStd x y)
+ printErrorAndStack
+```
+
+The `DecriptiveError`{.haskell} typeclass can be seen as a dedicated
+`Show`{.haskell}, to give textual representation of errors. It is inspired by
+the macros of `error_chain`.
+
+We can start from an empty row of errors, and allows ourselves to
+use more errors thanks to the `recover*` functions.
+
+[^tap]: The
+ [TypeApplications](https://medium.com/@zyxoas/abusing-haskell-dependent-types-to-make-redis-queues-safer-cc31db943b6c)
+ pragmas is probably one of my favourites.
+
+ When I use it, it feels almost like if I were writing a Coq document.
+
+## `cat` in Haskell using `ResultT`{.haskell}
+
+`ResultT`{.haskell} only cares about error handling. The rest of the work is up
+to the underlying monad `m`{.haskell}. That being said, nothing forbids us to
+provide fine-grained API, *e.g.*, for Filesystem-related functions. From an
+error handling perspective, the functions provided by Prelude (the standard
+library of Haskell) are pretty poor, and the documentation is not really
+precise regarding the kind of error we can encounter while using it.
+
+In this section, I will show you how we can leverage `ResultT`{.haskell} to
+**(i)** define an error-centric API for basic file management functions and
+**(ii)** use this API to implement a `cat`-like program which read a file and
+print its content in the standard output.
+
+### (A Lot Of) Error Types
+
+We could have one sum type to describe in the same place all the errors we can
+find, and later use the pattern matching feature of Haskell to determine which
+one has been raised. The thing is, this is already the job done by the row of
+errors of ~ResultT~. Besides, this means that we could raise an error for being
+not able to write something into a file in a function which /opens/ a file.
+
+Because ~ResultT~ is intended to be extensible, we should rather define several
+types, so we can have a fine-grained row of errors. Of course, too many types
+will become burdensome, so this is yet another time where we need to find the
+right balance.
+
+```haskell
+newtype AlreadyInUse = AlreadyInUse FilePath
+newtype DoesNotExist = DoesNotExist FilePath
+data AccessDeny = AccessDeny FilePath IO.IOMode
+data EoF = EoF
+data IllegalOperation = IllegalRead | IllegalWrite
+```
+
+To be honest, this is a bit too much for the real life, but we are in a blog post
+here, so we should embrace the potential of `ResultT`{.haskell}.
+
+### Filesystem API
+
+By reading the
+[`System.IO`{.haskell}](https://hackage.haskell.org/package/base-4.9.1.0/docs/System-IO.html)
+documentation, we can infer what our functions type signatures should look
+like. I will not discuss their actual implementation in this article, as this
+requires me to explain how `IO`{.haskell} deals with errors itself (and this
+article is already long enough to my taste). You can have a look at [this
+gist](https://gist.github.com/lthms/c669e68e284a056dc8c0c3546b4efe56) if you
+are interested.
+
+```haskell
+openFile :: ( '[AlreadyInUse, DoesNotExist, AccessDeny] :< err
+ , MonadIO m)
+ => FilePath -> IOMode -> ResultT msg err m Handle
+
+getLine :: ('[IllegalOperation, EoF] :< err, MonadIO m)
+ => IO.Handle
+ -> ResultT msg err m Text
+
+closeFile :: (MonadIO m)
+ => IO.Handle
+ -> ResultT msg err m ()
+```
+
+### Implementing `cat`
+
+We can use the `ResultT`{.haskell} monad, its monadic operations and our
+functions to deal with the file system in order to implement a `cat`-like
+program. I tried to comment on the implementation to make it easier to follow.
+
+```haskell
+cat :: FilePath -> ResultT String err IO ()
+cat path =
+ -- We will try to open and read this file to mimic
+ -- `cat` behaviour.
+ -- We advertise that in case something goes wrong
+ -- the process.
+ achieve ("cat " ++ path) $ do
+ -- We will recover from a potential error,
+ -- but we will abstract away the error using
+ -- the `DescriptiveError` typeclass. This way,
+ -- we do not need to give one handler by error
+ -- type.
+ recoverManyWith @[Fs.AlreadyInUse, Fs.DoesNotExist, Fs.AccessDeny, Fs.IllegalOperation]
+ @(Fs.DescriptiveError)
+ (do f <- Fs.openFile path Fs.ReadMode
+ -- `repeatUntil` works like `recover`, except
+ -- it repeats the computation until the error
+ -- actually happpens.
+ -- I could not have used `getLine` without
+ -- `repeatUntil` or `recover`, as it is not
+ -- in the row of errors allowed by
+ -- `recoverManyWith`.
+ repeatUntil @(Fs.EoF)
+ (Fs.getLine f >>= liftIO . print)
+ (\_ _ -> liftIO $ putStrLn "%EOF")
+ closeFile f)
+ printErrorAndStack
+ where
+ -- Using the `DescriptiveError` typeclass, we
+ -- can print both the stack of Strings which form
+ -- the context, and the description of the generic
+ -- error.
+ printErrorAndStack e ctx = do
+ liftIO . putStrLn $ Fs.describe e
+ liftIO $ putStrLn "stack:"
+ liftIO $ print ctx
+```
+
+The type signature of `cat`{.haskell} teaches us that this function handles any
+error it might encounter. This means we can use it anywhere we want: both in
+another computation inside `ResultT`{.haskell} which might raise errors
+completely unrelated to the file system, or we can use it with
+`runResultT`{.haskell}, escaping the `ResultT`{.haskell} monad (only to fall
+into the `IO`{.haskell} monad, but this is another story).
diff --git a/site/posts/ExtensibleTypeSafeErrorHandling.org b/site/posts/ExtensibleTypeSafeErrorHandling.org
deleted file mode 100644
index 67ad3b1..0000000
--- a/site/posts/ExtensibleTypeSafeErrorHandling.org
+++ /dev/null
@@ -1,398 +0,0 @@
-#+TITLE: Extensible Type-Safe Error Handling in Haskell
-
-#+SERIES: ./haskell.html
-
-#+BEGIN_EXPORT html
-<p>This article has originally been published on <span
-id="original-created-at">February 04, 2018</span>.</p>
-#+END_EXPORT
-
-#+BEGIN_EXPORT html
-<nav id="generate-toc"></nav>
-<div id="history">site/posts/ExtensibleTypeSafeErrorHandling.org</div>
-#+END_EXPORT
-
-A colleague of mine introduced me to the benefits of [[https://crates.io/crates/error-chain][~error-chain~]], a crate which
-aims to implement /“consistent error handling”/ for Rust. I found the overall
-design pretty convincing, and in his use case, the crate really makes its error
-handling clearer and flexible. I knew /pijul/ uses ~error-chain~ to, but I never
-had the occasion to dig more into it.
-
-At the same time, I have read quite a lot about /extensible effects/ in
-Functional Programming, for an academic article I have submitted to
-[[http://www.fm2018.org][Formal Methods 2018]][fn:fm2018]. In particular, the [[https://hackage.haskell.org/package/freer][freer]] package provides a very
-nice API to define monadic functions which may use well-identified effects. For
-instance, we can imagine that ~Console~ identifies the functions which may print
-to and read from the standard output. A function ~askPassword~ which displays a
-prompt and get the user password would have this type signature:
-
-#+BEGIN_SRC haskell
-askPassword :: Member Console r => Eff r ()
-#+END_SRC
-
-Compared to ~IO~, ~Eff~ allows for meaningful type signatures. It becomes easier
-to reason about function composition, and you know that a given function which
-lacks a given effect in its type signature will not be able to use them. As a
-predictable drawback, ~Eff~ can become burdensome to use.
-
-Basically, when my colleague showed me its Rust project and how he was using
-~error-chain~, the question popped out. *Can we use an approach similar to ~Eff~
-to implement a Haskell-flavoured ~error-chain~?*
-
-Spoiler alert: the answer is yes. In this post, I will dive into the resulting
-API, leaving for another time the details of the underlying
-implementation. Believe me, there is plenty to say. If you want to have a look
-already, the current implementation can be found on [[https://github.com/lethom/chain][GitHub]].
-
-In this article, I will use several “advanced” GHC pragmas. I will not explain
-each of them, but I will /try/ to give some pointers for the reader who wants to
-learn more.
-
-[fn:fm2018] If the odds are in my favour, I will have plenty of occasions to write
-more about this topic.
-
-* State of the Art
-
-This is not an academic publication, and my goal was primarily to explore the
-arcane of the Haskell type system, so I might have skipped the proper study of
-the state of the art. That being said, I have written programs in Rust and
-Haskell before.
-
-** Starting Point
-
-In Rust, ~Result<T, E>~ is the counterpart of ~Either E T~ in
-Haskell[fn:either]. You can use it to model to wrap either the result of a
-function (~T~) or an error encountered during this computation (~E~).
-Both ~Either~ and ~Result~ are used in order to achieve the same end, that is
-writing functions which might fail.
-
-On the one hand, ~Either E~ is a monad. It works exactly as ~Maybe~ (returning
-an error acts as a shortcut for the rest of the function), but gives you the
-ability to specify /why/ the function has failed. To deal with effects, the
-~mtl~ package provides ~EitherT~, a transformer version of ~Either~ to be used
-in a monad stack.
-
-On the other hand, the Rust language provides the ~?~ syntactic sugar, to
-achieve the same thing. That is, both languages provide you the means to write
-potentially failing functions without the need to care locally about failure. If
-your function ~B~ uses a function ~A~ which might fail, and want to fail
-yourself if ~A~ fails, it becomes trivial.
-
-Out of the box, neither ~EitherT~ nor ~Result~ is extensible. The functions must
-use the exact same ~E~, or errors must be converted manually.
-
-[fn:either] I wonder if they deliberately choose to swap the two type arguments.
-
-** Handling Errors in Rust
-
-Rust and the ~error-chain~ crate provide several means to overcome this
-limitation. In particular, it has the ~Into~ and ~From~ traits to ease the
-conversion from one error to another. Among other things, the ~error-chain~
-crate provides a macro to easily define a wrapper around many errors types,
-basically your own and the one defined by the crates you are using.
-
-I see several drawbacks to this approach. First, it is extensible if you take
-the time to modify the wrapper type each time you want to consider a new error
-type. Second, either you can either use one error type or every error
-type.
-
-However, the ~error-chain~ package provides a way to solve a very annoying
-limitation of ~Result~ and ~Either~. When you “catch” an error, after a given
-function returns its result, it can be hard to determine from where the error is
-coming from. Imagine you are parsing a very complicated source file, and the
-error you get is ~SyntaxError~ with no additional context. How would you feel?
-
-~error-chain~ solves this by providing an API to construct a chain of errors,
-rather than a single value.
-
-#+BEGIN_SRC rust
-my_function().chain_err(|| "a message with some context")?;
-#+END_SRC
-
-The ~chain_err~ function makes it easier to replace a given error in its
-context, leading to be able to write more meaningful error messages for
-instance.
-
-* The ResultT Monad
-
-The ~ResultT~ is an attempt to bring together the extensible power of ~Eff~ and
-the chaining of errors of ~chain_err~. I will admit that, for the latter, the
-current implementation of ~ResultT~ is probably less powerful, but to be honest
-I mostly cared about the “extensible” thing, so it is not very surprising.
-
-This monad is not an alternative to neither Monad Stacks a la mtl nor to the
-~Eff~ monad. In its current state, it aims to be a more powerful and flexible
-version of ~EitherT~.
-
-** Parameters
-
-As often in Haskell, the ~ResultT~ monad can be parameterised in several ways.
-
-#+BEGIN_SRC haskell
-data ResultT msg (err :: [*]) m a
-#+END_SRC
-
-- ~msg~ is the type of messages you can stack to provide more context to error
- handling
-- ~err~ is a /row of errors/[fn:row], it basically describes the set of errors
- you will eventually have to handle
-- ~m~ is the underlying monad stack of your application, knowing that ~ResultT~
- is not intended to be stacked itself
-- ~a~ is the expected type of the computation result
-
-[fn:row] You might have notice ~err~ is of kind ~[*]~. To write such a thing,
-you will need the [[https://www.schoolofhaskell.com/user/konn/prove-your-haskell-for-great-safety/dependent-types-in-haskell][DataKinds]] GHC pragmas.
-
-** ~achieve~ and ~abort~
-
-The two main monadic operations which comes with ~ResultT~ are ~achieve~ and
-~abort~. The former allows for building the context, by stacking so-called
-messages which describe what you want to do. The latter allows for bailing on a
-computation and explaining why.
-
-#+BEGIN_SRC haskell
-achieve :: (Monad m)
- => msg
- -> ResultT msg err m a
- -> ResultT msg err m a
-#+END_SRC
-
-~achieve~ should be used for ~do~ blocks. You can use ~<?>~ to attach a
-contextual message to a given computation.
-
-The type signature of ~abort~ is also interesting, because it introduces the
-~Contains~ typeclass (e.g., it is equivalent to ~Member~ for ~Eff~).
-
-#+BEGIN_SRC haskell
-abort :: (Contains err e, Monad m)
- => e
- -> ResultT msg err m a
-#+END_SRC
-
-This reads as follows: /“you can abort with an error of type ~e~ if and only if
-the row of errors ~err~ contains the type ~e~.”/
-
-For instance, imagine we have an error type ~FileError~ to describe
-filesystem-related errors. Then, we can imagine the following function:
-
-#+BEGIN_SRC haskell
-readContent :: (Contains err FileError, MonadIO m)
- => FilePath
- -> ResultT msg err m String
-#+END_SRC
-
-We could leverage this function in a given project, for instance to read its
-configuration files (for the sake of the example, it has several configuration
-files). This function can use its own type to describe ill-formed description
-(~ConfigurationError~).
-
-#+BEGIN_SRC haskell
-parseConfiguration :: (Contains err ConfigurationError, MonadIO m)
- => String
- -> String
- -> ResultT msg err m Configuration
-#+END_SRC
-
-To avoid repeating ~Contains~ when the row of errors needs to contains several
-elements, we introduce ~:<~[fn:top] (read /subset or equal/):
-
-#+BEGIN_SRC haskell
-getConfig :: ( '[FileError, ConfigurationError] :< err
- , MonadIO m)
- => ResultT String err m Configuration
-getConfig = do
- achieve "get configuration from ~/.myapp directory" $ do
- f1 <- readContent "~/.myapp/init.conf"
- <?> "fetch the main configuration"
- f2 <- readContent "~/.myapp/net.conf"
- <?> "fetch the net-related configuration"
-
- parseConfiguration f1 f2
-#+END_SRC
-
-You might see, now, why I say ~ResultT~ is extensible. You can use two functions
-with totally unrelated errors, as long as the caller advertises that with
-~Contains~ or ~:<~.
-
-[fn:top] If you are confused by ~:<~, it is probably because you were not aware
-of the [[https://ocharles.org.uk/blog/posts/2014-12-08-type-operators.html][TypeOperators]] before. Maybe it was for the best. :D
-
-** Recovering by Handling Errors
-
-Monads are traps, you can only escape them by playing with their
-rules. ~ResultT~ comes with ~runResultT~.
-
-#+BEGIN_SRC haskell
-runResultT :: Monad m => ResultT msg '[] m a -> m a
-#+END_SRC
-
-This might be surprising: we can only escape out from the ~ResultT~ if we do not
-use /any errors at all/. In fact, ~ResultT~ forces us to handle errors before
-calling ~runResultT~.
-
-~ResultT~ provides several functions prefixed by ~recover~. Their type
-signatures can be a little confusing, so we will dive into the simpler one:
-
-#+BEGIN_SRC haskell
-recover :: forall e m msg err a.
- (Monad m)
- => ResultT msg (e ': err) m a
- -> (e -> [msg] -> ResultT msg err m a)
- -> ResultT msg err m a
-#+END_SRC
-
-~recover~ allows for /removing/ an error type from the row of errors, To do
-that, it requires to provide an error handler to determine what to do with the
-error raised during the computation and the stack of messages at that
-time. Using ~recover~, a function may use more errors than advertised in its
-type signature, but we know by construction that in such a case, it handles
-these errors so that it is transparent for the function user. The type of the
-handler is ~e -> [msg] -> ResultT msg err m a~, which means the handler /can
-raise errors if required/. ~recoverWhile msg~ is basically a synonym for
-~achieve msg $ recover~. ~recoverMany~ allows for doing the same with a row of
-errors, by providing as many functions as required. Finally, ~recoverManyWith~
-simplifies ~recoverMany~: you can provide only one function tied to a given
-typeclass, on the condition that the handling errors implement this typeclass.
-
-Using ~recover~ and its siblings often requires to help a bit the Haskell
-type system, especially if we use lambdas to define the error handlers. Doing
-that is usually achieved with the ~Proxy a~ dataype (where ~a~ is a phantom
-type). I would rather use the TypeApplications[fn:tap] pragma.
-
-#+BEGIN_SRC haskell
-recoverManyWith @[FileError, NetworkError] @DescriptiveError
- (do x <- readFromFile f
- y <- readFromNetwork socket
- printToStd x y)
- printErrorAndStack
-#+END_SRC
-
-The ~DecriptiveError~ typeclass can be seen as a dedicated ~Show~, to give
-textual representation of errors. It is inspired by the macros of ~error_chain~.
-
-We can start from an empty row of errors, and allows ourselves to
-use more errors thanks to the ~recover*~ functions.
-
-[fn:tap] The [[https://medium.com/@zyxoas/abusing-haskell-dependent-types-to-make-redis-queues-safer-cc31db943b6c][TypeApplications]] pragmas is probably one of my favourites. When I
-use it, it feels almost like if I were writing some Gallina.
-
-* ~cat~ in Haskell using ResultT
-
-~ResultT~ only cares about error handling. The rest of the work is up to the
-underlying monad ~m~. That being said, nothing forbids us to provide
-fine-grained API for, e.g. Filesystem-related functions. From an error handling
-perspective, the functions provided by Prelude (the standard library of Haskell)
-are pretty poor, and the documentation is not really precise regarding the kind
-of error we can encounter while using it.
-
-In this section, I will show you how we can leverage ~ResultT~ to *(i)* define an
-error-centric API for basic file management functions and *(ii)* use this API to
-implement a ~cat~-like program which read a file and print its content in the
-standard output.
-
-** (A Lot Of) Error Types
-
-We could have one sum type to describe in the same place all the errors we can
-find, and later use the pattern matching feature of Haskell to determine which
-one has been raised. The thing is, this is already the job done by the row of
-errors of ~ResultT~. Besides, this means that we could raise an error for being
-not able to write something into a file in a function which /opens/ a file.
-
-Because ~ResultT~ is intended to be extensible, we should rather define several
-types, so we can have a fine-grained row of errors. Of course, too many types
-will become burdensome, so this is yet another time where we need to find the
-right balance.
-
-#+BEGIN_SRC haskell
-newtype AlreadyInUse = AlreadyInUse FilePath
-newtype DoesNotExist = DoesNotExist FilePath
-data AccessDeny = AccessDeny FilePath IO.IOMode
-data EoF = EoF
-data IllegalOperation = IllegalRead | IllegalWrite
-#+END_SRC
-
-To be honest, this is a bit too much for the real life, but we are in a blog post
-here, so we should embrace the potential of ~ResultT~.
-
-** Filesystem API
-
-By reading the [[https://hackage.haskell.org/package/base-4.9.1.0/docs/System-IO.html][System.IO]] documentation, we can infer what our functions type
-signatures should look like. I will not discuss their actual implementation in
-this article, as this requires me to explain how `IO` deals with errors itself
-(and this article is already long enough to my taste). You can have a look at
-[[https://gist.github.com/lethom/c669e68e284a056dc8c0c3546b4efe56][this gist]] if you are interested.
-
-#+BEGIN_SRC haskell
-openFile :: ( '[AlreadyInUse, DoesNotExist, AccessDeny] :< err
- , MonadIO m)
- => FilePath -> IOMode -> ResultT msg err m Handle
-#+END_SRC
-
-#+BEGIN_SRC haskell
-getLine :: ('[IllegalOperation, EoF] :< err, MonadIO m)
- => IO.Handle
- -> ResultT msg err m Text
-#+END_SRC
-
-#+BEGIN_SRC haskell
-closeFile :: (MonadIO m)
- => IO.Handle
- -> ResultT msg err m ()
-#+END_SRC
-
-** Implementing ~cat~
-
-We can use the ~ResultT~ monad, its monadic operations and our functions to deal
-with the file system in order to implement a ~cat~-like program. I tried to
-comment on the implementation to make it easier to follow.
-
-#+BEGIN_SRC haskell
-cat :: FilePath -> ResultT String err IO ()
-cat path =
- -- We will try to open and read this file to mimic
- -- `cat` behaviour.
- -- We advertise that in case something goes wrong
- -- the process.
- achieve ("cat " ++ path) $ do
- -- We will recover from a potential error,
- -- but we will abstract away the error using
- -- the `DescriptiveError` typeclass. This way,
- -- we do not need to give one handler by error
- -- type.
- recoverManyWith @[Fs.AlreadyInUse, Fs.DoesNotExist, Fs.AccessDeny, Fs.IllegalOperation]
- @(Fs.DescriptiveError)
- (do f <- Fs.openFile path Fs.ReadMode
- -- `repeatUntil` works like `recover`, except
- -- it repeats the computation until the error
- -- actually happpens.
- -- I could not have used `getLine` without
- -- `repeatUntil` or `recover`, as it is not
- -- in the row of errors allowed by
- -- `recoverManyWith`.
- repeatUntil @(Fs.EoF)
- (Fs.getLine f >>= liftIO . print)
- (\_ _ -> liftIO $ putStrLn "%EOF")
- closeFile f)
- printErrorAndStack
- where
- -- Using the `DescriptiveError` typeclass, we
- -- can print both the stack of Strings which form
- -- the context, and the description of the generic
- -- error.
- printErrorAndStack e ctx = do
- liftIO . putStrLn $ Fs.describe e
- liftIO $ putStrLn "stack:"
- liftIO $ print ctx
-#+END_SRC
-
-The type system of ~cat~ teaches us that this function handles any error it
-might encounter. This means we can use it anywhere we want… in another
-computation inside ~ResultT~ which might raise errors completely unrelated to
-the file system, for instance. Or! We can use it with ~runResultT~, escaping the
-~ResultT~ monad (only to fall into the ~IO~ monad, but this is another story).
-
-* Conclusion
-
-For once, I wanted to write about the /result/ of a project, instead of /how it
-is implemented/. Rest assured, I do not want to skip the latter. I need to clean
-up a bit the code before bragging about it.
diff --git a/site/posts/Ltac.org b/site/posts/Ltac.org
deleted file mode 100644
index 3dffb66..0000000
--- a/site/posts/Ltac.org
+++ /dev/null
@@ -1,34 +0,0 @@
-#+TITLE: A Series on Ltac
-
-#+SERIES: ./coq.html
-#+SERIES_PREV: ./StronglySpecifiedFunctions.html
-#+SERIES_NEXT: ./RewritingInCoq.html
-
-Ltac is the “tactic language” of Coq. It is commonly advertised as the common
-approach to write proofs, which tends to bias how it is introduced to
-new Coq users[fn::I know /I/ was introduced to Coq in a similar way in
-my Master courses.]. In this series, we present Ltac as the
-metaprogramming tool it is, since fundamentally it is an imperative
-language which allows for constructing Coq terms interactively and
-incrementally.
-
-- [[./LtacMetaprogramming.html][Ltac is an Imperative Metaprogramming Language]] ::
- Ltac generates terms, therefore it is a metaprogramming language. It does it
- incrementally, by using primitives to modifying an implicit state, therefore
- it is an imperative language. Henceforth, it is an imperative metaprogramming
- language.
-
-- [[./LtacPatternMatching.html][Pattern Matching on Types and Contexts]] ::
- Ltac allows for pattern matching on types and contexts. In this article, we
- give a short introduction on this feature of key importance. Ltac programs
- (“proof scripts”) generate terms, and the shape of said terms can be very
- different regarding the initial context. For instance, ~induction x~ will
- refine the current goal using an inductive principle dedicated to the type of
- ~x~.
-
-- [[./MixingLtacAndGallina.html][Mixing Ltac and Gallina Together for Fun and Profit]] ::
- One of the most misleading introduction to Coq is to say that “Gallina is for
- programs, while tactics are for proofs.” Gallina is the preferred way to
- construct programs, and tactics are the preferred way to construct proofs.
- The key word here is “preferred.” Coq actually allows for /mixing/
- Ltac and Gallina together.
diff --git a/site/posts/LtacMetaprogramming.md b/site/posts/LtacMetaprogramming.md
new file mode 100644
index 0000000..b98692e
--- /dev/null
+++ b/site/posts/LtacMetaprogramming.md
@@ -0,0 +1,279 @@
+---
+published: 2020-08-28
+modified: 2023-05-08
+tags: ['coq']
+series:
+ parent: series/Ltac.html
+ next: posts/LtacPatternMatching.html
+abstract: |
+ Ltac generates terms, therefore it is a metaprogramming language. It does
+ it incrementally, by using primitives to modifying an implicit state,
+ therefore it is an imperative language. Henceforth, it is an imperative
+ metaprogramming language.
+---
+
+# Ltac is an Imperative Metaprogramming Language
+
+Coq is often depicted as an _interactive_ proof assistant, thanks to its
+proof environment. Inside the proof environment, Coq presents the user a
+goal, and said user solves said goal by means of tactics which describes a
+logical reasoning. For instance, to reason by induction, one can use the
+`induction`{.coq} tactic, while a simple case analysis can rely on the
+`destruct`{.coq} or `case_eq`{.coq} tactics, etc. It is not uncommon for new
+Coq users to be introduced to Ltac, the Coq default tactic language, using this
+proof-centric approach. This is not surprising, since writing proofs remains
+the main use-case for Ltac. In practice though, this discourse remains an
+abstraction which hides away what actually happens under the hood when Coq
+executes a proof scripts.
+
+To really understand what Ltac is about, we need to recall ourselves that
+Coq kernel is mostly a typechecker. A theorem statement is expressed as a
+“type” (which lives in a dedicated sort called `Prop`{.coq}), and a proof of
+this theorem is a term of this type, just like the term `S (S O)`{.coq} ($2$)
+is of type `nat`{.coq}. Proving a theorem in Coq requires to construct a term
+of the type encoding said theorem, and Ltac allows for incrementally
+constructing this term, one step at a time.
+
+Ltac generates terms, therefore it is a metaprogramming language. It does it
+incrementally, by using primitives to modifying an implicit state, therefore
+it is an imperative language. Henceforth, it is an imperative
+metaprogramming language.
+
+To summarize, a goal presented by Coq inside the environment proof is a hole
+within the term being constructed. It is presented to users as:
+
+- A list of “hypotheses,” which are nothing more than the variables
+ in the scope of the hole
+- A type, which is the type of the term to construct to fill the hole
+
+We illustrate what happens under the hood of Coq executes a simple proof
+script. One can use the `Show Proof`{.coq} vernacular command to exhibit
+this.
+
+We illustrate how Ltac works with the following example.
+
+```coq
+Theorem add_n_O : forall (n : nat), n + O = n.
+Proof.
+```
+
+The `Proof`{.coq} vernacular command starts the proof environment. Since no
+tactic has been used, the term we are trying to construct consists solely in a
+hole, while Coq presents us a goal with no hypothesis, and with the exact type
+of our theorem, that is `forall (n : nat), n + O = n`{.coq}.
+
+A typical Coq course will explain students the `intro`{.coq} tactic allows for
+turning the premise of an implication into an hypothesis within the context.
+
+$$C \vdash P \rightarrow Q$$
+
+becomes
+
+$$C,\ P \vdash Q$$
+
+This is a fundamental rule of deductive reasoning, and `intro`{.coq} encodes it.
+It achieves this by refining the current hole into an anymous function.
+When we use
+
+```coq
+ intro n.
+```
+
+it refines the term
+
+```coq
+ ?Goal1
+```
+
+into
+
+```coq
+ fun (n : nat) => ?Goal2
+```
+
+The next step of this proof is to reason about induction on [n]. For [nat],
+it means that to be able to prove
+
+$$\forall n, \mathrm{P}\ n$$
+
+we need to prove that
+
+- $\mathrm{P}\ 0$
+- $\forall n, \mathrm{P}\ n \rightarrow \mathrm{P}\ (S n)$
+
+The `induction`{.coq} tactic effectively turns our goal into two subgoals. But
+why is that? Because, under the hood, Ltac is refining the current goal using
+the `nat_ind`{.coq} function automatically generated by Coq when `nat`{.coq}
+was defined. The type of `nat_ind`{.coq} is
+
+```coq
+ forall (P : nat -> Prop),
+ P 0
+ -> (forall n : nat, P n -> P (S n))
+ -> forall n : nat, P n
+```
+
+Interestingly enough, `nat_ind`{.coq} is not an axiom. It is a regular Coq
+function, whose definition is
+
+```coq
+ fun (P : nat -> Prop) (f : P 0)
+ (f0 : forall n : nat, P n -> P (S n)) =>
+ fix F (n : nat) : P n :=
+ match n as n0 return (P n0) with
+ | 0 => f
+ | S n0 => f0 n0 (F n0)
+ end
+```
+
+So, after executing
+
+```coq
+ induction n.
+```
+
+The hidden term we are constructing becomes
+
+```coq
+ (fun n : nat =>
+ nat_ind
+ (fun n0 : nat => n0 + 0 = n0)
+ ?Goal3
+ (fun (n0 : nat) (IHn : n0 + 0 = n0) => ?Goal4)
+ n)
+```
+
+And Coq presents us two goals.
+
+First, we need to prove $\mathrm{P}\ 0$, *i.e.*,
+$0 + 0 = 0$. Similarly to Coq presenting a goal when what we are actually doing
+is constructing a term, the use of $=$ and $+$ (*i.e.*, the Coq notations
+mechanism) hides much here. We can ask Coq to be more explicit by using the
+vernacular command `Set Printing All`{.coq} to learn that when Coq presents us
+a goal of the form `0 + 0 = 0`{.coq}, it is actually looking for a term of type
+`@eq nat (Nat.add O O) O`{.coq}.
+
+`Nat.add`{.coq} is a regular Coq (recursive) function.
+
+```coq
+ fix add (n m : nat) {struct n} : nat :=
+ match n with
+ | 0 => m
+ | S p => S (add p m)
+ end
+```
+
+Similarly, `eq`{.coq} is *not* an axiom. It is a regular inductive type, defined
+as follows.
+
+```coq
+Inductive eq (A : Type) (x : A) : A -> Prop :=
+| eq_refl : eq A x x
+```
+
+Coming back to our current goal, proving `@eq nat (Nat.add 0 0) 0`{.coq}[^equ1]
+requires to construct a term of a type whose only constructor is
+`eq_refl`{.coq}. `eq_refl`{.coq} accepts one argument, and encodes the proof
+that said argument is equal to itself. In practice, Coq typechecker will accept
+the term `@eq_refl _ x`{.coq} when it expects a term of type `@eq _ x y`{.coq}
+*if* it can reduce `x`{.coq} and `y`{.coq} to the same term.
+
+[^equ1]: That is, `0 + 0 = 0`{.coq}
+
+Is it the case for `@eq nat (Nat.add 0 0) 0`{.coq}? It is, since by definition
+of `Nat.add`{.coq}, `Nat.add 0 x`{.coq} is reduced to `x`{.coq}. We can use the
+`reflexivity`{.coq} tactic to tell Coq to fill the current hole with
+`eq_refl`{.coq}.
+
+```coq
+ + reflexivity.
+```
+
+Suspicious readers may rely on `Show Proof`{.coq} to verify this assertion.
+
+```coq
+ (fun n : nat =>
+ nat_ind
+ (fun n0 : nat => n0 + 0 = n0)
+ eq_refl
+ (fun (n0 : nat) (IHn : n0 + 0 = n0) => ?Goal4)
+ n)
+```
+
+`?Goal3`{.coq} has indeed be replaced by `eq_refl`.
+
+One goal remains, as we need to prove that if `n + 0 = n`{.coq}, then `S n + 0
+= S n`{.coq}. Coq can reduce `S n + 0`{.coq} to `S (n + 0)`{.coq} by definition
+of `Nat.add`{.coq}, but it cannot reduce `S n`{.coq} more than it already is.
+
+```coq
+ + cbn.
+```
+
+We cannot just use `reflexivity`{.coq} here (*i.e.*, fill the hole with
+`eq_refl`{.coq}), since `S (n + 0)`{.coq} and `S n`{.coq} cannot be reduced to
+the same term.
+
+However, at this point of the proof, we have the `IHn`{.coq} hypothesis (*i.e.*, the
+`IHn`{.coq} argument of the anonymous function whose body we are trying to
+construct). The `rewrite`{.coq} tactic allows for substituting in a type an
+occurence of `x`{.coq} by `y`{.coq} as long as we have a proof of `x = y`{.coq}. *)
+
+```coq
+ rewrite IHn.
+```
+
+Similarly to `induction`{.coq} using a dedicated function, `rewrite`{.coq} refines
+the current hole with the `eq_ind_r`{.coq} function[^noaxiom]. Replacing `n +
+0`{.coq} with `n`{.coq} transforms the goal into `S n = S n`{.coq}. Here, we
+can use `reflexivity`{.coq} (i.e., `eq_refl`{.coq}) to conclude. *)
+
+[^noaxiom]: Again, not an axiom.
+
+```coq
+ reflexivity.
+```
+
+After this last tactic, the work is done. There is no more goal to fill inside
+the proof term that we have carefully constructed.
+
+```coq
+ (fun n : nat =>
+ nat_ind
+ (fun n0 : nat => n0 + 0 = n0)
+ eq_refl
+ (fun (n0 : nat) (IHn : n0 + 0 = n0) =>
+ eq_ind_r (fun n1 : nat => S n1 = S n0) eq_refl IHn)
+ n)
+```
+
+We can finally use `Qed`{.coq} or `Defined`{.coq} to tell Coq to typecheck this
+term. That is, Coq does not trust Ltac, but rather typecheck the term to
+verify it is correct. This way, in case Ltac has a bug which makes it
+construct ill-formed type, at the very least Coq can reject it.
+
+```coq
+Qed.
+```
+
+In conclusion, tactics are used to incrementally refine hole inside a term
+until the latter is complete. They do it in a very specific manner, to
+encode certain reasoning rule.
+
+On the other hand, the `refine`{.coq} tactic provides a generic, low-level way
+to do the same thing. Knowing how a given tactic works allows for mimicking
+its behavior using the `refine`{.coq} tactic.
+
+If we take the previous theorem as an example, we can prove it using this
+alternative proof script.
+
+```coq
+Theorem add_n_O' : forall (n : nat), n + O = n.
+Proof.
+ refine (fun n => _).
+ refine (nat_ind (fun n => n + 0 = n) _ _ n).
+ + refine eq_refl.
+ + refine (fun m IHm => _).
+ refine (eq_ind_r (fun n => S n = S m) _ IHm).
+ refine eq_refl.
+Qed.
diff --git a/site/posts/LtacMetaprogramming.v b/site/posts/LtacMetaprogramming.v
deleted file mode 100644
index 6b086e3..0000000
--- a/site/posts/LtacMetaprogramming.v
+++ /dev/null
@@ -1,254 +0,0 @@
-(** #<nav><p class="series">Ltac.html</p>
- <p class="series-next">LtacPatternMatching.html</p></nav># *)
-
-(** * Ltac is an Imperative Metaprogramming Language *)
-
-(** #<div id="history">site/posts/LtacMetaprogramming.v</div># *)
-
-(** Coq is often depicted as an _interactive_ proof assistant, thanks to its
- proof environment. Inside the proof environment, Coq presents the user a
- goal, and said user solves said goal by means of tactics which describes a
- logical reasoning. For instance, to reason by induction, one can use the
- <<induction>> tactic, while a simple case analysis can rely on the
- <<destruct>> or <<case_eq>> tactics, etc. It is not uncommon for new Coq
- users to be introduced to Ltac, the Coq default tactic language, using this
- proof-centric approach. This is not surprising, since writing proofs remains
- the main use-case for Ltac. In practice though, this discourse remains an
- abstraction which hides away what actually happens under the hood when Coq
- executes a proof scripts.
-
- To really understand what Ltac is about, we need to recall ourselves that
- Coq kernel is mostly a typechecker. A theorem statement is expressed as a
- “type” (which lives in a dedicated sort called [Prop]), and a proof of this
- theorem is a term of this type, just like the term [S (S O)] (#<span
- class="im">#2#</span>#) is of type [nat]. Proving a theorem in Coq requires
- to construct a term of the type encoding said theorem, and Ltac allows for
- incrementally constructing this term, one step at a time.
-
- Ltac generates terms, therefore it is a metaprogramming language. It does it
- incrementally, by using primitives to modifying an implicit state, therefore
- it is an imperative language. Henceforth, it is an imperative
- metaprogramming language.
-
- To summarize, a goal presented by Coq inside the environment proof is a hole
- within the term being constructed. It is presented to users as:
-
- - A list of “hypotheses,” which are nothing more than the variables
- in the scope of the hole
- - A type, which is the type of the term to construct to fill the hole
-
- We illustrate what happens under the hood of Coq executes a simple proof
- script. One can use the <<Show Proof>> vernacular command to exhibit
- this.
-
- We illustrate how Ltac works with the following example. *)
-
-Theorem add_n_O : forall (n : nat), n + O = n.
-
-Proof.
-
-(** The <<Proof>> vernacular command starts the proof environment. Since no
- tactic has been used, the term we are trying to construct consists solely in
- a hole, while Coq presents us a goal with no hypothesis, and with the exact
- type of our theorem, that is [forall (n : nat), n + O = n].
-
- A typical Coq course will explain students the <<intro>> tactic allows for
- turning the premise of an implication into an hypothesis within the context.
-
- #<div class="dmath">#C \vdash P \rightarrow Q#</div>#
-
- becomes
-
- #<div class="dmath">#C,\ P \vdash Q#</div>#
-
- This is a fundamental rule of deductive reasoning, and <<intro>> encodes it.
- It achieves this by refining the current hole into an anymous function.
- When we use *)
-
- intro n.
-
-(** it refines the term
-
-<<
- ?Goal1
->>
-
- into
-
-<<
- fun (n : nat) => ?Goal2
->>
-
- The next step of this proof is to reason about induction on [n]. For [nat],
- it means that to be able to prove
-
- #<div class="dmath">#\forall n, \mathrm{P}\ n#</div>#
-
- we need to prove that
-
- - #<div class="imath">#\mathrm{P}\ 0#</div>#
- - #<div class="imath">#\forall n, \mathrm{P}\ n \rightarrow \mathrm{P}\ (S n)#</div>#
-
- The <<induction>> tactic effectively turns our goal into two subgoals. But
- why is that? Because, under the hood, Ltac is refining the current goal
- using the [nat_ind] function automatically generated by Coq when [nat] was
- defined. The type of [nat_ind] is
-
-<<
- forall (P : nat -> Prop),
- P 0
- -> (forall n : nat, P n -> P (S n))
- -> forall n : nat, P n
->>
- Interestingly enough, [nat_ind] is not an axiom. It is a regular Coq function, whose definition is
-
-<<
- fun (P : nat -> Prop) (f : P 0)
- (f0 : forall n : nat, P n -> P (S n)) =>
- fix F (n : nat) : P n :=
- match n as n0 return (P n0) with
- | 0 => f
- | S n0 => f0 n0 (F n0)
- end
->>
-
- So, after executing *)
-
- induction n.
-
-(** The hidden term we are constructing becomes
-
-<<
- (fun n : nat =>
- nat_ind
- (fun n0 : nat => n0 + 0 = n0)
- ?Goal3
- (fun (n0 : nat) (IHn : n0 + 0 = n0) => ?Goal4)
- n)
->>
-
- And Coq presents us two goals.
-
- First, we need to prove #<span class="dmath">#\mathrm{P}\ 0#</span>#, i.e.,
- #<span class="dmath">#0 + 0 = 0#</span>#. Similarly to Coq presenting a goal
- when what we are actually doing is constructing a term, the use of #<span
- class="dmath">#+#</span># and #<span class="dmath">#+#</span># (i.e., Coq
- notations mechanism) hide much here. We can ask Coq to be more explicit by
- using the vernacular command <<Set Printing All>> to learn that when Coq
- presents us a goal of the form [0 + 0 = 0], it is actually looking for a
- term of type [@eq nat (Nat.add O O) O].
-
- [Nat.add] is a regular Coq (recursive) function.
-
-<<
- fix add (n m : nat) {struct n} : nat :=
- match n with
- | 0 => m
- | S p => S (add p m)
- end
->>
-
- Similarly, [eq] is _not_ an axiom. It is a regular inductive type, defined
- as follows.
-
-<<
-Inductive eq (A : Type) (x : A) : A -> Prop :=
-| eq_refl : eq A x x
->>
-
- Coming back to our current goal, proving [@eq nat (Nat.add 0 0) 0] (i.e., [0
- + 0 = 0]) requires to construct a term of a type whose only constructor is
- [eq_refl]. [eq_refl] accepts one argument, and encodes the proof that said
- argument is equal to itself. In practice, Coq typechecker will accept the
- term [@eq_refl _ x] when it expects a term of type [@eq _ x y] _if_ it can
- reduce [x] and [y] to the same term.
-
- Is it the case for [0 + 0 = 0]? It is, since by definition of [Nat.add], [0
- + x] is reduced to [x]. We can use the <<reflexivity>> tactic to tell Coq to
- fill the current hole with [eq_refl]. *)
-
- + reflexivity.
-
- (** Suspicious readers may rely on <<Show Proof>> to verify this assertion
- assert:
-<<
- (fun n : nat =>
- nat_ind
- (fun n0 : nat => n0 + 0 = n0)
- eq_refl
- (fun (n0 : nat) (IHn : n0 + 0 = n0) => ?Goal4)
- n)
->>
-
- <<?Goal3>> has indeed be replaced by [eq_refl].
-
- One goal remains, as we need to prove that if [n + 0 = n], then [S n + 0 = S
- n]. Coq can reduce [S n + 0] to [S (n + 0)] by definition of [Nat.add], but
- it cannot reduce [S n] more than it already is. We can request it to do so
- using tactics such as [cbn]. *)
-
- + cbn.
-
-(** We cannot just use <<reflexivity>> here (i.e., fill the hole with
- [eq_refl]), since [S (n + 0)] and [S n] cannot be reduced to the same term.
- However, at this point of the proof, we have the [IHn] hypothesis (i.e., the
- [IHn] argument of the anonymous function whose body we are trying to
- construct). The <<rewrite>> tactic allows for substituting in a type an
- occurence of [x] by [y] as long as we have a proof of [x = y]. *)
-
- rewrite IHn.
-
- (** Similarly to <<induction>> using a dedicated function , <<rewrite>> refines
- the current hole with the [eq_ind_r] function (not an axiom!). Replacing [n
- + 0] with [n] transforms the goal into [S n = S n]. Here, we can use
- <<reflexivity>> (i.e., [eq_refl]) to conclude. *)
-
- reflexivity.
-
-(** After this last tactic, the work is done. There is no more goal to fill
- inside the proof term that we have carefully constructed.
-
-<<
- (fun n : nat =>
- nat_ind
- (fun n0 : nat => n0 + 0 = n0)
- eq_refl
- (fun (n0 : nat) (IHn : n0 + 0 = n0) =>
- eq_ind_r (fun n1 : nat => S n1 = S n0) eq_refl IHn)
- n)
->>
-
- We can finally use [Qed] or [Defined] to tell Coq to typecheck this
- term. That is, Coq does not trust Ltac, but rather typecheck the term to
- verify it is correct. This way, in case Ltac has a bug which makes it
- construct ill-formed type, at the very least Coq can reject it. *)
-
-Qed.
-
-(** In conclusion, tactics are used to incrementally refine hole inside a term
- until the latter is complete. They do it in a very specific manner, to
- encode certain reasoning rule.
-
- On the other hand, the <<refine>> tactic provides a generic, low-level way
- to do the same thing. Knowing how a given tactic works allows for mimicking
- its behavior using the <<refine>> tactic.
-
- If we take the previous theorem as an example, we can prove it using this
- alternative proof script. *)
-
-Theorem add_n_O' : forall (n : nat), n + O = n.
-
-Proof.
- refine (fun n => _).
- refine (nat_ind (fun n => n + 0 = n) _ _ n).
- + refine eq_refl.
- + refine (fun m IHm => _).
- refine (eq_ind_r (fun n => S n = S m) _ IHm).
- refine eq_refl.
-Qed.
-
-(** ** Conclusion *)
-
-(** This concludes our introduction to Ltac as an imperative metaprogramming
- language. In the #<a href="LtacPatternMatching.html">#next part of this series#</a>#, we
- present Ltac powerful pattern matching capabilities. *)
diff --git a/site/posts/LtacPatternMatching.md b/site/posts/LtacPatternMatching.md
new file mode 100644
index 0000000..9cc926e
--- /dev/null
+++ b/site/posts/LtacPatternMatching.md
@@ -0,0 +1,203 @@
+---
+published: 2020-08-28
+tags: ['coq']
+series:
+ parent: series/Ltac.html
+ prev: posts/LtacMetaprogramming.html
+ next: posts/MixingLtacAndGallina.html
+abstract: |
+ Ltac allows for pattern matching on types and contexts. In this article, we
+ give a short introduction on this feature of key importance. Ltac programs
+ (“proof scripts”) generate terms, and the shape of said terms can be very
+ different regarding the initial context. For instance, `induction x`{.coq}
+ will refine the current goal using an inductive principle dedicated to the
+ type of `x`{.coq}.
+---
+
+# Pattern Matching on Types and Contexts
+
+In the [a previous article](/posts/LtacMetaprogramming.html) of our series on
+Ltac, we have shown how tactics allow for constructing Coq terms incrementally.
+Ltac programs (“proof scripts”) generate terms, and the shape of said terms can
+be very different regarding the initial context. For instance, `induction
+x`{.coq} will refine the current goal using an inductive principle dedicated to
+the type of `x`{.coq}.
+
+This is possible because Ltac allows for pattern matching on types and
+contexts. In this article, we give a short introduction on this feature of
+key importance.
+
+## To `lazymatch`{.coq} or to `match`{.coq}
+
+Gallina provides one pattern matching construction, whose semantics is always
+the same: for a given term, the first pattern to match will always be selected.
+On the contrary, Ltac provides several pattern matching constructions with
+different semantics. This key difference has probably been motivated because
+Ltac is not a total language: a tactic may not always succeed.
+
+Ltac programmers can use `match`{.coq} or `lazymatch`{.coq}. One the one hand,
+with `match`{.coq}, if one pattern matches, but the branch body fails, Coq will
+backtrack and try the next branch. On the other hand, `lazymatch`{.coq} will
+stop on error.
+
+So, for instance, the two following tactics will print two different messages:
+
+```coq
+Ltac match_failure :=
+ match goal with
+ | _
+ => fail "fail in first branch"
+ | _
+ => fail "fail in second branch"
+ end.
+
+Ltac lazymatch_failure :=
+ lazymatch goal with
+ | _
+ => fail "fail in first branch"
+ | _
+ => fail "fail in second branch"
+ end.
+```
+
+We can try that quite easily by starting a dumb goal (eg. `Goal (True).`{.coq})
+and call our tactic. For `match_failure`{.coq}, we get:
+
+```
+Ltac call to "match_failure" failed.
+Error: Tactic failure: fail in second branch.
+```
+
+On the other hand, for `lazymatch_failure`{.coq}, we get:
+
+```
+Ltac call to "match_failure'" failed.
+Error: Tactic failure: fail in first branch.
+```
+
+Moreover, pattern matching allows for matching over *patterns*, not just
+constants. Here, Ltac syntax differs from Gallina’s. In Gallina, if a
+variable name is used in a pattern, Gallina creates a new variable in the
+scope of the related branch. If a variable with the same name already
+existed, it is shadowed. On the contrary, in Ltac, using a variable name
+as-is in a pattern implies an equality check.
+
+To illustrate this difference, we can take the example of the following
+tactic.
+
+```coq
+Ltac successive x y :=
+ lazymatch y with
+ | S x => idtac
+ | _ => fail
+ end.
+```
+
+`successive x y`{.coq} will fails if `y`{.coq} is not the successor of
+`x`{.coq}. On the contrary, the “syntactically equivalent” function in Gallina
+will exhibit a totally different behavior.
+
+```coq
+Definition successor (x y : nat) : bool :=
+ match y with
+ | S x => true
+ | _ => false
+ end.
+```
+
+Here, the first branch of the pattern match is systematically selected when
+`y`{.coq} is not `O`{.coq}, and in this branch, the argument `x`{.coq} is shadowed by the
+predecessor of `y`{.coq}.
+
+For Ltac to adopt a behavior similar to Gallina, the `?`{.coq} prefix shall be
+used. For instance, the following tactic will check whether its argument
+has a known successor, prints it if it does, or fail otherwise.
+
+```coq
+Ltac print_pred_or_zero x :=
+ match x with
+ | S ?x => idtac x
+ | _ => fail
+ end.
+```
+
+On the one hand, `print_pred_or_zero 3`{.coq} will print `2`{.coq}. On the
+other hand, if there exists a variable `x : nat`{.coq} in the context, calling
+`print_pred_or_zero x`{.coq} will fail, since the exact value of `x`{.coq} is
+not known.
+
+### Pattern Matching on Types with `type of`{.coq}
+
+The `type of`{.coq} operator can be used in conjunction to pattern matching to
+generate different terms according to the type of a variable. We could
+leverage this to reimplement `induction`{.coq} for instance.
+
+As an example, we propose the following `not_nat`{.coq} tactic which, given an
+argument `x`{.coq}, fails if `x`{.coq} is of type `nat`{.coq}.
+
+```coq
+Ltac not_nat x :=
+ lazymatch type of x with
+ | nat => fail "argument is of type nat"
+ | _ => idtac
+ end.
+```
+
+With this definition, `not_nat true`{.coq} succeeds since `true`{.coq} is of
+type `bool`{.coq}, and `not_nat O`{.coq} since `O`{.coq} encodes $0$ in
+`nat`{.coq}.
+
+We can also use the `?`{.coq} prefix to write true pattern. For instance, the
+following tactic will fail if the type of its supplied argument has at least
+one parameter.
+
+```coq
+Ltac not_param_type x :=
+ lazymatch type of x with
+ | ?f ?a => fail "argument is of type with parameter"
+ | _ => idtac
+ end.
+```
+
+Both `not_param_type (@nil nat)`{.coq} of type `list nat`{.coq} and `@eq_refl
+nat 0`{.coq} of type `0 = 0`{.coq} fail, but `not_param_type 0`{.coq} of type
+`nat`{.coq} succeeds. *)
+
+## Pattern Matching on the Context with `goal`{.coq}
+
+Lastly, we discuss how Ltac allows for inspecting the context (*i.e.*, the
+hypotheses and the goal) using the `goal`{.coq} keyword.
+
+For instance, we propose a naive implementation of the `subst`{.coq} tactic
+as follows.
+
+```coq
+Ltac subst' :=
+ repeat
+ match goal with
+ | H : ?x = _ |- context[?x]
+ => repeat rewrite H; clear H
+ end.
+```
+
+With `goal`{.coq}, patterns are of the form `H : (pattern), ... |-
+(pattern)`{.coq}.
+
+- At the left side of `|-`{.coq}, we match on hypotheses. Beware that
+ contrary to variable name in pattern, hypothesis names behaves as in
+ Gallina (i.e., fresh binding, shadowing, etc.). In the branch, we are
+ looking for equations, i.e., an hypothesis of the form `?x = _`{.coq}.
+- At the right side of `|-`{.coq}, we match on the goal.
+
+In both cases, Ltac makes available an interesting operator,
+`context[(pattern)`{.coq}], which is satisfies if `(pattern)`{.coq} appears somewhere in
+the object we are pattern matching against. So, the branch of the `match`{.coq}
+reads as follows: we are looking for an equation `H`{.coq} which specifies the
+value of an object `x`{.coq} which appears in the goal. If such an equation
+exists, `subst'`{.coq} tries to `rewrite x`{.coq} as many time as possible.
+
+This implementation of `subst'`{.coq} is very fragile, and will not work if the
+equation is of the form `_ = ?x`{.coq}, and it may behaves poorly if we have
+“transitive equations”, such as there exists hypotheses `?x = ?y`{.coq} and `?y
+= _`{.coq}. Motivated readers may be interested in proposing more robust
+implementation of `subst'`{.coq}.
diff --git a/site/posts/LtacPatternMatching.v b/site/posts/LtacPatternMatching.v
deleted file mode 100644
index a0b8a4d..0000000
--- a/site/posts/LtacPatternMatching.v
+++ /dev/null
@@ -1,188 +0,0 @@
-(** #<nav><p class="series">Ltac.html</p>
- <p class="series-prev">./LtacMetaprogramming.html</p>
- <p class="series-next">./MixingLtacAndGallina.html</p></nav># *)
-
-(** * Pattern Matching on Types and Contexts *)
-
-(** In the #<a href="LtacMetaprogramming.html">#previous article#</a># of our
- series on Ltac, we have shown how tactics allow for constructing Coq terms
- incrementally. Ltac programs (“proof scripts”) generate terms, and the
- shape of said terms can be very different regarding the initial context. For
- instance, [induction x] will refine the current goal using an inductive
- principle dedicated to the type of [x].
-
- This is possible because Ltac allows for pattern matching on types and
- contexts. In this article, we give a short introduction on this feature of
- key importance. *)
-
-(** #<nav id="generate-toc"></nav>#
-
- #<div id="history">site/posts/LtacPatternMatching.v</div># *)
-
-(** ** To [lazymatch] or to [match] *)
-
-(** Gallina provides one pattern matching construction, whose semantics is
- always the same: for a given term, the first pattern to match will always be
- selected. On the contrary, Ltac provides several pattern matching
- constructions with different semantics. This key difference has probably
- been motivated because Ltac is not a total language: a tactic may not always
- succeed.
-
- Ltac programmers can use [match] or [lazymatch]. One the one hand, with
- [match], if one pattern matches, but the branch body fails, Coq will
- backtrack and try the next branch. On the other hand, [lazymatch] will stop
- on error.
-
- So, for instance, the two following tactics will print two different
- messages: *)
-
-Ltac match_failure :=
- match goal with
- | _
- => fail "fail in first branch"
- | _
- => fail "fail in second branch"
- end.
-
-Ltac lazymatch_failure :=
- lazymatch goal with
- | _
- => fail "fail in first branch"
- | _
- => fail "fail in second branch"
- end.
-
-(** We can try that quite easily by starting a dumb goal (eg. [Goal (True).])
- and call our tactic. For [match_failure], we get:
-
-<<
-Ltac call to "match_failure" failed.
-Error: Tactic failure: fail in second branch.
->>
-
- On the other hand, for [lazymatch_failure], we get:
-
-<<
-Ltac call to "match_failure'" failed.
-Error: Tactic failure: fail in first branch.
->>
-
- Moreover, pattern matching allows for matching over _patterns_, not just
- constants. Here, Ltac syntax differs from Gallina’s. In Gallina, if a
- variable name is used in a pattern, Gallina creates a new variable in the
- scope of the related branch. If a variable with the same name already
- existed, it is shadowed. On the contrary, in Ltac, using a variable name
- as-is in a pattern implies an equality check.
-
- To illustrate this difference, we can take the example of the following
- tactic. *)
-
-Ltac successive x y :=
- lazymatch y with
- | S x => idtac
- | _ => fail
- end.
-
-(** [successive x y] will fails if [y] is not the successor of [x]. On the
- contrary, the “syntactically equivalent” function in Gallina will exhibit
- a totally different behavior. *)
-
-Definition successor (x y : nat) : bool :=
- match y with
- | S x => true
- | _ => false
- end.
-
-(** Here, the first branch of the pattern match is systematically selected when
- [y] is not O, and in this branch, the argument [x] is shadowed by the
- predecessor of [y].
-
- For Ltac to adopt a behavior similar to Gallina, the [?] prefix shall be
- used. For instance, the following tactic will check whether its argument
- has a known successor, prints it if it does, or fail otherwise. *)
-
-Ltac print_pred_or_zero x :=
- match x with
- | S ?x => idtac x
- | _ => fail
- end.
-
-(** On the one hand, [print_pred_or_zero 3] will print [2]. On the other hand,
- if there exists a variable [x : nat] in the context, calling
- [print_pred_or_zero x] will fail, since the exact value of [x] is not
- known. *)
-
-(** ** Pattern Matching on Types with [type of] *)
-
-(** The [type of] operator can be used in conjunction to pattern matching to
- generate different terms according to the type of a variable. We could
- leverage this to reimplement <<induction>> for instance.
-
- As an example, we propose the following <<not_nat>> tactic which, given an
- argument [x], fails if [x] is of type [nat]. *)
-
-Ltac not_nat x :=
- lazymatch type of x with
- | nat => fail "argument is of type nat"
- | _ => idtac
- end.
-
-(** With this definition, <<not_nat true>> succeeds since [true] is of type
- [bool], and [not_nat O] since [O] encodes #<span class="imath">#0#</span># in
- [nat].
-
- We can also use the [?] prefix to write true pattern. For instance, the
- following tactic will fail if the type of its supplied argument has at least
- one parameter. *)
-
-Ltac not_param_type x :=
- lazymatch type of x with
- | ?f ?a => fail "argument is of type with parameter"
- | _ => idtac
- end.
-
-(** Both <<not_param_type (@nil nat)>> of type [list nat] and
- <<(@eq_refl nat 0)>> of type [0 = 0] fail, but <<not_param_type 0>> of type [nat]
- succeeds. *)
-
-(** ** Pattern Matching on the Context with [goal] *)
-
-(** Lastly, we discuss how Ltac allows for inspecting the context (i.e., the
- hypotheses and the goal) using the [goal] keyword.
-
- For instance, we propose a naive implementation of the [subst] tactic
- as follows. *)
-
-Ltac subst' :=
- repeat
- match goal with
- | H : ?x = _ |- context[?x]
- => repeat rewrite H; clear H
- end.
-
-(** With [goal], patterns are of the form <<H : (pattern), ... |- (pattern)>>.
-
- - At the left side of [|-], we match on hypotheses. Beware that
- contrary to variable name in pattern, hypothesis names behaves as in
- Gallina (i.e., fresh binding, shadowing, etc.). In the branch, we are
- looking for equations, i.e., an hypothesis of the form [?x = _].
- - At the right side of [|-], we match on the goal.
-
- In both cases, Ltac makes available an interesting operator,
- [context[(pattern)]], which is satisfies if [(pattern)] appears somewhere in
- the object we are pattern matching against. So, the branch of the [match]
- reads as follows: we are looking for an equation [H] which specifies the
- value of an object [x] which appears in the goal. If such an equation
- exists, <<subst'>> tries to <<rewrite>> [x] as many time as possible.
-
- This implementation of [subst'] is very fragile, and will not work if the
- equation is of the form [_ = ?x], and it may behaves poorly if we have
- “transitive equations”, such as there exists hypotheses [?x = ?y] and [?y =
- _]. Motivated readers may be interested in proposing more robust
- implementation of [subst']. *)
-
-(** ** Conclusion *)
-
-(** This concludes our tour on Ltac pattern matching capabilities. In the #<a
- href="MixingLtacAndGallina.html">#next article of this series#</a>#, we
- explain how Ltac and Gallina can actually be used simultaneously. *)
diff --git a/site/posts/MixingLtacAndGallina.md b/site/posts/MixingLtacAndGallina.md
new file mode 100644
index 0000000..f03e7e3
--- /dev/null
+++ b/site/posts/MixingLtacAndGallina.md
@@ -0,0 +1,186 @@
+---
+published: 2020-07-26
+modified: 2020-08-28
+series:
+ parent: series/Ltac.html
+ prev: posts/LtacPatternMatching.html
+tags: ['coq']
+abstract: |
+ One of the most misleading introduction to Coq is to say that “Gallina is
+ for programs, while tactics are for proofs.” Gallina is the preferred way
+ to construct programs, and tactics are the preferred way to construct
+ proofs. The key word here is “preferred.” Coq actually allows for *mixing*
+ Ltac and Gallina together.
+---
+
+# Mixing Ltac and Gallina for Fun and Profit
+
+One of the most misleading introduction to Coq is to say that “Gallina is
+for programs, while tactics are for proofs.” Indeed, in Coq we construct
+terms of given types, always. Terms encodes both programs and proofs about
+these programs. Gallina is the preferred way to construct programs, and
+tactics are the preferred way to construct proofs.
+
+The key word here is “preferred.” We do not always need to use tactics to
+construct a proof term. Conversly, there are some occasions where
+constructing a program with tactics become handy. Furthermore, Coq actually
+allows for *mixing together* Ltac and Gallina.
+
+In the [previous article of this series](/posts/LtacPatternMatching.html), we
+discuss how Ltac provides two very interesting features:
+
+- With `match goal with`{.coq} it can inspect its context
+- With `match type of _ with`{.coq} it can pattern matches on types
+
+It turns out these features are more than handy when it comes to
+metaprogramming (that is, the generation of programs by programs).
+
+## A Tale of Two Worlds, and Some Bridges
+
+Constructing terms proofs directly in Gallina often happens when one is
+writing dependently-typed definition. For instance, we can write a type safe
+`from_option`{.coq} function (inspired by [this very nice
+write-up](https://plv.csail.mit.edu/blog/unwrapping-options.html)) such that
+the option to unwrap shall be accompagnied by a proof that said option contains
+something. This extra argument is used in the `None`{.coq} case to derive a
+proof of `False`{.coq}, from which we can derive
+anything.
+
+```coq
+Definition is_some {α} (x : option α) : bool :=
+ match x with Some _ => true | None => false end.
+
+Lemma is_some_None {α} (x : option α)
+ : x = None -> is_some x <> true.
+Proof. intros H. rewrite H. discriminate. Qed.
+
+Definition from_option {α}
+ (x : option α) (some : is_some x = true)
+ : α :=
+ match x as y return x = y -> α with
+ | Some x => fun _ => x
+ | None => fun equ => False_rect α (is_some_None x equ some)
+ end eq_refl.
+```
+
+In `from_option`{.coq}, we construct two proofs without using tactics:
+
+- `False_rect α (is_some_None x equ some)`{.coq} to exclude the absurd case
+- `eq_refl`{.coq} in conjunction with a dependent pattern matching (if you are
+ not familiar with this trick: the main idea is to allow Coq to
+ “remember” that `x = None`{.coq} in the second branch)
+
+We can use another approach. We can decide to implement `from_option`{.coq}
+with a proof script.
+
+```coq
+Definition from_option' {α}
+ (x : option α) (some : is_some x = true)
+ : α.
+Proof.
+ case_eq x.
+ + intros y _.
+ exact y.
+ + intros equ.
+ rewrite equ in some.
+ now apply is_some_None in some.
+Defined.
+```
+
+There is a third approach we can consider: mixing Gallina terms, and tactics.
+This is possible thanks to the `ltac:()`{.coq} feature.
+
+```coq
+Definition from_option'' {α}
+ (x : option α) (some : is_some x = true)
+ : α :=
+ match x as y return x = y -> α with
+ | Some x => fun _ => x
+ | None => fun equ => ltac:(rewrite equ in some;
+ now apply is_some_None in some)
+ end eq_refl.
+```
+
+When Coq encounters `ltac:()`{.coq}, it treats it as a hole. It sets up a
+corresponding goal, and tries to solve it with the supplied tactic.
+
+Conversly, there exists ways to construct terms in Gallina when writing a proof
+script. Certains tactics takes such terms as arguments. Besides, Ltac provides
+`constr:()`{.coq} and `uconstr:()`{.coq} which work similarly to
+`ltac:()`{.coq}. The difference between `constr:()`{.coq} and
+`uconstr:()`{.coq} is that Coq will try to assign a type to the argument of
+`constr:()`{.coq}, but will leave the argument of `uconstr:()`{.coq} untyped.
+
+For instance, consider the following tactic definition.
+
+```coq
+Tactic Notation "wrap_id" uconstr(x) :=
+ let f := uconstr:(fun x => x) in
+ exact (f x).
+```
+
+Both `x`{.coq} the argument of `wrap_id`{.coq} and `f`{.coq} the anonymous identity function
+are not typed. It is only when they are composed together as an argument of
+`exact`{.coq} (which expects a typed argument, more precisely of the type of the
+goal) that Coq actually tries to typecheck it.
+
+As a consequence, `wrap_id`{.coq} generates a specialized identity function for
+each specific context.
+
+```coq
+Definition zero : nat := ltac:(wrap_id 0).
+```
+
+The generated anonymous identity function is `fun x : nat => x`{.coq}.
+
+```coq
+Definition empty_list α : list α := ltac:(wrap_id nil).
+```
+
+The generated anonymous identity function is `fun x : list α => x`{.coq}.
+Besides, we do not need to give more type information about `nil`{.coq}. If
+`wrap_id`{.coq} were to be expecting a typed term, we would have to replace
+`nil`{.coq} by [(@nil α)].
+
+## Beware the Automation Elephant in the Room
+
+Proofs and computational programs are encoded in Coq as terms, but there is a
+fundamental difference between them, and it is highlighted by one of the axiom
+provided by the Coq standard library: proof irrelevance.
+
+Proof irrelevance states that two proofs of the same theorem (i.e., two proof
+terms which share the same type) are essentially equivalent, and can be
+substituted without threatening the trustworthiness of the system. From a
+formal methods point of view, it makes sense. Even if we value “beautiful
+proofs,” we mostly want correct proofs.
+
+The same reasoning does _not_ apply to computational programs. Two functions of
+type `nat -> nat -> nat`{.coq} are unlikely to be equivalent. For instance,
+`add`{.coq}, `mul`{.coq} or `sub`{.coq} share the same type, but computes
+totally different results.
+
+Using tactics such as `auto`{.coq} to generate terms which do not live inside
+`Prop`{.coq} is risky, to say the least. For instance,
+
+```coq
+Definition add (x y : nat) : nat := ltac:(auto).
+```
+
+This works, but it is certainly not what you would expect:
+
+```coq
+add = fun _ y : nat => y
+ : nat -> nat -> nat
+```
+
+That being said, if we keep that in mind, and assert the correctness of the
+generated programs (either by providing a proof, or by extensively testing it),
+there is no particular reason not to use Ltac to generate terms when it makes
+sens.
+
+Dependently-typed programming can help here. If we decorate the return type of
+a function with the expected properties of the result wrt. the function’s
+arguments, we can ensure the function is correct, and conversly prevent tactics
+such as `auto`{.coq} to generate “incorrect” terms. Interested readers may
+refer to [the dedicated series on this very
+website](/posts/StronglySpecifiedFunctions.html).
diff --git a/site/posts/MixingLtacAndGallina.v b/site/posts/MixingLtacAndGallina.v
deleted file mode 100644
index d77cd8a..0000000
--- a/site/posts/MixingLtacAndGallina.v
+++ /dev/null
@@ -1,165 +0,0 @@
-(** #<nav><p class="series">Ltac.html</p>
- <p class="series-prev">./LtacPatternMatching.html</p></nav># *)
-
-(** * Mixing Ltac and Gallina for Fun and Profit *)
-
-(** One of the most misleading introduction to Coq is to say that “Gallina is
- for programs, while tactics are for proofs.” Indeed, in Coq we construct
- terms of given types, always. Terms encodes both programs and proofs about
- these programs. Gallina is the preferred way to construct programs, and
- tactics are the preferred way to construct proofs.
-
- The key word here is “preferred.” We do not always need to use tactics to
- construct a proof term. Conversly, there are some occasions where
- constructing a program with tactics become handy. Furthermore, Coq actually
- allows for _mixing together_ Ltac and Gallina.
-
- In the #<a href="LtacPatternMatching.html">#previous article of this
- series#</a>#, we discuss how Ltac provides two very interesting features:
-
- - With [match goal with] it can inspect its context
- - With [match type of _ with] it can pattern matches on types
-
- It turns out these features are more than handy when it comes to
- metaprogramming (that is, the generation of programs by programs). *)
-
-(** #<nav id="generate-toc"></nav>#
-
- #<div id="history">site/posts/MixingLtacAndGallina.v</div># *)
-
-(** ** A Tale of Two Worlds, and Some Bridges *)
-
-(** Constructing terms proofs directly in Gallina often happens when one is
- writing dependently-typed definition. For instance, we can write a type safe
- [from_option] function (inspired by #<a
- href="https://plv.csail.mit.edu/blog/unwrapping-options.html">#this very
- nice write-up#</a>#) such that the option to unwrap shall be accompagnied by
- a proof that said option contains something. This extra argument is used in
- the [None] case to derive a proof of [False], from which we can derive
- anything. *)
-
-Definition is_some {α} (x : option α) : bool :=
- match x with Some _ => true | None => false end.
-
-Lemma is_some_None {α} (x : option α)
- : x = None -> is_some x <> true.
-Proof. intros H. rewrite H. discriminate. Qed.
-
-Definition from_option {α}
- (x : option α) (some : is_some x = true)
- : α :=
- match x as y return x = y -> α with
- | Some x => fun _ => x
- | None => fun equ => False_rect α (is_some_None x equ some)
- end eq_refl.
-
-(** In [from_option], we construct two proofs without using tactics:
-
- - [False_rect α (is_some_None x equ some)] to exclude the absurd case
- - [eq_refl] in conjunction with a dependent pattern matching (if you are
- not familiar with this trick: the main idea is to allow Coq to
- “remember” that [x = None] in the second branch)
-
- We can use another approach. We can decide to implement [from_option]
- with a proof script. *)
-
-Definition from_option' {α}
- (x : option α) (some : is_some x = true)
- : α.
-
-Proof.
- case_eq x.
- + intros y _.
- exact y.
- + intros equ.
- rewrite equ in some.
- now apply is_some_None in some.
-Defined.
-
-(** There is a third approach we can consider: mixing Gallina terms, and
- tactics. This is possible thanks to the [ltac:()] feature. *)
-
-Definition from_option'' {α}
- (x : option α) (some : is_some x = true)
- : α :=
- match x as y return x = y -> α with
- | Some x => fun _ => x
- | None => fun equ => ltac:(rewrite equ in some;
- now apply is_some_None in some)
- end eq_refl.
-
-(** When Coq encounters [ltac:()], it treats it as a hole. It sets up a
- corresponding goal, and tries to solve it with the supplied tactic.
-
- Conversly, there exists ways to construct terms in Gallina when writing a
- proof script. Certains tactics takes such terms as arguments. Besides, Ltac
- provides [constr:()] and [uconstr:()] which work similarly to [ltac:()].
- The difference between [constr:()] and [uconstr:()] is that Coq will try to
- assign a type to the argument of [constr:()], but will leave the argument of
- [uconstr:()] untyped.
-
- For instance, consider the following tactic definition. *)
-
-Tactic Notation "wrap_id" uconstr(x) :=
- let f := uconstr:(fun x => x) in
- exact (f x).
-
-(** Both [x] the argument of [wrap_id] and [f] the anonymous identity function
- are not typed. It is only when they are composed together as an argument of
- [exact] (which expects a typed argument, more precisely of the type of the
- goal) that Coq actually tries to typecheck it.
-
- As a consequence, [wrap_id] generates a specialized identity function for
- each specific context. *)
-
-Definition zero : nat := ltac:(wrap_id 0).
-
-(** The generated anonymous identity function is [fun x : nat => x]. *)
-
-Definition empty_list α : list α := ltac:(wrap_id nil).
-
-(** The generated anonymous identity function is [fun x : list α => x]. Besides,
- we do not need to give more type information about [nil]. If [wrap_id] were
- to be expecting a typed term, we would have to replace [nil] by [(@nil
- α)]. *)
-
-(** ** Beware the Automation Elephant in the Room *)
-
-(** Proofs and computational programs are encoded in Coq as terms, but there is
- a fundamental difference between them, and it is highlighted by one of the
- axiom provided by the Coq standard library: proof irrelevance.
-
- Proof irrelevance states that two proofs of the same theorem (i.e., two
- proof terms which share the same type) are essentially equivalent, and can
- be substituted without threatening the trustworthiness of the system. From a
- formal methods point of view, it makes sense. Even if we value “beautiful
- proofs,” we mostly want correct proofs.
-
- The same reasoning does _not_ apply to computational programs. Two functions
- of type [nat -> nat -> nat] are unlikely to be equivalent. For instance,
- [add], [mul] or [sub] share the same type, but computes totally different
- results.
-
- Using tactics such as [auto] to generate terms which do not live inside
- [Prop] is risky, to say the least. For instance, *)
-
-Definition add (x y : nat) : nat := ltac:(auto).
-
-(** This works, but it is certainly not what you would expect:
-
-<<
-add = fun _ y : nat => y
- : nat -> nat -> nat
->>
-
- That being said, if we keep that in mind, and assert the correctness of the
- generated programs (either by providing a proof, or by extensively testing
- it), there is no particular reason not to use Ltac to generate terms when it
- makes sens.
-
- Dependently-typed programming can help here. If we decorate the return type
- of a function with the expected properties of the result wrt. the function’s
- arguments, we can ensure the function is correct, and conversly prevent
- tactics such as [auto] to generate “incorrect” terms. Interested readers may
- refer to #<a href="/posts/StronglySpecifiedFunctions.html">#the dedicated
- series on this very website#</a>. *)
diff --git a/site/opinions/MonadTransformers.org b/site/posts/MonadTransformers.md
index 22f4edd..6f1d5aa 100644
--- a/site/opinions/MonadTransformers.org
+++ b/site/posts/MonadTransformers.md
@@ -1,29 +1,23 @@
-#+TITLE: Monad Transformers are a Great Abstraction
+---
+published: 2017-07-15
+tags: ['haskell', 'opinions']
+abstract: |
+ Monads are hard to get right, monad transformers are harder. Yet, they
+ remain a very powerful abstraction.
+---
-#+SERIES: index.html
-#+SERIES_NEXT: StackedGit.html
-
-This article has originally been published on @@html:<span
-id="original-created-at">@@July 15, 2017@@html:</span>@@. [[mn:doubts][Time has
-passed since the publication of this article. Whether or not I remain
-in sync with its conclusions is an open question. Monad Transformers
-are a great abstraction, but given the choice, I would probably choose
-another approach.]]
-
-#+BEGIN_EXPORT html
-<div id="history">site/opinions/MonadTransformers.org</div>
-#+END_EXPORT
+# Monad Transformers are a Great Abstraction
Monads are hard to get right. I think it took me around a year of Haskelling to
feel like I understood them. The reason is, to my opinion, there is not such
-thing as /the/ Monad. It is even the contrary. When someone asks me how I would
+thing as *the* Monad. It is even the contrary. When someone asks me how I would
define Monads in only a few words, I say monads are a convenient formalism to
chain specific computations. Once I’ve got that, I started noticing “monadic
-construction” everywhere, from the Rust ~?~ operator to the [[https://blog.drewolson.org/elixirs-secret-weapon/][Elixir ~with~
-keyword]].
+construction” everywhere, from the Rust `?`{.rust} operator to the [Elixir
+`with`{.elixir} keyword](https://blog.drewolson.org/elixirs-secret-weapon/).
Haskell often uses another concept above Monads: Monad Transformers. This allows
-you to work not only with /one/ Monad, but rather a stack. Each Monad brings its
+you to work not only with *one* Monad, but rather a stack. Each Monad brings its
own properties and you can mix them into your very own one. That you can’t have
in Rust or Elixir, but it works great in Haskell. Unfortunately, it is not an
easy concept and it can be hard to understand. This article is not an attempt to
@@ -31,50 +25,59 @@ do so, but rather a postmortem review of one situation where I found them
extremely useful. If you think you have understood how they work, but don’t see
the point yet, you might find here a beginning of answer.
-Recently, I ran into a very good example of why Monad Transformers worth it. I
-have been working on a project called [[https://github.com/ogma-project][ogma]] for a couple years now. In a
+Recently, I ran into a very good example of why Monad Transformers worth it[^doubts]. I
+have been working on a project called ogma for a couple years now. In a
nutshell, I want to build “a tool” to visualize in time and space a
storytelling. We are not here just yet, but in the meantime I have wrote a
-software called ~celtchar~ to build a novel from a list of files. One of its
+software called `celtchar` to build a novel from a list of files. One of its
newest feature is the choice of language, and by extension, the typographic
rules. This information is read from a configuration file very early in the
program flow. Unfortunately, its use comes much later, after several function
calls.
+[^doubts]: Time has passed since the publication of this article. Whether or
+ not I remain in sync with its conclusions is an open question. Monad
+ Transformers are a great abstraction, but nowadays I would probably try to
+ choose another approach.
+
In Haskell, you deal with that kind of challenges by relying on the Reader
Monad. It carries an environment in a transparent way. The only thing is, I was
already using the State Monad to carry the computation result. But that’s not an
issue with the Monad Transformers.
-#+BEGIN_SRC diff
+```diff
-type Builder = StateT Text IO
+type Builder = StateT Text (ReaderT Language IO)
-#+END_SRC
+```
-As you may have already understood, I wasn't using the “raw” ~State~ Monad, but
-rather the transformer version ~StateT~. The underlying Monad was ~IO~, because
-I needed to be able to read some files from the filesystem. By replacing ~IO~ by
-~ReaderT Language IO~, I basically fixed my “carry the variable to the correct
-function call easily” problem.
+As you may have already understood, I wasn't using the “raw” `State`{.haskell}
+Monad, but rather the transformer version `StateT`{.haskell}. The underlying
+Monad was `IO`{.haskell}, because I needed to be able to read some files from
+the filesystem. By replacing `IO`{.haskell} by `ReaderT Language IO`{.haskell},
+I basically fixed my “carry the variable to the correct function call easily”
+problem.
Retrieving the chosen language is as simple as:
-#+BEGIN_SRC haskell
+```haskell
getLanguage :: Builder Language
getLanguage = lift ask
-#+END_SRC
+```
-And that was basically it. The complete [[https://github.com/ogma-project/celtchar/commit/65fbda8159d21d681e4e711a37fa3f05b49e6cdd][commit]] can be found on Github.
+And that was basically it.
Now, my point is not that Monad Transformers are the ultimate beast we will have
-to tame once and then everything will be shiny and easy. There are a lot of
-other way to achieve what I did with my ~Builder~ stack. For instance, in an
-OO language, I probably would have to add a new class member to my ~Builder~
+to tame once and then everything will be shiny and easy[^funny]. There are a lot of
+other way to achieve what I did with my `Builder`{.haskell} stack. For instance, in an
+OO language, I probably would have to add a new class member to my `Builder`{.haskell}
class and I would have done basically the same thing.
-However, there is something I really like about this approach: the ~Builder~
-type definition gives you a lot of useful information already. Both the ~State~
-and ~Reader~ Monads have a well established semantics most Haskellers will
-understand in a glance. A bit of documentation won’t hurt, but I suspect it is
-not as necessary as one could expect. Moreover, the presence of the ~IO~ Monad
-tells everyone using the ~Builder~ Monad might cause I/O.
+[^funny]: It is amusing to see Past Me being careful here.
+
+However, there is something I really like about this approach: the
+`Builder`{.haskell} type definition gives you a lot of useful information
+already. Both the `State`{.haskell} and `Reader`{.haskell} Monads have a well
+established semantics most Haskellers will understand in a glance. A bit of
+documentation won’t hurt, but I suspect it is not as necessary as one could
+expect. Moreover, the presence of the `IO`{.haskell} Monad tells everyone using
+the `Builder`{.haskell} Monad might cause I/O.
diff --git a/site/posts/NeoVimOcamlInterfacesAndLSP.org b/site/posts/NeoVimOcamlInterfacesAndLSP.org
deleted file mode 100644
index ed36a2f..0000000
--- a/site/posts/NeoVimOcamlInterfacesAndLSP.org
+++ /dev/null
@@ -1,54 +0,0 @@
-#+TITLE: Neovim, OCaml Interfaces, Tree-Sitter and LSP
-
-#+SERIES: ./miscellaneous.html
-#+SERIES_PREV: ./RankNTypesInOCaml.html
-
-Can we all agree that witnessing syntax highlighting being absolutely off is
-probably the most annoying thing that can happen to anybody?
-
-I mean, just look at this horror.
-
-#+CAPTION: Syntax highlighting being absolutely wrong
-#+NAME: fig:wrong-highlingting
-[[../img/wrong-highlighting.png]]
-
-What you are looking at is the result of trying to enable ~tree-sitter~ for
-OCaml hacking and calling it a day. In a nutshell, OCaml ~mli~ files are
-quickly turning into a random mess of nonsensical colors, and I didn’t know
-why. I tried to blame
-[[https://github.com/tree-sitter/tree-sitter-ocaml/issues/72][~tree-sitter-ocaml~]],
-but of course I was wrong.
-
-The issue is subtle, and to be honest, I don’t know if I totally grasp it. But
-from my rough understanding, it breaks down as follows.
-
-- ~tree-sitter-ocaml~ defines two grammars: ~ocaml~ for the ~ml~ files, and
- ~ocaml_interface~ (but ~ocamlinterface~ also works) for the ~mli~ files
-- By default, neovim uses the filetype ~ocaml~ for ~mli~ files, so the incorrect
- parser is being used for syntax highlighting. This explains the root issue
-- Bonus: ~ocamllsp~ does not recognize the ~ocamlinterface~ filetype by
- default (but somehow use the ~ocaml.interface~ id for ~mli~ files…[fn::There
- is probably something to be done here.])
-
-So, in order to have both ~tree-sitter~ and ~ocamllsp~ working at the same time,
-I had to tweak my configuration a little bit.
-
-#+begin_src lua
-lspconfig.ocamllsp.setup({
- -- Necessary to get tree sitter to work with .mli files
- filetypes = vim.list_extend(
- require('lspconfig.server_configurations.ocamllsp')
- .default_config
- .filetypes,
- { 'ocamlinterface' }
- ),
-})
-
-vim.cmd([[au! BufNewFile,BufRead *.mli setfiletype ocamlinterface]])
-#+end_src
-
-And now, I am blessed with a consistent syntax highlighting for my ~mli~ files.
-
-#+CAPTION: Syntax highlighting being absolutely wrong
-#+NAME: fig:wrong-highlingting
-[[../img/good-highlighting.png]]
diff --git a/site/posts/NeovimOCamlTreeSitterAndLSP.md b/site/posts/NeovimOCamlTreeSitterAndLSP.md
new file mode 100644
index 0000000..9769d94
--- /dev/null
+++ b/site/posts/NeovimOCamlTreeSitterAndLSP.md
@@ -0,0 +1,56 @@
+---
+published: 2023-05-01
+modified: 2023-05-02
+tags: ['ocaml', 'neovim']
+abstract: |
+ Can we all agree that witnessing syntax highlighting being absolutely off
+ is probably the most annoying thing that can happen to anybody?
+---
+
+# Neovim, OCaml Interfaces, Tree-Sitter and LSP
+
+Can we all agree that witnessing syntax highlighting being absolutely off is
+probably the most annoying thing that can happen to anybody?
+
+I mean, just look at this horror.
+
+#[Syntax highlighting being absolutely wrong.](/img/wrong-highlighting.png)
+
+What you are looking at is the result of trying to enable `tree-sitter` for
+OCaml hacking and calling it a day. In a nutshell, OCaml `mli` files are
+quickly turning into a random mess of nonsensical colors, and I didn’t know
+why. I tried to blame
+[`tree-sitter-ocaml`](https://github.com/tree-sitter/tree-sitter-ocaml/issues/72),
+but of course I was wrong.
+
+The issue is subtle, and to be honest, I don’t know if I totally grasp it. But
+from my rough understanding, it breaks down as follows.
+
+- `tree-sitter-ocaml` defines two grammars: `ocaml` for the `ml` files, and
+ `ocaml_interface` (but `ocamlinterface` also works) for the `mli` files
+- By default, neovim uses the filetype `ocaml` for `mli` files, so the incorrect
+ parser is being used for syntax highlighting. This explains the root issue
+- Bonus: `ocamllsp` does not recognize the `ocamlinterface` filetype by
+ default (but somehow use the `ocaml.interface` id for `mli` files…[^contrib]).
+
+[^contrib]: There is probably something to be done here.
+
+So, in order to have both `tree-sitter` and `ocamllsp` working at the same
+time, I had to tweak my configuration a little bit.
+
+``` lua
+lspconfig.ocamllsp.setup({
+ filetypes = vim.list_extend(
+ require('lspconfig.server_configurations.ocamllsp')
+ .default_config
+ .filetypes,
+ { 'ocamlinterface' }
+ ),
+})
+
+vim.cmd([[au! BufNewFile,BufRead *.mli setfiletype ocamlinterface]])
+```
+
+And now, I am blessed with a consistent syntax highlighting for my `mli` files.
+
+#[Syntax highlighting being absolutely right.](/img/good-highlighting.png)
diff --git a/site/posts/November2022.md b/site/posts/November2022.md
new file mode 100644
index 0000000..d35e0a9
--- /dev/null
+++ b/site/posts/November2022.md
@@ -0,0 +1,46 @@
+---
+published: 2022-11-19
+modified: 2023-05-09
+tags: ['spatial-shell', 'nanowrimo', 'coqffi']
+series:
+ parent: series/Retrospectives.html
+ prev: posts/September2022.html
+abstract: |
+ Spatial Sway has basically reached the MVP stage, I failed to fully commit
+ to this year’s NaNoWriMo, and someone has worked on adding some support for
+ `coqffi` to `dune`.
+---
+
+# What happened in October and November 2022?
+
+It is November 19 today, and I’m one month and 4 days late for the October
+Retrospective! Truth is, `$WORK`{.bash} has been intense lately, to a point
+where I have not made much progress on my side projects. Anyway.
+
+I have implemented the last feature I was really missing in my daily
+use of Spatial Sway: moving windows to adjacent workspaces. As a
+result, I think I can say that Spatial Sway has really reached the
+“Minimum Viable Product” stage, with a convenient UX, and a nice
+enough UI. It is still lacking when it comes to configurability,
+though. It is the next item of my TODO list, but I have no idea when I
+will implement the support for a configuration file.
+
+Another highlight of the past two months was the
+[NaNoWriMo](https://nanowrimo.org). I took the last week of October and the
+first week of November off to plan and start writing a fiction project for it.
+Writing again was really nice, and I even gave writing fiction in English a
+shot. That made me uncover a bug in the English support of
+[ogam](https://crates.io/crates/ogam), my markup language for fiction writers,
+which led me to publish a fix on Crates.io. However, as soon as I came back to
+`$WORK`{.bash}, my writing spree ended. That’s Okay, though. It gave me plenty
+of ideas for future sessions. Thanks, NaNoWriMo! Sorry to quit so soon, and see
+you next year, maybe.
+
+Finally, a nice surprise of the past month is that [someone has started working
+on adding proper support for `coqffi` to
+`dune`](https://github.com/ocaml/dune/pull/6489), the build system for OCaml
+and Coq! I’m thrilled by this. Thanks,
+[**@Alizter**](https://github.com/Alizter)!
+
+This wraps-up this retrospective. I hope I will have more interesting,
+concrete news to share next month.
diff --git a/site/posts/RankNTypesInOCaml.md b/site/posts/RankNTypesInOCaml.md
new file mode 100644
index 0000000..c133c69
--- /dev/null
+++ b/site/posts/RankNTypesInOCaml.md
@@ -0,0 +1,57 @@
+---
+published: 2022-08-07
+modified: 2022-08-12
+tags: ['ocaml']
+abstract: |
+ In OCaml, it is not possible to write a function whose argument is a
+ polymorphic function. Trying to write such a function results in the
+ type-checker complaining back at you. The trick to be able to write such a
+ function is to use records.
+---
+
+# Writing a Function Whose Argument is a Polymorphic Function in OCaml
+
+In OCaml, it is not possible to write a function whose argument is a
+polymorphic function. Trying to write such a function results in the
+type-checker complaining back at you.
+
+```ocaml
+let foo (type a b) id (x : a) (y : b) = (id x, id y)
+```
+
+```
+Line 1, characters 50-51:
+1 | let foo (type a b) id (x : a) (y : b) = (id x, id y);;
+ ^
+Error: This expression has type b but an expression was expected
+of type a
+```
+
+When OCaml tries to type-check `foo`{.ocaml}, it infers `id`{.ocaml} expects an
+argument of type `a`{.ocaml} because of `id x`{.ocaml}, then fails when trying
+to type-check `id y`{.ocaml}.
+
+The trick to be able to write `foo`{.ocaml} is to use records. Indeed, while
+the argument of a function cannot be polymorphic, the field of a record can.
+This effectively makes it possible to write `foo`{.ocaml}, at the cost of a
+level of indirection.
+
+```ocaml
+type id = {id : 'a. 'a -> 'a}
+
+let foo {id} x y = (id x, id y)
+```
+
+From a runtime perspective, it is possible to tell OCaml to remove the
+introduced indirection with the `unboxed`{.ocaml} annotation. There is nothing
+we can do in the source, though. We need to destruct `id`{.ocaml} in
+`foo`{.ocaml}, and we need to construct it at its call-site.
+
+```ocaml
+g {id = fun x -> x}
+```
+
+As a consequence, this solution is not a silver bullet, but it is an option
+that is worth considering if, *e.g.*, it allows to export a cleaner API to the
+consumer of a module. Personally, I have been considering this trick recently
+to remove the need for a library to be implemented as a functor.
diff --git a/site/posts/RankNTypesInOCaml.org b/site/posts/RankNTypesInOCaml.org
deleted file mode 100644
index 56f359c..0000000
--- a/site/posts/RankNTypesInOCaml.org
+++ /dev/null
@@ -1,54 +0,0 @@
-#+TITLE: Writing a Function Whose Argument is a Polymorphic Function in OCaml
-
-#+SERIES: ./miscellaneous.html
-#+SERIES_PREV: ./DiscoveringCommonLisp.html
-#+SERIES_NEXT: ./NeoVimOcamlInterfacesAndLSP.html
-
-#+BEGIN_EXPORT html
-<div id="history">site/posts/RankNTypesInOCaml.org</div>
-#+END_EXPORT
-
-In OCaml, it is not possible to write a function whose argument is a
-polymorphic function. Trying to write such a function results in the
-type-checker complaining back at you.
-
-#+begin_src ocaml :results verbatim :exports both
-let foo (type a b) id (x : a) (y : b) = (id x, id y)
-#+end_src
-
-#+RESULTS:
-: Line 1, characters 50-51:
-: 1 | let foo (type a b) id (x : a) (y : b) = (id x, id y);;
-: ^
-: Error: This expression has type b but an expression was expected of type a
-
-When OCaml tries to type-check ~foo~, it infers ~id~ expects an
-argument of type ~a~ because of ~id x~, then fails when trying to
-type-check ~id y~.
-
-The trick to be able to write ~foo~ is to use records. Indeed, while
-the argument of a function cannot be polymorphic, the field of a
-record can. This effectively makes it possible to write ~foo~, at the
-cost us a level of indirection.
-
-#+begin_src ocaml :results verbatim :exports code
-type id = {id : 'a. 'a -> 'a}
-
-let foo {id} x y =
- (id x, id y)
-#+end_src
-
-From a runtime perspective, it is possible to tell OCaml to remove the
-introduced indirection with the ~unboxed~ annotation. There is nothing
-we can do in the source, though. We need to destruct ~id~ in ~foo~,
-and we need to construct it at its call-site.
-
-#+begin_src ocaml :exports code
-g {id = fun x -> x}
-#+end_src
-
-As a consequence, this solution is not a silver bullet, but it is an
-option that is worth considering if, /e.g./, it allows to export a
-cleaner API to the consumer of a module[fn::Personally, I have been
-considering this trick recently to remove the need for a library to be
-implemented as a functor].
diff --git a/site/posts/RewritingInCoq.md b/site/posts/RewritingInCoq.md
new file mode 100644
index 0000000..e22f814
--- /dev/null
+++ b/site/posts/RewritingInCoq.md
@@ -0,0 +1,384 @@
+---
+published: 2017-05-13
+tags: ['coq']
+abstract: |
+ The `rewrite`{.coq} tactics are really useful, since they are not limited
+ to the Coq built-in equality relation.
+---
+
+# Rewriting in Coq
+
+I have to confess something. In the codebase of SpecCert lies a shameful
+secret, which takes the form of a set of unnecessary axioms.
+
+I thought I couldn’t avoid them at first, but it was before I heard about
+“generalized rewriting,” setoids and morphisms. Now, I know the truth, and I
+will have to update SpecCert eventually. But, in the meantime, let me try to
+explain how it is possible to rewrite a term in a proof using a ad-hoc
+equivalence relation and, when necessary, a proper morphism.
+
+## Case Study: Gate System
+
+Now, why would anyone want such a thing as “generalized rewriting” when the
+`rewrite`{.coq} tactic works just fine.
+
+The thing is: it does not in some cases. To illustrate my statement, we will
+consider the following definition of a gate in Coq:
+
+```coq
+Record Gate :=
+ { open: bool
+ ; lock: bool
+ ; lock_is_close: lock = true -> open = false
+ }.
+```
+
+According to this definition, a gate can be either open or closed. It can also
+be locked, but if it is, it cannot be open at the same time. To express this
+constrain, we embed the appropriate proposition inside the Record. By doing so,
+we *know* for sure that we will never meet an ill-formed `Gate`{.coq} instance.
+The Coq engine will prevent it, because to construct a gate, one will have to
+prove the `lock_is_close`{.coq} predicate holds.
+
+The `program`{.coq} attribute makes it easy to work with embedded proofs. For
+instance, defining the ”open gate” is as easy as:
+
+```coq
+Require Import Coq.Program.Tactics.
+
+#[program]
+Definition open_gate :=
+ {| open := true
+ ; lock := false
+ |}.
+```
+
+Under the hood, `program`{.coq} proves what needs to be proven, that is the
+`lock_is_close`{.coq} proposition. Just have a look at its output:
+
+```
+open_gate has type-checked, generating 1 obligation(s)
+Solving obligations automatically...
+open_gate_obligation_1 is defined
+No more obligations remaining
+open_gate is defined
+```
+
+In this case, using `Program`{.coq} is a bit like cracking a nut with a
+sledgehammer. We can easily do it ourselves using the `refine`{.coq} tactic.
+
+```coq
+Definition open_gate': Gate.
+ refine ({| open := true
+ ; lock := false
+ |}).
+ intro Hfalse.
+ discriminate Hfalse.
+Defined.
+```
+
+## `Gate`{.coq} Equality
+
+What does it mean for two gates to be equal? Intuitively, we know they have to
+share the same states (`open`{.coq} and `lock`{.coq} is our case).
+
+### Leibniz Equality Is Too Strong
+
+When you write something like `a = b`{.coq} in Coq, the `=`{.coq} refers to the
+`eq`{.coq} function and this function relies on what is called the Leibniz Equality:
+`x`{.coq} and `y`{.coq} are equal iff every property on `A`{.coq} which is true
+of `x`{.coq} is also true of `y`{.coq}.
+
+As for myself, when I first started to write some Coq code, the
+Leibniz Equality was not really something I cared about and I tried to
+prove something like this:
+
+```coq
+Lemma open_gates_are_equal (g g': Gate)
+ (equ : open g = true) (equ' : open g' = true)
+ : g = g'.
+```
+
+Basically, it means that if two doors are open, then they are equal. That made
+sense to me, because by definition of `Gate`{.coq}, a locked door is closed,
+meaning an open door cannot be locked.
+
+Here is an attempt to prove the `open_gates_are_equal`{.coq} lemma.
+
+```coq
+Proof.
+ assert (forall g, open g = true -> lock g = false). {
+ intros [o l h] equo.
+ cbn in *.
+ case_eq l; auto.
+ intros equl.
+ now rewrite (h equl) in equo.
+ }
+ assert (lock g = false) by apply (H _ equ).
+ assert (lock g' = false) by apply (H _ equ').
+ destruct g; destruct g'; cbn in *; subst.
+```
+
+The term to prove is now:
+
+```
+{| open := true; lock := false; lock_is_close := lock_is_close0 |} =
+{| open := true; lock := false; lock_is_close := lock_is_close1 |}
+```
+
+The next tactic I wanted to use `reflexivity`{.coq}, because I'd basically proven
+`open g = open g'`{.coq} and `lock g = lock g'`{.coq}, which meets my definition of equality
+at that time.
+
+Except Coq wouldn’t agree. See how it reacts:
+
+```
+Unable to unify "{| open := true; lock := false; lock_is_close := lock_is_close1 |}"
+ with "{| open := true; lock := false; lock_is_close := lock_is_close0 |}".
+```
+
+It cannot unify the two records. More precisely, it cannot unify
+`lock_is_close1`{.coq} and `lock_is_close0`{.coq}. So we abort and try something
+else.
+
+```coq
+Abort.
+```
+
+### Ah-Hoc Equivalence Relation
+
+This is a familiar pattern. Coq cannot guess what we have in mind. Giving a
+formal definition of “our equality” is fortunately straightforward.
+
+```coq
+Definition gate_eq
+ (g g': Gate)
+ : Prop :=
+ open g = open g' /\ lock g = lock g'.
+```
+
+Because “equality” means something very specific in Coq, we won't say “two
+gates are equal” anymore, but “two gates are equivalent”. That is,
+`gate_eq`{.coq} is an equivalence relation. But “equivalence relation” is also
+something very specific. For instance, such relation needs to be symmetric (`R
+x y -> R y x`{.coq}), reflexive (`R x x`{.coq}) and transitive (`R x y -> R y z
+-> R x z`{.coq}).
+
+```coq
+Require Import Coq.Classes.Equivalence.
+
+#[program]
+Instance Gate_Equivalence
+ : Equivalence gate_eq.
+
+Next Obligation.
+ split; reflexivity.
+Defined.
+
+Next Obligation.
+ intros g g' [Hop Hlo].
+ symmetry in Hop; symmetry in Hlo.
+ split; assumption.
+Defined.
+
+Next Obligation.
+ intros g g' g'' [Hop Hlo] [Hop' Hlo'].
+ split.
+ + transitivity (open g'); [exact Hop|exact Hop'].
+ + transitivity (lock g'); [exact Hlo|exact Hlo'].
+Defined.
+```
+
+Afterwards, the `symmetry`{.coq}, `reflexivity`{.coq} and `transitivity`{.coq}
+tactics also works with `gate_eq`{.coq}, in addition to `eq`{.coq}. We can try
+again to prove the `open_gate_are_the_same`{.coq} lemma and it will
+work[^lemma].
+
+[^lemma]: I know I should have proven an intermediate lemma to avoid code
+ duplication. Sorry about that, it hurts my eyes too.
+
+```coq
+Lemma open_gates_are_the_same:
+ forall (g g': Gate),
+ open g = true
+ -> open g' = true
+ -> gate_eq g g'.
+Proof.
+ induction g; induction g'.
+ cbn.
+ intros H0 H2.
+ assert (lock0 = false).
+ + case_eq lock0; [ intro H; apply lock_is_close0 in H;
+ rewrite H0 in H;
+ discriminate H
+ | reflexivity
+ ].
+ + assert (lock1 = false).
+ * case_eq lock1; [ intro H'; apply lock_is_close1 in H';
+ rewrite H2 in H';
+ discriminate H'
+ | reflexivity
+ ].
+ * subst.
+ split; reflexivity.
+Qed.
+```
+
+## Equivalence Relations and Rewriting
+
+So here we are, with our ad-hoc definition of gate equivalence. We can use
+`symmetry`{.coq}, `reflexivity`{.coq} and `transitivity`{.coq} along with
+`gate_eq`{.coq} and it works fine because we have told Coq `gate_eq`{.coq} is
+indeed an equivalence relation for `Gate`{.coq}.
+
+Can we do better? Can we actually use `rewrite`{.coq} to replace an occurrence
+of `g`{.coq} by an occurrence of `g’`{.coq} as long as we can prove that
+`gate_eq g g’`{.coq}? The answer is “yes”, but it will not come for free.
+
+Before moving forward, just consider the following function:
+
+```coq
+Require Import Coq.Bool.Bool.
+
+Program Definition try_open
+ (g: Gate)
+ : Gate :=
+ if eqb (lock g) false
+ then {| lock := false
+ ; open := true
+ |}
+ else g.
+```
+
+It takes a gate as an argument and returns a new gate. If the former is not
+locked, the latter is open. Otherwise the argument is returned as is.
+
+```coq
+Lemma gate_eq_try_open_eq
+ : forall (g g': Gate),
+ gate_eq g g'
+ -> gate_eq (try_open g) (try_open g').
+Proof.
+ intros g g' Heq.
+Abort.
+```
+
+What we could have wanted to do is: `rewrite Heq`{.coq}. Indeed, `g`{.coq} and `g’`{.coq}
+“are the same” (`gate_eq g g’`{.coq}), so, _of course_, the results of `try_open g`{.coq} and
+`try_open g’`{.coq} have to be the same. Except...
+
+```
+Error: Tactic failure: setoid rewrite failed: Unable to satisfy the following constraints:
+UNDEFINED EVARS:
+ ?X49==[g g' Heq |- relation Gate] (internal placeholder) {?r}
+ ?X50==[g g' Heq (do_subrelation:=Morphisms.do_subrelation)
+ |- Morphisms.Proper (gate_eq ==> ?X49@{__:=g; __:=g'; __:=Heq}) try_open] (internal placeholder) {?p}
+ ?X52==[g g' Heq |- relation Gate] (internal placeholder) {?r0}
+ ?X53==[g g' Heq (do_subrelation:=Morphisms.do_subrelation)
+ |- Morphisms.Proper (?X49@{__:=g; __:=g'; __:=Heq} ==> ?X52@{__:=g; __:=g'; __:=Heq} ==> Basics.flip Basics.impl) eq]
+ (internal placeholder) {?p0}
+ ?X54==[g g' Heq |- Morphisms.ProperProxy ?X52@{__:=g; __:=g'; __:=Heq} (try_open g')] (internal placeholder) {?p1}
+```
+
+What Coq is trying to tell us here —in a very poor manner, I’d say— is actually
+pretty simple. It cannot replace `g`{.coq} by `g’`{.coq} because it does not
+know if two equivalent gates actually give the same result when passed as the
+argument of `try_open`{.coq}. This is actually what we want to prove, so we
+cannot use `rewrite`{.coq} just yet, because it needs this result so it can do
+its magic. Chicken and egg problem.
+
+In other words, we are making the same mistake as before: not telling Coq what
+it cannot guess by itself.
+
+The `rewrite`{.coq} tactic works out of the box with the Coq equality
+(`eq`{.coq}, or most likely `=`{.coq}) because of the Leibniz Equality:
+`x`{.coq} and `y`{.coq} are equal iff every property on `A`{.coq} which is true
+of `x`{.coq} is also true of `y`{.coq}
+
+This is a pretty strong property, and one a lot of equivalence relations do not
+have. Want an example? Consider the relation `R`{.coq} over `A`{.coq} such that
+forall `x`{.coq} and `y`{.coq}, `R x y`{.coq} holds true. Such relation is
+reflexive, symmetric and reflexive. Yet, there is very little chance that given
+a function `f : A -> B`{.coq} and `R’`{.coq} an equivalence relation over
+`B`{.coq}, `R x y -> R' (f x) (f y)`{.coq}. Only if we have this property, we
+would know that we could rewrite `f x`{.coq} by `f y`{.coq}, *e.g.*, in `R' z
+(f x)`{.coq}. Indeed, by transitivity of `R’`{.coq}, we can deduce `R' z (f
+y)`{.coq} from `R' z (f x)`{.coq} and `R (f x) (f y)`{.coq}.
+
+If `R x y -> R' (f x) (f y)`{.coq}, then `f`{.coq} is a morphism because it
+preserves an equivalence relation. In our previous case, `A`{.coq} is
+`Gate`{.coq}, `R`{.coq} is `gate_eq`{.coq}, `f`{.coq} is `try_open`{.coq} and
+therefore `B`{.coq} is `Gate`{.coq} and `R’`{.coq} is `gate_eq`{.coq}. To make
+Coq aware that `try_open`{.coq} is a morphism, we can use the following syntax:
+*)
+
+```coq
+#[local]
+Open Scope signature_scope.
+
+Require Import Coq.Classes.Morphisms.
+
+#[program]
+Instance try_open_Proper
+ : Proper (gate_eq ==> gate_eq) try_open.
+```
+
+This notation is actually more generic because you can deal with functions that
+take more than one argument. Hence, given `g : A -> B -> C -> D`{.coq},
+`R`{.coq} (respectively `R’`{.coq}, `R’’`{.coq} and `R’’’`{.coq}) an equivalent
+relation of `A`{.coq} (respectively `B`{.coq}, `C`{.coq} and `D`{.coq}), we can
+prove `f`{.coq} is a morphism as follows:
+
+```coq
+Add Parametric Morphism: (g)
+ with signature (R) ==> (R') ==> (R'') ==> (R''')
+ as <name>.
+```
+
+Back to our `try_open`{.coq} morphism. Coq needs you to prove the following
+goal:
+
+```
+1 subgoal, subgoal 1 (ID 50)
+
+ ============================
+ forall x y : Gate, gate_eq x y -> gate_eq (try_open x) (try_open y)
+```
+
+Here is a way to prove that:
+
+```coq
+Next Obligation.
+ intros g g' Heq.
+ assert (gate_eq g g') as [Hop Hlo] by (exact Heq).
+ unfold try_open.
+ rewrite <- Hlo.
+ destruct (bool_dec (lock g) false) as [Hlock|Hnlock]; subst.
+ + rewrite Hlock.
+ split; cbn; reflexivity.
+ + apply not_false_is_true in Hnlock.
+ rewrite Hnlock.
+ cbn.
+ exact Heq.
+Defined.
+```
+
+Now, back to our `gate_eq_try_open_eq`{.coq}, we now can use `rewrite`{.coq}
+and `reflexivity`{.coq}.
+
+```coq
+Require Import Coq.Setoids.Setoid.
+
+Lemma gate_eq_try_open_eq
+ : forall (g g': Gate),
+ gate_eq g g'
+ -> gate_eq (try_open g) (try_open g').
+Proof.
+ intros g g' Heq.
+ rewrite Heq.
+ reflexivity.
+Qed.
+```
+
+We did it! We actually rewrite `g`{.coq} as `g’`{.coq}, even if we weren’t able
+to prove `g = g’`{.coq}.
diff --git a/site/posts/RewritingInCoq.v b/site/posts/RewritingInCoq.v
deleted file mode 100644
index f58e9e0..0000000
--- a/site/posts/RewritingInCoq.v
+++ /dev/null
@@ -1,349 +0,0 @@
-(** #<nav><p class="series">./coq.html</p>
- <p class="series-prev">./Ltac.html</p>
- <p class="series-next">./ClightIntroduction.html</p></nav># *)
-
-(** * Rewriting in Coq *)
-
-(** I have to confess something. In the codebase of SpecCert lies a shameful
- secret. It takes the form of a set of unnecessary axioms. I thought I
- couldn’t avoid them at first, but it was before I heard about “generalized
- rewriting,” setoids and morphisms. Now, I know the truth, and I will have
- to update SpecCert eventually. But, in the meantime, let me try to explain
- in this article originally published on #<span id="original-created-at">May
- 13, 2017</span> how it is possible to rewrite a term in a proof using a
- ad-hoc equivalence relation and, when necessary, a proper morphism. *)
-
-(** #<nav id="generate-toc"></nav>#
-
- #<div id="history">site/posts/RewritingInCoq.v</div># *)
-
-(** ** Gate: Our Case Study *)
-
-(** Now, why would anyone want such a thing as “generalized rewriting” when the
- [rewrite] tactic works just fine.
-
- The thing is: it does not in some cases. To illustrate my statement, we will
- consider the following definition of a gate in Coq: *)
-
-Record Gate :=
- { open: bool
- ; lock: bool
- ; lock_is_close: lock = true -> open = false
- }.
-
-(** According to this definition, a gate can be either open or closed. It can
- also be locked, but if it is, it cannot be open at the same time. To express
- this constrain, we embed the appropriate proposition inside the Record. By
- doing so, we _know_ for sure that we will never meet an ill-formed Gate
- instance. The Coq engine will prevent it, because to construct a gate, one
- will have to prove the [lock_is_close] predicate holds.
-
- The [program] attribute makes it easy to work with embedded proofs. For
- instance, defining the ”open gate” is as easy as: *)
-
-Require Import Coq.Program.Tactics.
-
-#[program]
-Definition open_gate :=
- {| open := true
- ; lock := false
- |}.
-
-(** Under the hood, [program] proves what needs to be proven, that is the
- [lock_is_close] proposition. Just have a look at its output:
-
-<<
-open_gate has type-checked, generating 1 obligation(s)
-Solving obligations automatically...
-open_gate_obligation_1 is defined
-No more obligations remaining
-open_gate is defined
->>
-
- In this case, using <<Program>> is a bit like cracking a nut with a
- sledgehammer. We can easily do it ourselves using the [refine] tactic. *)
-
-Definition open_gate': Gate.
- refine ({| open := true
- ; lock := false
- |}).
- intro Hfalse.
- discriminate Hfalse.
-Defined.
-
-(** ** Gate Equality
-
-What does it mean for two gates to be equal? Intuitively, we know they
-have to share the same states ([open] and [lock] is our case).
-
-*** Leibniz Equality Is Too Strong
-
-When you write something like [a = b] in Coq, the [=] refers to the
-[eq] function and this function relies on what is called the Leibniz
-Equality: [x] and [y] are equal iff every property on [A] which is
-true of [x] is also true of [y]
-
-As for myself, when I first started to write some Coq code, the
-Leibniz Equality was not really something I cared about and I tried to
-prove something like this: *)
-
-Lemma open_gates_are_equal (g g': Gate)
- (equ : open g = true) (equ' : open g' = true)
- : g = g'.
-
-(** Basically, it means that if two doors are open, then they are equal. That
-made sense to me, because by definition of [Gate], a locked door is closed,
-meaning an open door cannot be locked.
-
-Here is an attempt to prove the [open_gates_are_equal] lemmas. *)
-
-Proof.
- assert (forall g, open g = true -> lock g = false). {
- intros [o l h] equo.
- cbn in *.
- case_eq l; auto.
- intros equl.
- now rewrite (h equl) in equo.
- }
- assert (lock g = false) by apply (H _ equ).
- assert (lock g' = false) by apply (H _ equ').
- destruct g; destruct g'; cbn in *; subst.
-
-(** The term to prove is now:
-
-<<
-{| open := true; lock := false; lock_is_close := lock_is_close0 |} =
-{| open := true; lock := false; lock_is_close := lock_is_close1 |}
->>
-
-The next tactic I wanted to use [reflexivity], because I'd basically proven
-[open g = open g'] and [lock g = lock g'], which meets my definition of equality
-at that time.
-
-Except Coq wouldn’t agree. See how it reacts:
-
-<<
-Unable to unify "{| open := true; lock := false; lock_is_close := lock_is_close1 |}"
- with "{| open := true; lock := false; lock_is_close := lock_is_close0 |}".
->>
-
-It cannot unify the two records. More precisely, it cannot unify
-[lock_is_close1] and [lock_is_close0]. So we abort and try something
-else. *)
-
-Abort.
-
-(** *** Ah hoc Equivalence Relation
-
-This is a familiar pattern. Coq cannot guess what we have in mind. Giving a
-formal definition of “our equality” is fortunately straightforward. *)
-
-Definition gate_eq
- (g g': Gate)
- : Prop :=
- open g = open g' /\ lock g = lock g'.
-
-(** Because “equality” means something very specific in Coq, we won't say “two
-gates are equal” anymore, but “two gates are equivalent”. That is, [gate_eq] is
-an equivalence relation. But “equivalence relation” is also something very
-specific. For instance, such relation needs to be symmetric ([R x y -> R y x]),
-reflexive ([R x x]) and transitive ([R x y -> R y z -> R x z]). *)
-
-Require Import Coq.Classes.Equivalence.
-
-#[program]
-Instance Gate_Equivalence
- : Equivalence gate_eq.
-
-Next Obligation.
- split; reflexivity.
-Defined.
-
-Next Obligation.
- intros g g' [Hop Hlo].
- symmetry in Hop; symmetry in Hlo.
- split; assumption.
-Defined.
-
-Next Obligation.
- intros g g' g'' [Hop Hlo] [Hop' Hlo'].
- split.
- + transitivity (open g'); [exact Hop|exact Hop'].
- + transitivity (lock g'); [exact Hlo|exact Hlo'].
-Defined.
-
-(** Afterwards, the [symmetry], [reflexivity] and [transitivity] tactics also
-works with [gate_eq], in addition to [eq]. We can try again to prove the
-[open_gate_are_the_same] lemma and it will work[fn:lemma]. *)
-
-Lemma open_gates_are_the_same:
- forall (g g': Gate),
- open g = true
- -> open g' = true
- -> gate_eq g g'.
-Proof.
- induction g; induction g'.
- cbn.
- intros H0 H2.
- assert (lock0 = false).
- + case_eq lock0; [ intro H; apply lock_is_close0 in H;
- rewrite H0 in H;
- discriminate H
- | reflexivity
- ].
- + assert (lock1 = false).
- * case_eq lock1; [ intro H'; apply lock_is_close1 in H';
- rewrite H2 in H';
- discriminate H'
- | reflexivity
- ].
- * subst.
- split; reflexivity.
-Qed.
-
-(** [fn:lemma] I know I should have proven an intermediate lemma to avoid code
-duplication. Sorry about that, it hurts my eyes too.
-
-** Equivalence Relations and Rewriting
-
-So here we are, with our ad-hoc definition of gate equivalence. We can use
-[symmetry], [reflexivity] and [transitivity] along with [gate_eq] and it works
-fine because we have told Coq [gate_eq] is indeed an equivalence relation for
-[Gate].
-
-Can we do better? Can we actually use [rewrite] to replace an occurrence of [g]
-by an occurrence of [g’] as long as we can prove that [gate_eq g g’]? The answer
-is “yes”, but it will not come for free.
-
-Before moving forward, just consider the following function: *)
-
-Require Import Coq.Bool.Bool.
-
-Program Definition try_open
- (g: Gate)
- : Gate :=
- if eqb (lock g) false
- then {| lock := false
- ; open := true
- |}
- else g.
-
-(** It takes a gate as an argument and returns a new gate. If the former is not
-locked, the latter is open. Otherwise the argument is returned as is. *)
-
-Lemma gate_eq_try_open_eq
- : forall (g g': Gate),
- gate_eq g g'
- -> gate_eq (try_open g) (try_open g').
-Proof.
- intros g g' Heq.
-Abort.
-
-(** What we could have wanted to do is: [rewrite Heq]. Indeed, [g] and [g’]
-“are the same” ([gate_eq g g’]), so, _of course_, the results of [try_open g] and
-[try_open g’] have to be the same. Except...
-
-<<
-Error: Tactic failure: setoid rewrite failed: Unable to satisfy the following constraints:
-UNDEFINED EVARS:
- ?X49==[g g' Heq |- relation Gate] (internal placeholder) {?r}
- ?X50==[g g' Heq (do_subrelation:=Morphisms.do_subrelation)
- |- Morphisms.Proper (gate_eq ==> ?X49@{__:=g; __:=g'; __:=Heq}) try_open] (internal placeholder) {?p}
- ?X52==[g g' Heq |- relation Gate] (internal placeholder) {?r0}
- ?X53==[g g' Heq (do_subrelation:=Morphisms.do_subrelation)
- |- Morphisms.Proper (?X49@{__:=g; __:=g'; __:=Heq} ==> ?X52@{__:=g; __:=g'; __:=Heq} ==> Basics.flip Basics.impl) eq]
- (internal placeholder) {?p0}
- ?X54==[g g' Heq |- Morphisms.ProperProxy ?X52@{__:=g; __:=g'; __:=Heq} (try_open g')] (internal placeholder) {?p1}
->>
-
-What Coq is trying to tell us here —in a very poor manner, I’d say— is actually
-pretty simple. It cannot replace [g] by [g’] because it does not know if two
-equivalent gates actually give the same result when passed as the argument of
-[try_open]. This is actually what we want to prove, so we cannot use [rewrite]
-just yet, because it needs this result so it can do its magic. Chicken and egg
-problem.
-
-In other words, we are making the same mistake as before: not telling Coq what
-it cannot guess by itself.
-
-The [rewrite] tactic works out of the box with the Coq equality ([eq], or most
-likely [=]) because of the Leibniz Equality: [x] and [y] are equal iff every
-property on [A] which is true of [x] is also true of [y]
-
-This is a pretty strong property, and one a lot of equivalence relations do not
-have. Want an example? Consider the relation [R] over [A] such that forall [x]
-and [y], [R x y] holds true. Such relation is reflexive, symmetric and
-reflexive. Yet, there is very little chance that given a function [f : A -> B]
-and [R’] an equivalence relation over [B], [R x y -> R' (f x) (f y)]. Only if we
-have this property, we would know that we could rewrite [f x] by [f y], e.g. in
-[R' z (f x)]. Indeed, by transitivity of [R’], we can deduce [R' z (f y)] from
-[R' z (f x)] and [R (f x) (f y)].
-
-If [R x y -> R' (f x) (f y)], then [f] is a morphism because it preserves an
-equivalence relation. In our previous case, [A] is [Gate], [R] is [gate_eq],
-[f] is [try_open] and therefore [B] is [Gate] and [R’] is [gate_eq]. To make Coq
-aware that [try_open] is a morphism, we can use the following syntax: *)
-
-#[local]
-Open Scope signature_scope.
-
-Require Import Coq.Classes.Morphisms.
-
-#[program]
-Instance try_open_Proper
- : Proper (gate_eq ==> gate_eq) try_open.
-
-(** This notation is actually more generic because you can deal with functions
-that take more than one argument. Hence, given [g : A -> B -> C -> D], [R]
-(respectively [R’], [R’’] and [R’’’]) an equivalent relation of [A]
-(respectively [B], [C] and [D]), we can prove [f] is a morphism as follows:
-
-<<
-Add Parametric Morphism: (g)
- with signature (R) ==> (R') ==> (R'') ==> (R''')
- as <name>.
->>
-
-Back to our [try_open] morphism. Coq needs you to prove the following
-goal:
-
-<<
-1 subgoal, subgoal 1 (ID 50)
-
- ============================
- forall x y : Gate, gate_eq x y -> gate_eq (try_open x) (try_open y)
->>
-
-Here is a way to prove that: *)
-
-Next Obligation.
- intros g g' Heq.
- assert (gate_eq g g') as [Hop Hlo] by (exact Heq).
- unfold try_open.
- rewrite <- Hlo.
- destruct (bool_dec (lock g) false) as [Hlock|Hnlock]; subst.
- + rewrite Hlock.
- split; cbn; reflexivity.
- + apply not_false_is_true in Hnlock.
- rewrite Hnlock.
- cbn.
- exact Heq.
-Defined.
-
-(** Now, back to our [gate_eq_try_open_eq], we now can use [rewrite] and
-[reflexivity]. *)
-
-Require Import Coq.Setoids.Setoid.
-
-Lemma gate_eq_try_open_eq
- : forall (g g': Gate),
- gate_eq g g'
- -> gate_eq (try_open g) (try_open g').
-Proof.
- intros g g' Heq.
- rewrite Heq.
- reflexivity.
-Qed.
-
-(** We did it! We actually rewrite [g] as [g’], even if we weren’t able to prove
-[g = g’]. *)
diff --git a/site/posts/September2022.md b/site/posts/September2022.md
new file mode 100644
index 0000000..d004ded
--- /dev/null
+++ b/site/posts/September2022.md
@@ -0,0 +1,116 @@
+---
+published: 2022-09-18
+modified: 2023-05-09
+series:
+ parent: series/Retrospectives.html
+ prev: posts/August2022.html
+ next: posts/November2022.html
+tags: ['spatial-shell', 'meta']
+abstract: |
+ In a nutshell, my latest hobby project (Spatial Sway) works well enough
+ so that I can use it daily, and I have done some unsuccessful experiments
+ for this website.
+---
+
+# What happened in September 2022?
+
+It is September 18 today, and it has already been a month since I
+decided to start these retrospectives. This means it is time to take a
+step back and reflect of what happened these past few thirty days or
+so[^syntax].
+
+[^syntax]: There is the shocking news that I have started to use syntax
+ highlighting again. But let’s not dwelve too much into it just yet.
+
+## Spatial Sway
+
+A few days after publishing my August Retrospective, I have learnt
+the existence of [Material Shell](https://material-shell.com), an extension for
+GNOME 3 that provides a very interesting user experience.
+
+I tried it for a few hours, but the thing kept crashing (it’s
+probably on me, I did not even remember I had Gnome installed on my
+machine, and I would not be surprised the culprit was my dusty setup
+rather than Material Shell itself). The experience remained very
+promising, though. Their “spatial model” especially felt like a very
+good fit for me. Basically, the main idea is that you have a grid of
+windows, with your workspaces acting as the rows. You can navigate
+horizontally (from one workspace to another), or horizontally, and
+you choose how many windows you want to see at once on your screen.
+
+And so for a few hours, I was a bit frustrated by the situation…
+until I learnt about how one can actually manage and extend Sway
+(the Wayland compositor I use for several years now) thanks to its IPC
+protocol. I spend like three days experimenting, first in Rust, then in
+OCaml[^ocaml], and by the end of the week, I had a first working prototype I
+called [Spatial Sway](https://github.com/lthms/spatial-shell). It works pretty
+well, enough at least that I am using it daily for several weeks now. It feels
+clunky at time, but it works well, and I have been able to write a
+[Waybar](https://github.com/Alexays/Waybar) configuration heavily inspired on
+Material Shell UI.
+
+[^ocaml]: This was actually an interesting thought process. I am using OCaml at
+ `$WORK`{.bash} for about more than a year now.
+
+ I have curated a setup that works pretty well, and I am familiar with the
+ development tools. On the contrary, I had not written a line of Rust for at
+ least a year, my Emacs configuration for this language was broken, and I
+ had lost all my fluancy in this language. Still, I was not expecting to
+ pick OCaml when I started this project.
+
+Overall, I am pretty satisfied with this turnout. Writing a hobbyist
+software project is always nice, but the one you can integrate in
+your daily workflow are the best one. The last time I did that was
+[**keyrd**](https://sr.ht/~lthms/keyrd), my little keystrokes counting
+daemon[^keyrcount].
+
+[^keyrcount]: 19,970,965 since I started using it at the time of writing this
+ article
+
+Anyway, lots remain to be said about Spatial Sway, but I might save
+it for a bit later. I still have some key features to implement
+(notably, moving a window to another workspaces), then I will
+probably try to advertise it a bit. I am under the impression this
+project could be of interest for other, and I would love to see it
+used by folks willing to give a Material Shell-like experience a
+try, without having to deal with Gnome Shell. By the way,
+considering Sway is a drop-in replacement for i3, and that it
+implements the exact same IPC protocol, there is no reason why
+Spatial Sway is actually Sway specific, and I will rename it Spatial
+Shell at some point.
+
+#[Mandatory screenshot of Spatial Sway.](/img/spatial-sway-preview.png)
+
+## This Website
+
+On a side note, I have started to refine the layout of this website
+a bit. Similarly, I have written a new, curated home page where I
+want to highlight the most recent things I have published on the
+Internet.
+
+I have been experimenting with
+[Alectryon](https://github.com/cpitclaudel/alectryon/) as a way to replace
+`coqdoc`, to improve the readability of my Coq-related articles. Unfortunately,
+it looks like this tool is missing [a key feature I
+need](https://github.com/cpitclaudel/alectryon/issues/86). I might try to get
+my hand dirty and implement it my self, if I find the time and the motivation
+in the following weeks.
+
+Finally, reading about how [Xe Iaso’s talk about how she generates her
+blog](https://xeiaso.net/talks/how-my-website-works) was very inspiring too me.
+I can only suggest you to have a look.
+
+Though not to the same extend, Ialso think I have spent way too much effort in
+my website. Most of my Coq-related articles are actual Coq program, expect the
+articles about `coqffi` which are actual literate programs. Hell, this website
+itself used to be a literate program of sort, until I stopped using my
+homegrown literate programming toolchain **`cleopatra`** last month. At some
+point, I have even spent a bit of time to ensure most of the pages of this
+website were granted a 100/100 on websites like PageSpeed Insight[^goodnews]. I
+had almost forgot.
+
+[^goodnews]: Good news, I’ve just checked, and it still is!
+
+A lot remains to be done, but watching this talk made me reflect on
+the job done. And opened my eyes to new perspective, too. We will
+see what translates into reality.
diff --git a/site/posts/StackedGit.md b/site/posts/StackedGit.md
new file mode 100644
index 0000000..7e88721
--- /dev/null
+++ b/site/posts/StackedGit.md
@@ -0,0 +1,275 @@
+---
+published: 2022-01-16
+modified: 2022-08-07
+tags: ['stacked-git', 'workflow']
+abstract: |
+ I’ve been using Stacked Git at work since early 2021, and as of January
+ 2022, it has become a cornerstone of my daily workflow.
+---
+
+# How I Use Stacked Git at `$WORK`{.bash}
+
+According to [my Lobste.rs history](https://lobste.rs/s/s6quvg/stacked_git), I
+have run into [Stacked Git](https://stacked-git.github.io) in early April,
+2021, and I remember its promises hit a soft spot. A few weeks later, I was
+submitting [a *pull request* to teach Stacked Git to sign
+commits](https://github.com/stacked-git/stgit/pull/100). It was all I needed to
+start using it at `$WORK`{.bash}, and now it has become a cornerstone of my
+development workflow.
+
+## What is Stacked Git?
+
+Before going any further, it is probably a good idea to take a moment and
+present Stacked Git. The website introduces the tool as follows:
+
+> Stacked Git, *StGit* for short, is an application for managing Git
+> commits as a stack of patches.
+
+There is a few things to unpack here. First and as its name suggests, Stacked
+Git is a tool built on top of Git[^pijul]. It is *not* a brand new VCS, and as
+a consequence you keep to use all your existing tools and plugins[^magit].
+Secondly, Stacked Git helps you curate your Git history, by turning your
+commits into patches, and your branches into stacks of patches. This speaks to
+me, maybe because I have been fascinated by email-based workflows for quite
+some time.
+
+[^pijul]: My main takeaway from my Pijul adventure is connected to this. Git
+ is not limited to the `git` binary. Git comes with a collection of powerful
+ forges, nice editor plugins, and years of good practices.
+
+ To this day, it’s neither the bugs nor the breaking changes that made me
+ quite Pijul. Those were expected. What I naively did not anticipate is the
+ dry feeling that Pijul was just the `pijul` binary, which left me with a
+ lot of tasks to do manually.
+
+[^magit]: I am looking at you, Magit.
+
+To me, the two core features of Stacked Git are (1) allowing you to
+name your commits, and (2) to navigate among them.
+Together, they create a wonderful companion to help you keep your
+history clean.
+
+## My Subset of Stacked Git
+
+I do not want this article to be a Stacked Git tutorial.
+Fortunately, I don’t really use the tool at its full potential.
+I only care about a relatively small subset of commands I feel
+comfortable with and use daily.
+
+First, to decide which commits are part of my “stack of patches,” I
+can count of these commands:
+
+- `stg new NAME` creates an empty commit, and gives it the name
+ `NAME`.
+ Having a way to identify a patch with a meaningful name that is
+ resistant to rebase and amend is very nice.
+ These are two properties commit hashes do not have.
+- `stg uncommit NAME` names the most recent commit under my
+ stack with `NAME` and integrates it into it. I do this when I am
+ tasked to work on a merge request made by a colleague, for
+ instance.
+- `stg commit` removes from my stack its last patch. I do this when
+ said commit has been merged into `master`.
+
+Once my stack of patches is ready, the fun begins.
+
+At a given time, a patch can either be (1) applied, (2) unapplied, or (3)
+hidden. On the one hand, if a patch is applied it is part of the Git history.
+On the other hand, unapplying a patch means removing it from the working branch
+(but not from the stack of patches of Stacked Git). If a patch becomes
+unrelevant, but you don’t want to remove it entierely because it can become
+handy later, you can hide it. A hidden patch sits beside the stack of patches,
+and can be reintegrated if need be.
+
+Analoguous to `git log` ---which allows you to visualize your Git history---,
+`stg series` gives you a view the state of your stack of patches. Patches
+prefixed with `+` (or `>`) are applied, while `-` means the patch is unapplied.
+
+Then,
+
+- `stg pop` unapplies the patch on top of the list of applied
+ patches.
+- `stg push` applies the patch on the bottom of the list of unapplied
+ patches.
+- `stg goto NAME` unapplies or applies the necessary patches so that
+ `NAME` becomes the top patch of the list of applied patches.
+
+`HEAD` and the worktree are updated accordingly.
+
+In addition, `stg sink` and `stg float` allow to reorganize your
+stack of patches, moving patches around.
+Basically, they are like `git rebase -i`, but without having to use
+`$EDITOR`.
+
+Modifying patches is done with `stg refresh`.
+It’s akin to `git commit --amend`, except it is more powerful because
+you can modify any applied patches with the `-p` option.
+I’d always encourage you to `stg goto` first, because `stg refresh
+-p` remains unfortunately error prone (nothing prevents you to target
+the wrong patch).
+But when used carefully, it can be very handy.
+
+Finally, `stg rebase REF` moves your stack of patches on top of `REF`[^rebase].
+It is akin to `git rebase --onto`, but more straightforward. What happens is
+Stacked Git pop all the patches of my stack, reset the `HEAD` of the current
+branch to `REF`, and tries applying the patches one by one In case of
+conflicts, the process stop, and I am left with an empty patch, and a dirty
+worktree with conflicts to solve. The hidden gem is that, contrary to `git
+rebase`, the repository is not “in the middle of a rebase.”
+
+Suppos there are many conflicting patches still waiting in my stack of patches,
+and an urgent task I need to take care of first. I can just leave them here. I
+can switch to another branch, and when I come back, I get my patches back. I
+call this feature “incremental rebases.”
+
+[^rebase]: Stacked Git is supposedly able to detect, during a rebase, which of
+ your patches have been applied to your target branch. I’d rather use `stg
+ uncommit`{.bash} before do the rebase, though.
+
+And that is basically it. In a nutshell, Stacked Git equips commits with the
+same features as branches.
+
+## My Stacked Git Workflow
+
+As mentioned in the introduction of this article, Stacked Git has become a
+cornerstone of my workflow. I’ve been asked a few times what this workflow is,
+and why Magit is not enough[^magit2]. So let’s try to do that. But first, a
+warning. Yes, because Stacked Git is only a wrapper above Git, everything I
+will explain can be achieved using Git alone, especially if you are a Magit
+wizard.
+
+[^magit2]: It’s always about Magit. ;)
+
+Stacked Git makes just everything so more convenient to me.
+
+### Planning My Commits Ahead Of Time
+
+I’ve been introduced to Git with a pretty simple workflow: I am
+supposed to start working on a feature, and once it’s ready, I
+can commit, and move on to the next task on my todo list.
+
+To me, this approach is backward.
+It makes you set your intent after the fact.
+With Stacked Git, I often try to plan my final history /before
+writing the very first line of code/.
+Using `stack new`, I create my patches, and take the time to write
+their description.
+It helps me visualizing where I want to go.
+Then, I use `stack goto` to go back to the beginning of my stack,
+and start working.
+
+It is not, and cannot be, an exact science. I often have to refine
+them as my work progresses.
+Yet, I think my Git history is cleaner, more focused, since I have
+started this exercise.
+
+### Getting My Fixup Commits Right
+
+Reviews are a fundamental aspect of a software developer job.
+At `$WORK`, we use Gitlab and their merge requests workflow,
+which I find very annoying, because it does not provide meaningful
+ways to compare two versions of your submission[^gitlab].
+
+[^gitlab]: There is a notion of “versions” in Gitlab, but its ergonomics fall
+ short of my expectations for such tool.
+
+What we end up doing is creating “fixup commits”, and we push them
+to Gitlab so that reviewers can easily verify that their feedback
+have correctly been taken into account.
+
+A fixup commit is a commit that will eventually be squashed into
+another.
+You can understand it as a delayed `git commit --amend`.
+Git has some built-in features to manipulate them.
+You create them with `git commit --fixup=<HASH>`, and they are
+interpreted in a specific manner by `git rebase -i`.
+But they have always felt to me like a sordid hack.
+It is way too easy to create a fixup commit that targets the wrong
+commit, and you can end up with strange conflicts when you finally
+squash them.
+That being said, if used carefully, they are a powerful tool to
+keep a Git history clean.
+
+I am not sure we are using them carefully, though.
+
+Some reviews can be excruciating, with dozens of comments to
+address, and theoretically as many fixup commits to create.
+Then you push all of them on Gitlab, and days later, after the
+green light from the reviewer, you get to call `git rebase`
+and discover your history is broken, you have tones of conflicts
+to fix, and you’re good for a long afternoon of untangling.
+
+The main reason behind this mess is that you end up fixing a commit
+from the `HEAD` of your working branch, not the commit itself.
+But with Stacked Git, things are different.
+With `stg goto`, I put my working tree in the best state possible
+to fix a commit: the commit itself.
+I can use `stg new` to create a fixup commit, with a meaningful
+name.
+Then, I am forced to deal with the potential conflicts it brings
+when I call `stg push`.
+
+Once my reviewer is happy with my work, I can call `stg squash`.
+It is less automated than `git rebase -i`, but the comfort I gained
+during the development is worth this little annoyance.
+
+### Managing Stacked Merge Requests
+
+At `$WORK`, we are trying to change how we deliver new features to
+our `master` branch.
+More precisely, we want to merge smaller contributions more
+frequently.
+We have had our fair share of large and complex merge requests that
+were a nightmare to review in the past, and it’s really not a fun
+position to be put in.
+
+For a few months, I have been involved in a project wherein we
+decided /not/ to fall in the same trap again.
+We agreed on a “planning of merge requests” and started working.
+The first merge request was soon opened.
+We’ve nominated a “owner” to take care of the review, and the rest
+of the team carried on.
+Before the first merge request was merged, the second one was
+declared ready, and another owner was appointed.
+Then, the owner of the first merge request had a baby, and yours
+truly ended up having to manage two interdependent merge requests.
+
+It turns out Stacked Git is a wonderful tool to help me keep this
+under control.
+
+I only have one branch, and I use the same workflow to deal with
+feedbacks, even if they are coming from more than one one merge
+request.
+To remember the structure of everything, I just prefix the name of
+my patches with a merge request nickname.
+So my stack will look something like this:
+
+```
++ mr1-base
++ mr1-tests
++ mr1-doc
+> mr2-command
+- mr2-tests
+```
+
+A reviewer leaves a hard-truth comment that requires a significant rework of
+the oldest merge request? `stg goto` reverts my worktree in the appropriate
+state, and `stg push` allows me to deal with conflicts one patch at a time. If
+at some point I need to spend more time on the oldest merge request, I can
+continue my work, knowing the patches related to the newest one are awaiting in
+my stack.
+
+The most annoying part is when the time comes to push everything. I need to
+`stg goto` at the last patch of each merge request, and `git push
+HEAD:the-branch`. It’s not horrible. But I will probably try to automate it at
+some point.
+
+## Conclusion
+
+Overall, I am really thankful to Stacked Git’s authors! Thank you! You are
+making my interactions with Git fun and carefree. You provide me some of the
+convenience of patch-based VCS like [Darcs](http://darcs.net) and
+[Pijul](https://pijul.org), but without sacrificing the power of Git.
+
+I encourage anyone to at least give it a try, and I really hope I
+will be able to contribute back to Stacked Git in the near future.
diff --git a/site/posts/StackedGit2.md b/site/posts/StackedGit2.md
new file mode 100644
index 0000000..ac38e92
--- /dev/null
+++ b/site/posts/StackedGit2.md
@@ -0,0 +1,138 @@
+---
+published: 2023-01-16
+tags: ['stacked-git', 'workflow']
+abstract: |
+ One year has passed, and I keep using Stacked Git almost daily. How I am
+ using it has slightly changed, though.
+---
+
+# How I Keep Using Stacked Git at `$WORK`{.bash}
+
+One year ago, I have published an article summarizing [my experience using
+Stacked Git at `$WORK`{.bash}](/posts/StackedGit.html). Twelve months later,
+enough has changed to motivate a spin-off piece.
+
+## Stacked Git is *Fast*
+
+Firstly, it is important to state that my main complain about
+Stacked Git is now a thing of the past[^edit]! Stacked Git does not feel slow
+anymore, and far from it. This is because [Stacked Git 2.0 has been rewritten
+in Rust](https://github.com/stacked-git/stgit/discussions/185). While RiiR
+(*Rewrite it in Rust*) is a running meme on the Internet, in this particular
+case, the result is very exciting.
+
+[^edit]: For fairness, I have removed the related section in my previous
+ write-up.
+
+Thanks to the performance boost, my Zsh prompt does not take 0.1s to
+appear!
+
+Speaking of Zsh prompt, basically what I ended up displaying is `(<TOP PATCH
+NAME> <APPLIED PATCHES COUNT>/<PATCHSET SIZE> <HIDDEN PATCHES COUNT)`. For
+instance, `(fix-1337 1/2 3)`.
+
+In case you want to take inspiration in my somewhat working configuration, here
+is the snippet of interest.
+
+```bash
+local series_top="$(stg top 2> /dev/null)"
+local total="$(stg series 2> /dev/null | wc -l)"
+local hidden="$(stg series --hidden 2> /dev/null | wc -l)"
+
+if [[ "${total}" -gt 0 ]]; then
+ local not_applied="$(stg series | grep -E '^-' | wc -l)"
+ local applied="$(($total - $not_applied))"
+
+ if [[ -z "${series_top}" ]]; then
+ series_top="·"
+ fi
+
+ echo -n "(${status_color}${series_top} ${applied}/${total} ${hidden})"
+ echo -n " ($(current_branch))"
+fi
+```
+
+## Branchless Workflow
+
+Last year, I was using Stacked Git on top of git branches. More precisely, I
+had one branch for each (stack of) Merge Request. It worked well, because my
+typical MR counted 3 to 4 commits in average.
+
+Fast forward today, and things have changed on this front too. In a nutshell, I
+have become a “one commit per MR” maximalist of sort[^onecommit]. I find this
+approach very effective to get more focused reviews, and to reduce the time it
+takes for a given MR to be integrated into the main branch.
+
+[^onecommit]: It goes without saying that this approach comes with its set of
+ drawbacks too.
+
+ During the past year, I’ve pushed fairly large commits which could have
+ been splitted into several smaller ones, for the sake of keeping my “one
+ commit per MR” policy. I have also had to manage large stacks of MRs.
+
+My previous approach based on git branches did not scale well with
+this new mindset, and during the course of the year, I stopped using
+branches altogether[^branchless].
+
+[^branchless]: I have not invented the branchless workflow, of
+ course.
+
+ After it was first published, someone posted a link to my Stacked Git
+ article on Hacker News, and [*@arxanas* posted a comment about
+ `git-branchless`](https://news.ycombinator.com/item?id=29959224). I tried
+ the tool, and even if it never clicked for me, I was really compelled by
+ its core ideas.
+
+ Similarly, [Drew DeVault has published a complete article on its own
+ branchless workflow in
+ 2020](https://drewdevault.com/2020/04/06/My-weird-branchless-git-workflow.html).
+
+These days, I proceed as follows.
+
+1. I name each patch after the branch to which I will push it on our
+ upstream Git remote.
+2. 99% of the time, I push my work using `git push -f upstream @:$(stg
+ top)`{.bash}
+3. I created a small git plugin I called `git-prepare` which allows
+ me to select one of the patch of my current patchset using `fzf`,
+ and which pops all other patches that are currently applied.
+
+`git-prepare` is really straightforward:
+
+```bash
+#!/bin/sh
+patch=$(stg series -P | fzf)
+
+if [[ ! $? -eq 0 ]] ; then
+ exit $?
+fi
+
+if [ -n "$(stg series -A)" ]; then
+ stg pop -a
+fi
+
+stg push ${patch}
+```
+
+The main hurdle which I still need to figure out is how to deal with
+stacked MRs. Currently, this is very manual. I need to remember
+which commit belongs to the stack, the order and dependencies of
+these commits, and I need to publish each commit individually using
+`stg push; git push @:$(stg top)`{.bash}.
+
+The pragmatic answer is definitely to come back to git branches *for
+this particular use case*, but it's not the *fun* answer. So from
+time to time, I try to experiment alternative approaches. My current
+intuition is that, by adopting a naming convention for my patches, I
+could probably implement a thin tooling on top of Stacked Git to
+deal with dependents commits.
+
+## Conclusion
+
+Putting aside stacked MRs for now, I am really satisfied with my
+workflow. It’s very lightweight and intuitive, and working without
+Stacked Git now feels backward and clunky.
+
+So I will take this opportunity to thank one more time Stacked Git’s
+authors and contributors. You all are making my professional like
+easier with your project.
diff --git a/site/posts/StackedGitPatchTheory.md b/site/posts/StackedGitPatchTheory.md
new file mode 100644
index 0000000..5cd81be
--- /dev/null
+++ b/site/posts/StackedGitPatchTheory.md
@@ -0,0 +1,119 @@
+---
+published: 2023-01-26
+tags: ['stacked-git', 'ideas']
+abstract: |
+ Could patch dependencies could help reduce the friction my branchless
+ workflow suffers from when it comes to stacked MRs?
+---
+
+# Patch Dependencies for Stacked Git
+
+Every time I catch myself thinking about dependencies between
+changeset of a software project, the fascinating field of patch
+theories comes to my mind.
+
+A “patch theory” usually refers to the mathematical foundation behind
+the data model of so-called Patch-based DVCS like Darcs and
+Pijul. More precisely, a patch theory is an encoding of the state of a
+repository, equipped with operations (gathered in so-called patches,
+not to be confused with `GNU diff` patches) one can do to this
+state. For instance, my rough understanding of Pijul’s patch theory is
+that a repository is an oriented graph of lines, and a patch is a set
+of operations onto this graph.
+
+An interesting aspect of patch theory is that it requires a partial
+order for its patches, from which a Patch-based DVCS derives a
+dependency graph. In a nutshell, a patch $P$ depends on the patches
+which are responsible for the presence of the lines that $P$
+modifies.
+
+I have always found this idea of a dependency graph for the patches
+of a repository fascinating, because I though it would be a very
+valuable tool in the context of software development.
+
+I wanted to slightly change the definition of what a patch
+dependency is, though. See, the partial order of Darcs and Pijul
+focus on syntactic dependencies: the relation between lines in text
+files. They need that to reconstruct these text files in the file
+system of their users. As a software developers writing these text
+files, I quickly realized these dependencies were of little interest
+to me, though. What I wanted to be able to express was that a
+feature introduced by a patch $P$ relied on a fix introduced by a
+patch $P'$.
+
+I have experimented with Darcs and Pijul quite a bit, with this idea
+stuck in the back of my mind. At the end of this journey, I
+convinced myself[^caution] (1) this beautiful idea I
+had simply could not scale, and (2) neither I nor our industry is
+ready to give up on the extensive ecosystem that has been built on top
+of `git` just yet. As a consequence, my interest in Patch-based DVCS
+decreased sensibly.
+
+[^caution]: I am not trying to convince you, per say. This is a very personal
+ and subjective feedback, it does not mean someone else couldn't reach a
+ different conclusion.
+
+Until very recently, that is. I got reminded of the appeal of a
+dependency graph for changesets when I started adopted a Gitlab
+workflow centered around Stacked Git and smaller, sometimes
+interdependent MRs.
+
+A manually curated graph dependency for a whole repository is not
+practical, but what about my local queue of patches, patiently
+waiting to be integrated into the upstream repository I am
+contributing too? Not only does it look like a more approachable
+task, it could make synchronizing my stacked MRs a lot easier.
+
+The workflow I have in mind would proceed as follows.
+
+- Stacked Git’s `new` and `edit` commands could be extended to let
+ developers declare dependencies between their patches. It would be
+ the commands’ responsibility to enforce the wellfoundness of the
+ dependency graph (*e.g.*, prevent the introduction of cycles in the
+ graph, and maybe diamonds too[^diamond]).
+- The `series` command could be improved to display the resulting
+ dependency graph.
+- `push` and `pop` would automatically take care (pushing or popping)
+ of the selected patch(es) dependencies.
+- Ideally, Stacked Git would get a new command `prepare <PATCH NAME>`
+ which would pop every patches applied, then only only push `<PATCH
+ NAME>` and its dependencies (in the reverse order). Developers could
+ fix conflicts if need be. That is, Stacked Git would not be
+ responsible for the consistency or correctness of the dependency
+ graph.
+- Stacked Git could get commands to detect potential issues with the
+ dependency graph specified by the developer (mostly consisting in
+ dry-run of `prepare` to check if it would lead to conflicts).
+
+[^diamond]: At least in a first version. There is definitely value in being
+ able to work with two independent patches in conjunction with a third one
+ that needs them both. That being said, our goal here is to organize our
+ work locally, and if it is made easier by declaring artificial dependency,
+ this is a pragmatic sacrifice I am personally willing to make.
+
+Because what we want is semantic dependencies, not syntactic dependencies
+between patches, I really think it makes a lot of sense to completely delegate
+the dependencies declaration to the developer[^future]. The very mundane
+example that convinced me is the `CHANGELOG` file any mature software project
+ends up maintaining. If the contribution guidelines require to modify the
+`CHANGELOG` file in the same commit as a feature is introduced, then the
+patches to two independent features will systematically conflict. This does not
+mean, from my patch queue perspective, I should be forced to `pop` the first
+commit before starting to work on the second one. It just means that when I
+call `stg prepare`, I can have to fix a conflict, but fixing Git conflicts is
+part of the job after all[^rerere]. If for some reasons solving a conflict
+proves to be too cumbersome, I can always acknowledge that, and declare a new
+dependency to the appropriate patch. It only means I and my reviewers will be
+constrained a bit more than expected when dealing with my stack of MRs.
+
+[^future]: Further versions of Stacked Git could explore computing the
+ dependency graph automatically, similarly to what Git does. But I think
+ that if Darcs and Pijul told us anything, it's that this computation is far
+ from being trivial.
+
+[^rerere]: And we have tools to help us. I wonder to which extends `git rerere`
+ could save the day in some cases, for instance.
+
+I am under the impression that this model extends quite nicely the current way
+Stacked Git is working. To its core, it extends its data model to constraint a
+bit `push` and `pop`, and empowers developers to organize a bit its local mess.
diff --git a/site/posts/StronglySpecifiedFunctions.org b/site/posts/StronglySpecifiedFunctions.org
deleted file mode 100644
index 4a608c7..0000000
--- a/site/posts/StronglySpecifiedFunctions.org
+++ /dev/null
@@ -1,16 +0,0 @@
-#+TITLE: A Series on Strongly-Specified Functions in Coq
-
-#+SERIES: ./coq.html
-#+SERIES_NEXT: ./Ltac.html
-
-Using dependent types and the ~Prop~ sort, it becomes possible to specify
-functions whose arguments and results are constrained by properties. Using such
-a “strongly-specified” function requires to provide a proof that the supplied
-arguments satisfy the expected properties, and allows for soundly assuming the
-results are correct too. However, implementing dependently-typed functions can
-be challenging. In this series, we explore several approaches available to Coq
-developers.
-
-- [[./StronglySpecifiedFunctionsRefine.html][Implementing Strongly-Specified Functions with the ~refine~ Tactic]] ::
-
-- [[./StronglySpecifiedFunctionsProgram.html][Implementing Strongly-Specified Functions with the ~Program~ Framework]] ::
diff --git a/site/posts/StronglySpecifiedFunctionsProgram.md b/site/posts/StronglySpecifiedFunctionsProgram.md
new file mode 100644
index 0000000..da07982
--- /dev/null
+++ b/site/posts/StronglySpecifiedFunctionsProgram.md
@@ -0,0 +1,553 @@
+---
+published: 2017-01-01
+tags: ['coq']
+series:
+ parent: series/StronglySpecifiedFunctions.html
+ prev: posts/StronglySpecifiedFunctionsRefine.html
+abstract: |
+ `Program`{.coq} is the heir of the `refine`{.coq} tactic. It gives you a
+ convenient way to embed proofs within functional programs that are supposed
+ to fade away during code extraction.
+---
+
+# Implementing Strongly-Specified Functions with the `Program`{.coq} Framework
+
+## The Theory
+
+If I had to explain `Program`{.coq}, I would say `Program`{.coq} is the heir of
+the `refine`{.coq} tactic. It gives you a convenient way to embed proofs within
+functional programs that are supposed to fade away during code extraction. But
+what do I mean when I say "embed proofs" within functional programs? I found
+two ways to do it.
+
+### Invariants
+
+First, we can define a record with one or more fields of type
+`Prop`{.coq}. By doing so, we can constrain the values of other fields. Put
+another way, we can specify invariant for our type. For instance, in
+[SpecCert](https://github.com/lthms/SpecCert), I have defined the memory
+controller's SMRAMC register as follows:
+
+```coq
+Record SmramcRegister := {
+ d_open: bool;
+ d_lock: bool;
+ lock_is_close: d_lock = true -> d_open = false;
+}.
+```
+
+So `lock_is_closed`{.coq} is an invariant I know each instance of
+`SmramcRegister` will have to comply with, because every time I
+will construct a new instance, I will have to prove
+`lock_is_closed`{.coq} holds true. For instance:
+
+```coq
+Definition lock (reg: SmramcRegister)
+ : SmramcRegister.
+ refine ({| d_open := false; d_lock := true |}).
+```
+
+Coq leaves us this goal to prove.
+
+```
+reg : SmramcRegister
+============================
+true = true -> false = false
+```
+
+This sound reasonable enough.
+
+```coq
+Proof.
+ trivial.
+Defined.
+```
+
+We have witness in my previous article about strongly-specified
+functions that mixing proofs and regular terms may leads to
+cumbersome code.
+
+From that perspective, `Program`{.coq} helps. Indeed, the `lock`{.coq} function
+can also be defined as follows:
+
+```coq
+From Coq Require Import Program.
+
+#[program]
+Definition lock' (reg: SmramcRegister)
+ : SmramcRegister :=
+ {| d_open := false
+ ; d_lock := true
+ |}.
+```
+
+### Pre and Post Conditions
+
+Another way to "embed proofs in a program" is by specifying pre-
+and post-conditions for its component. In Coq, this is done using
+sigma-types.
+
+On the one hand, a precondition is a proposition a function input has to
+satisfy in order for the function to be applied. For instance, a precondition
+for `head : forall {a}, list a -> a`{.coq} the function that returns the first
+element of a list `l`{.coq} requires `l`{.coq} to contain at least one element.
+We can write that using a sigma-type. The type of `head`{.coq} then becomes
+`forall {a} (l: list a | l <> []) : a`{.coq}.
+
+On the other hand, a post condition is a proposition a function
+output has to satisfy in order for the function to be correctly
+implemented. In this way, `head` should in fact return the first
+element of `l`{.coq} and not something else.
+
+`Program`{.coq} makes writing this specification straightforward.
+
+```coq
+#[program]
+Definition head {a} (l : list a | l <> [])
+ : { x : a | exists l', x :: l' = l }.
+```
+
+We recall that because `{ l: list a | l <> [] }`{.coq} is not the same as `list
+a`{.coq}, in theory we cannot just compare `l`{.coq} with [x :: l'] (we need to
+use `proj1_sig`{.coq}). One benefit on `Program`{.coq} is to deal with it using
+an implicit coercion.
+
+Note that for the type inference to work as expected, the
+unwrapped value (here, `x :: l'`{.coq}) needs to be the left operand of
+`=`{.coq}.
+
+Now that `head`{.coq} have been specified, we have to implement it.
+
+```coq
+#[program]
+Definition head {a} (l: list a | l <> [])
+ : { x : a | exists l', cons x l' = l } :=
+ match l with
+ | x :: l' => x
+ | [] => !
+ end.
+
+Next Obligation.
+ exists l'.
+ reflexivity.
+Qed.
+```
+
+I want to highlight several things here:
+
+- We return `x`{.coq} (of type `a`{.coq}) rather than a sigma-type, then
+ `Program`{.coq} is smart enough to wrap it. To do so, it tries to prove the post
+ condition and because it fails, we have to do it ourselves (this is the
+ Obligation we solve after the function definition.)
+- The `[]`{.coq} case is absurd regarding the precondition, we tell Coq that
+ using the bang (`!`{.coq}) symbol.
+
+We can have a look at the extracted code:
+
+```ocaml
+(** val head : 'a1 list -> 'a1 **)
+let head = function
+| Nil -> assert false (* absurd case *)
+| Cons (a, _) -> a
+```
+
+The implementation is pretty straightforward, but the pre- and
+post conditions have faded away. Also, the absurd case is
+discarded using an assertion. This means one thing: [head] should
+not be used directly from the Ocaml world. "Interface" functions
+have to be total. *)
+
+## The Practice
+
+```coq
+From Coq Require Import Lia.
+```
+
+I have challenged myself to build a strongly specified library. My goal was to
+define a type `vector : nat -> Type -> Type`{.coq} such as `vector a n`{.coq}
+is a list of `n`{.coq} instance of `a`{.coq}.
+
+```coq
+Inductive vector (a : Type) : nat -> Type :=
+| vcons {n} : a -> vector a n -> vector a (S n)
+| vnil : vector a O.
+
+Arguments vcons [a n] _ _.
+Arguments vnil {a}.
+```
+
+I had three functions in mind: `take`{.coq}, `drop`{.coq} and `extract`{.coq}.
+I learned few lessons. My main take-away remains: do not use sigma-types,
+`Program`{.coq} and dependent-types together. From my point of view, Coq is not
+yet ready for this. Maybe it is possible to make those three work together, but
+I have to admit I did not find out how. As a consequence, my preconditions are
+defined as extra arguments.
+
+To be able to specify the post conditions my three functions and
+some others, I first defined `nth`{.coq} to get the _nth_ element of a
+vector.
+
+My first attempt to write `nth`{.coq} was a failure.
+
+```coq
+#[program]
+Fixpoint nth {a n}
+ (v : vector a n) (i : nat) {struct v}
+ : option a :=
+ match v, i with
+ | vcons x _, O => Some x
+ | vcons x r, S i => nth r i
+ | vnil, _ => None
+ end.
+```
+
+raised an anomaly.
+
+```coq
+#[program]
+Fixpoint nth {a n}
+ (v : vector a n) (i : nat) {struct v}
+ : option a :=
+ match v with
+ | vcons x r =>
+ match i with
+ | O => Some x
+ | S i => nth r i
+ end
+ | vnil => None
+ end.
+```
+
+With `nth`{.coq}, it is possible to give a very precise definition of
+`take`{.coq}:
+
+```coq
+#[program]
+Fixpoint take {a n}
+ (v : vector a n) (e : nat | e <= n)
+ : { u : vector a e | forall i : nat,
+ i < e -> nth u i = nth v i } :=
+ match e with
+ | S e' => match v with
+ | vcons x r => vcons x (take r e')
+ | vnil => !
+ end
+ | O => vnil
+ end.
+
+Next Obligation.
+ now apply le_S_n.
+Defined.
+
+Next Obligation.
+ induction i.
+ + reflexivity.
+ + apply e0.
+ now apply Lt.lt_S_n.
+Defined.
+
+Next Obligation.
+ now apply PeanoNat.Nat.nle_succ_0 in H.
+Defined.
+
+Next Obligation.
+ now apply PeanoNat.Nat.nlt_0_r in H.
+Defined.
+```
+
+As a side note, I wanted to define the post condition as follows:
+`{ v': vector A e | forall (i : nat | i < e), nth v' i = nth v i
+}`{.coq}. However, this made the goals and hypotheses become very hard
+to read and to use. Sigma-types in sigma-types: not a good
+idea.
+
+```ocaml
+(** val take : 'a1 vector -> nat -> 'a1 vector **)
+
+let rec take v = function
+| O -> Vnil
+| S e' ->
+ (match v with
+ | Vcons (_, x, r) -> Vcons (e', x, (take r e'))
+ | Vnil -> assert false (* absurd case *))
+```
+
+Then I could tackle `drop` in a very similar manner:
+
+```coq
+#[program]
+Fixpoint drop {a n}
+ (v : vector a n) (b : nat | b <= n)
+ : { v': vector a (n - b) | forall i,
+ i < n - b -> nth v' i = nth v (b + i) } :=
+ match b with
+ | 0 => v
+ | S n => (match v with
+ | vcons _ r => (drop r n)
+ | vnil => !
+ end)
+ end.
+
+Next Obligation.
+ now rewrite <- Minus.minus_n_O.
+Defined.
+
+Next Obligation.
+ induction n;
+ rewrite <- eq_rect_eq;
+ reflexivity.
+Defined.
+
+Next Obligation.
+ now apply le_S_n.
+Defined.
+
+Next Obligation.
+ now apply PeanoNat.Nat.nle_succ_0 in H.
+Defined.
+```
+
+The proofs are easy to write, and the extracted code is exactly what one might
+want it to be:
+
+```ocaml
+(** val drop : 'a1 vector -> nat -> 'a1 vector **)
+let rec drop v = function
+| O -> v
+| S n ->
+ (match v with
+ | Vcons (_, _, r) -> drop r n
+ | Vnil -> assert false (* absurd case *))
+```
+
+But `Program`{.coq} really shone when it comes to implementing extract. I just
+had to combine `take`{.coq} and `drop`{.coq}. *)
+
+```coq
+#[program]
+Definition extract {a n} (v : vector a n)
+ (e : nat | e <= n) (b : nat | b <= e)
+ : { v': vector a (e - b) | forall i,
+ i < (e - b) -> nth v' i = nth v (b + i) } :=
+ take (drop v b) (e - b).
+
+
+Next Obligation.
+ transitivity e; auto.
+Defined.
+
+Next Obligation.
+ now apply PeanoNat.Nat.sub_le_mono_r.
+Defined.
+
+Next Obligation.
+ destruct drop; cbn in *.
+ destruct take; cbn in *.
+ rewrite e1; auto.
+ rewrite <- e0; auto.
+ lia.
+Defined.
+```
+
+The proofs are straightforward because the specifications of `drop`{.coq} and
+`take`{.coq} are precise enough, and we do not need to have a look at their
+implementations. The extracted version of `extract`{.coq} is as clean as we can
+anticipate.
+
+```ocaml
+(** val extract : 'a1 vector -> nat -> nat -> 'a1 vector **)
+let extract v e b =
+ take (drop v b) (sub e b)
+```
+
+I was pretty happy, so I tried some more. Each time, using `nth`{.coq}, I managed
+to write a precise post condition and to prove it holds true. For instance,
+given `map`{.coq} to apply a function `f`{.coq} to each element of a vector `v`{.coq}:
+
+```coq
+#[program]
+Fixpoint map {a b n} (v : vector a n) (f : a -> b)
+ : { v': vector b n | forall i,
+ nth v' i = option_map f (nth v i) } :=
+ match v with
+ | vnil => vnil
+ | vcons a v => vcons (f a) (map v f)
+ end.
+
+Next Obligation.
+ induction i.
+ + reflexivity.
+ + apply e.
+Defined.
+```
+
+I also managed to specify and write `append`{.coq}:
+
+```
+#[program]
+Fixpoint append {a n m}
+ (v : vector a n) (u : vector a m)
+ : { w : vector a (n + m) | forall i,
+ (i < n -> nth w i = nth v i) /\
+ (n <= i -> nth w i = nth u (i - n))
+ } :=
+ match v with
+ | vnil => u
+ | vcons a v => vcons a (append v u)
+ end.
+
+Next Obligation.
+ split.
+ + now intro.
+ + intros _.
+ now rewrite PeanoNat.Nat.sub_0_r.
+Defined.
+
+Next Obligation.
+ rename wildcard' into n.
+ destruct (Compare_dec.lt_dec i (S n)); split.
+ + intros _.
+ destruct i.
+ ++ reflexivity.
+ ++ cbn.
+ specialize (a1 i).
+ destruct a1 as [a1 _].
+ apply a1.
+ auto with arith.
+ + intros false.
+ lia.
+ + now intros.
+ + intros ord.
+ destruct i.
+ ++ lia.
+ ++ cbn.
+ specialize (a1 i).
+ destruct a1 as [_ a1].
+ apply a1.
+ auto with arith.
+Defined.
+```
+
+Finally, I tried to implement `map2`{.coq} that takes a vector of `a`{.coq}, a vector of
+`b`{.coq} (both of the same size) and a function `f : a -> b -> c`{.coq} and returns a
+vector of `c`{.coq}.
+
+First, we need to provide a precise specification for `map2`{.coq}. To do that, we
+introduce `option_app`{.coq}, a function that Haskellers know all to well as being
+part of the `Applicative`{.haskell} type class.
+
+```coq
+Definition option_app {a b}
+ (opf: option (a -> b))
+ (opx: option a)
+ : option b :=
+ match opf, opx with
+ | Some f, Some x => Some (f x)
+ | _, _ => None
+end.
+```
+
+We thereafter use `<$>`{.coq} as an infix operator for `option_map`{.coq} and `<*>`{.coq} as
+an infix operator for `option_app`{.coq}. *)
+
+```coq
+Infix "<$>" := option_map (at level 50).
+Infix "<*>" := option_app (at level 55).
+```
+
+Given two vectors `v`{.coq} and `u`{.coq} of the same size and a function `f`{.coq}, and given
+`w`{.coq} the result computed by `map2`{.coq}, then we can propose the following
+specification for `map2`{.coq}:
+
+`forall (i : nat), nth w i = f <$> nth v i <*> nth u i`{.coq}
+
+This reads as follows: the `i`{.coq}th element of `w`{.coq} is the result of applying
+the `i`{.coq}th elements of `v`{.coq} and `u`{.coq} to `f`{.coq}.
+
+It turns out implementing `map2`{.coq} with the `Program`{.coq} framework has
+proven to be harder than I originally expected. My initial attempt was the
+following:
+
+```coq
+#[program]
+Fixpoint map2 {a b c n}
+ (v : vector a n) (u : vector b n)
+ (f : a -> b -> c) {struct v}
+ : { w: vector c n | forall i,
+ nth w i = f <$> nth v i <*> nth u i
+ } :=
+ match v, u with
+ | vcons x rst, vcons x' rst' =>
+ vcons (f x x') (map2 rst rst' f)
+ | vnil, vnil => vnil
+ | _, _ => !
+ end.
+```
+
+```
+Illegal application:
+The term "@eq" of type "forall A : Type, A -> A -> Prop"
+cannot be applied to the terms
+ "nat" : "Set"
+ "S wildcard'" : "nat"
+ "b" : "Type"
+The 3rd term has type "Type" which should be coercible
+to "nat".
+```
+
+So I had to fallback to defining the function in pure Ltac.
+
+```coq
+#[program]
+Fixpoint map2 {a b c n}
+ (v : vector a n) (u : vector b n)
+ (f : a -> b -> c) {struct v}
+ : { w: vector c n | forall i,
+ nth w i = f <$> nth v i <*> nth u i
+ } := _.
+
+Next Obligation.
+ dependent induction v; dependent induction u.
+ + remember (IHv u f) as u'.
+ inversion u'.
+ refine (exist _ (vcons (f a0 a1) x) _).
+ intros i.
+ induction i.
+ * reflexivity.
+ * apply (H i).
+ + refine (exist _ vnil _).
+ reflexivity.
+Qed.
+```
+
+## Is It Usable?
+
+This post mostly gives the "happy ends" for each function. I think I tried
+to hard for what I got in return and therefore I am convinced `Program`{.coq}
+is not ready (at least for a dependent type, I cannot tell for the rest). For
+instance, I found at least one bug in Program logic (I still have to report
+it). Have a look at the following code:
+
+```coq
+#[program]
+Fixpoint map2 {a b c n}
+ (u : vector a n) (v : vector b n)
+ (f : a -> b -> c) {struct v}
+ : vector c n :=
+ match u with
+ | _ => vnil
+ end.
+```
+
+It gives the following error:
+
+```
+Error: Illegal application:
+The term "@eq" of type "forall A : Type, A -> A -> Prop"
+cannot be applied to the terms
+ "nat" : "Set"
+ "0" : "nat"
+ "wildcard'" : "vector A n'"
+The 3rd term has type "vector A n'" which should be
+coercible to "nat".
+```
diff --git a/site/posts/StronglySpecifiedFunctionsProgram.v b/site/posts/StronglySpecifiedFunctionsProgram.v
deleted file mode 100644
index 94397cf..0000000
--- a/site/posts/StronglySpecifiedFunctionsProgram.v
+++ /dev/null
@@ -1,526 +0,0 @@
-(** * Implementing Strongly-Specified Functions with the <<Program>> Framework
-
- This is the second article (initially published on #<span
- id="original-created-at">January 01, 2017</span>#) of a series of two on how
- to write strongly-specified functions in Coq. You can read the previous part
- #<a href="./StronglySpecifiedFunctionsRefine.html">here</a>#. # *)
-
-(** #<nav id="generate-toc"></nav>#
-
- #<div id="history">site/posts/StronglySpecifiedFunctionsProgram.v</div># *)
-
-(** ** The Theory *)
-
-(** If I had to explain `Program`, I would say `Program` is the heir
- of the `refine` tactic. It gives you a convenient way to embed
- proofs within functional programs that are supposed to fade away
- during code extraction. But what do I mean when I say "embed
- proofs" within functional programs? I found two ways to do it. *)
-
-(** *** Invariants *)
-
-(** First, we can define a record with one or more fields of type
- [Prop]. By doing so, we can constrain the values of other fields. Put
- another way, we can specify invariant for our type. For instance, in
- SpecCert, I have defined the memory controller's SMRAMC register
- as follows: *)
-
-Record SmramcRegister := {
- d_open: bool;
- d_lock: bool;
- lock_is_close: d_lock = true -> d_open = false;
-}.
-
-(** So [lock_is_closed] is an invariant I know each instance of
- `SmramcRegister` will have to comply with, because every time I
- will construct a new instance, I will have to prove
- [lock_is_closed] holds true. For instance: *)
-
-Definition lock
- (reg: SmramcRegister)
- : SmramcRegister.
- refine ({| d_open := false; d_lock := true |}).
-
-(** Coq leaves us this goal to prove.
-
-<<
-reg : SmramcRegister
-============================
-true = true -> false = false
->>
-
- This sound reasonable enough. *)
-
-Proof.
- trivial.
-Defined.
-
-(** We have witness in my previous article about strongly-specified
- functions that mixing proofs and regular terms may leads to
- cumbersome code.
-
- From that perspective, [Program] helps. Indeed, the [lock]
- function can also be defined as follows: *)
-
-From Coq Require Import Program.
-
-#[program]
-Definition lock'
- (reg: SmramcRegister)
- : SmramcRegister :=
- {| d_open := false
- ; d_lock := true
- |}.
-
-(** *** Pre and Post Conditions *)
-
-(** Another way to "embed proofs in a program" is by specifying pre-
- and post-conditions for its component. In Coq, this is done using
- sigma-types. *)
-
-(** On the one hand, a precondition is a proposition a function input
- has to satisfy in order for the function to be applied. For
- instance, a precondition for [head : forall {a}, list a -> a] the
- function that returns the first element of a list [l] requires [l]
- to contain at least one element. We can write that using a
- sigma-type. The type of [head] then becomes [forall {a} (l: list a
- | l <> []) : a]
-
- On the other hand, a post condition is a proposition a function
- output has to satisfy in order for the function to be correctly
- implemented. In this way, `head` should in fact return the first
- element of [l] and not something else.
-
- <<Program>> makes writing this specification straightforward. *)
-
-(* begin hide *)
-From Coq Require Import List.
-Import ListNotations.
-(* end hide *)
-#[program]
-Definition head {a} (l : list a | l <> [])
- : { x : a | exists l', x :: l' = l }.
-(* begin hide *)
-Abort.
-(* end hide *)
-
-(** We recall that because `{ l: list a | l <> [] }` is not the same
- as [list a], in theory we cannot just compare [l] with [x ::
- l'] (we need to use [proj1_sig]). One benefit on <<Program>> is to
- deal with it using an implicit coercion.
-
- Note that for the type inference to work as expected, the
- unwrapped value (here, [x :: l']) needs to be the left operand of
- [=].
-
- Now that [head] have been specified, we have to implement it. *)
-
-#[program]
-Definition head {a} (l: list a | l <> [])
- : { x : a | exists l', cons x l' = l } :=
- match l with
- | x :: l' => x
- | [] => !
- end.
-
-Next Obligation.
- exists l'.
- reflexivity.
-Qed.
-
-(** I want to highlight several things here:
-
- - We return [x] (of type [a]) rather than a sigma-type, then <<Program>> is smart
- enough to wrap it. To do so, it tries to prove the post condition and because
- it fails, we have to do it ourselves (this is the Obligation we solve after
- the function definition.)
- - The [[]] case is absurd regarding the precondition, we tell Coq that using
- the bang (`!`) symbol.
-
- We can have a look at the extracted code:
-
-<<
-(** val head : 'a1 list -> 'a1 **)
-let head = function
-| Nil -> assert false (* absurd case *)
-| Cons (a, _) -> a
->>
-
- The implementation is pretty straightforward, but the pre- and
- post conditions have faded away. Also, the absurd case is
- discarded using an assertion. This means one thing: [head] should
- not be used directly from the Ocaml world. "Interface" functions
- have to be total. *)
-
-(** ** The Practice *)
-
-From Coq Require Import Lia.
-
-(** I have challenged myself to build a strongly specified library. My goal was to
- define a type [vector : nat -> Type -> Type] such as [vector a n] is a list of
- [n] instance of [a]. *)
-
-Inductive vector (a : Type) : nat -> Type :=
-| vcons {n} : a -> vector a n -> vector a (S n)
-| vnil : vector a O.
-
-Arguments vcons [a n] _ _.
-Arguments vnil {a}.
-
-(** I had three functions in mind: [take], [drop] and [extract]. I
- learned few lessons. My main take-away remains: do not use
- sigma-types, <<Program>> and dependent-types together. From my
- point of view, Coq is not yet ready for this. Maybe it is possible
- to make those three work together, but I have to admit I did not
- find out how. As a consequence, my preconditions are defined as
- extra arguments.
-
- To be able to specify the post conditions my three functions and
- some others, I first defined [nth] to get the _nth_ element of a
- vector.
-
- My first attempt to write [nth] was a failure.
-
-<<
-#[program]
-Fixpoint nth {a n}
- (v : vector a n) (i : nat) {struct v}
- : option a :=
- match v, i with
- | vcons x _, O => Some x
- | vcons x r, S i => nth r i
- | vnil, _ => None
- end.
->>
-
- raises an anomaly. *)
-
-#[program]
-Fixpoint nth {a n}
- (v : vector a n) (i : nat) {struct v}
- : option a :=
- match v with
- | vcons x r =>
- match i with
- | O => Some x
- | S i => nth r i
- end
- | vnil => None
- end.
-
-(** With [nth], it is possible to give a very precise definition of [take]: *)
-
-#[program]
-Fixpoint take {a n}
- (v : vector a n) (e : nat | e <= n)
- : { u : vector a e | forall i : nat,
- i < e -> nth u i = nth v i } :=
- match e with
- | S e' => match v with
- | vcons x r => vcons x (take r e')
- | vnil => !
- end
- | O => vnil
- end.
-
-Next Obligation.
- now apply le_S_n.
-Defined.
-
-Next Obligation.
- induction i.
- + reflexivity.
- + apply e0.
- now apply Lt.lt_S_n.
-Defined.
-
-Next Obligation.
- now apply PeanoNat.Nat.nle_succ_0 in H.
-Defined.
-
-Next Obligation.
- now apply PeanoNat.Nat.nlt_0_r in H.
-Defined.
-
-(** As a side note, I wanted to define the post condition as follows:
- [{ v': vector A e | forall (i : nat | i < e), nth v' i = nth v i
- }]. However, this made the goals and hypotheses become very hard
- to read and to use. Sigma-types in sigma-types: not a good
- idea.
-
-<<
-(** val take : 'a1 vector -> nat -> 'a1 vector **)
-
-let rec take v = function
-| O -> Vnil
-| S e' ->
- (match v with
- | Vcons (_, x, r) -> Vcons (e', x, (take r e'))
- | Vnil -> assert false (* absurd case *))
->>
-
- Then I could tackle `drop` in a very similar manner: *)
-
-#[program]
-Fixpoint drop {a n}
- (v : vector a n) (b : nat | b <= n)
- : { v': vector a (n - b) | forall i,
- i < n - b -> nth v' i = nth v (b + i) } :=
- match b with
- | 0 => v
- | S n => (match v with
- | vcons _ r => (drop r n)
- | vnil => !
- end)
- end.
-
-Next Obligation.
- now rewrite <- Minus.minus_n_O.
-Defined.
-
-Next Obligation.
- induction n;
- rewrite <- eq_rect_eq;
- reflexivity.
-Defined.
-
-Next Obligation.
- now apply le_S_n.
-Defined.
-
-Next Obligation.
- now apply PeanoNat.Nat.nle_succ_0 in H.
-Defined.
-
-(** The proofs are easy to write, and the extracted code is exactly what one might
- want it to be:
-
-<<
-(** val drop : 'a1 vector -> nat -> 'a1 vector **)
-let rec drop v = function
-| O -> v
-| S n ->
- (match v with
- | Vcons (_, _, r) -> drop r n
- | Vnil -> assert false (* absurd case *))
->>
-
- But <<Program>> really shone when it comes to implementing extract. I just
- had to combine [take] and [drop]. *)
-
-#[program]
-Definition extract {a n} (v : vector a n)
- (e : nat | e <= n) (b : nat | b <= e)
- : { v': vector a (e - b) | forall i,
- i < (e - b) -> nth v' i = nth v (b + i) } :=
- take (drop v b) (e - b).
-
-
-Next Obligation.
- transitivity e; auto.
-Defined.
-
-Next Obligation.
- now apply PeanoNat.Nat.sub_le_mono_r.
-Defined.
-
-Next Obligation.
- destruct drop; cbn in *.
- destruct take; cbn in *.
- rewrite e1; auto.
- rewrite <- e0; auto.
- lia.
-Defined.
-
-(** The proofs are straightforward because the specifications of [drop] and
- [take] are precise enough, and we do not need to have a look at their
- implementations. The extracted version of [extract] is as clean as we can
- anticipate.
-
-<<
-(** val extract : 'a1 vector -> nat -> nat -> 'a1 vector **)
-let extract v e b =
- take (drop v b) (sub e b)
->>
- *)
-
-(** I was pretty happy, so I tried some more. Each time, using [nth], I managed
- to write a precise post condition and to prove it holds true. For instance,
- given [map] to apply a function [f] to each element of a vector [v]: *)
-
-#[program]
-Fixpoint map {a b n} (v : vector a n) (f : a -> b)
- : { v': vector b n | forall i,
- nth v' i = option_map f (nth v i) } :=
- match v with
- | vnil => vnil
- | vcons a v => vcons (f a) (map v f)
- end.
-
-Next Obligation.
- induction i.
- + reflexivity.
- + apply e.
-Defined.
-
-(** I also managed to specify and write [append]: *)
-
-Program Fixpoint append {a n m}
- (v : vector a n) (u : vector a m)
- : { w : vector a (n + m) | forall i,
- (i < n -> nth w i = nth v i) /\
- (n <= i -> nth w i = nth u (i - n))
- } :=
- match v with
- | vnil => u
- | vcons a v => vcons a (append v u)
- end.
-
-Next Obligation.
- split.
- + now intro.
- + intros _.
- now rewrite PeanoNat.Nat.sub_0_r.
-Defined.
-
-Next Obligation.
- rename wildcard' into n.
- destruct (Compare_dec.lt_dec i (S n)); split.
- + intros _.
- destruct i.
- ++ reflexivity.
- ++ cbn.
- specialize (a1 i).
- destruct a1 as [a1 _].
- apply a1.
- auto with arith.
- + intros false.
- lia.
- + now intros.
- + intros ord.
- destruct i.
- ++ lia.
- ++ cbn.
- specialize (a1 i).
- destruct a1 as [_ a1].
- apply a1.
- auto with arith.
-Defined.
-
-(** Finally, I tried to implement [map2] that takes a vector of [a], a vector of
- [b] (both of the same size) and a function [f : a -> b -> c] and returns a
- vector of [c].
-
- First, we need to provide a precise specification for [map2]. To do that, we
- introduce [option_app], a function that Haskellers know all to well as being
- part of the <<Applicative>> type class. *)
-
-Definition option_app {a b}
- (opf: option (a -> b))
- (opx: option a)
- : option b :=
- match opf, opx with
- | Some f, Some x => Some (f x)
- | _, _ => None
-end.
-
-(** We thereafter use [<$>] as an infix operator for [option_map] and [<*>] as
- an infix operator for [option_app]. *)
-
-Infix "<$>" := option_map (at level 50).
-Infix "<*>" := option_app (at level 55).
-
-(** Given two vectors [v] and [u] of the same size and a function [f], and given
- [w] the result computed by [map2], then we can propose the following
- specification for [map2]:
-
- [forall (i : nat), nth w i = f <$> nth v i <*> nth u i]
-
- This reads as follows: the [i]th element of [w] is the result of applying
- the [i]th elements of [v] and [u] to [f].
-
- It turns out implementing [map2] with the <<Program>> framework has proven
- to be harder than I originally expected. My initial attempt was the
- following:
-
-<<
-#[program]
-Fixpoint map2 {a b c n}
- (v : vector a n) (u : vector b n)
- (f : a -> b -> c) {struct v}
- : { w: vector c n | forall i,
- nth w i = f <$> nth v i <*> nth u i
- } :=
- match v, u with
- | vcons x rst, vcons x' rst' =>
- vcons (f x x') (map2 rst rst' f)
- | vnil, vnil => vnil
- | _, _ => !
- end.
->>
-
-<<
-Illegal application:
-The term "@eq" of type "forall A : Type, A -> A -> Prop"
-cannot be applied to the terms
- "nat" : "Set"
- "S wildcard'" : "nat"
- "b" : "Type"
-The 3rd term has type "Type" which should be coercible
-to "nat".
->>
- *)
-
-#[program]
-Fixpoint map2 {a b c n}
- (v : vector a n) (u : vector b n)
- (f : a -> b -> c) {struct v}
- : { w: vector c n | forall i,
- nth w i = f <$> nth v i <*> nth u i
- } := _.
-
-Next Obligation.
- dependent induction v; dependent induction u.
- + remember (IHv u f) as u'.
- inversion u'.
- refine (exist _ (vcons (f a0 a1) x) _).
- intros i.
- induction i.
- * reflexivity.
- * apply (H i).
- + refine (exist _ vnil _).
- reflexivity.
-Qed.
-
-(** ** Is It Usable? *)
-
-(** This post mostly gives the "happy ends" for each function. I think I tried
- to hard for what I got in return and therefore I am convinced <<Program>> is
- not ready (at least for a dependent type, I cannot tell for the rest). For
- instance, I found at least one bug in Program logic (I still have to report
- it). Have a look at the following code:
-
-<<
-#[program]
-Fixpoint map2 {a b c n}
- (u : vector a n) (v : vector b n)
- (f : a -> b -> c) {struct v}
- : vector c n :=
- match u with
- | _ => vnil
- end.
->>
-
- It gives the following error:
-
-<<
-Error: Illegal application:
-The term "@eq" of type "forall A : Type, A -> A -> Prop"
-cannot be applied to the terms
- "nat" : "Set"
- "0" : "nat"
- "wildcard'" : "vector A n'"
-The 3rd term has type "vector A n'" which should be
-coercible to "nat".
->>
- *)
diff --git a/site/posts/StronglySpecifiedFunctionsRefine.md b/site/posts/StronglySpecifiedFunctionsRefine.md
new file mode 100644
index 0000000..6f541c8
--- /dev/null
+++ b/site/posts/StronglySpecifiedFunctionsRefine.md
@@ -0,0 +1,409 @@
+---
+published: 2015-01-11
+tags: ['coq']
+series:
+ parent: series/StronglySpecifiedFunctions.html
+ next: posts/StronglySpecifiedFunctionsProgram.html
+abstract: |
+ We see how to implement strongly-specified list manipulation functions in
+ Coq. Strong specifications are used to ensure some properties on functions'
+ arguments and return value. It makes Coq type system very expressive.
+---
+
+# Implementing Strongly-Specified Functions with the `refine`{.coq} Tactic
+
+I started to play with Coq, the interactive theorem prover
+developed by Inria, a few weeks ago. It is a very powerful tool,
+yet hard to master. Fortunately, there are some very good readings
+if you want to learn (I recommend the Coq'Art). This article is
+not one of them.
+
+In this article, we will see how to implement strongly-specified
+list manipulation functions in Coq. Strong specifications are used
+to ensure some properties on functions' arguments and return
+value. It makes Coq type system very expressive. Thus, it is
+possible to specify in the type of the function `pop`{.coq} that the return
+value is the list passed in argument in which the first element has been
+removed for example.
+
+## Is This List Empty?
+
+It's the first question to deal with when manipulating
+lists. There are some functions that require their arguments not
+to be empty. It's the case for the `pop`{.coq} function, for instance:
+it is not possible to remove the first element of a list that does
+not have any elements in the first place.
+
+When one wants to answer such a question as “Is this list empty?”,
+he has to keep in mind that there are two ways to do it: by a
+predicate or by a boolean function. Indeed, `Prop`{.coq} and `bool`{.coq} are
+two different worlds that do not mix easily. One solution is to
+write two definitions and to prove their equivalence. That is
+`forall args, predicate args <-> bool_function args = true`{.coq}.
+
+Another solution is to use the `sumbool`{.coq} type as middleman. The
+scheme is the following:
+
+- Defining `predicate : args → Prop`{.coq}
+- Defining `predicate_dec : args -> { predicate args } + { ~predicate args }`{.coq}
+- Defining `predicate_b`{.coq}:
+
+```coq
+Definition predicate_b (args) :=
+ if predicate_dec args then true else false.
+```
+
+### Defining the `empty`{.coq} Predicate *)
+
+A list is empty if it is `[]`{.coq} (`nil`{.coq}). It's as simple as that!
+
+```coq
+Definition empty {a} (l : list a) : Prop := l = [].
+```
+
+### Defining a decidable version of `empty`{.coq}
+
+A decidable version of `empty`{.coq} is a function which takes a list
+`l`{.coq} as its argument and returns either a proof that `l`{.coq} is empty,
+or a proof that `l`{.coq} is not empty. This is encoded in the Coq
+standard library with the `sumbool`{.coq} type, and is written as
+follows: `{ empty l } + { ~ empty l }`{.coq}.
+
+```coq
+Definition empty_dec {a} (l : list a)
+ : { empty l } + { ~ empty l }.
+Proof.
+ refine (match l with
+ | [] => left _ _
+ | _ => right _ _
+ end);
+ unfold empty; trivial.
+ unfold not; intro H; discriminate H.
+Defined.
+```
+
+In this example, I decided to use the `refine`{.coq} tactic which is
+convenient when we manipulate the `Set`{.coq} and `Prop`{.coq} sorts at the
+same time.
+
+### Defining `empty_b`{.coq}
+
+With `empty_dec`{.coq}, we can define `empty_b`{.coq}.
+
+```coq
+Definition empty_b {a} (l : list a) : bool :=
+ if empty_dec l then true else false.
+```
+
+Let's try to extract `empty_b`{.coq}:
+
+```ocaml
+type bool =
+| True
+| False
+
+type sumbool =
+| Left
+| Right
+
+type 'a list =
+| Nil
+| Cons of 'a * 'a list
+
+(** val empty_dec : 'a1 list -> sumbool **)
+
+let empty_dec = function
+| Nil -> Left
+| Cons (a, l0) -> Right
+
+(** val empty_b : 'a1 list -> bool **)
+
+let empty_b l =
+ match empty_dec l with
+ | Left -> True
+ | Right -> False
+```
+
+In addition to `'a list`{.ocaml}, Coq has created the `sumbool`{.ocaml} and
+`bool`{.ocaml} types and `empty_b`{.ocaml} is basically a translation from the
+former to the latter. We could have stopped with `empty_dec`{.ocmal}, but
+`Left`{.ocaml} and `Right`{.ocaml} are less readable that `True`{.ocaml} and
+`False`{.ocaml}. Note that it is possible to configure the Extraction mechanism
+to use primitive OCaml types instead, but this is out of the scope of this
+article.
+
+## Defining Some Utility Functions
+
+### Defining `pop`{.coq} *)
+
+There are several ways to write a function that removes the first
+element of a list. One is to return `nil` if the given list was
+already empty:
+
+```coq
+Definition pop {a} ( l :list a) :=
+ match l with
+ | _ :: l => l
+ | [] => []
+ end.
+```
+
+But it's not really satisfying. A `pop` call over an empty list should not be
+possible. It can be done by adding an argument to `pop`: the proof that the
+list is not empty.
+
+```coq
+Definition pop {a} (l : list a) (h : ~ empty l)
+ : list a.
+```
+
+There are, as usual when it comes to lists, two cases to
+consider.
+
+- `l = x :: rst`{.coq}, and therefore `pop (x :: rst) h`{.coq} is `rst`{.coq}
+- `l = []`{.coq}, which is not possible since we know `l`{.coq} is not empty.
+
+The challenge is to convince Coq that our reasoning is
+correct. There are, again, several approaches to achieve that. We
+can, for instance, use the `refine`{.coq} tactic again, but this time we
+need to know a small trick to succeed as using a “regular” `match`{.coq}
+will not work.
+
+From the following goal:
+
+```
+ a : Type
+ l : list a
+ h : ~ empty l
+ ============================
+ list a
+```
+
+Using the `refine`{.coq} tactic naively, for instance this way:
+
+```coq
+ refine (match l with
+ | _ :: rst => rst
+ | [] => _
+ end).
+```
+
+leaves us the following goal to prove:
+
+```
+ a : Type
+ l : list a
+ h : ~ empty l
+ ============================
+ list a
+```
+
+Nothing has changed! Well, not exactly. See, `refine`{.coq} has taken
+our incomplete Gallina term, found a hole, done some
+type-checking, found that the type of the missing piece of our
+implementation is `list a`{.coq} and therefore has generated a new
+goal of this type. What `refine`{.coq} has not done, however, is
+remember that we are in the case where `l = []`{.coq}.
+
+We need to generate a goal from a hole wherein this information is
+available. It is possible using a long form of `match`{.coq}. The
+general approach is this: rather than returning a value of type
+`list a`{.coq}, our match will return a function of type [l = ?l' ->
+list a], where `?l`{.coq} is value of `l`{.coq} for a given case (that is,
+either `x :: rst`{.coq} or `[]`{.coq}). Of course, As a consequence, the type
+of the `match`{.coq} in now a function which awaits a proof to return
+the expected result. Fortunately, this proof is trivial: it is
+`eq_refl`{.coq}.
+
+```coq
+ refine (match l as l'
+ return l = l' -> list a
+ with
+ | _ :: rst => fun _ => rst
+ | [] => fun equ => _
+ end eq_refl).
+```
+
+For us to conclude the proof, this is way better.
+
+```
+ a : Type
+ l : list a
+ h : ~ empty l
+ equ : l = []
+ ============================
+ list a
+```
+
+We conclude the proof, and therefore the definition of `pop`{.coq}.
+
+```coq
+ rewrite equ in h.
+ exfalso.
+ now apply h.
+Defined.
+```
+
+It's better and yet it can still be improved. Indeed, according to its type,
+`pop`{.coq} returns “some list”. As a matter of fact, `pop`{.coq} returns “the
+same list without its first argument”. It is possible to write
+such precise definition thanks to sigma-types, defined as:
+
+```coq
+Inductive sig (A : Type) (P : A -> Prop) : Type :=
+ exist : forall (x : A), P x -> sig P.
+```
+
+Rather that `sig A p`{.coq}, sigma-types can be written using the
+notation `{ a | P }`{.coq}. They express subsets, and can be used to constraint
+arguments and results of functions.
+
+We finally propose a strongly-specified definition of `pop`{.coq}.
+
+```coq
+Definition pop {a} (l : list a | ~ empty l)
+ : { l' | exists a, proj1_sig l = cons a l' }.
+```
+
+If you think the previous use of `match`{.coq} term was ugly, brace yourselves.
+
+```coq
+ refine (match proj1_sig l as l'
+ return proj1_sig l = l'
+ -> { l' | exists a, proj1_sig l = cons a l' }
+ with
+ | [] => fun equ => _
+ | (_ :: rst) => fun equ => exist _ rst _
+ end eq_refl).
+```
+
+This leaves us two goals to tackle.
+
+First, we need to discard the case where `l`{.coq} is the empty list.
+
+```
+ a : Type
+ l : {l : list a | ~ empty l}
+ equ : proj1_sig l = []
+ ============================
+ {l' : list a | exists a0 : a, proj1_sig l = a0 :: l'}
+```
+
+```coq
+ + destruct l as [l nempty]; cbn in *.
+ rewrite equ in nempty.
+ exfalso.
+ now apply nempty.
+```
+
+Then, we need to prove that the result we provide (`rst`{.coq}) when the
+list is not empty is correct with respect to the specification of
+`pop`{.coq}.
+
+```
+ a : Type
+ l : {l : list a | ~ empty l}
+ a0 : a
+ rst : list a
+ equ : proj1_sig l = a0 :: rst
+ ============================
+ exists a1 : a, proj1_sig l = a1 :: rst
+```
+
+```coq
+ + destruct l as [l nempty]; cbn in *.
+ rewrite equ.
+ now exists a0.
+Defined.
+```
+
+Let's have a look at the extracted code:
+
+```ocaml
+(** val pop : 'a1 list -> 'a1 list **)
+
+let pop = function
+| Nil -> assert false (* absurd case *)
+| Cons (a, l0) -> l0
+```
+
+If one tries to call `pop nil`{.coq}, the `assert`{.coq} ensures the call fails. Extra
+information given by the sigma-type have been stripped away. It can be
+confusing, and in practice it means that, we you rely on the extraction
+mechanism to provide a certified OCaml module, you _cannot expose
+strongly-specified functions in its public interface_ because nothing in the
+OCaml type system will prevent a miseuse which will in practice leads to an
+`assert false`{.ocaml}. *)
+
+## Defining `push`{.coq}
+
+It is possible to specify `push`{.coq} the same way `pop`{.coq} has been. The only
+difference is `push`{.coq} accepts lists with no restriction at all. Thus, its
+definition is a simpler, and we can write it without `refine`{.coq}.
+
+```coq
+Definition push {a} (l : list a) (x : a)
+ : { l' | l' = x :: l } :=
+ exist _ (x :: l) eq_refl.
+```
+
+And the extracted code is just as straightforward.
+
+```ocaml
+let push l a =
+ Cons (a, l)
+```
+
+## Defining `head`{.coq}
+
+Same as `pop`{.coq} and `push`{.coq}, it is possible to add extra information in the
+type of `head`{.coq}, namely the returned value of `head`{.coq} is indeed the firt value
+of `l`{.coq}.
+
+```coq
+Definition head {a} (l : list a | ~ empty l)
+ : { x | exists r, proj1_sig l = x :: r }.
+```
+
+It's not a surprise its definition is very close to `pop`{.coq}.
+
+```coq
+ refine (match proj1_sig l as l'
+ return proj1_sig l = l' -> _
+ with
+ | [] => fun equ => _
+ | x :: _ => fun equ => exist _ x _
+ end eq_refl).
+```
+
+The proof are also very similar, and are left to read as an exercise for
+passionate readers.
+
+```coq
+ + destruct l as [l falso]; cbn in *.
+ rewrite equ in falso.
+ exfalso.
+ now apply falso.
+ + exists l0.
+ now rewrite equ.
+Defined.
+```
+
+Finally, the extracted code is as straightforward as it can get.
+
+```ocaml
+let head = function
+| Nil -> assert false (* absurd case *)
+| Cons (a, l0) -> a
+```
+
+## Conclusion
+
+Writing strongly-specified functions allows for reasoning about the result
+correctness while computing it. This can help in practice. However, writing
+these functions with the `refine`{.coq} tactic does not enable a very idiomatic
+Coq code.
+
+To improve the situation, the `Program`{.coq} framework distributed with the
+Coq standard library helps, but it is better to understand what `Program`{.coq}
+achieves under its hood, which is basically what we have done in this article.
diff --git a/site/posts/StronglySpecifiedFunctionsRefine.v b/site/posts/StronglySpecifiedFunctionsRefine.v
deleted file mode 100644
index 4ffb385..0000000
--- a/site/posts/StronglySpecifiedFunctionsRefine.v
+++ /dev/null
@@ -1,396 +0,0 @@
-(** * Implementing Strongly-Specified Functions with the <<refine>> Tactic *)
-
-(** This is the first article (initially published on #<span
- class="original-created-at">January 11, 2015</span>#) of a series of two on
- how to write strongly-specified functions in Coq. You can read the next part
- #<a href="./StronglySpecifiedFunctionsProgram.html">here</a>#.
-
- I started to play with Coq, the interactive theorem prover
- developed by Inria, a few weeks ago. It is a very powerful tool,
- yet hard to master. Fortunately, there are some very good readings
- if you want to learn (I recommend the Coq'Art). This article is
- not one of them.
-
- In this article, we will see how to implement strongly-specified
- list manipulation functions in Coq. Strong specifications are used
- to ensure some properties on functions' arguments and return
- value. It makes Coq type system very expressive. Thus, it is
- possible to specify in the type of the function [pop] that the
- return value is the list passed in argument in which the first
- element has been removed for example.
-
- #<nav id="generate-toc"></nav>#
-
- #<div id="history">site/posts/StronglySpecifiedFunctionsRefine.v</div># *)
-
-(** ** Is this list empty? *)
-
-(** Since we will manipulate lists in this article, we first
- enable several notations of the standard library. *)
-
-From Coq Require Import List.
-Import ListNotations.
-
-(** It's the first question to deal with when manipulating
- lists. There are some functions that require their arguments not
- to be empty. It's the case for the [pop] function, for instance:
- it is not possible to remove the first element of a list that does
- not have any elements in the first place.
-
- When one wants to answer such a question as “Is this list empty?”,
- he has to keep in mind that there are two ways to do it: by a
- predicate or by a boolean function. Indeed, [Prop] and [bool] are
- two different worlds that do not mix easily. One solution is to
- write two definitions and to prove their equivalence. That is
- [forall args, predicate args <-> bool_function args = true].
-
- Another solution is to use the [sumbool] type as middleman. The
- scheme is the following:
-
- - Defining [predicate : args → Prop]
- - Defining [predicate_dec : args -> { predicate args } + { ~predicate args }]
- - Defining [predicate_b]:
-
-<<
-Definition predicate_b (args) :=
- if predicate_dec args then true else false.
->>
- *)
-
-(** *** Defining the <<empty>> predicate *)
-
-(** A list is empty if it is [[]] ([nil]). It's as simple as that! *)
-
-Definition empty {a} (l : list a) : Prop := l = [].
-
-(** *** Defining a decidable version of <<empty>> *)
-
-(** A decidable version of [empty] is a function which takes a list
- [l] as its argument and returns either a proof that [l] is empty,
- or a proof that [l] is not empty. This is encoded in the Coq
- standard library with the [sumbool] type, and is written as
- follows: [{ empty l } + { ~ empty l }]. *)
-
-Definition empty_dec {a} (l : list a)
- : { empty l } + { ~ empty l }.
-
-Proof.
- refine (match l with
- | [] => left _ _
- | _ => right _ _
- end);
- unfold empty; trivial.
- unfold not; intro H; discriminate H.
-Defined.
-
-(** In this example, I decided to use the [refine] tactic which is
- convenient when we manipulate the [Set] and [Prop] sorts at the
- same time. *)
-
-(** *** Defining <<empty_b>> *)
-
-(** With [empty_dec], we can define [empty_b]. *)
-
-Definition empty_b {a} (l : list a) : bool :=
- if empty_dec l then true else false.
-
-(** Let's try to extract [empty_b]:
-
-<<
-type bool =
-| True
-| False
-
-type sumbool =
-| Left
-| Right
-
-type 'a list =
-| Nil
-| Cons of 'a * 'a list
-
-(** val empty_dec : 'a1 list -> sumbool **)
-
-let empty_dec = function
-| Nil -> Left
-| Cons (a, l0) -> Right
-
-(** val empty_b : 'a1 list -> bool **)
-
-let empty_b l =
- match empty_dec l with
- | Left -> True
- | Right -> False
->>
-
- In addition to <<list 'a>>, Coq has created the <<sumbool>> and
- <<bool>> types and [empty_b] is basically a translation from the
- former to the latter. We could have stopped with [empty_dec], but
- [Left] and [Right] are less readable that [True] and [False]. Note
- that it is possible to configure the Extraction mechanism to use
- primitive OCaml types instead, but this is out of the scope of
- this article. *)
-
-(** ** Defining some utility functions *)
-
-(** *** Defining [pop] *)
-
-(** There are several ways to write a function that removes the first
- element of a list. One is to return `nil` if the given list was
- already empty: *)
-
-Definition pop {a} ( l :list a) :=
- match l with
- | _ :: l => l
- | [] => []
- end.
-
-(** But it's not really satisfying. A `pop` call over an empty list
- should not be possible. It can be done by adding an argument to
- `pop`: the proof that the list is not empty. *)
-
-(* begin hide *)
-Reset pop.
-(* end hide *)
-Definition pop {a} (l : list a) (h : ~ empty l)
- : list a.
-
-(** There are, as usual when it comes to lists, two cases to
- consider.
-
- - [l = x :: rst], and therefore [pop (x :: rst) h] is [rst]
- - [l = []], which is not possible since we know [l] is not empty.
-
- The challenge is to convince Coq that our reasoning is
- correct. There are, again, several approaches to achieve that. We
- can, for instance, use the [refine] tactic again, but this time we
- need to know a small trick to succeed as using a “regular” [match]
- will not work.
-
- From the following goal:
-
-<<
- a : Type
- l : list a
- h : ~ empty l
- ============================
- list a
->>
- *)
-
-(** Using the [refine] tactic naively, for instance this way: *)
-
- refine (match l with
- | _ :: rst => rst
- | [] => _
- end).
-
-(** leaves us the following goal to prove:
-
-<<
- a : Type
- l : list a
- h : ~ empty l
- ============================
- list a
->>
-
- Nothing has changed! Well, not exactly. See, [refine] has taken
- our incomplete Gallina term, found a hole, done some
- type-checking, found that the type of the missing piece of our
- implementation is [list a] and therefore has generated a new
- goal of this type. What [refine] has not done, however, is
- remember that we are in the case where [l = []]!
-
- We need to generate a goal from a hole wherein this information is
- available. It is possible using a long form of [match]. The
- general approach is this: rather than returning a value of type
- [list a], our match will return a function of type [l = ?l' ->
- list a], where [?l] is value of [l] for a given case (that is,
- either [x :: rst] or [[]]). Of course, As a consequence, the type
- of the [match] in now a function which awaits a proof to return
- the expected result. Fortunately, this proof is trivial: it is
- [eq_refl]. *)
-
-(* begin hide *)
- Undo.
-(* end hide *)
- refine (match l as l'
- return l = l' -> list a
- with
- | _ :: rst => fun _ => rst
- | [] => fun equ => _
- end eq_refl).
-
-(** For us to conclude the proof, this is way better.
-
-<<
- a : Type
- l : list a
- h : ~ empty l
- equ : l = []
- ============================
- list a
->>
-
- We conclude the proof, and therefore the definition of [pop]. *)
-
- rewrite equ in h.
- exfalso.
- now apply h.
-Defined.
-
-(** It's better and yet it can still be improved. Indeed, according to its type,
- [pop] returns “some list”. As a matter of fact, [pop] returns “the
- same list without its first argument”. It is possible to write
- such precise definition thanks to sigma-types, defined as:
-
-<<
-Inductive sig (A : Type) (P : A -> Prop) : Type :=
- exist : forall (x : A), P x -> sig P.
->>
-
- Rather that [sig A p], sigma-types can be written using the
- notation [{ a | P }]. They express subsets, and can be used to constraint
- arguments and results of functions.
-
- We finally propose a strongly-specified definition of [pop]. *)
-
-(* begin hide *)
-Reset pop.
-(* end hide *)
-Definition pop {a} (l : list a | ~ empty l)
- : { l' | exists a, proj1_sig l = cons a l' }.
-
-(** If you think the previous use of [match] term was ugly, brace yourselves. *)
-
- refine (match proj1_sig l as l'
- return proj1_sig l = l'
- -> { l' | exists a, proj1_sig l = cons a l' }
- with
- | [] => fun equ => _
- | (_ :: rst) => fun equ => exist _ rst _
- end eq_refl).
-
-(** This leaves us two goals to tackle.
-
- First, we need to discard the case where [l] is the empty list.
-
-<<
- a : Type
- l : {l : list a | ~ empty l}
- equ : proj1_sig l = []
- ============================
- {l' : list a | exists a0 : a, proj1_sig l = a0 :: l'}
->>
- *)
-
- + destruct l as [l nempty]; cbn in *.
- rewrite equ in nempty.
- exfalso.
- now apply nempty.
-
-(** Then, we need to prove that the result we provide ([rst]) when the
- list is not empty is correct with respect to the specification of
- [pop].
-
-<<
- a : Type
- l : {l : list a | ~ empty l}
- a0 : a
- rst : list a
- equ : proj1_sig l = a0 :: rst
- ============================
- exists a1 : a, proj1_sig l = a1 :: rst
->>
- *)
-
- + destruct l as [l nempty]; cbn in *.
- rewrite equ.
- now exists a0.
-Defined.
-
-(** Let's have a look at the extracted code:
-
-<<
-(** val pop : 'a1 list -> 'a1 list **)
-
-let pop = function
-| Nil -> assert false (* absurd case *)
-| Cons (a, l0) -> l0
->>
-
- If one tries to call [pop nil], the [assert] ensures the call fails. Extra
- information given by the sigma-type have been stripped away. It can be
- confusing, and in practice it means that, we you rely on the extraction
- mechanism to provide a certified OCaml module, you _cannot expose
- strongly-specified functions in its public interface_ because nothing in the
- OCaml type system will prevent a miseuse which will in practice leads to an
- <<assert false>>. *)
-
-(** ** Defining [push] *)
-
-(** It is possible to specify [push] the same way [pop] has been. The only
- difference is [push] accepts lists with no restriction at all. Thus, its
- definition is a simpler, and we can write it without [refine]. *)
-
-Definition push {a} (l : list a) (x : a)
- : { l' | l' = x :: l } :=
- exist _ (x :: l) eq_refl.
-
-(** And the extracted code is just as straightforward.
-
-<<
-let push l a =
- Cons (a, l)
->>
- *)
-
-(** ** Defining [head] *)
-
-(** Same as [pop] and [push], it is possible to add extra information in the
- type of [head], namely the returned value of [head] is indeed the firt value
- of [l]. *)
-
-Definition head {a} (l : list a | ~ empty l)
- : { x | exists r, proj1_sig l = x :: r }.
-
-(** It's not a surprise its definition is very close to [pop]. *)
-
- refine (match proj1_sig l as l'
- return proj1_sig l = l' -> _
- with
- | [] => fun equ => _
- | x :: _ => fun equ => exist _ x _
- end eq_refl).
-
-(** The proof are also very similar, and are left to read as an exercise for
- passionate readers. *)
-
- + destruct l as [l falso]; cbn in *.
- rewrite equ in falso.
- exfalso.
- now apply falso.
- + exists l0.
- now rewrite equ.
-Defined.
-
-(** Finally, the extracted code is as straightforward as it can get.
-
-<<
-let head = function
-| Nil -> assert false (* absurd case *)
-| Cons (a, l0) -> a
->>
- *)
-
-(** ** Conclusion & Moving Forward *)
-
-(** Writing strongly-specified functions allows for reasoning about the result
- correctness while computing it. This can help in practice. However, writing
- these functions with the [refine] tactic does not enable a very idiomatic
- Coq code.
-
- To improve the situation, the <<Program>> framework distributed with the Coq
- standard library helps, but it is better to understand what <<Program>> achieves
- under its hood, which is basically what we have done in this article. *)
diff --git a/site/posts/Thanks.org b/site/posts/Thanks.org
deleted file mode 100644
index 4815bff..0000000
--- a/site/posts/Thanks.org
+++ /dev/null
@@ -1,47 +0,0 @@
-#+TITLE: Thanks!
-
-#+SERIES: ./meta.html
-#+SERIES_NEXT: ./cleopatra.html
-
-This website could not exist without many awesome free software
-projects. Although I could not list them all even if I wanted, my
-desire is at least to try keeping up-to-date a curated description of
-the most significant ones.
-
-#+BEGIN_EXPORT html
-<nav id="generate-toc"></nav>
-<div id="history">site/posts/Thanks.org</div>
-#+END_EXPORT
-
-* Authoring Content
-
-- [[https://www.gnu.org/software/emacs][Emacs]] ::
- Emacs is an extensible editor which I use daily to author this website
- content, and write the code related to this website (and any code, really). It
- is part of the [[https://www.gnu.org/gnu/gnu.html][GNU project]].
-- [[https://orgmode.org/][Org mode]] ::
- Org mode is a major mode of Emacs which I use to author several posts. It has
- initially been written by [[https://staff.science.uva.nl/~dominik/][Carsten Dominik]], and is currently maintained by
- [[http://bzg.fr/][Bastien Guerry]].
-- [[https://coq.inria.fr/][Coq]] ::
- Coq is a theorem prover and a proof assistant built by [[https://www.inria.fr/fr][Inria]]. Many of my posts
- on Coq are regular Coq file processed by ~coqdoc~.
-
-* Static Website Generation
-
-- [[https://soupault.app][soupault]] ::
- Soupault is a static website generator and HTML processor written by [[https://www.baturin.org/][Daniil
- Baturin]].
-
-* Frontend
-
-- [[https://katex.org][\im \KaTeX \mi]] ::
- \im \KaTeX \mi is the “fastest” math typesetting library for the web, and is
- used to render inline mathematics in my posts at build time. It has been
- created by [[https://github.com/xymostech][Emily Eisenberg]] and
- [[https://sophiebits.com/][Sophie Alpert]], with the help of
- [[https://github.com/KaTeX/KaTeX/graphs/contributors][many contributors]].
-- [[https://https://github.com/edwardtufte/tufte-css/][Tufte CSS]] ::
- Tufte CSS is a project by Dave Liepmann and many contributors intended to
- recreate Edward Tufte’s distinctive style for HTML document. The CSS of this
- website borrows several CSS perls from Tufte CSS.
diff --git a/site/posts/cleopatra.org b/site/posts/cleopatra.org
deleted file mode 100644
index 6081708..0000000
--- a/site/posts/cleopatra.org
+++ /dev/null
@@ -1,66 +0,0 @@
-#+TITLE: An Unfinished Series on How This Static Website Used to be Generated
-
-#+SERIES: ./meta.html
-#+SERIES_PREV: ./Thanks.html
-
-At some point, I felt like the whole process of generating this
-website was interesting enough so that it would deserve a write-up of
-its own, but the risk was that such a piece of text would quickly
-become out-dated. This is reminescent of documenting any software
-project, and I was aware at that time of a dedicated paradigm to
-prevent these kind of issues: [[http://www.literateprogramming.com/][literate programming]].
-
-I spent quite some time turning my custom toolchain into a literate
-program, so that its actual code source would actually be the
-write-ups I wanted to add to my website. This was an interesting
-challenge, since it meant *~cleopatra~* would have to generate itself
-before it could build my website. In other words, *~cleopatra~*
-achieves the bootstsrapping challenge!
-
-I really enjoyed this first experiment with literate programming, and
-I started using *~cleopatra~* for other projects of mine where
-literate programming felt like an interesting choice. In doing so, it
-quickly became clear *~cleopatra~* was cumbersome to set-up for a new
-project. At the end, [[https://cleopatra.soap.coffee][I ended up rewriting it]] to overcome the specific
-issues posed by its initial design[fn::For the record, this second
-version is also implemented using literate programming, and if I was
-first using the first version to build it, I quickly “made the
-bootstrap jump.”]. But the so-called generation processes I had
-written for *~cleopatra~* the first basically “just worked” with
-*~cleopatra~* the second.
-
-Now, I don’t use *~cleopatra~* anymore. Literate programming is a fun
-paradigm, but I never took the time to actually document in depth most
-of the bits on how this website is built. So I took the various
-scripts extracted by *~cleopatra~*, and recreated a straightforward
-~makefile~ file on top of it. The nice thing is, it now takes way less
-time to build!
-
-Anyway, coming back to this series, it is just the very reason why I
-started using *~cleopatra~* in the first place: the generation
-processes I was using to generate this website, written as literate
-programs. If you are curious, you can have a look.
-
-- [[./cleopatra/dependencies.org][Installing Dependencies]] ::
-
-- [[file:cleopatra/coq.org][Authoring Contents with Coq ~(TODO)~]] ::
-
-- [[./cleopatra/org.org][Authoring Contents with ~org-mode~ ~(TODO)~]] ::
-
-- [[./cleopatra/literate-programming.org][Literate Programming Projects]] ::
- Literate programming is an interesting exercice, and it is
- particularly well-suited for blog posts, since at the very least it
- provides the tool to enforce the code presented to readers is
- correct. We use Emacs and ~org-mode~ to tangle the literate
- programming projects present in the ~posts/~ directory of this
- website.
-
-- [[./cleopatra/theme.org][Layout and Style]] ::
-
-- [[./cleopatra/soupault.org][Processing HTML with ~soupault~]] ::
- ~soupault~ is a HTML processor, and it can be used as a static website
- generator. We leverage *~soupault~* to provide a unified look and feel to a
- website generated with diverse tools.
-
-*Appendix:* In case you are curious, you can have a look at
-[[./posts/CleopatraV1.html][the first implementaiton of *~cleopatra~*]].
diff --git a/site/posts/cleopatra/commands.org b/site/posts/cleopatra/commands.org
deleted file mode 100644
index fbf0430..0000000
--- a/site/posts/cleopatra/commands.org
+++ /dev/null
@@ -1,36 +0,0 @@
-#+TITLE: Adhoc *~cleopatra~* commands
-
-#+SERIES: ../cleopatra.html
-#+SERIES_PREV: ./soupault.html
-
-In this generation process, we provide adhoc commands to ease the
-authoring experience. A given command ~<cmd>~ is implemented as a
-~makefile~ rule, and can be called with ~cleopatra <cmd>~.
-
-#+BEGIN_EXPORT html
-<nav id="generate-toc"></nav>
-<div id="history">site/cleopatra/commands.org</div>
-#+END_EXPORT
-
-* ~serve~
-
- This command spawns a simple HTTP server which allows us to navigate
- the website more easily.
-
- #+begin_src makefile :tangle commands.mk
-serve :
- @cleopatra echo Spwaning "HTTP server"
- @cd out && python -m http.server
- #+end_src
-
-* ~update~
-
- This commands updates the various dependencies locally installed to
- build this website, such as ~soupault~ for instance.
-
- #+begin_src makefile :tangle commands.mk
-update :
- @cleopatra echo "Updating" "OCaml dependencies"
- @opam update
- @opam upgrade -y
- #+end_src
diff --git a/site/posts/cleopatra/coq.org b/site/posts/cleopatra/coq.org
deleted file mode 100644
index 81f3d27..0000000
--- a/site/posts/cleopatra/coq.org
+++ /dev/null
@@ -1,47 +0,0 @@
-#+TITLE: Authoring Content with Coq
-
-#+SERIES: ../cleopatra.html
-#+SERIES_PREV: ./dependencies.html
-#+SERIES_NEXT: ./org.html
-
-#+BEGIN_EXPORT html
-<nav id="generate-toc"></nav>
-<div id="history">site/cleopatra/coq.org</div>
-#+END_EXPORT
-
-
-* Author Guidelines
-
-* Under the Hood
-
-#+BEGIN_SRC makefile :tangle coq.mk
-COQ_POSTS := $(shell find site/ -name "*.v")
-COQ_HTML := $(COQ_POSTS:.v=.html)
-COQ_ARTIFACTS := $(COQ_POSTS:.v=.vo) \
- $(COQ_POSTS:.v=.vok) \
- $(COQ_POSTS:.v=.vos) \
- $(COQ_POSTS:.v=.glob) \
- $(join $(dir ${COQ_POSTS}),$(addprefix ".",$(notdir $(COQ_POSTS:.v=.aux))))
-
-coq-build : ${COQ_HTML}
-
-soupault-build : coq-build
-
-ARTIFACTS += ${COQ_ARTIFACTS} .lia.cache
-ARTIFACTS += ${COQ_HTML}
-
-COQLIB := "https://coq.inria.fr/distrib/current/stdlib/"
-COQCARG := -async-proofs-cache force \
- -w -custom-entry-overriden
-COQDOCARG := --no-index --charset utf8 --short \
- --body-only --coqlib "${COQLIB}" \
- --external "https://coq-community.org/coq-ext-lib/v0.11.2/" ExtLib \
- --external "https://compcert.org/doc/html" compcert \
- --external "https://lysxia.github.io/coq-simple-io" SimpleIO
-
-%.html : %.v coq.mk _opam/init
- @cleopatra echo Exporting "$*.v"
- @coqc ${COQCARG} $<
- @coqdoc ${COQDOCARG} -d $(shell dirname $<) $<
- @rm -f $(shell dirname $<)/coqdoc.css
-#+END_SRC
diff --git a/site/posts/cleopatra/dependencies.org b/site/posts/cleopatra/dependencies.org
deleted file mode 100644
index 7858df4..0000000
--- a/site/posts/cleopatra/dependencies.org
+++ /dev/null
@@ -1,95 +0,0 @@
-#+TITLE: Installing Dependencies
-
-#+SERIES: ../cleopatra.html
-#+SERIES_NEXT: ./coq.html
-
-* OCaml and Coq
-
- #+caption: Dependencies for Coq articles
- #+name: coq-deps
- | Package | Version |
- |--------------+---------|
- | coq | 8.13.2 |
- | coq-compcert | 3.8 |
-
- #+caption: Dependencies for the ~coqffi~ series
- #+name: lp-deps
- | Package | Version |
- |---------------+-------------|
- | dune | 2.9.0 |
- | coq-coqffi | 1.0.0~beta7 |
- | coq-simple-io | 1.5.0 |
-
- #+caption: Soupault
- #+name: soupault-deps
- | Package | Version |
- |----------+---------|
- | soupault | 4.0.1 |
-
- #+name: deps-listing
- #+begin_src emacs-lisp :noweb yes :var coq-deps=coq-deps :var lp-deps=lp-deps :var soupault-deps=soupault-deps :results value raw :exports none
-;; We use this Emacs Lisp snippet to generate the list of dependencies
-;; we have to install with Opam
-(defun fmt-deps (d)
- (mapconcat (lambda (d) (format "%s" d)) d "."))
-
-(string-join
- (append (mapcar 'fmt-deps lp-deps)
- (mapcar 'fmt-deps soupault-deps)
- (mapcar 'fmt-deps coq-deps))
- " ")
- #+end_src
-
- #+begin_src makefile :tangle dependencies.mk :noweb yes
-OCAML_VERSION := 4.12.0
-OCAML := ocaml-base-compiler.${OCAML_VERSION}
-
-_opam/init :
- @cleopatra echo "Creating" "a local Opam switch"
- @opam switch create . ${OCAML} --repos default,coq-released || true
- @cleopatra echo "Installing" "OCaml dependencies"
- @opam install <<deps-listing()>> -y
- @touch $@
-
-CONFIGURE += _opam
- #+end_src
-
-* Frontend
-
- #+caption: Frontend dependencies
- #+name: frontend-deps
- | Package | Version |
- |---------------+---------|
- | katex | 0.13.13 |
- | minify | 7.0.2 |
- | normalize.css | 8.0.1 |
-
- #+name: frontend-listing
- #+begin_src emacs-lisp :var frontend-deps=frontend-deps :exports none
-;; We use this Emacs Lisp snippet to generate the list of dependencies
-;; we have to install with npm
-(defun fmt-deps (d)
- (format " \"%s\": \"^%s\"" (nth 0 d) (nth 1 d)))
-
-(string-join (mapcar 'fmt-deps frontend-deps) ",\n")
- #+end_src
-
- #+begin_src json :tangle package.json :noweb yes
-{
- "dependencies": {
- <<frontend-listing()>>
- }
-}
- #+end_src
-
- #+begin_src makefile :tangle dependencies.mk :noweb yes
-package-lock.json : package.json
- @cleopatra echo "Installing" "frontend dependencies"
- @npm install
-
-CONFIGURE += package-lock.json
- #+end_src
-
- #+begin_src makefile :tangle dependencies.mk :noweb yes
-dependencies-prebuild : _opam/init package-lock.json
- #+end_src
diff --git a/site/posts/cleopatra/literate-programming.org b/site/posts/cleopatra/literate-programming.org
deleted file mode 100644
index 63e0b02..0000000
--- a/site/posts/cleopatra/literate-programming.org
+++ /dev/null
@@ -1,82 +0,0 @@
-#+TITLE: Literate Programming Projects
-
-#+SERIES: ../cleopatra.html
-#+SERIES_PREV: ./org.html
-#+SERIES_NEXT: ./theme.html
-
-Literate programming is an interesting exercice. It forces programmers
-to think about how to present their code for other people to
-understand it. It poses several significant challenges, in particular
-in terms of code refactoring. If a given piece of code is too much
-entangled with proses explaining it, rewriting it becomes cumbersome.
-
-That being said, literate programming is particularly well-suited for
-blog posts, since at the very least it provides the tool to enforce
-the code presented to readers is correct.
-
-#+BEGIN_EXPORT html
-<nav id="generate-toc"></nav>
-<div id="history">site/cleopatra/literate-programming.org</div>
-#+END_EXPORT
-
-* Tangling
-
-We use Emacs and ~org-mode~ to tangle the literate programming
-projects present in the ~posts/~ directory of this website. This is
-done with the following emacs lisp script.
-
-#+BEGIN_SRC emacs-lisp :tangle export-lp.el
-;; opinionated configuration provided by cleopatra
-(cleopatra:configure)
-
-;; allow the execution of shell block code
-(org-babel-do-load-languages
- 'org-babel-load-languages
- '((shell . t)))
-
-;; scan the posts/ directory and tangled it into lp/
-(setq org-publish-project-alist
- '(("lp"
- :base-directory "site/posts"
- :publishing-directory "lp"
- :recursive t
- :publishing-function cleopatra:tangle-publish)))
-
-(org-publish-all)
-#+END_SRC
-
-Tangling literate programming is done in the =prebuild= phase of
-*~cleopatra~*.
-
-#+BEGIN_SRC makefile :tangle literate-programming.mk
-literate-programming-prebuild :
- @cleopatra echo "Tangling" "literate programming project"
- @cleopatra exec -- cleopatra-run-elisp export-lp.el \
- >> build.log 2>&1
-
-ARTIFACTS += lp/ site/posts/deps.svg
-#+END_SRC
-
-* Building
-
-In the =build= phase, we actually try to compile the tangled projects.
-As of now, there is only one literate program: [[../posts/CoqffiEcho.org][the Echo server
-implemented in Coq]] which demonstrates how ~coqffi~ can be used to
-implement realistic software projects.
-
-#+BEGIN_SRC makefile :tangle literate-programming.mk
-COQFFI_ARCHIVE := site/files/coqffi-tutorial.tar.gz
-
-coqffi-tutorial-build : literate-programming-prebuild _opam/init
- @cleopatra echo "Building" "coqffi tutorial"
- @cd lp/coqffi-tutorial; dune build --display quiet
- @cleopatra echo "Archiving" "coqffi tutorial"
- @rm -f ${COQFFI_ARCHIVE}
- @tar --exclude="_build" -C lp/ -czvf ${COQFFI_ARCHIVE} coqffi-tutorial \
- 2>&1 >> build.log
-
-site/posts/CoqffiEcho.html : coqffi-tutorial-build
-literate-programming-build : coqffi-tutorial-build
-
-ARTIFACTS += ${COQFFI_ARCHIVE}
-#+END_SRC
diff --git a/site/posts/cleopatra/org.org b/site/posts/cleopatra/org.org
deleted file mode 100644
index 9f5a77c..0000000
--- a/site/posts/cleopatra/org.org
+++ /dev/null
@@ -1,144 +0,0 @@
-#+TITLE: Authoring Content with ~org-mode~
-
-#+SERIES: ../cleopatra.html
-#+SERIES_PREV: ./coq.html
-#+SERIES_NEXT: ./literate-programming.html
-
-#+BEGIN_EXPORT html
-<nav id="generate-toc"></nav>
-<div id="history">site/cleopatra/org.org</div>
-#+END_EXPORT
-
-* Author Guidelines
-
-* Implementation
-
-#+begin_src makefile :tangle org.mk
-EMACS := cleopatra-emacs
-
-ORG_IN := $(shell find site/ -name "*.org")
-ORG_OUT := $(ORG_IN:.org=.html)
-
-org-prebuild : .emacs
-org-build : ${ORG_OUT}
-
-soupault-build : org-build
-
-ARTIFACTS += ${ORG_OUT}
-CONFIGURE += .emacs
-
-EXPORT := --batch \
- --load="${ROOT}/scripts/packages.el" \
- --load="${ROOT}/scripts/export-org.el" \
- 2>> build.log
-
-INIT := --batch --load="${ROOT}/scripts/packages.el" \
- 2>> build.log
-
-.emacs : scripts/packages.el
- @cleopatra echo Initiating "Emacs configuration"
- @${EMACS} ${INIT}
- @touch .emacs
-
-%.html : %.org scripts/packages.el scripts/export-org.el \
- .emacs org.mk
- @cleopatra echo Exporting "$*.org"
- @${EMACS} $< ${EXPORT}
-#+end_src
-
-#+begin_src emacs-lisp :tangle scripts/packages.el
-(use-package ox-tufte :ensure t)
-#+end_src
-
-We also use the OCaml backend for ~org-babel~ to ensure our OCaml
-snippets are well-typed, among other things.
-
-#+begin_src emacs-lisp :tangle scripts/packages.el
-(use-package tuareg :ensure t
- :config
- (require 'ob-ocaml))
-#+end_src
-
-#+begin_src emacs-lisp :tangle scripts/export-org.el
-(cleopatra:configure)
-
-(org-babel-do-load-languages
- 'org-babel-load-languages
- '((dot . t)
- (shell . t)
- (ocaml . t)))
-
-(setq org-export-with-toc nil
- org-html-htmlize-output-type nil
- org-export-with-section-numbers nil)
-
-(add-to-list 'org-entities-user
- '("im" "\\(" nil "<span class=\"imath\">" "" "" ""))
-(add-to-list 'org-entities-user
- '("mi" "\\)" nil "</span>" "" "" ""))
-
-(defun with-keyword (keyword k)
- "Look-up for keyword KEYWORD, and call continuation K with its value."
- (pcase (org-collect-keywords `(,keyword))
- (`((,keyword . ,kw))
- (when kw (funcall k (string-join kw " "))))))
-
-(defun get-keyword (keyword)
- "Look-up for keyword KEYWORD, and returns its value"
- (with-keyword keyword (lambda (x) x)))
-
-(defun get-org-title (path)
- "Fetch the title of an Org file whose path is PATH."
- (with-temp-buffer
- (find-file-read-only path)
- (get-keyword "TITLE")))
-
-(defun insert-title ()
- "Insert the title of the article."
- (with-keyword
- "TITLE"
- (lambda (title)
- (insert
- (format "\n\n@@html:<h1>@@ %s @@html:</h1>@@\n\n" title)))))
-
-(defun insert-series ()
- "Insert the series root link."
- (with-keyword
- "SERIES"
- (lambda (series)
- (insert "\n\n#+attr_html: :class series\n")
- (insert series))))
-
-(defun insert-series-prev ()
- "Insert the series previous article link."
- (with-keyword
- "SERIES_PREV"
- (lambda (series-prev)
- (insert "\n\n#+attr_html: :class series-prev\n")
- (insert series-prev))))
-
-(defun insert-series-next ()
- "Insert the series next article link."
- (with-keyword
- "SERIES_NEXT"
- (lambda (series-next)
- (insert "\n\n#+attr_html: :class series-next\n")
- (insert series-next))))
-
-(defun insert-nav ()
- "Insert the navigation links."
- (when (get-keyword "SERIES")
- (insert "\n\n#+begin_nav\n")
- (insert-series)
- (insert-series-prev)
- (insert-series-next)
- (insert "\n\n#+end_nav\n")))
-
-(beginning-of-buffer)
-(insert-nav)
-(insert-title)
-
-(let ((outfile (org-export-output-file-name ".html"))
- (org-html-footnotes-section "<!-- %s --><!-- %s -->"))
- (org-export-to-file 'tufte-html outfile nil nil nil t))
-#+end_src
diff --git a/site/posts/cleopatra/soupault.org b/site/posts/cleopatra/soupault.org
deleted file mode 100644
index 3fdb8d6..0000000
--- a/site/posts/cleopatra/soupault.org
+++ /dev/null
@@ -1,702 +0,0 @@
-#+TITLE: ~soupault~
-
-#+SERIES: ../cleopatra.html
-#+SERIES_PREV: ./theme.html
-#+SERIES_NEXT: ./commands.html
-
-We use ~soupault~ to build this website[fn::~soupault~ is an awesome
-free software project, with a unique approach to static website
-generation. You should definitely [[https://soupault.app][check out their website]]!].
-
-#+begin_export html
-<nav id="generate-toc"></nav>
-#+end_export
-
-* Installation
-
- We install ~soupault~ in a local switch. We use a witness file
- ~_opam/.init~ to determine whether or not our switch has always been
- created during a previous invocation of *~cleopatra~*.
-
- #+begin_src makefile :tangle soupault.mk
-CONFIGURE += _opam rss.json
-ARTIFACTS += out
-
-soupault-prebuild : _opam/init
- #+end_src
-
- Using ~soupault~ is as simple as calling it, without any particular
- command-line arguments.
-
- #+begin_src makefile :tangle soupault.mk
-soupault-build : dependencies-prebuild style.min.css
- @cleopatra echo "Executing" "soupault"
- @soupault
- #+end_src
-
- We now describe our configuration file for ~soupault~.
-
-* Configuration
-
- #+name: base-dir
- #+begin_src verbatim :noweb yes :exports none
-~lthms
- #+end_src
-
-** Global Settings
-
- The options of the ~[settings]~ section of a ~soupault~
- configuration are often self-explanatory, and we do not spend too
- much time to detaul them.
-
- #+begin_src toml :tangle soupault.conf :noweb yes
-[settings]
-strict = true
-site_dir = "site"
-build_dir = "out/<<base-dir>>"
-doctype = "<!DOCTYPE html>"
-clean_urls = false
-generator_mode = true
-complete_page_selector = "html"
-default_content_selector = "main"
-default_content_action = "append_child"
-page_file_extensions = ["html"]
-ignore_extensions = [
- "v", "vo", "vok", "vos", "glob",
- "html~", "org"
-]
-default_template_file = "templates/main.html"
-pretty_print_html = false
- #+end_src
-
-** Setting Page Title
-
- We use the “page title” widget to set the title of the webpage
- based on the first (and hopefully the only) ~<h1>~ tag of the
- page.
-
- #+begin_src toml :tangle soupault.conf
-[widgets.page-title]
-widget = "title"
-selector = "h1"
-default = "~lthms"
-prepend = "~lthms: "
- #+end_src
-
-** Acknowledging ~soupault~
-
- When creating a new ~soupault~ project (using ~soupault --init~),
- the default configuration file suggests advertising the use of
- ~soupault~. Rather than hard-coding the used version of ~soupault~
- (which is error-prone), we rather determine the version of
- ~soupault~ with the following script.
-
- #+NAME: soupault-version
- #+begin_src bash :results verbatim output
-soupault --version | head -n 1 | tr -d '\n'
- #+end_src
-
- The configuration of the widget ---initially provided by
- ~soupault~--- becomes less subject to the obsolescence[fn::That
- is, as long as ~soupault~ does not change the output of its
- ~--version~ option.].
-
- #+begin_src toml :tangle soupault.conf :noweb yes
-[widgets.generator-meta]
-widget = "insert_html"
-html = """<meta name="generator" content="<<soupault-version()>>">"""
-selector = "head"
- #+end_src
-
-** Prefixing Internal URLs
-
- On the one hand, internal links can be absolute, meaning they
- start with a leading ~/~, and therefore are relative to the
- website root. On the other hand, website (especially static
- website) can be placed in larger context. For instance, my
- personal website lives inside the ~~lthms~ directory of the
- ~soap.coffee~ domain[fn::To my experience in hosting webapps and
- websites, this set-up is way harder to get right than I initially
- expect.].
-
- The purpose of this plugin is to rewrite internal URLs which are relative to the
- root, in order to properly prefix them.
-
- From a high-level perspective, the plugin structure is the following.
-
- First, we validate the widget configuration.
-
- #+BEGIN_SRC lua :tangle plugins/urls-rewriting.lua
-prefix_url = config["prefix_url"]
-
-if not prefix_url then
- Plugin.fail("Missing mandatory field: `prefix_url'")
-end
-
-if not Regex.match(prefix_url, "^/(.*)") then
- prefix_url = "/" .. prefix_url
-end
-
-if not Regex.match(prefix_url, "(.*)/$") then
- prefix_url = prefix_url .. "/"
-end
- #+END_SRC
-
- Then, we propose a generic function to enumerate and rewrite tags
- which can have.
-
- #+BEGIN_SRC lua :tangle plugins/urls-rewriting.lua
-function prefix_urls (links, attr, prefix_url)
- index, link = next(links)
-
- while index do
- href = HTML.get_attribute(link, attr)
-
- if href then
- if Regex.match(href, "^/") then
- href = Regex.replace(href, "^/*", "")
- href = prefix_url .. href
- end
-
- HTML.set_attribute(link, attr, href)
- end
- index, link = next(links, index)
- end
-end
- #+END_SRC
-
- Finally, we use this generic function for relevant tags.
-
- #+BEGIN_SRC lua :tangle plugins/urls-rewriting.lua
-prefix_urls(HTML.select(page, "a"), "href", prefix_url)
-prefix_urls(HTML.select(page, "link"), "href", prefix_url)
-prefix_urls(HTML.select(page, "img"), "src", prefix_url)
-prefix_urls(HTML.select(page, "script"), "src", prefix_url)
-prefix_urls(HTML.select(page, "use"), "href", prefix_url)
- #+END_SRC
-
- Again, configuring soupault to use this plugin is relatively
- straightforward.
-
- #+BEGIN_SRC toml :tangle soupault.conf :noweb yes
-[widgets.urls-rewriting]
-widget = "urls-rewriting"
-prefix_url = "<<base-dir>>"
-after = "mark-external-urls"
- #+END_SRC
-
-** Marking External Links
-
- #+BEGIN_SRC lua :tangle plugins/external-urls.lua
-function mark(name)
- return '<span class="icon"><svg><use href="/img/icons.svg#'
- .. name ..
- '"></use></svg></span>'
-end
-
-links = HTML.select(page, "a")
-
-index, link = next(links)
-
-while index do
- href = HTML.get_attribute(link, "href")
-
- if href then
- if Regex.match(href, "^https?://github.com") then
- icon = HTML.parse(mark("github"))
- HTML.append_child(link, icon)
- elseif Regex.match(href, "^https?://") then
- icon = HTML.parse(mark("external-link"))
- HTML.append_child(link, icon)
- end
- end
-
- index, link = next(links, index)
-end
- #+END_SRC
-
- #+BEGIN_SRC toml :tangle soupault.conf
-[widgets.mark-external-urls]
-after = "generate-history"
-widget = "external-urls"
- #+END_SRC
-
-** Generating a Table of Contents
-
- The ~toc~ widget allows for generating a table of contents for
- HTML files which contains a node matching a given ~selector~ (in
- the case of this document, ~#generate-toc~).
-
- #+begin_src toml :tangle soupault.conf
-[widgets.table-of-contents]
-widget = "toc"
-selector = "#generate-toc"
-action = "replace_content"
-valid_html = true
-min_level = 2
-max_level = 3
-numbered_list = false
-heading_links = true
-heading_link_text = " §"
-heading_links_append = true
-heading_link_class = "anchor-link"
-
-[widgets.append-toc-title]
-widget = "insert_html"
-selector = "#generate-toc"
-action = "prepend_child"
-html = '<h2>Table of Contents</h2>'
-after = "table-of-contents"
- #+end_src
-
-** Generating Per-File Revisions Tables
-
-*** Users Instructions
-
- This widgets allows to generate a so-called “revisions table” of
- the filename contained in a DOM element of id ~history~, based on
- its history. Paths should be relative to the directory from which
- you start the build process (typically, the root of your
- repository). The revisions table notably provides hyperlinks to a
- ~git~ webview for each commit.
-
- For instance, considering the following HTML snippet
-
- #+begin_src html
-<div id="history">
- site/posts/FooBar.org
-</div>
- #+end_src
-
- This plugin will replace the content of this ~<div>~ with the
- revisions table of ~site/posts/FooBar.org~.
-
-*** Customization
-
- The base of the URL webview for the document you are currently
- reading is src_verbatim[:noweb yes :exports code]{<<repo>>}.
-
- #+name: repo
- #+begin_src verbatim :exports none
-https://src.soap.coffee/soap.coffee/lthms.git
- #+end_src
-
- The template used to generate the revision table is the following.
-
- #+begin_src html :tangle templates/history.html :noweb yes
-<details id="history">
- <summary>Revisions</summary>
- <p>
- This revisions table has been automatically generated
- from <a href="<<repo>>">the
- <code>git</code> history of this website repository</a>, and the
- change descriptions may not always be as useful as they should.
- </p>
-
- <p>
- You can consult the source of this file in its current version
- <a href="<<repo>>/tree/{{file}}">here</a>.
- </p>
-
- <table class="fullwidth">
- {{#history}}
- <tr>
- <td class="date"
-{{#created}}
- id="created-at"
-{{/created}}
-{{#modified}}
- id="modified-at"
-{{/modified}}
- >{{date}}</td>
- <td class="subject">{{subject}}</td>
- <td class="commit">
- <a href="<<repo>>/commit/{{filename}}/?id={{hash}}">{{abbr_hash}}</a>
- </td>
- </tr>
- {{/history}}
- </table>
-</details>
- #+end_src
-
-*** Implementation
-
- We use the built-in [[https://soupault.neocities.org/reference-manual/#widgets-preprocess-element][=preprocess_element=]] to implement, which
- means we need a script which gets its input from the standard
- input, and echoes its output to the standard input.
-
- #+begin_src toml :tangle soupault.conf
-[widgets.generate-history]
-widget = "preprocess_element"
-selector = "#history"
-command = 'scripts/history.sh templates/history.html'
-action = "replace_element"
- #+end_src
-
- This plugin proceeds as follows:
-
- 1. Using an ad-hoc script, it generates a JSON containing for each revision
- - The subject, date, hash, and abbreviated hash of the related commit
- - The name of the file at the time of this commit
- 2. This JSON is passed to a mustache engine (~haskell-mustache~) with a
- proper template
- 3. The content of the selected DOM element is replaced with the output of
- ~haskell-mustache~
-
- This translates in Bash like this.
-
- #+begin_src bash :tangle scripts/history.sh :shebang "#!/usr/bin/bash"
-function main () {
- local file="${1}"
- local template="${2}"
-
- tmp_file=$(mktemp)
- generate_json ${file} > ${tmp_file}
- haskell-mustache ${template} ${tmp_file}
- rm ${tmp_file}
-}
- #+end_src
-
- Generating the expected JSON is therefore as simple as:
-
- - Fetching the logs
- - Reading 8 line from the logs, parse the filename from the 6th
- line
- - Outputing the JSON
-
- We will use ~git~ to get the information we need. By default,
- ~git~ subcommands use a pager when its output is likely to be
- long. This typically includes ~git-log~. To disable this
- behavior, ~git~ exposes the ~--no-pager~ command. Besides, we
- also need ~--follow~ and ~--stat~ to deal with file
- renaming. Without this option, ~git-log~ stops when the file
- first appears in the repository, even if this “creation” is
- actually a renaming. Therefore, the ~git~ command line we use to
- collect our history is
-
- #+name: gitlog
- #+begin_src bash :tangle scripts/history.sh :noweb yes
-function gitlog () {
- local file="${1}"
- git --no-pager log \
- --follow \
- --stat=10000 \
- --pretty=format:'%s%n%h%n%H%n%cs%n' \
- "${file}"
-}
- #+end_src
-
- This function will generate a sequence of 8 lines containing all
- the relevant information we are looking for, for each commit,
- namely:
-
- - Subject
- - Abbreviated hash
- - Full hash
- - Date
- - Empty line
- - Change summary
- - Shortlog
- - Empty line
-
- For instance, the =gitlog= function will output the following
- lines for the last commit of this very file:
-
- #+begin_src bash :results verbatim :exports results :noweb yes
-<<gitlog>>
-gitlog "soupault.org" | head -n8
- #+end_src
-
- Among other things, the 6th line contains the filename. We need
- to extract it, and we do that with ~sed~. In case of file
- renaming, we need to parse something of the form ~both/to/{old =>
- new}~.
-
- #+begin_src bash :tangle scripts/history.sh :noweb yes
-function parse_filename () {
- local line="${1}"
- local shrink='s/ *\(.*\) \+|.*/\1/'
- local unfold='s/\(.*\){\(.*\) => \(.*\)}/\1\3/'
-
- echo ${line} | sed -e "${shrink}" | sed -e "${unfold}"
-}
- #+end_src
-
- The next step is to process the logs to generate the expected
- JSON. We have to deal with the fact that JSON does not allow the
- last item of an array to be concluded by ",". Besides, we also
- want to indicate which commit is responsible for the creation of
- the file. To do that, we use two variables: =idx= and
- =last_entry=. When =idx= is equal to 0, we know it is the latest
- commit. When =idx= is equal to =last_entry=, we know we are
- looking at the oldest commit for that file.
-
- #+begin_src bash :tangle scripts/history.sh :noweb yes
-function generate_json () {
- local input="${1}"
- local logs="$(gitlog ${input})"
-
- if [ ! $? -eq 0 ]; then
- exit 1
- fi
-
- let "idx=0"
- let "last_entry=$(echo "${logs}" | wc -l) / 8"
-
- local subject=""
- local abbr_hash=""
- local hash=""
- local date=""
- local file=""
- local created="true"
- local modified="false"
-
- echo -n "{"
- echo -n "\"file\": \"${input}\""
- echo -n ",\"history\": ["
-
- while read -r subject; do
- read -r abbr_hash
- read -r hash
- read -r date
- read -r # empty line
- read -r file
- read -r # short log
- read -r # empty line
-
- if [ ${idx} -ne 0 ]; then
- echo -n ","
- fi
-
- if [ ${idx} -eq ${last_entry} ]; then
- created="true"
- modified="false"
- else
- created="false"
- modified="true"
- fi
-
- output_json_entry "${subject}" \
- "${abbr_hash}" \
- "${hash}" \
- "${date}" \
- "$(parse_filename "${file}")" \
- "${created}" \
- "${modified}"
-
- let idx++
- done < <(echo "${logs}")
-
- echo -n "]}"
-}
- #+end_src
-
- Generating the JSON object for a given commit is as simple as
-
- #+begin_src bash :tangle scripts/history.sh :noweb yes
-function output_json_entry () {
- local subject="${1}"
- local abbr_hash="${2}"
- local hash="${3}"
- local date="${4}"
- local file="${5}"
- local created="${6}"
- local last_entry="${7}"
-
- echo -n "{\"subject\": \"${subject}\""
- echo -n ",\"created\":${created}"
- echo -n ",\"modified\":${modified}"
- echo -n ",\"abbr_hash\":\"${abbr_hash}\""
- echo -n ",\"hash\":\"${hash}\""
- echo -n ",\"date\":\"${date}\""
- echo -n ",\"filename\":\"${file}\""
- echo -n "}"
-}
- #+end_src
-
- And we are done! We can safely call the =main= function to generate
- our revisions table.
-
- #+begin_src bash :tangle scripts/history.sh
-main "$(cat)" "${1}"
- #+end_src
-
-** Rendering Equations Offline
- :PROPERTIES:
- :CUSTOM_ID: katex
- :END:
-
-*** Users instructions
-
- Inline equations written in the DOM under the class
- src_css{.imath} and using the \im \LaTeX \mi syntax can be
- rendered once and for all by ~soupault~. User For instance,
- ~<span class="imath">\LaTeX</span>~ is rendered \im \LaTeX \mi as
- expected.
-
- Using this widgets requires being able to inject raw HTML in
- input files.
-
-*** Implementation
-
- #+begin_src js :tangle scripts/render-equations.js
-var katex = require("katex");
-var fs = require("fs");
-var input = fs.readFileSync(0);
-var displayMode = process.env.DISPLAY != undefined;
-
-var html = katex.renderToString(String.raw`${input}`, {
- throwOnError : false,
- displayModed : displayMode
-});
-
-console.log(html)
- #+end_src
-
- We reuse once again the =preprocess_element= widget. The selector
- is ~.imath~ (~i~ stands for inline in this context), and we
- replace the previous content with the result of our script.
-
- #+begin_src toml :tangle soupault.conf
-[widgets.inline-math]
-widget = "preprocess_element"
-selector = ".imath"
-command = "node scripts/render-equations.js"
-action = "replace_content"
-
-[widgets.display-math]
-widget = "preprocess_element"
-selector = ".dmath"
-command = "DISPLAY=1 node scripts/render-equations.js"
-action = "replace_content"
- #+end_src
-
-** RSS Feed
-
- #+begin_src toml :tangle soupault.conf
-[index]
-index = true
-dump_json = "rss.json"
-extract_after_widgets = ["urls-rewriting"]
-
-[index.fields]
-title = { selector = ["h1"] }
-modified-at = { selector = ["#modified-at"] }
-created-at = { selector = ["#created-at"] }
- #+end_src
-
-** Series Navigation
-
- #+begin_src lua :tangle plugins/series.lua
-function get_title_from_path (path)
- if Sys.is_file(path) then
- local content_raw = Sys.read_file(path)
- local content_dom = HTML.parse(content_raw)
- local title = HTML.select_one(content_dom, "h1")
-
- if title then
- return String.trim(HTML.inner_html(title))
- else
- Plugin.fail(path .. ' has no <h1> tag')
- end
- else
- Plugin.fail(path .. ' is not a file')
- end
-end
- #+end_src
-
- #+begin_src lua :tangle plugins/series.lua
-function generate_nav_item_from_title (title, url, template)
- local env = {}
- env["url"] = url
- env["title"] = title
- local new_content = String.render_template(template, env)
- return HTML.parse(new_content)
-end
- #+end_src
-
- #+begin_src lua :tangle plugins/series.lua
-function generate_nav_items (cwd, cls, template)
- local elements = HTML.select(page, cls)
-
- local i = 1
- while elements[i] do
- local element = elements[i]
- local url = HTML.strip_tags(element)
- local path = Sys.join_path(cwd, url)
- local title_str = get_title_from_path(path)
-
- HTML.replace_content(
- element,
- generate_nav_item_from_title(title_str, url, template)
- )
-
- i = i + 1
- end
-end
- #+end_src
-
- #+begin_src lua :tangle plugins/series.lua
-cwd = Sys.dirname(page_file)
-
-home_template = 'This article is part of the series “<a href="{{ url }}">{{ title }}</a>.”'
-nav_template = '<a href="{{ url }}">{{ title }}</a>'
-
-generate_nav_items(cwd, ".series", home_template)
-generate_nav_items(cwd, ".series-prev", nav_template)
-generate_nav_items(cwd, ".series-next", nav_template)
- #+end_src
-
-#+begin_src toml :tangle soupault.conf
-[widgets.series]
-widget = "series"
-#+end_src
-
-** Injecting Minified CSS
-
- #+begin_src lua :tangle plugins/css.lua
-style = HTML.select_one(page, "style")
-
-if style then
- css = HTML.create_text(Sys.read_file("style.min.css"))
- HTML.replace_content(style, css)
-end
- #+end_src
-
- #+begin_src toml :tangle soupault.conf
-[widgets.css]
-widget = "css"
- #+end_src
-
-** Cleaning-up
-
- #+begin_src lua :tangle plugins/clean-up.lua
-function remove_if_empty(html)
- if String.trim(HTML.inner_html(html)) == "" then
- HTML.delete(html)
- end
-end
- #+end_src
-
- #+begin_src lua :tangle plugins/clean-up.lua
-function remove_all_if_empty(cls)
- local elements = HTML.select(page, cls)
-
- local i = 1
- while elements[i] do
- local element = elements[i]
- remove_if_empty(element)
- i = i + 1
- end
-end
- #+end_src
-
- #+begin_src lua :tangle plugins/clean-up.lua
-remove_all_if_empty("p") -- introduced by org-mode
-remove_all_if_empty("div.code") -- introduced by coqdoc
- #+end_src
-
-#+begin_src toml :tangle soupault.conf
-[widgets.clean-up]
-widget = "clean-up"
-#+end_src
diff --git a/site/posts/cleopatra/theme.org b/site/posts/cleopatra/theme.org
deleted file mode 100644
index 9b1a129..0000000
--- a/site/posts/cleopatra/theme.org
+++ /dev/null
@@ -1,573 +0,0 @@
-#+TITLE: Layout and Style
-
-#+SERIES: ../cleopatra.html
-#+SERIES_PREV: ./literate-programming.html
-#+SERIES_NEXT: ./soupault.html
-
-#+BEGIN_EXPORT html
-<nav id="generate-toc"></nav>
-<div id="history">site/cleopatra/theme.org</div>
-#+END_EXPORT
-
-* Setup
-
- As often when it comes to frontend development, we will use several
- tools hosted in the ~npm~ packages repository. ~npm~ is infamous
- for downloading lots of files and to store it in the ~node_modules/~
- directory. We configure *~cleopatra~* accordingly.
-
- #+begin_src makefile :tangle theme.mk
-CONFIGURE += package.json package-lock.json node_modules
- #+end_src
-
-* Base CSS
-
- We know construct piece by piece the “base” CSS layout which we will
- inject inside a ~<style>~ tag in each web page.
-
-** Layout
-
- Our goal is to have a three columns layout: one aside menu, with
- the top-level navigation links (technical articles, news, etc.) and
- the table of contents of the current pages if relevant, one main
- area for the webpage content, and a margin column with side notes
- and margin notes.
-
- #+caption: Widths of page components (in ~rem~)
- #+name: widths
- | Content | 35 |
- | Gutter | 3 |
- | Margin | 13 |
-
- #+name: main-width
- #+begin_src emacs-lisp :exports none :noweb yes :var widths=widths[,1]
-(nth 0 widths)
- #+end_src
-
- #+name: gutter-width
- #+begin_src emacs-lisp :exports none :noweb yes :var widths=widths[,1]
-(nth 1 widths)
- #+end_src
-
- #+name: margin-width
- #+begin_src emacs-lisp :exports none :noweb yes :var widths=widths[,1]
-(nth 2 widths)
- #+end_src
-
- #+begin_src css :tangle style.css :noweb yes
-:root {
- --main-width: <<main-width()>>rem;
- --gutter-width: <<gutter-width()>>rem;
- --margin-width: <<margin-width()>>rem;
- --code-width: calc(var(--main-width) + var(--gutter-width) + var(--margin-width));
- --body-width: calc(var(--main-width) + 2 * (var(--gutter-width) + var(--margin-width)));
-}
- #+end_src
-
- According to CSS’ own [[https://www.w3.org/TR/css-variables-1/#using-variables][specification]], you cannot use ~var()~ inside
- media queries. As a consequnece, for our theme to be responsive,
- the full width of the page content (\im 2 \times (that is,
- \mathrm{margin\_width} + \mathrm{gutter\_width}) +
- \mathrm{content\_width} \mi or call_body-width[:results raw]()rem)
- has to be hard-coded[fn::Fortunately, this is a literate
- program. This value is actually programmatically computed, so that
- we do not have to worry about forgetting to update it].
-
- #+name: body-width
- #+begin_src bash :exports none :noweb yes
-echo $((2 * (<<margin-width()>> + <<gutter-width()>>) + <<main-width()>>))
- #+end_src
-
- #+begin_src css :tangle style.css :noweb yes
-@media (max-width: <<body-width()>>rem) {
- :root {
- --body-width: var(--main-width);
- --code-width: var(--main-width);
- }
-}
- #+end_src
-
- And now, we are free to actually implement the layout.
-
- #+begin_src css :tangle style.css :noweb yes
-,* {
- box-sizing: border-box;
-}
-
-.fullwidth {
- width: var(--body-width);
-}
-
-@media (min-width: <<body-width()>>rem) {
- .fullwidth {
- margin-left: calc(-1 * (var(--margin-width) + var(--gutter-width)));
- }
-}
-
-html {
- font-size: 1rem;
-}
-
-body {
- line-height: 1.4;
- max-width: var(--body-width);
- margin-left: auto;
- margin-right: auto;
-}
-
-aside {
- background: var(--bg);
- z-index: 9999;
- width: var(--body-width);
- align-self: flex-start;
- position: sticky;
- top: 0;
-}
-
-aside nav {
- text-align: center;
- border-bottom: 1px solid var(--fade);
-}
-
-aside nav ul {
- list-style: none;
- padding: 1rem 0;
- margin: 0;
-}
-
-aside nav li {
- display: inline;
-}
-
-aside nav li:not(:first-of-type)::before {
- content: " · ";
-}
-
-main {
- counter-reset: sidenote-counter;
- max-width: var(--main-width);
- margin: auto;
-}
-
-main nav {
- font-style: italic;
- color: var(--fg-plus);
- background: var(--current-line);
- padding: .5rem 1rem;
-}
-
-main nav .series-next {
- text-align: right;
-}
-
-main nav p.series-next::after {
- content: " →";
-}
-
-main nav p.series-prev::before {
- content: "← ";
-}
-
-img {
- max-width: 100%;
-}
-
-#whoami.marginnote {
- color: var(--fg);
- margin-bottom: 2em;
-}
-
-img.avatar {
- border-radius: 20px;
- display: block;
- max-width: 90%;
- margin: auto;
-}
-
-dd {
- margin-left: 0;
- margin-bottom: 0.5rem;
-}
-
-.sidenote,
-.marginnote {
- font-size: smaller;
- position: relative;
- width: var(--margin-width);
-}
-
-.sidenote {
- margin-right: calc(-1 * (var(--margin-width) + var(--gutter-width)));
- float: right;
- clear: right;
-}
-
-.marginnote {
- float: left;
- clear: left;
- margin-left: calc(-1 * (var(--margin-width) + var(--gutter-width)));
-}
-
-input.margin-toggle {
- display: none;
-}
-
-label.sidenote-number {
- display: inline;
-}
-
-label.margin-toggle:not(.sidenote-number) {
- display: none;
-}
-
-.sidenote-number:after,
-.sidenote:before {
- position: relative;
- vertical-align: baseline;
-}
-
-.sidenote-number {
- counter-increment: sidenote-counter;
-}
-
-.sidenote-number::after {
- content: "(" counter(sidenote-counter, lower-greek) ")";
- font-size: 60%;
- top: -0.4rem;
- left: 0.1rem;
-}
-
-.sidenote::before {
- content: "(" counter(sidenote-counter, lower-greek) ")";
- font-size: 70%;
- top: -0.5rem;
- right: 0.1rem;
-}
-
-div.code,
-pre {
- width: var(--code-width);
- overflow-x: auto;
- overflow-y: hidden;
- padding: 1rem 2rem;
-}
-
-main {
- padding-top: 4.2rem;
- padding-bottom: 4.2rem;
-}
-
-h1 {
- text-align: center;
-}
-
-h2, h3, h4 {
- font-style: italic;
-}
-
-h1, h2, h3, h4 {
- color: var(--doc);
- font-family: serif;
- font-weight: normal;
-}
-
-dt {
- font-weight: bold;
-}
-
-div.code,
-span.inlinecode,
-pre,
-tt,
-.dmath,
-.imath {
- font-family: monospace;
- font-size: 85%;
-}
-
-details {
- margin: 1.5rem 0;
-}
-
-table {
- border-top: 2px solid var(--fg);
- border-bottom: 2px solid var(--fg);
- border-collapse: collapse;
- width: 100%;
- margin: 1.5rem 0;
-}
-
-th {
- font-weight: normal;
- text-transform: uppercase;
-}
-
-td,
-th {
- border-top: 1px solid var(--fade);
- height: 2em;
- padding: 0 1em;
-}
-
-td.date,
-td.commit {
- text-align: center;
- font-size: 0.75em;
- font-family: monospace;
-}
-
-/* max-width has to be equal to --body-width */
-@media (max-width: <<body-width()>>rem) {
- body {
- padding: 2rem;
- margin: auto;
- display: block;
- }
-
- aside {
- width: var(--main-width);
- margin: auto;
- }
-
- label.margin-toggle:not(.sidenote-number) {
- display: inline;
- }
-
- .sidenote,
- .marginnote {
- display: none;
- }
-
- .margin-toggle:checked + .sidenote,
- .margin-toggle:checked + .marginnote {
- display: block;
- float: left;
- left: 1rem;
- clear: both;
- width: 95%;
- margin: 1rem 2.5%;
- vertical-align: baseline;
- position: relative;
- }
-
- label {
- cursor: pointer;
- }
-
- pre, aside, div.code {
- width: 100%;
- }
-}
- #+end_src
-
-** Colors
-
- #+begin_src css :tangle style.css
-:root {
- --bg: white;
- --bg-plus: #f7f7f7;
- --current-line: #fbfbfb;
- --fade: #cfcecb;
- --fg: #3c3c3c;
- --fg-plus: #575757;
- --doc: black;
- --warning: #bd745e;
- --red: #b3534b;
- --green: #6d9319;
- --yellow: #d4b100;
-}
- #+end_src
-
- #+begin_src css :tangle style.css
-body {
- font-family: sans-serif;
- color: var(--fg);
- background: var(--bg);
-}
-
-h2 a.anchor-link,
-h3 a.anchor-link,
-h4 a.anchor-link {
- display: none;
- font-style: normal;
- text-decoration: none;
- font-family: monospace;
- font-size: smaller;
- color: var(--doc);
-}
-
-[id] {
- scroll-margin-top: 4rem;
-}
-
-h2:hover a.anchor-link,
-h3:hover a.anchor-link,
-h4:hover a.anchor-link {
- display: inline;
-}
-
-.sidenote,
-.marginnote {
- color: var(--fg-plus);
-}
-
-pre,
-code,
-div.code,
-span.inlinecode,
-tt {
- color: var(--doc);
-}
- #+end_src
-
-** Coq
-
- #+begin_src css :tangle style.css
-div.code {
- white-space: nowrap;
-}
-
-div.code,
-span.inlinecode {
- font-family : monospace;
-}
-
-.paragraph {
- margin-bottom : .8em;
-}
-
-.code a[href] {
- color : inherit;
- text-decoration : none;
- background : var(--bg-plus);
- padding : .1rem .15rem .1rem .15rem;
- border-radius : 15%;
-}
-
-.code .icon {
- display: none;
-}
-#+END_SRC
-
-** Icons
-
- #+begin_src css :tangle style.css
-.icon svg {
- display: inline;
- width: 1em;
- height: .9em;
- vertical-align: text-top;
-}
-
-a[href], .margin-toggle {
- color: #0000ee;
-}
-
-a[href] .icon svg {
- fill: #0000ee;
-}
-
-a[href]:visited {
- color: #25008b;
-}
-
-a[href]:visited .icon svg {
- fill: #25008b;
-}
-
-.url-mark.fa {
- display: inline;
- font-size: 90%;
- width: 1em;
-}
-
-.url-mark.fa-github::before {
- content: "\00a0\f09b";
-}
-
-.url-mark.fa-external-link::before {
- content: "\00a0\f08e";
-}
- #+end_src
-
-** Minify CSS
-
- #+begin_src shell :shebang #!/bin/bash :tangle scripts/css.sh
-minify="$(npm bin)/minify"
-normalize="$(npm root)/normalize.css/normalize.css"
-style="style.css"
-
-# minify add a newline character at the end of its input
-# we remove it using `head'
-echo "
-@charset \"UTF-8\";
-$(cat ${normalize})
-$(cat ${style})
-" | ${minify} --css | head -c -1 > style.min.css
- #+end_src
-
- #+begin_src makefile :tangle theme.mk
-style.min.css : style.css dependencies-prebuild
- @cleopatra echo "Minifying" "CSS"
- @scripts/css.sh
-
-ARTIFACTS += style.min.css
-
-theme-build : style.min.css
- #+end_src
-
-* HTML Templates
-
- It would be best if we had a preprocessing step to inject the
- minified style, rather than using ~soupault~ to do the work once per
- page.
-
- #+begin_src html :tangle templates/main.html :noweb yes
-<html lang="en">
- <head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <style></style>
- <link href="https://soap.coffee/+vendors/katex.0.11.1+swap/katex.css" rel="stylesheet" media="none" onload="if(media!='all')media='all'">
- <title></title>
- </head>
- <body>
- <aside>
- <nav>
- <ul>
- <li>
- <a href="/">Technical Posts</a>
- </li>
- <li>
- <a href="/opinions">Opinions</a>
- </li>
- <li>
- <a href="/news">News</a>
- </li>
- </ul>
- </nav>
- </aside>
- <main>
- <span id="whoami" class="marginnote">
- <img class="avatar" src="/img/vampy.jpg" />
-
- <p>
- Hi, I’m <strong>lthms</strong>.
- </p>
-
- <p>
- I don’t like syntax highlighting, but I like
- types and functional programming languages.
- He/him.
- </p>
-
- <p>
- Interested in starting a discussion? Don’t hesitate to <a
- href="mailto:~lthms/public-inbox@lists.sr.ht">shoot me an
- email</a>.
- </p>
- </span>
- </main>
- </body>
-</html>
- #+end_src
diff --git a/site/posts/coq.org b/site/posts/coq.org
deleted file mode 100644
index b8b6818..0000000
--- a/site/posts/coq.org
+++ /dev/null
@@ -1,44 +0,0 @@
-#+SERIES: ./index.html
-#+SERIES_NEXT: haskell.html
-
-#+TITLE: About Coq
-
-Coq is a formal proof management system which provides a pure
-functional language with nice dependent types together with an
-environment for writing machine-checked proofs.
-
-- [[./StronglySpecifiedFunctions.org][A Series on Strongly-Specified Funcions in Coq]] ::
- Using dependent types and the ~Prop~ sort, it becomes possible to specify
- functions whose arguments and results are constrained by properties.
- Using such a “strongly-specified” function requires to provide a proof that
- the supplied arguments satisfy the expected properties, and allows for soundly
- assuming the results are correct too. However, implementing dependently-typed
- functions can be challenging.
-
-- [[./Ltac.org][A Series on Ltac]] ::
- Ltac is the “tactic language” of Coq. It is commonly advertised as the common
- approach to write proofs, which tends to bias how it is introduced to new Coq
- users (/e.g./, in Master courses). In this series, we present Ltac as the
- metaprogramming tool it is, since fundamentally it is an imperative language
- which allows for constructing Coq terms interactively and incrementally.
-
-- [[./RewritingInCoq.html][Rewriting in Coq]] ::
- The ~rewrite~ tactics are really useful, since they are not limited to the Coq
- built-in equality relation.
-
-- [[./ClightIntroduction.html][A Study of Clight and its Semantics]] ::
- Clight is a “simplified” C AST used by CompCert, the certified C compiler. In
- this write-up, we prove a straighforward functional property of a small C
- function, as an exercise to discover the Clight semantics.
-
-- [[./AlgebraicDatatypes.html][Proving Algebraic Datatypes are “Algebraic”]] ::
- The set of types which can be defined in a language together with ~+~ and ~*~
- form an “algebraic structure” in the mathematical sense, hence the name. It
- means the definitions of ~+~ and ~*~ have to satisfy properties such as
- commutativity or the existence of neutral elements.
-
-- [[./Coqffi.org][A Series on ~coqffi~]] ::
- ~coqffi~ generates Coq FFI modules from compiled OCaml interface
- modules (~.cmi~). In practice, it greatly reduces the hassle to
- together OCaml and Coq modules within the same codebase, especially
- when used together with the ~dune~ build system.
diff --git a/site/posts/haskell.org b/site/posts/haskell.org
deleted file mode 100644
index cf06b89..0000000
--- a/site/posts/haskell.org
+++ /dev/null
@@ -1,13 +0,0 @@
-#+SERIES: index.html
-#+SERIES_PREV: coq.html
-#+SERIES_NEXT: miscellaneous.html
-
-#+TITLE: About Haskell
-
-Haskell is a pure, lazy, functional programming language with a very
-expressive type system.
-
-- [[./ExtensibleTypeSafeErrorHandling.html][Extensible, Type-Safe Error Handling In Haskell]] ::
- Ever heard of “extensible effects?” By applying the same principle, but for
- error handling, the result is nice, type-safe API for Haskell, with a lot of
- GHC magic under the hood.
diff --git a/site/posts/index.md b/site/posts/index.md
new file mode 100644
index 0000000..c1ac8b5
--- /dev/null
+++ b/site/posts/index.md
@@ -0,0 +1,3 @@
+# Archives
+
+@[archives](all)
diff --git a/site/posts/index.org b/site/posts/index.org
deleted file mode 100644
index bc53280..0000000
--- a/site/posts/index.org
+++ /dev/null
@@ -1,28 +0,0 @@
-#+TITLE: Technical Articles
-
-Over the past years, I have tried to capitalize on my findings. More
-often than not, I have lacked in regularity, but I hope I have made up
-for it in exoticism.
-
-#+begin_export html
-<nav id="generate-toc"></nav>
-#+end_export
-
-* About Coq
- :PROPERTIES:
- :CUSTOM_ID: coq
- :END:
-
- #+include: ./coq.org
-
-* About Haskell
-
- #+include: ./haskell.org
-
-* Miscellaneous
-
- #+include: ./miscellaneous.org
-
-* About this Website
-
- #+include: ./meta.org
diff --git a/site/posts/meta.org b/site/posts/meta.org
deleted file mode 100644
index f6c1de5..0000000
--- a/site/posts/meta.org
+++ /dev/null
@@ -1,16 +0,0 @@
-#+TITLE: About this Website
-
-#+SERIES: ./index.html
-#+SERIES_PREV: miscellaneous.html
-
-The generation of this website is far from being trivial, and requires
-the combination of —probably too— many tools. For instance, even if I
-mostly use Org mode for authoring content, most of my write-ups about
-Coq are actually Coq files, and I use ~coqdoc~ to generate the HTML
-pages you read.
-
-- [[./Thanks.org][Thanks!]] ::
- This website could not exist without many awesome free software
- projects. Although I could not list them all even if I wanted, my
- desire is at least to try keeping up-to-date a curated description
- of the most significant ones.
diff --git a/site/posts/miscellaneous.org b/site/posts/miscellaneous.org
deleted file mode 100644
index 61ec5c7..0000000
--- a/site/posts/miscellaneous.org
+++ /dev/null
@@ -1,24 +0,0 @@
-#+SERIES: index.html
-#+SERIES_PREV: haskell.html
-#+SERIES_NEXT: meta.html
-
-#+TITLE: Miscellaneous
-
-Over the years, I have made a habit of learning new programming
-languages, out of curiosity, and I intend to continue this way for the
-time being.
-
-- [[./DiscoveringCommonLisp.html][Discovering Common Lisp with ~trivial-gamekit~]] ::
- Common Lisp is a venerable programming languages like no other I
- know. From the creation of a Lisp package up to the creation of a
- standalone executable, we explore the shore of this strange beast.
-
-- [[./RankNTypesInOCaml.html][Writing a Function Whose Argument is a Polymorphic Function in OCaml]] ::
- In OCaml, it is not possible to write a function whose argument is a
- polymorphic function. Trying to write such a function results in the
- type-checker complaining back at you. The trick to be able to write
- such a function is to use records.
-
-- [[./NeoVimOcamlInterfacesAndLSP.html][Neovim, OCaml Interfaces, Tree-Sitter and LSP]] ::
- Can we all agree that witnessing syntax highlighting being absolutely off is
- probably the most annoying thing that can happen to anybody?
diff --git a/site/projects/index.org b/site/projects/index.org
deleted file mode 100644
index 705ef55..0000000
--- a/site/projects/index.org
+++ /dev/null
@@ -1,5 +0,0 @@
-#+BEGIN_EXPORT html
-<h1>Projects</h1>
-#+END_EXPORT
-
-To be written. One day.
diff --git a/site/projects/keyr/stats.html b/site/projects/keyr/stats.html
deleted file mode 100644
index eabec58..0000000
--- a/site/projects/keyr/stats.html
+++ /dev/null
@@ -1,107 +0,0 @@
-<h1>Keystrokes Reporting</h1>
-
-<p>
- We have counted <span id="global_count">0</span> on <strong>lthms</strong>’
- computers since <span id="start_date">today</span>, thanks to
- <a href="https://sr.ht/~lthms/keyr"><strong>keyr</strong></a>.
-</p>
-
-<p>
- <strong>Beware:</strong> Contrary to the rest of this blog, you need to enable
- JavaScript for this webpage to work.
-</p>
-
-<h2>during the past 10 days</h2>
-
-<div id="heatmap-weeks" class="fullwidth"></div>
-
-<h2>during the past 6 months</h2>
-
-<div id="heatmap-year" class="fullwidth"></div>
-
-<script src="https://soap.coffee/+vendors/d3.js/d3.v3.min.js"></script>
-<script src="https://soap.coffee/+vendors/cal-heatmap/cal-heatmap.3.3.10.min.js"></script>
-
-<style>
- #heatmap-weeks,
- #heatmap-year {
- overflow-x : scroll;
- }
-</style>
-
-<script type="text/javascript">
- // First, we load the necessary CSS
- document.getElementsByTagName('style')[0]
- .insertAdjacentHTML(
- 'beforebegin',
- '<link rel="stylesheet" href="https://soap.coffee/+vendors/cal-heatmap/cal-heatmap.3.3.10.css" />'
- );
-
- let gcount = document.getElementById("global_count");
- let start_date = document.getElementById("start_date");
-
- let min_date = new Date();
-
- const months = {
- 0: 'January',
- 1: 'February',
- 2: 'March',
- 3: 'April',
- 4: 'May',
- 5: 'June',
- 6: 'July',
- 7: 'August',
- 8: 'September',
- 9: 'October',
- 10: 'November',
- 11: 'December'
- }
-
-fetch('https://keyrhub.soap.coffee/view/lthms')
- .then((response) =>
- response.json()
- .then((raw_datas) => {
-
- var global_count = 0;
- var min_date = new Date() / 1000;
-
- for (var prop in raw_datas) {
- global_count += raw_datas[prop];
- min_date = Math.min(min_date, prop);
- }
-
- min_date = new Date(min_date * 1000);
-
- start_date.innerText =
- `${months[min_date.getMonth()]} ${min_date.getDate()}, ${min_date.getFullYear()}`;
- gcount.innerText = global_count.toLocaleString();
-
- var calweeks = new CalHeatMap();
-
- calweeks.init({
- itemSelector: "#heatmap-weeks",
- itemName: "keystroke",
- data: raw_datas,
- start: new Date(new Date() - 9 * 24 * 60 * 60 * 1000),
- domain: "day",
- subDomain: "hour",
- domainGutter: 0,
- range: 10,
- legend: [500, 3000, 5000, 7000]
- });
-
- var calyear = new CalHeatMap();
-
- calyear.init({
- itemSelector: "#heatmap-year",
- itemName: "keystroke",
- data: raw_datas,
- domain: "month",
- subDomain: "day",
- domainGutter: 10,
- start: new Date(new Date() - 5 * 30 * 24 * 60 * 60 * 1000),
- range: 6,
- legend: [1000, 15000, 30000, 45000]
- });
- }));
-</script>
diff --git a/site/projects/knitting.html b/site/projects/knitting.html
deleted file mode 100644
index 727c027..0000000
--- a/site/projects/knitting.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<h1>Knitting Journal</h1>
-
-<p>
- I have started knitting on August, 2020. Here is my progress, hidden in plain
- sight. Feel free to have a look!
-</p>
-
-<div id="gallery">
- <img src="/img/knitting-20200901.jpeg" alt="Knitting in progress with blue yarn" />
-</div>
diff --git a/site/series/Ltac.md b/site/series/Ltac.md
new file mode 100644
index 0000000..0d379ed
--- /dev/null
+++ b/site/series/Ltac.md
@@ -0,0 +1,22 @@
+---
+abstract: |
+ Ltac is the “tactic language” of Coq. It is commonly advertised as the
+ common approach to write proofs, which tends to bias how it is introduced
+ to new Coq users. In this series, we present Ltac as the metaprogramming
+ tool it is, since fundamentally it is an imperative language which allows
+ for constructing Coq terms interactively and incrementally.
+---
+
+# A Series on Ltac
+
+Ltac is the “tactic language” of Coq. It is commonly advertised as the common
+approach to write proofs, which tends to bias how it is introduced to
+new Coq users[^anecdote]. In this series, we present Ltac as the
+metaprogramming tool it is, since fundamentally it is an imperative
+language which allows for constructing Coq terms interactively and
+incrementally.
+
+[^anecdote]: I know *I* was introduced to Coq in a similar way in
+ my Master courses.
+
+@[series](Test)
diff --git a/site/series/Retrospectives.md b/site/series/Retrospectives.md
new file mode 100644
index 0000000..5def2cf
--- /dev/null
+++ b/site/series/Retrospectives.md
@@ -0,0 +1,9 @@
+---
+abstract: |
+ A collection of articles I have written to reflect on what happened at a
+ given time. It is heavily inspired by Drew DeVault “status update” series.
+---
+
+# Retrospectives
+
+@[series](.)
diff --git a/site/series/StronglySpecifiedFunctions.md b/site/series/StronglySpecifiedFunctions.md
new file mode 100644
index 0000000..7f72776
--- /dev/null
+++ b/site/series/StronglySpecifiedFunctions.md
@@ -0,0 +1,19 @@
+---
+abstract: |
+ Using dependent types and the `Prop`{.coq} sort, it becomes possible to
+ specify functions whose arguments and results are constrained by
+ properties. However, implementing such functions can be challenging. In
+ this series, we explore several approaches available to Coq developers.
+---
+
+# A Series on Strongly-Specified Functions in Coq
+
+Using dependent types and the `Prop`{.coq} sort, it becomes possible to specify
+functions whose arguments and results are constrained by properties. Using
+such a “strongly-specified” function requires to provide a proof that the
+supplied arguments satisfy the expected properties, and allows for soundly
+assuming the results are correct too. However, implementing such functions can
+be challenging. In this series, we explore several approaches available to Coq
+developers.
+
+@[series](.)
diff --git a/site/series/index.md b/site/series/index.md
new file mode 100644
index 0000000..043f1f9
--- /dev/null
+++ b/site/series/index.md
@@ -0,0 +1,7 @@
+# Series
+
+A series gathers together a collection of contents (that is, either posts or
+other series) into a coherent whole. This is by opposition to tags, which only
+allows for signaling that two posts refers to a given subject.
+
+@[allSeries](.)
diff --git a/site/styles/highlight.css b/site/styles/highlight.css
new file mode 100644
index 0000000..275239a
--- /dev/null
+++ b/site/styles/highlight.css
@@ -0,0 +1,10 @@
+pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
+ Theme: GitHub
+ Description: Light theme as seen on github.com
+ Author: github.com
+ Maintainer: @Hirse
+ Updated: 2021-05-15
+
+ Outdated base version: https://github.com/primer/github-syntax-light
+ Current colors taken from GitHub's CSS
+*/.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0} \ No newline at end of file
diff --git a/site/tags/index.md b/site/tags/index.md
new file mode 100644
index 0000000..5d11493
--- /dev/null
+++ b/site/tags/index.md
@@ -0,0 +1,3 @@
+# Tags
+
+@[tags](.)
diff --git a/soupault.conf b/soupault.conf
deleted file mode 100644
index 003e6dc..0000000
--- a/soupault.conf
+++ /dev/null
@@ -1,102 +0,0 @@
-[settings]
-strict = true
-site_dir = "site"
-build_dir = "out/~lthms"
-doctype = "<!DOCTYPE html>"
-clean_urls = false
-generator_mode = true
-complete_page_selector = "html"
-default_content_selector = "main"
-default_content_action = "append_child"
-page_file_extensions = ["html"]
-ignore_extensions = [
- "v", "vo", "vok", "vos", "glob",
- "html~", "org"
-]
-default_template_file = "templates/main.html"
-pretty_print_html = false
-caching = true
-cache_dir = ".soupault-cache"
-
-[asset_processors]
-png = "pngcrush {{source_file_path}} {{target_dir}}/{{source_file_name}}"
-
-[widgets.page-title]
-widget = "title"
-selector = "h1"
-default = "~lthms"
-prepend = "~lthms: "
-
-[widgets.generator-meta]
-widget = "insert_html"
-html = """<meta name="generator" content="soupault 4.2.0">"""
-selector = "head"
-
-[widgets.urls-rewriting]
-widget = "urls-rewriting"
-prefix_url = "~lthms"
-after = "mark-external-urls"
-
-[widgets.mark-external-urls]
-after = "generate-history"
-widget = "external-urls"
-
-[widgets.table-of-contents]
-widget = "toc"
-selector = "#generate-toc"
-action = "replace_content"
-valid_html = true
-min_level = 2
-max_level = 3
-numbered_list = false
-heading_links = true
-heading_link_text = " §"
-heading_links_append = true
-heading_link_class = "anchor-link"
-
-[widgets.append-toc-title]
-widget = "insert_html"
-selector = "#generate-toc"
-action = "prepend_child"
-html = '<h2>Table of Contents</h2>'
-after = "table-of-contents"
-
-[widgets.generate-history]
-widget = "preprocess_element"
-selector = "#history"
-command = 'scripts/history.sh templates/history.html'
-action = "replace_element"
-
-[widgets.inline-math]
-widget = "preprocess_element"
-selector = ".imath"
-command = "node scripts/render-equations.js"
-action = "replace_content"
-
-[widgets.display-math]
-widget = "preprocess_element"
-selector = ".dmath"
-command = "DISPLAY=1 node scripts/render-equations.js"
-action = "replace_content"
-
-[index]
-index = true
-dump_json = "rss.json"
-extract_after_widgets = ["urls-rewriting"]
-
-[index.fields]
-title = { selector = ["h1"] }
-modified-at = { selector = ["#modified-at"] }
-created-at = { selector = ["#created-at"] }
-
-[widgets.series]
-widget = "series"
-
-[widgets.css]
-widget = "css"
-
-[widgets.clean-up]
-widget = "clean-up"
-
-[widgets.notes]
-widget = "notes" \ No newline at end of file
diff --git a/soupault.mk b/soupault.mk
deleted file mode 100644
index 988edef..0000000
--- a/soupault.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-CONFIGURE += _opam rss.json
-ARTIFACTS += out
-
-soupault-prebuild : _opam/init
-
-soupault-build : dependencies-prebuild style.min.css
- @pretty-echo.sh "Executing" "soupault"
- @soupault
diff --git a/soupault.toml b/soupault.toml
new file mode 100644
index 0000000..b0b7512
--- /dev/null
+++ b/soupault.toml
@@ -0,0 +1,146 @@
+[settings]
+ soupault_version = "4.5.0"
+ strict = true
+ verbose = false
+ debug = false
+ site_dir = "site"
+ build_dir = "out/~lthms"
+ page_file_extensions = ["html", "md"]
+ clean_urls = false
+ keep_extensions = ["html"]
+ default_extension = "html"
+ ignore_extensions = ["draft"]
+
+ generator_mode = true
+ complete_page_selector = "html"
+ default_template_file = "templates/main.html"
+ default_content_selector = "main"
+ default_content_action = "append_child"
+ keep_doctype = true
+ doctype = "<!DOCTYPE html>"
+ pretty_print_html = false
+
+ plugin_discovery = true
+ plugin_dirs = ["plugins"]
+
+ caching = true
+ cache_dir = ".soupault-cache"
+
+[preprocessors]
+ md = './scripts/md-render.js'
+
+[widgets.page-title]
+ widget = "title"
+ selector = "h1"
+ default = "~lthms"
+ prepend = "~lthms: "
+ force = false
+
+[widgets.generator-meta]
+ widget = "insert_html"
+ html = '<meta name="generator" content="soupault">'
+ selector = "head"
+
+[widgets.mark-external-urls]
+ widget = "external-urls"
+
+[widgets.urls-rewriting]
+ after = "series"
+ widget = "urls-rewriting"
+ prefix_url = "~lthms"
+
+[widgets.footnote-postprocess]
+ widget = "footnote-postprocess"
+
+[widgets.notes]
+ widget = "notes"
+ after = "footnote-postprocess"
+
+[widgets.css]
+ widget = "css"
+
+[widgets.series]
+ widget = "series"
+
+[widgets.meta]
+ after = "urls-rewriting"
+ widget = "meta"
+
+[widgets.move-tags]
+ widget = "move-tags"
+
+[index]
+ index = true
+ index_first = true
+ dump_json = "test.json"
+ extract_after_widgets = ["series"]
+
+[index.fields.title]
+ selector = ["h1"]
+
+[index.fields.date]
+ selector = ["time#published"]
+ extract_attribute = "datetime"
+ fallback_to_content = false
+
+[index.fields.date_str]
+ selector = ["time#published"]
+
+[index.fields.tags]
+ selector = ["a.tag"]
+ select_all = true
+
+[index.fields.series_url]
+ selector = "p.series a"
+ extract_attribute = "href"
+ fallback_to_content = false
+
+[index.fields.series_name]
+ selector = "p.series a"
+
+[index.fields.series_prev_url]
+ selector = "p.series-prev a"
+ extract_attribute = "href"
+ fallback_to_content = false
+
+[index.fields.series_next_url]
+ selector = "p.series-next a"
+ extract_attribute = "href"
+ fallback_to_content = false
+
+[index.fields.abstract]
+ selector = "#meta-tags .description"
+
+[index.views.tags]
+ section = "posts/"
+ sort_by = "date"
+ sort_type = "calendar"
+ date_formats = ["%F"]
+ index_selector = "#tags-index"
+ index_template_file = "templates/index_tags.html"
+ file = "plugins/tags-index.lua"
+
+[widgets.series-index]
+ widget = "series-index"
+ index_selector = "#series-index"
+ index_template_file = "templates/index_series.html"
+
+[index.views.series]
+ sort_by = "title"
+ sort_type = "lexicographic"
+ sort_descending = false
+ index_selector = "#all-series-index"
+ index_item_template = """
+ <dt><a href="{{ url }}">{{ title }}</a></dt>
+ <dd>{% if abstract %}{{ abstract }}{% endif %}</dd>
+"""
+
+[index.views.archives]
+ section = "posts/"
+ sort_by = "date"
+ sort_type = "calendar"
+ date_formats = ["%F"]
+ index_selector = "#archives-index"
+ index_full_template_file = "templates/index_archives_full.html"
+ index_short_template_file = "templates/index_archives_short.html"
+ file = "plugins/archives-index.lua"
diff --git a/style.css b/style.css
index 20b8ab2..f2986fb 100644
--- a/style.css
+++ b/style.css
@@ -1,10 +1,22 @@
+* {
+ box-sizing: border-box;
+}
+
+html {
+ font-size: 100%;
+}
+
:root {
--main-width: 42rem;
- --gutter-width: 5rem;
- --margin-width: 13rem;
+ --gutter-width: 4rem;
+ --margin-width: 16rem;
--body-width: calc(var(--main-width) + 2 * (var(--gutter-width) + var(--margin-width)));
}
+#meta-tags {
+ display: none;
+}
+
/*
{{Note: width}}
@@ -13,21 +25,43 @@
media queries. As a consequnece, for our theme to be responsive,
the full width of the page content is
- 2 * (margin_width + gutter_width) + content_width = 78rem
+ 2 * (margin_width + gutter_width) + content_width = 82rem
*/
/* See {{Note: width}} */
-@media (max-width: 78rem) {
+@media (max-width: 82rem) {
:root {
--body-width: var(--main-width);
--width: var(--main-width);
}
}
+body {
+ font-family: Inter, sans-serif;
+ line-height: 1.8;
+ max-width: var(--body-width);
+ text-align: justify;
+ padding-top: 1em;
+ padding-bottom: 1em;
+ margin-left: auto;
+ margin-right: auto;
+ color: #0a0a0a;
+}
+
+main {
+ counter-reset: sidenote-counter;
+ max-width: var(--main-width);
+ margin: auto;
+}
+
+img {
+ max-width: 100%;
+}
+
figure {
- padding-top: 3rem;
- padding-bottom: 3rem;
+ padding-top: 1rem;
+ padding-bottom: 1rem;
}
figure, figcaption {
@@ -36,12 +70,13 @@ figure, figcaption {
margin-right:0;
}
-* {
- box-sizing: border-box;
+figcaption p {
+ margin-top: 0;
+ margin-left: 0;
}
/* See {{Note: width}} */
-@media (min-width: 78rem) {
+@media (min-width: 82rem) {
.fullwidth, figure {
margin-left: calc(-1 * (var(--margin-width) + var(--gutter-width)));
}
@@ -49,98 +84,33 @@ figure, figcaption {
figure, figcaption, .fullwidth {
width: var(--body-width);
}
-}
-
-html {
- font-size: 1rem;
-}
-
-body {
- line-height: 1.4;
- max-width: var(--body-width);
- margin-left: auto;
- margin-right: auto;
-}
-
-aside {
- background: var(--bg);
- z-index: 9999;
- width: var(--body-width);
- align-self: flex-start;
- position: sticky;
- top: 0;
-}
-aside nav {
- text-align: center;
-}
-aside nav ul {
- list-style: none;
- padding: 1rem 0;
- margin: 0;
-}
-
-aside nav li {
- display: inline;
-}
-
-aside nav li:not(:first-of-type)::before {
- content: " · ";
-}
-
-main {
- counter-reset: sidenote-counter;
- max-width: var(--main-width);
- margin: auto;
-}
-
-main nav {
- font-style: italic;
- color: var(--fg-plus);
- background: var(--current-line);
- padding: .5rem 1rem;
-}
-
-main nav .series-next {
- text-align: right;
-}
-
-main nav p.series-next::after {
- content: " →";
-}
-
-main nav p.series-prev::before {
- content: "← ";
-}
-
-img {
- max-width: 100%;
-}
+ .sidenote.note-left {
+ padding-right: 1em;
+ border-right: 1px solid black;
+ }
-#whoami.marginnote {
- color: var(--fg);
- margin-bottom: 2em;
-}
+ .sidenote.note-right {
+ padding-left: 1em;
+ border-left: 1px solid black;
+ }
-img.avatar {
- border-radius: 20px;
- border: 1px solid black;
- display: block;
- max-width: 90%;
- margin: auto;
+ ul.tags-list {
+ columns: 2;
+ }
}
-dd {
- margin-left: 0;
- margin-bottom: 0.5rem;
+ul.tags-list {
+ list-style-type: none;
}
.sidenote,
-.marginnote {
+.marginblock {
font-size: smaller;
position: relative;
width: var(--margin-width);
+ margin-bottom: 1em;
}
.note-right {
@@ -155,6 +125,15 @@ dd {
margin-left: calc(-1 * (var(--margin-width) + var(--gutter-width)));
}
+.footnote-p:not(:first-child) {
+ display: block;
+ margin-top: .5em;
+}
+
+.footnote-p.narrow:not(:first-child) {
+ margin-top: .15em;
+}
+
input.margin-toggle {
display: none;
}
@@ -178,92 +157,21 @@ label.margin-toggle:not(.sidenote-number) {
}
.sidenote-number::after {
- content: "(" counter(sidenote-counter, lower-greek) ")";
+ content: "(" counter(sidenote-counter) ")";
font-size: 60%;
top: -0.4rem;
left: 0.1rem;
}
.sidenote::before {
- content: "(" counter(sidenote-counter, lower-greek) ")";
+ content: "(" counter(sidenote-counter) ")";
font-size: 70%;
top: -0.5rem;
right: 0.1rem;
}
-div.code,
-pre {
- overflow-x: auto;
- overflow-y: hidden;
- padding: 1rem 2rem;
-}
-
-main {
- padding-top: 4.2rem;
- padding-bottom: 4.2rem;
-}
-
-h1 {
- text-align: center;
-}
-
-h2, h3, h4 {
- font-style: italic;
-}
-
-h1, h2, h3, h4 {
- color: var(--doc);
- font-family: serif;
- font-weight: normal;
-}
-
-dt {
- font-weight: bold;
-}
-
-div.code,
-span.inlinecode,
-pre,
-tt,
-.dmath,
-.imath {
- font-family: monospace;
- font-size: 85%;
-}
-
-details {
- margin: 1.5rem 0;
-}
-
-table {
- border-top: 2px solid var(--fg);
- border-bottom: 2px solid var(--fg);
- border-collapse: collapse;
- width: 100%;
- margin: 1.5rem 0;
-}
-
-th {
- font-weight: normal;
- text-transform: uppercase;
-}
-
-td,
-th {
- border-top: 1px solid var(--fade);
- height: 2em;
- padding: 0 1em;
-}
-
-td.date,
-td.commit {
- text-align: center;
- font-size: 0.75em;
- font-family: monospace;
-}
-
/* See {{Note: width}} */
-@media (max-width: 78rem) {
+@media (max-width: 82rem) {
body {
padding: 2rem;
margin: auto;
@@ -279,19 +187,17 @@ td.commit {
display: inline;
}
- .sidenote,
- .marginnote {
+ .sidenote {
display: none;
}
.margin-toggle:checked + .sidenote,
- .margin-toggle:checked + .marginnote {
+ .marginblock {
display: block;
float: left;
- left: 1rem;
clear: both;
width: 95%;
- margin: 1rem 2.5%;
+ margin: 1rem 2em;
vertical-align: baseline;
position: relative;
}
@@ -303,119 +209,194 @@ td.commit {
pre, aside, div.code {
width: 100%;
}
+
+ .marginblock {
+ text-align: center;
+ margin-top: 0;
+ }
+
+ .full-only {
+ display: none !important;
+ }
+
+ #whoami img {
+ max-width: 12em;
+ }
}
-:root {
- --bg: white;
- --bg-plus: #f7f7f7;
- --current-line: #fbfbfb;
- --fade: #cfcecb;
- --fg: black;
- --fg-plus: black;
- --doc: black;
- --warning: #bd745e;
- --red: #b3534b;
- --green: #6d9319;
- --yellow: #d4b100;
+/*
+#whoami nav {
}
-body {
- font-family: Inter, sans-serif;
- color: var(--fg);
- background: var(--bg);
+#whoami nav ul {
+ padding-left: 0rem;
+ margin: 0;
+ list-style: none;
+ text-align: center;
}
-h2 a.anchor-link,
-h3 a.anchor-link,
-h4 a.anchor-link {
- display: none;
- font-style: normal;
- text-decoration: none;
- font-family: monospace;
- font-size: smaller;
- color: var(--doc);
+#whoami nav ul li {
+ font-size: 1rem;
+}
+
+#whoami nav ul ul {
+ font-size: 0.95rem;
+}
+
+#whoami nav ul ul li {
+ font-size: 0.95rem;
+}
+
+#whoami nav ul a {
+ font-weight: normal;
}
+*/
-[id] {
- scroll-margin-top: 4rem;
+nav#main-nav {
+ text-align: center;
+}
+
+nav#main-nav ul {
+ list-style: none;
+ padding: 1rem 0;
+ margin: 0;
}
-h2:hover a.anchor-link,
-h3:hover a.anchor-link,
-h4:hover a.anchor-link {
+nav#main-nav li {
display: inline;
}
-.sidenote,
-.marginnote {
- color: var(--fg-plus);
+nav#main-nav li:not(:first-of-type)::before {
+ width: 2rem;
+ content: " · ";
}
-pre,
-code,
-tt {
- color: var(--doc);
- background: var(--bg-plus);
+nav#main-nav a {
+ font-weight: normal;
}
-div.code {
- white-space: nowrap;
+main {
+ counter-reset: sidenote-counter;
+ max-width: var(--main-width);
+ margin: auto;
}
-div.code,
-span.inlinecode {
- font-family : monospace;
+h1 {
+ text-align: center;
+ line-height: 1.3em
}
-.paragraph {
- margin-bottom : .8em;
+#tags-list {
+ text-align: center;
+ font-size: smaller;
+ padding-bottom: 1em;
}
-.code a[href] {
- color : black !important;
- text-decoration : none;
- background : var(--bg-plus);
- padding : .1rem .15rem .1rem .15rem;
- border-radius : 15%;
+h2, h3, h4 {
+ font-style: italic;
+ border-bottom: 1px solid black;
+ text-align: left;
}
-.code .icon {
- display: none;
+h1, h2, h3, h4 {
+ color: black;
+ /*font-family: serif;*/
+ font-weight: normal;
+}
+
+pre {
+ border-radius: .1em;
+ border: 1px solid silver;
}
.icon svg {
- display: inline;
+ display: inline-block;
width: 1em;
height: .9em;
- vertical-align: text-top;
}
-a[href], .margin-toggle {
- color: blue;
+a[href] {
+ text-decoration-line: underline;
+ text-decoration-color: black;
+ text-decoration-style: dashed;
+ text-underline-offset: .3em;
+ color: black;
+ /*font-weight: 500;*/
}
-a[href] .icon svg {
- fill: blue;
+a[href]:hover {
+ text-decoration-color: black;
}
-a[href]:visited {
- color: #15157c;
+img.avatar {
+ border: 1px solid black;
+ border-radius: .5em;
}
-a[href]:visited .icon svg {
- fill: #15157c;
+main nav#series-nav {
+ padding: 1em 2em;
+ border: 1px solid #eee;
+ background: #fafafa;
+ border-radius: .2em;
+}
+main nav#series-nav .series-next {
+ text-align: right;
}
-.url-mark.fa {
- display: inline;
- font-size: 90%;
- width: 1em;
+main nav#series-nav p.series-next::after {
+ content: " →";
+}
+
+main nav#series-nav p.series-prev::before {
+ content: "← ";
}
-.url-mark.fa-github::before {
- content: "\00a0\f09b";
+.index {
+ padding-top: 2em;
+ padding-bottom: 2em;
}
-.url-mark.fa-external-link::before {
- content: "\00a0\f08e";
+.index dt,
+.index li {
+ text-align: left;
+}
+
+.index dd {
+ margin-left: 0;
+ margin-bottom: 1rem;
+}
+
+.index dd:not(:empty) {
+ border-bottom: 1px solid #ddd;
+}
+
+.index p {
+ margin-top: 0;
+}
+
+/* Dirty patching of the github highlight.js theme*/
+p code.hljs,
+dt code.hljs,
+li code.hljs {
+ padding: 0;
+}
+
+table {
+ border-top: 2px solid #555;
+ border-bottom: 2px solid #555;
+ border-collapse: collapse;
+ width: 100%;
+ margin: 1.5rem 0;
+}
+
+th {
+ font-weight: normal;
+ text-transform: uppercase;
+}
+
+td,
+th {
+ border-top: 1px solid silver;
+ height: 2em;
+ padding: 0 1em;
}
diff --git a/templates/history.html b/templates/history.html
deleted file mode 100644
index b106c4f..0000000
--- a/templates/history.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<details id="history">
- <summary>Revisions</summary>
- <p>
- This revisions table has been automatically generated
- from <a href="https://src.soap.coffee/soap.coffee/lthms.git">the
- <code>git</code> history of this website repository</a>, and the
- change descriptions may not always be as useful as they should.
- </p>
-
- <p>
- You can consult the source of this file in its current version
- <a href="https://src.soap.coffee/soap.coffee/lthms.git/tree/{{file}}">here</a>.
- </p>
-
- <table class="fullwidth">
- {{#history}}
- <tr>
- <td class="date"
-{{#created}}
- id="created-at"
-{{/created}}
-{{#modified}}
- id="modified-at"
-{{/modified}}
- >{{date}}</td>
- <td class="subject">{{subject}}</td>
- <td class="commit">
- <a href="https://src.soap.coffee/soap.coffee/lthms.git/commit/{{filename}}/?id={{hash}}">{{abbr_hash}}</a>
- </td>
- </tr>
- {{/history}}
- </table>
-</details>
diff --git a/templates/index_archives_full.html b/templates/index_archives_full.html
new file mode 100644
index 0000000..3267e14
--- /dev/null
+++ b/templates/index_archives_full.html
@@ -0,0 +1,17 @@
+<dl>
+ {% for article in contents %}
+ {% set series_url = article.series_url %}
+ {% set series_name = article.series_name %}
+ {% set abstract = article.abstract %}
+ {% if article.date %}
+ <dt>
+ <a href="{{ article.url }}">{{ article.title }}</a>
+ <span style="font-size: 90%">
+ (<time datetime="{{ article.date }}">{{ article.date_str }}</date>{%if series_name %},
+ <a title="{{ series_name }}" href="{{ series_url }}"><span class="icon"><svg><use href="/img/icons.svg#book"></use></svg></span></a>{% endif %})
+ </span>
+ </dt>
+ <dd>{%if abstract %}{{ abstract }}{% endif %}</dd>
+ {% endif %}
+ {% endfor %}
+</dl>
diff --git a/templates/index_archives_short.html b/templates/index_archives_short.html
new file mode 100644
index 0000000..7ec12dc
--- /dev/null
+++ b/templates/index_archives_short.html
@@ -0,0 +1,12 @@
+<ul>
+ {% for article in contents %}
+ {% if article.date %}
+ <li><a href="{{ article.url }}">{{ article.title }}</a>
+ <span style="font-size: 90%">
+ (<time datetime="{{ article.date }}">{{ article.date_str }}</date>{%if article.series_name %},
+ <a title="{{ article.series_name }}" href="{{ article.series_url }}"><span class="icon"><svg><use href="/img/icons.svg#book"></use></svg></span></a>{% endif %})
+ </span>
+ </li>
+ {% endif %}
+ {% endfor %}
+</ul>
diff --git a/templates/index_series.html b/templates/index_series.html
new file mode 100644
index 0000000..7d04640
--- /dev/null
+++ b/templates/index_series.html
@@ -0,0 +1,9 @@
+<dl>
+ {% for article in entries %}
+ {% set abstract = article.abstract %}
+ <dt>
+ <a href="{{ article.url }}">{{ article.title }}</a>
+ </dt>
+ <dd>{%if abstract %}{{ abstract }}{% endif %}</dd>
+ {% endfor %}
+</dl>
diff --git a/templates/index_tags.html b/templates/index_tags.html
new file mode 100644
index 0000000..05c4d17
--- /dev/null
+++ b/templates/index_tags.html
@@ -0,0 +1,8 @@
+<ul class="tags-list">
+{% for tag in tags %}
+ <li>
+ <a href="{{ tag.name }}.html"><span class="icon"><svg><use href="/img/icons.svg#tag"></use></svg></span> <code>{{ tag.name }}</code></a>
+ ({{ tag.contents | length }})
+ </li>
+{% endfor %}
+</ul>
diff --git a/templates/main.html b/templates/main.html
index fec8e57..843f52f 100644
--- a/templates/main.html
+++ b/templates/main.html
@@ -2,49 +2,60 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
- <style></style>
+ <title> <!-- set automatically, see soupault.toml --> </title>
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="author" content="Thomas Letan">
+ <meta name="twitter:creator" content="@_lthms_" />
<link href="https://soap.coffee/+vendor/katex/0.16.4/katex.min.css" rel="stylesheet" media="none" onload="if(media!='all')media='all'">
- <title></title>
+ <link href="/styles/highlight.css" rel="stylesheet">
+ <style> /* set automatically, see soupault.toml */ </style>
</head>
<body>
- <aside>
- <nav>
+ <main>
+ <span id="whoami" class="marginblock">
+ <a href="/" title="Home">
+ <img class="avatar" alt="lthms' avatar, a hand drawing looking person, wearing a headset, close to a window on a raining night" src="/img/thinking.png" />
+ </a>
+ <p class="full-only">
+ Hi, I’m <strong>lthms</strong> (<em>he/him</em>).
+ </p>
+
+ <p class="full-only">
+ You read something which caught your attention and you are interested
+ in starting a discussion? Don’t hesitate to <a
+ href="mailto:~lthms/public-inbox@lists.sr.ht">shoot me an
+ email</a>.
+ </p>
+ </span>
+ <nav id="main-nav">
<ul>
<li>
- <a href="/">Home</a>
+ <a href="/">
+ <span class="icon"><svg><use href="/img/icons.svg#home"></use></svg></span>
+ Home
+ </a>
</li>
<li>
- <a href="/posts">Technical Posts</a>
+ <a href="/posts">
+ <span class="icon"><svg><use href="/img/icons.svg#scroll"></use></svg></span>
+ Archives
+ </a>
</li>
<li>
- <a href="/opinions">Opinions</a>
+ <a href="/tags">
+ <span class="icon"><svg><use href="/img/icons.svg#tags"></use></svg></span>
+ Tags
+ </a>
</li>
<li>
- <a href="/news">News</a>
+ <a href="/series">
+ <span class="icon"><svg><use href="/img/icons.svg#book"></use></svg></span>
+ Series
+ </a>
</li>
</ul>
</nav>
- </aside>
- <main>
- <span id="whoami" class="marginnote">
- <img class="avatar" alt="lthms' avatar, a hand drawing looking person, wearing a headset, close to a window on a raining night" src="/img/thinking.png" />
- <p>
- Hi, I’m <strong>lthms</strong>.
- </p>
-
- <p>
- I didn’t like syntax highlighting, but I like
- types and functional programming languages.
- He/him.
- </p>
-
- <p>
- Interested in starting a discussion? Don’t hesitate to <a
- href="mailto:~lthms/public-inbox@lists.sr.ht">shoot me an
- email</a>.
- </p>
- </span>
</main>
</body>
</html>
diff --git a/theme.mk b/theme.mk
deleted file mode 100644
index 6dc306a..0000000
--- a/theme.mk
+++ /dev/null
@@ -1,7 +0,0 @@
-style.min.css : style.css dependencies-prebuild
- @pretty-echo.sh "Minifying" "CSS"
- @css.sh
-
-ARTIFACTS += style.min.css
-
-theme-build : style.min.css