Amélia Liao 9 months ago
parent
commit
3a29725003
129 changed files with 1796 additions and 629 deletions
  1. +98
    -0
      css/agda.scss
  2. +0
    -45
      css/code.css
  3. +0
    -13
      css/computermodern.css
  4. +205
    -116
      css/default.scss
  5. +419
    -0
      css/fonts.scss
  6. +62
    -0
      css/vars.scss
  7. +2
    -2
      package.json
  8. +4
    -15
      pages/contact.md
  9. +75
    -13
      pages/index.html
  10. +2
    -3
      pages/oss.md
  11. +1
    -1
      pages/posts/2018-03-14-amulet-safety.md
  12. +1
    -1
      pages/posts/2018-03-27-amulet-gadts.md
  13. +7
    -7
      pages/posts/2021-03-07-cubical.md
  14. +1
    -1
      pages/posts/2021-06-07-ax-j.md
  15. +2
    -2
      pages/posts/2021-09-03-parsing-layout.md
  16. +420
    -0
      pages/posts/2021-09-05-outsidein-x.md
  17. +0
    -232
      rubtmpjd3t_2bh.log
  18. BIN
      site
  19. +63
    -40
      site.hs
  20. +2
    -3
      stack.yaml
  21. +11
    -18
      stack.yaml.lock
  22. +0
    -10
      static/default.css
  23. BIN
      static/icon/android-chrome-192x192.png
  24. BIN
      static/icon/android-chrome-512x512.png
  25. BIN
      static/icon/apple-touch-icon.png
  26. BIN
      static/icon/cube-128x.png
  27. BIN
      static/icon/favicon-16x16.png
  28. BIN
      static/icon/favicon-32x32.png
  29. BIN
      static/icon/favicon.ico
  30. BIN
      static/icon/pfp-alt.png
  31. BIN
      static/icon/[email protected]
  32. BIN
      static/icon/[email protected]
  33. BIN
      static/icon/[email protected]
  34. BIN
      static/icon/[email protected]
  35. BIN
      static/icon/pfp-tired.png
  36. BIN
      static/icon/[email protected]
  37. BIN
      static/icon/[email protected]
  38. BIN
      static/icon/[email protected]
  39. BIN
      static/icon/[email protected]
  40. BIN
      static/icon/pfp.jpg
  41. BIN
      static/icon/pfp.png
  42. BIN
      static/icon/[email protected]
  43. BIN
      static/icon/[email protected]
  44. BIN
      static/icon/[email protected]
  45. BIN
      static/icon/[email protected]
  46. +0
    -93
      static/licenses/LICENSE.FantasqueSansMono
  47. +110
    -0
      static/licenses/LICENSE.Iosevka
  48. +200
    -0
      static/licenses/LICENSE.Noto
  49. +10
    -0
      static/svg/amulet.svg
  50. +31
    -0
      static/svg/gitea.svg
  51. +3
    -0
      static/svg/github.svg
  52. +46
    -0
      static/svg/kofi.svg
  53. +3
    -0
      static/svg/twitter.svg
  54. BIN
      static/ttf/iosevk-abbie-bold.ttf
  55. BIN
      static/ttf/iosevk-abbie-bolditalic.ttf
  56. BIN
      static/ttf/iosevk-abbie-boldoblique.ttf
  57. BIN
      static/ttf/iosevk-abbie-extended.ttf
  58. BIN
      static/ttf/iosevk-abbie-extendedbold.ttf
  59. BIN
      static/ttf/iosevk-abbie-extendedbolditalic.ttf
  60. BIN
      static/ttf/iosevk-abbie-extendedboldoblique.ttf
  61. BIN
      static/ttf/iosevk-abbie-extendedextrabold.ttf
  62. BIN
      static/ttf/iosevk-abbie-extendedextrabolditalic.ttf
  63. BIN
      static/ttf/iosevk-abbie-extendedextraboldoblique.ttf
  64. BIN
      static/ttf/iosevk-abbie-extendedheavy.ttf
  65. BIN
      static/ttf/iosevk-abbie-extendedheavyitalic.ttf
  66. BIN
      static/ttf/iosevk-abbie-extendedheavyoblique.ttf
  67. BIN
      static/ttf/iosevk-abbie-extendeditalic.ttf
  68. BIN
      static/ttf/iosevk-abbie-extendedmedium.ttf
  69. BIN
      static/ttf/iosevk-abbie-extendedmediumitalic.ttf
  70. BIN
      static/ttf/iosevk-abbie-extendedmediumoblique.ttf
  71. BIN
      static/ttf/iosevk-abbie-extendedoblique.ttf
  72. BIN
      static/ttf/iosevk-abbie-extendedsemibold.ttf
  73. BIN
      static/ttf/iosevk-abbie-extendedsemibolditalic.ttf
  74. BIN
      static/ttf/iosevk-abbie-extendedsemiboldoblique.ttf
  75. BIN
      static/ttf/iosevk-abbie-extrabold.ttf
  76. BIN
      static/ttf/iosevk-abbie-extrabolditalic.ttf
  77. BIN
      static/ttf/iosevk-abbie-extraboldoblique.ttf
  78. BIN
      static/ttf/iosevk-abbie-heavy.ttf
  79. BIN
      static/ttf/iosevk-abbie-heavyitalic.ttf
  80. BIN
      static/ttf/iosevk-abbie-heavyoblique.ttf
  81. BIN
      static/ttf/iosevk-abbie-italic.ttf
  82. BIN
      static/ttf/iosevk-abbie-medium.ttf
  83. BIN
      static/ttf/iosevk-abbie-mediumitalic.ttf
  84. BIN
      static/ttf/iosevk-abbie-mediumoblique.ttf
  85. BIN
      static/ttf/iosevk-abbie-oblique.ttf
  86. BIN
      static/ttf/iosevk-abbie-regular.ttf
  87. BIN
      static/ttf/iosevk-abbie-semibold.ttf
  88. BIN
      static/ttf/iosevk-abbie-semibolditalic.ttf
  89. BIN
      static/ttf/iosevk-abbie-semiboldoblique.ttf
  90. BIN
      static/woff2/iosevk-abbie-bold.woff2
  91. BIN
      static/woff2/iosevk-abbie-bolditalic.woff2
  92. BIN
      static/woff2/iosevk-abbie-boldoblique.woff2
  93. BIN
      static/woff2/iosevk-abbie-extended.woff2
  94. BIN
      static/woff2/iosevk-abbie-extendedbold.woff2
  95. BIN
      static/woff2/iosevk-abbie-extendedbolditalic.woff2
  96. BIN
      static/woff2/iosevk-abbie-extendedboldoblique.woff2
  97. BIN
      static/woff2/iosevk-abbie-extendedextrabold.woff2
  98. BIN
      static/woff2/iosevk-abbie-extendedextrabolditalic.woff2
  99. BIN
      static/woff2/iosevk-abbie-extendedextraboldoblique.woff2
  100. BIN
      static/woff2/iosevk-abbie-extendedheavy.woff2

+ 98
- 0
css/agda.scss View File

@ -0,0 +1,98 @@
$code-bg: hsl(230,1%,98%);
$code-fg: #ABB2BF;
$code-red: #D65122;
$code-red-br: #AE3B36;
$code-green: #88B966;
$code-yellow: #DEB468;
$code-orange: #C58853;
$code-blue: #519DEB;
$code-pink: #C678DD;
$code-cyan: #48A8B5;
$code-white: #ABB2BF;
$code-grey: #7F848E;
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 400;
font-stretch: normal;
font-style: normal;
src: url('/static/woff2/iosevk-abbie-regular.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-regular.ttf') format('truetype');
}
body {
margin: 0;
background-color:$code-bg;
color:#ABB2BF;
}
html {
background-color: $code-bg;
color:#ABB2BF;
}
pre.Agda {
margin: 0;
padding: 1em;
background-color: $code-bg;
color: $code-fg;
}
@keyframes highlight {
0% {
background-color: #F5DEB3;
}
100% {
background-color: $code-bg;
}
}
/* Aspects. */
.Agda {
.Comment { color: $code-grey; }
.Background { background-color: $code-bg; }
.Markup { color: $code-fg; }
.Keyword { color: $code-orange; }
.String { color: $code-red; }
.Number { color: $code-pink; }
.Symbol { color: $code-fg; }
.PrimitiveType { color: $code-blue; }
.Pragma { color: $code-fg; }
/* NameKinds. */
.Bound { color: $code-fg; }
.Generalizable { color: $code-fg; }
.InductiveConstructor { color: $code-green; }
.CoinductiveConstructor { color: $code-green; }
.Datatype { color: $code-blue; }
.Field { color: #F570B7; }
.Function { color: $code-blue; }
.Module { color: $code-pink; }
.Postulate { color: $code-blue; }
.Primitive { color: $code-blue; }
.Record { color: $code-blue; }
/* OtherAspects. */
.UnsolvedMeta { color: $code-fg; background: yellow }
.UnsolvedConstraint { color: $code-fg; background: yellow }
.TerminationProblem { color: $code-fg; background: #FFA07A }
.IncompletePattern { color: $code-fg; background: #F5DEB3 }
.Error { color: red; text-decoration: underline }
.TypeChecks { color: $code-fg; background: #ADD8E6 }
.Deadcode { color: $code-fg; background: #808080 }
.ShadowingInTelescope { color: $code-fg; background: #808080 }
/* Standard attributes. */
a { text-decoration: none }
a[href]:hover {
text-decoration: 2px #B4EEB4 underline dotted;
}
a[href]:target {
animation: highlight 2.5s;
}
background-color: #282C34;
font-family: 'Iosevka', 'Fantasque Sans Mono', 'Roboto Mono', monospace;
font-weight: 400;
font-size: 16pt;
}

+ 0
- 45
css/code.css View File

@ -1,45 +0,0 @@
code, div.sourceCode {
font-family: Fantasque Sans Mono, Fira Mono, monospace;
font-size: 15pt;
}
code.inline {
background-color: rgb(250,250,250);
/* border: 1px solid rgb(200,200,200); */
}
table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
margin: 0; padding: 0; vertical-align: baseline; border: none;
}
table.sourceCode {
width: 100%; line-height: 100%;
}
td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
td.sourceCode { padding-left: 5px; }
code.kw, span.kw { color: #af005f; } /* Keyword */
code.dt, span.dt { color: #5f5faf; } /* DataType */
code.dv, span.dv { color: #268bd2; } /* DecVal */
code.bn, span.bn { color: #268bd2; } /* BaseN */
code.fl, span.fl { color: #268bd2; } /* Float */
code.ch, span.ch { color: #cb4b16; } /* Char */
code.st, span.st { color: #cb4b16; } /* String */
code.co, span.co { color: #8a8a8a; } /* Comment */
code.ot, span.ot { color: #007020; } /* Other */
code.al, span.al { color: #ff0000; } /* Alert */
code.fu, span.fu { color: #666666; } /* Function */
code.er, span.er { color: #ff0000; } /* Error */
code.wa, span.wa { color: #60a0b0; } /* Warning */
code.cn, span.cn { color: #880000; } /* Constant */
code.sc, span.sc { color: #4070a0; } /* SpecialChar */
code.vs, span.vs { color: #4070a0; } /* VerbatimString */
code.ss, span.ss { color: #2aa198; } /* SpecialString */
code.va, span.va { color: #073642; } /* Variable */
code.cf, span.cf { color: #007020; } /* ControlFlow */
code.op, span.op { color: #d33682; } /* Operator */
code.pp, span.pp { color: #bc7a00; } /* Preprocessor */
code.at, span.at { color: #7d9029; } /* Attribute */
code.do, span.do { color: #ba2121; } /* Documentation */
code.an, span.an { color: #60a0b0; } /* Annotation */
code.cv, span.cv { color: #60a0b0; } /* CommentVar */

+ 0
- 13
css/computermodern.css View File

@ -1,13 +0,0 @@
@font-face {
font-family: "Computer Modern";
src: url('/static/cmunorm.woff');
}
.math {
font-family: Computer Modern, serif;
}
.math .display {
font-size: 18px;
text-align: center;
}

+ 205
- 116
css/default.scss View File

@ -1,50 +1,4 @@
$purple-50: #faf5ff;
$purple-100: #f3e8ff;
$purple-200: #e9d5ff;
$purple-300: #d8b4fe;
$purple-400: #c084fc;
$purple-500: #a855f7;
$purple-600: #9333ea;
$purple-700: #7e22ce;
$purple-800: #6b21a8;
$purple-900: #581c87;
$yellow-50: #fefce8;
$yellow-100: #fef9c3;
$yellow-200: #fef08a;
$yellow-300: #fde047;
$yellow-400: #facc15;
$yellow-500: #eab308;
$yellow-600: #ca8a04;
$yellow-700: #a16207;
$yellow-800: #854d0e;
$yellow-900: #713f12;
$bluegray-50: #f8fafc;
$bluegray-100: #f1f5f9;
$bluegray-200: #e2e8f0;
$bluegray-300: #cbd5e1;
$bluegray-400: #94a3b8;
$bluegray-500: #64748b;
$bluegray-600: #475569;
$bluegray-700: #334155;
$bluegray-800: #1e293b;
$bluegray-900: #0f172a;
$red-50: #fef2f2;
$red-100: #fee2e2;
$red-200: #fecaca;
$red-300: #fca5a5;
$red-400: #f87171;
$red-500: #ef4444;
$red-600: #dc2626;
$red-700: #b91c1c;
$red-800: #991b1b;
$red-900: #7f1d1d;
$nav-height: 48px;
$font-size: 14pt;
@import "vars.scss";
@mixin center-that-bitch {
display: flex;
@ -76,7 +30,7 @@ body {
overflow-x: clip;
}
header {
body > header {
background-color: $purple-600;
display: flex;
height: $nav-height;
@ -132,15 +86,14 @@ header {
}
@mixin material {
padding-left: 1em;
padding-top: 0.2em;
padding-bottom: 0.2em;
padding: 1em;
margin-top: 1em;
margin-bottom: 1em;
padding-right: 1em;
box-shadow: 2px 2px 6px black;
border-radius: 10px;
}
main {
@ -154,6 +107,10 @@ main {
box-sizing: border-box;
div#title h2 {
display: none;
}
div#post-toc-container {
aside#toc {
display: none;
@ -164,6 +121,7 @@ main {
width: 100%;
line-height: 1.5;
}
}
div#post-info {
@ -205,6 +163,18 @@ figure {
}
}
ol, ul {
padding-left: 1.2em;
li {
margin-top: 5px;
margin-bottom: 5px;
p {
margin-top: 5px;
margin-bottom: 5px;
}
}
}
.katex-display {
> span.katex {
white-space: normal;
@ -226,72 +196,64 @@ div.mathpar {
}
}
.sourceCode {
font-size: $font-size;
}
div.sourceCode {
background-color: $yellow-50;
div.columns {
blockquote, details.blockquote {
padding-right: 1em;
padding-left: 1em;
padding-top: 0.2em;
padding-bottom: 0.2em;
@include material;
border: 0;
}
}
flex-grow: 0;
height: auto;
code, pre, .sourceCode {
font-size: $font-size * 1.2;
font-family: 'Iosevka', 'Fantasque Sans Mono', Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace;
font-weight: 500;
}
div.code-container {
padding: 0;
display: flex;
flex-direction: column;
div.sourceCode, pre {
background-color: $code-bg;
color: $code-fg;
flex-grow: 0;
box-shadow: 2px 2px 6px black;
@include material;
> div.sourceCode, > pre {
background-color: $yellow-50;
overflow-x: auto;
line-height: 1.2;
code {
display: block;
}
border: 0;
box-shadow: none;
padding: 2em;
padding-bottom: 0.5em;
padding-top: 0.5em;
> pre {
padding: unset;
margin-top: unset;
margin-bottom: unset;
box-shadow: unset;
margin: 0;
pre {
margin-top: 5px;
margin-bottom: 5px;
}
overflow-x: auto;
}
// this is fucking criminal lmao
div.empty-code-tag {
background-color: $yellow-50;
overflow-y: clip;
}
}
div.code-tag {
display: flex;
flex-direction: row-reverse;
height: 1.4em;
background-color: $yellow-300;
padding: 0.2em;
span {
@include center-that-bitch;
margin-right: 0.4em;
}
}
p > code {
white-space: nowrap;
}
blockquote {
@include left-bordered-block($bluegray-700);
blockquote, details.blockquote {
@include material;
background-color: $bluegray-100;
background-color: $purple-100;
padding-left: 2.5em;
margin-left: 0;
margin-right: 0;
h2 {
margin-top: 0;
}
}
table {
@ -310,14 +272,14 @@ ul#post-list {
list-style-type: none;
display: flex;
flex-direction: column;
gap: 2em;
.post-list-item {
@include left-bordered-block($yellow-500);
@include material;
background-color: $yellow-50;
margin: 0;
padding: 1em;
background-color: $yellow-50;
.post-list-header {
margin-top: 0.2em;
@ -352,7 +314,6 @@ div.contact-list {
max-width: 33%;
flex-grow: 1;
p {
margin: 0;
}
@ -378,7 +339,7 @@ div.contact-list {
}
@media only screen and (max-width: 450px) {
header {
body > header {
div#logo {
width: 100%;
display: flex;
@ -392,32 +353,66 @@ div.contact-list {
}
@media only screen and (min-width: 1500px) {
.narrow-only {
display: none !important;
}
main {
max-width: 100%;
> h1 {
font-size: 26pt;
> div#title {
font-size: 15pt;
h1, h2 {
margin: 0;
}
h2 {
font-style: italic;
font-weight: normal;
display: block;
z-index: 1;
}
margin-top: 0.5em;
margin-bottom: 1em;
@include center-that-bitch;
}
div#post-toc-container {
display: grid;
grid-template-columns: 0.5fr 2fr 0.5fr;
gap: 1em;
aside#toc {
display: block !important;
max-width: 200px;
h3 { @include center-that-bitch; }
ul {
border-left: 2px solid $bluegray-400;
list-style-type: none;
padding-left: 1em;
div#toc-container {
overflow-x: hidden;
width: 100%;
position: sticky;
top: 2em;
overflow-y: auto;
max-height: 90vh;
bottom: 2em;
ul {
border-left: 2px solid $bluegray-400;
list-style-type: none;
padding-left: 1em;
a {
text-decoration: none;
}
a {
text-decoration: none;
a:hover {
text-decoration: underline;
}
}
}
}
article {
@ -426,10 +421,104 @@ div.contact-list {
margin: auto;
}
}
div.columns {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1em;
}
}
}
#index {
padding-top: 4em;
a.ico-left {
img {
clip-path: url(#squircle);
width: 96px;
height: 96px;
}
float: left;
margin-right: 1em;
width: 96px;
height: 96px;
}
a.ico-right {
img {
clip-path: url(#squircle);
width: 96px;
height: 96px;
}
float: right;
margin-left: 1em;
width: 96px;
height: 96px;
}
div#social {
display: flex;
flex-direction: row;
justify-content: center;
width: 100%;
gap: 8px;
img {
width: 48px;
height: 48px;
clip-path: url(#squircle);
transition: width 0.25s, height 0.25s;
&:hover {
width: 54px;
height: 54px;
}
}
a {
filter: drop-shadow(2px 2px 3px rgba(50, 50, 0, 0.5));
height: 54px;
}
}
}
@media only screen and (min-width: 1500px) {
#index {
display: grid;
grid-template-columns: 0.20fr 0.75fr 0.20fr 1fr 0.20fr;
}
}
details {
margin-top: 1em;
margin-bottom: 1em;
}
}
// Styles for code
code.kw, span.kw { color: $code-pink; } /* Keyword */
code.dt, span.dt { color: $code-blue; } /* DataType */
code.dv, span.dv { color: $code-orange; } /* DecVal */
code.bn, span.bn { color: $code-orange; } /* BaseN */
code.fl, span.fl { color: $code-orange; } /* Float */
code.ch, span.ch { color: $code-green; } /* Char */
code.st, span.st { color: $code-green; } /* String */
code.co, span.co { color: $code-grey; } /* Comment */
code.ot, span.ot { color: $code-green; } /* Other */
code.al, span.al { color: #ff0000; } /* Alert */
code.fu, span.fu { color: $code-fg; } /* Function */
code.er, span.er { color: #ff0000; } /* Error */
code.wa, span.wa { color: #60a0b0; } /* Warning */
code.cn, span.cn { color: $code-orange; } /* Constant */
code.sc, span.sc { color: $code-yellow; } /* SpecialChar */
code.vs, span.vs { color: $code-blue; } /* VerbatimString */
code.ss, span.ss { color: $code-green; } /* SpecialString */
code.va, span.va { color: $code-fg; } /* Variable */
code.cf, span.cf { color: $code-pink; } /* ControlFlow */
code.op, span.op { color: $code-green; } /* Operator */
code.pp, span.pp { color: $code-orange; } /* Preprocessor */
code.at, span.at { color: $code-green; } /* Attribute */
code.do, span.do { color: $code-red; } /* Documentation */
code.an, span.an { color: $code-red; } /* Annotation */
code.cv, span.cv { color: $code-red; } /* CommentVar */

+ 419
- 0
css/fonts.scss View File

@ -0,0 +1,419 @@
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 400;
font-stretch: normal;
font-style: normal;
src: url('/static/woff2/iosevk-abbie-regular.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-regular.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 400;
font-stretch: expanded;
font-style: normal;
src: url('/static/woff2/iosevk-abbie-extended.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extended.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 400;
font-stretch: normal;
font-style: oblique;
src: url('/static/woff2/iosevk-abbie-oblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-oblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Oblique';
font-display: swap;
font-weight: 400;
font-stretch: normal;
src: url('/static/woff2/iosevk-abbie-oblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-oblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 400;
font-stretch: expanded;
font-style: oblique;
src: url('/static/woff2/iosevk-abbie-extendedoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Oblique';
font-display: swap;
font-weight: 400;
font-stretch: expanded;
src: url('/static/woff2/iosevk-abbie-extendedoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 400;
font-stretch: normal;
font-style: italic;
src: url('/static/woff2/iosevk-abbie-italic.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-italic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 400;
font-stretch: expanded;
font-style: italic;
src: url('/static/woff2/iosevk-abbie-extendeditalic.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendeditalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 500;
font-stretch: normal;
font-style: normal;
src: url('/static/woff2/iosevk-abbie-medium.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-medium.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 500;
font-stretch: expanded;
font-style: normal;
src: url('/static/woff2/iosevk-abbie-extendedmedium.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedmedium.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 500;
font-stretch: normal;
font-style: oblique;
src: url('/static/woff2/iosevk-abbie-mediumoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-mediumoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Oblique';
font-display: swap;
font-weight: 500;
font-stretch: normal;
src: url('/static/woff2/iosevk-abbie-mediumoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-mediumoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 500;
font-stretch: expanded;
font-style: oblique;
src: url('/static/woff2/iosevk-abbie-extendedmediumoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedmediumoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Oblique';
font-display: swap;
font-weight: 500;
font-stretch: expanded;
src: url('/static/woff2/iosevk-abbie-extendedmediumoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedmediumoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 500;
font-stretch: normal;
font-style: italic;
src: url('/static/woff2/iosevk-abbie-mediumitalic.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-mediumitalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 500;
font-stretch: expanded;
font-style: italic;
src: url('/static/woff2/iosevk-abbie-extendedmediumitalic.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedmediumitalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 600;
font-stretch: normal;
font-style: normal;
src: url('/static/woff2/iosevk-abbie-semibold.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-semibold.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 600;
font-stretch: expanded;
font-style: normal;
src: url('/static/woff2/iosevk-abbie-extendedsemibold.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedsemibold.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 600;
font-stretch: normal;
font-style: oblique;
src: url('/static/woff2/iosevk-abbie-semiboldoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-semiboldoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Oblique';
font-display: swap;
font-weight: 600;
font-stretch: normal;
src: url('/static/woff2/iosevk-abbie-semiboldoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-semiboldoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 600;
font-stretch: expanded;
font-style: oblique;
src: url('/static/woff2/iosevk-abbie-extendedsemiboldoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedsemiboldoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Oblique';
font-display: swap;
font-weight: 600;
font-stretch: expanded;
src: url('/static/woff2/iosevk-abbie-extendedsemiboldoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedsemiboldoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 600;
font-stretch: normal;
font-style: italic;
src: url('/static/woff2/iosevk-abbie-semibolditalic.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-semibolditalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 600;
font-stretch: expanded;
font-style: italic;
src: url('/static/woff2/iosevk-abbie-extendedsemibolditalic.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedsemibolditalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 700;
font-stretch: normal;
font-style: normal;
src: url('/static/woff2/iosevk-abbie-bold.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-bold.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 700;
font-stretch: expanded;
font-style: normal;
src: url('/static/woff2/iosevk-abbie-extendedbold.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedbold.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 700;
font-stretch: normal;
font-style: oblique;
src: url('/static/woff2/iosevk-abbie-boldoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-boldoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Oblique';
font-display: swap;
font-weight: 700;
font-stretch: normal;
src: url('/static/woff2/iosevk-abbie-boldoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-boldoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 700;
font-stretch: expanded;
font-style: oblique;
src: url('/static/woff2/iosevk-abbie-extendedboldoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedboldoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Oblique';
font-display: swap;
font-weight: 700;
font-stretch: expanded;
src: url('/static/woff2/iosevk-abbie-extendedboldoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedboldoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 700;
font-stretch: normal;
font-style: italic;
src: url('/static/woff2/iosevk-abbie-bolditalic.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-bolditalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 700;
font-stretch: expanded;
font-style: italic;
src: url('/static/woff2/iosevk-abbie-extendedbolditalic.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedbolditalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 800;
font-stretch: normal;
font-style: normal;
src: url('/static/woff2/iosevk-abbie-extrabold.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extrabold.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 800;
font-stretch: expanded;
font-style: normal;
src: url('/static/woff2/iosevk-abbie-extendedextrabold.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedextrabold.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 800;
font-stretch: normal;
font-style: oblique;
src: url('/static/woff2/iosevk-abbie-extraboldoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extraboldoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Oblique';
font-display: swap;
font-weight: 800;
font-stretch: normal;
src: url('/static/woff2/iosevk-abbie-extraboldoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extraboldoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 800;
font-stretch: expanded;
font-style: oblique;
src: url('/static/woff2/iosevk-abbie-extendedextraboldoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedextraboldoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Oblique';
font-display: swap;
font-weight: 800;
font-stretch: expanded;
src: url('/static/woff2/iosevk-abbie-extendedextraboldoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedextraboldoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 800;
font-stretch: normal;
font-style: italic;
src: url('/static/woff2/iosevk-abbie-extrabolditalic.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extrabolditalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 800;
font-stretch: expanded;
font-style: italic;
src: url('/static/woff2/iosevk-abbie-extendedextrabolditalic.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedextrabolditalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 900;
font-stretch: normal;
font-style: normal;
src: url('/static/woff2/iosevk-abbie-heavy.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-heavy.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 900;
font-stretch: expanded;
font-style: normal;
src: url('/static/woff2/iosevk-abbie-extendedheavy.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedheavy.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 900;
font-stretch: normal;
font-style: oblique;
src: url('/static/woff2/iosevk-abbie-heavyoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-heavyoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Oblique';
font-display: swap;
font-weight: 900;
font-stretch: normal;
src: url('/static/woff2/iosevk-abbie-heavyoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-heavyoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 900;
font-stretch: expanded;
font-style: oblique;
src: url('/static/woff2/iosevk-abbie-extendedheavyoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedheavyoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Oblique';
font-display: swap;
font-weight: 900;
font-stretch: expanded;
src: url('/static/woff2/iosevk-abbie-extendedheavyoblique.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedheavyoblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 900;
font-stretch: normal;
font-style: italic;
src: url('/static/woff2/iosevk-abbie-heavyitalic.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-heavyitalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka';
font-display: swap;
font-weight: 900;
font-stretch: expanded;
font-style: italic;
src: url('/static/woff2/iosevk-abbie-extendedheavyitalic.woff2') format('woff2'), url('/static/ttf/iosevk-abbie-extendedheavyitalic.ttf') format('truetype');
}

+ 62
- 0
css/vars.scss View File

@ -0,0 +1,62 @@
$purple-50: #faf5ff;
$purple-100: #f3e8ff;
$purple-200: #e9d5ff;
$purple-300: #d8b4fe;
$purple-400: #c084fc;
$purple-500: #a855f7;
$purple-600: #9333ea;
$purple-700: #7e22ce;
$purple-800: #6b21a8;
$purple-900: #581c87;
$yellow-50: #fefce8;
$yellow-100: #fef9c3;
$yellow-200: #fef08a;
$yellow-300: #fde047;
$yellow-400: #facc15;
$yellow-500: #eab308;
$yellow-600: #ca8a04;
$yellow-700: #a16207;
$yellow-800: #854d0e;
$yellow-900: #713f12;
$bluegray-50: #f8fafc;
$bluegray-100: #f1f5f9;
$bluegray-200: #e2e8f0;
$bluegray-300: #cbd5e1;
$bluegray-400: #94a3b8;
$bluegray-500: #64748b;
$bluegray-600: #475569;
$bluegray-700: #334155;
$bluegray-800: #1e293b;
$bluegray-900: #0f172a;
$red-50: #fef2f2;
$red-100: #fee2e2;
$red-200: #fecaca;
$red-300: #fca5a5;
$red-400: #f87171;
$red-500: #ef4444;
$red-600: #dc2626;
$red-700: #b91c1c;
$red-800: #991b1b;
$red-900: #7f1d1d;
$nav-height: 48px;
$font-size: 14pt;
$code-bg: #282C34;
$code-fg: #ABB2BF;
$code-red: #D65122;
$code-red-br: #AE3B36;
$code-green: #88B966;
$code-yellow: #DEB468;
$code-orange: #C58853;
$code-blue: #519DEB;
$code-pink: #C678DD;
$code-cyan: #48A8B5;
$code-white: #ABB2BF;
$code-grey: #7F848E;
// foo

+ 2
- 2
package.json View File

@ -1,7 +1,7 @@
{
"name": "blag",
"version": "1.0.0",
"description": "[Go here instead](https://abby.how)",
"description": "[Go here instead](https://amelia.how)",
"main": "index.js",
"dependencies": {
"katex": "^0.13.11",
@ -14,7 +14,7 @@
},
"repository": {
"type": "git",
"url": "https://git.abby.how/abby/blag.git"
"url": "https://git.amelia.how/amelia/blag.git"
},
"author": "",
"license": "ISC"


+ 4
- 15
pages/contact.md View File

@ -8,19 +8,10 @@ span#reading-length { display: none; }
</style>
<div class="contact-list">
<div class="contact-card">
<div class="contact-header">
<img alt="profile picture" title="Abbie's profile picture" src=/static/icon/[email protected] />
<span class="username">Abbie#4600</span>
</div>
My Discord friend requests are always open. Feel free to add me for questions or comments!
</div>
<div class="contact-card">
<div class="contact-header">
<img alt="profile picture" title="Abbie's profile picture" src=/static/icon/[email protected] />
<span class="username">@plt_abbie</span>
<span class="username">@plt_amy</span>
</div>
<span>
@ -30,12 +21,11 @@ span#reading-length { display: none; }
<div class="contact-card">
<div class="contact-header">
<img alt="profile picture" title="Abbie's profile picture" src=/static/icon/[email protected] />
<span class="username">abbie</span>
<span class="username">{ames}</span>
</div>
<span>
Message me directly on <a href="https://libera.chat">Libera</a> IRC, or join `##dependent` to talk about types!
I'm active in `##dependent` on [libera.chat](https://libera.chat) to talk about types!
</span>
</div>
@ -45,8 +35,7 @@ If you like what I do, here are some ways you can support this blog:
<div class="contact-list">
<div class="contact-card">
<span class="username"><a href="https://ko-fi.com/pltabby">Ko-fi</a></span>
<span class="username"><a href="https://ko-fi.com/plt_amy">Ko-fi</a></span>
You can send me a one-time donation on Ko-Fi. Just remember not to read the name on the receipt!
(Paypal sucks)
</div>


+ 75
- 13
pages/index.html View File

@ -2,16 +2,78 @@
title: Home
---
<div id="post-toc-container">
<article>
<p>
You've reached Abigail's blog, a place where I exposit and reflect on programming languages and type theory.
Here's the posts I've written recently:
</p>
<h2>Posts</h2>
$partial("templates/post-list.html")$
<p>…or you can find more in the <a href="/archive.html">archives</a>.</p>
</article>
</div>
<div id=index>
<div style="grid-column: 2;">
<h2 id=hi>Hi!</h2>
<p style="height: 98px">
<a class=ico-left href="/">
<img alt="profile picture" decoding=async src="/static/icon/[email protected]" />
</a>
I'm Amélia, a non-binary (they/them) mathematician & programmer. This
blog is where I write about programming languages: their implementation,
their semantics, etc.
</p>
<hr />
<div id=social>
<a href="https://twitter.com/plt_amy">
<img class=social decoding=async title="My Twitter profile" src="/static/svg/twitter.svg" style="background-color: #1DA1F2;" />
</a>
<a href="https://github.com/plt-amy">
<img class=social decoding=async title="My GitHub profile" src="/static/svg/github.svg" style="background-color: black;" />
</a>
<a href="https://git.amelia.how">
<img class=social decoding=async title="My personal Gitea" src="/static/svg/gitea.svg" style="background-color: #609926;" />
</a>
<a href="https://ko-fi.com/plt_amy">
<img class=social decoding=async title="Buy me a coffee on Ko-Fi" src="/static/svg/kofi.svg" style="background-color: #434B57;" />
</a>
</div>
<hr />
<p style="height: 98px">
<a class=ico-right href="https://cubical.1lab.dev">
<img class=ico-right alt="cube" decoding=async src="/static/icon/cube-128x.png" />
</a>
In addition to this blog, I maintain <a
href="https://cubical.1lab.dev">the 1Lab</a>, a formalised, cross-linked
reference resource for Homotopy Type Theory, done in Cubical Agda.
</p>
<hr />
<p style="height: 98px">
<a class=ico-left href="https://amulet.works">
<img alt="amulet" decoding=async src="/static/svg/amulet.svg" />
</a>
My most significant project other than this blog and the 1lab is <a
href="https://amulet.works">Amulet</a>, a functional programming
language in the ML tradition with support for advanced type-level
programming.
</p>
</div>
<div style="grid-column: 4;">
<h2>Posts</h2>
<p>
Here are the lastest 5 posts from the blog:
</p>
$partial("templates/post-list.html")$
<p>…or you can find more in the <a href="/archive.html">archives</a>.</p>
</div>
</div>
<style>
main > div#title {
display: none;
}
</style>

+ 2
- 3
pages/oss.md View File

@ -8,7 +8,6 @@ span#reading-length { display: none; }
This blog redistributes (parts of) the following free software projects:
* **KaTeX** is a fast JavaScript library for rendering LaTeX on the client. I use it to pre-generate amazing looking mathematics at compile time. **KaTeX is licensed under the terms of the MIT license, with a copy available [here](/static/licenses/LICENSE.KaTeX)**.
* **KaTeX** is a fast JavaScript library for rendering LaTeX on the client. I use it to pre-generate amazing looking mathematics at compile time. **KaTeX is licensed under the terms of the MIT license; A copy is available [here](/static/licenses/LICENSE.KaTeX)**.
* **Fantasque Sans Mono** is the programming font I've used for the past 3 years, including on this website. **Fantasque Sans Mono is distributed under the terms of the Open Font License (OFL), with a copy available [here](/static/licenses/LICENSE.FantasqueSansMono)**.
* **Iosevka** is a customizable monospace font designed for programmers. It's used in this website for code blocks, and also for any Agda developments I've shared under the amelia.how domain. **Iosevka is distributed under the terms of the SIL Open Font License; A copy is available [here](/static/licenses/LICENSE.Iosevka).**

+ 1
- 1
pages/posts/2018-03-14-amulet-safety.md View File

@ -270,7 +270,7 @@ family with its polymorphic, inferred type system.
[Theorems for Free]: https://people.mpi-sws.org/~dreyer/tor/papers/wadler.pdf
[my last post]: /posts/2018-02-18.html
[an implementation of the ST monad]: https://txt.abby.how/st-monad.ml.html
[an implementation of the ST monad]: https://txt.amelia.how/st-monad.ml.html
[an entire compiler]: http://compcert.inria.fr/
[operating system kernel]: https://sel4.systems/


+ 1
- 1
pages/posts/2018-03-27-amulet-gadts.md View File

@ -244,4 +244,4 @@ error, as you can see [here].
[his thesis]: https://repository.brynmawr.edu/cgi/viewcontent.cgi?article=1074&context=compsci_pubs
[two weeks ago]: /posts/2018-03-14.html
[here]: https://i.abby.how/68c4d.png
[here]: https://i.amelia.how/68c4d.png

+ 7
- 7
pages/posts/2021-03-07-cubical.md View File

@ -94,7 +94,7 @@ singContr : {A : Type} {a : A} -> isContr (Singl A a)
singContr {A} {a} = ((a, \i -> a), \y i -> (y.2 i, \j -> y.2 (iand i j)))
```
This proof is written syntactically, in the language of [cubical](https://git.abby.how/abby/cubical). This proof appears on [line 114] of the massive source file which has everything I've tried to prove with this so far. What's a module system? The actual proof file has some preliminaries which would be interesting if you care about how cubical type theory is actually implemented.
This proof is written syntactically, in the language of [cubical](https://git.amelia.how/amelia/cubical). This proof appears on [line 114] of the massive source file which has everything I've tried to prove with this so far. What's a module system? The actual proof file has some preliminaries which would be interesting if you care about how cubical type theory is actually implemented.
Another operation on equalities which is very hard in MLTT, but trivial with cubes, is function extensionality. You can see why this would be simple if you consider that a pointwise equality between functions would be an element of $A \to \mathbb{I} \to B$, while an equality between functions themselves is an element of $\mathbb{I} \to A \to B$. By simply swapping the binders, we get the naive function extensionality.
@ -415,7 +415,7 @@ Applying the proof that $\mathrm{true} \not\equiv \mathrm{false}$ we have a cont
To say that our universe $\mathscr{U}$ with its infinitely many types is lacking some is... weird, I'll admit. However, it's missing a lot of them! A countably infinite amount, in fact. While we have all inductive types, we only have the zero-dimensional inductive types, and not the higher inductive types!
I've written about these before a bit in the previous post, about induction. In short, while inductive types allow us to define types with points, higher inductive types let us define types with points and paths. Full disclosure, of time of writing, the implementation of HITs in [cubical](https://git.abby.how/abby/cubical) is partial, in that their fibrancy structure is a big `error`. However we can still write some simple proofs involving them.
I've written about these before a bit in the previous post, about induction. In short, while inductive types allow us to define types with points, higher inductive types let us define types with points and paths. Full disclosure, of time of writing, the implementation of HITs in [cubical](https://git.amelia.how/amelia/cubical) is partial, in that their fibrancy structure is a big `error`. However we can still write some simple proofs involving them.
### The Interval
@ -549,7 +549,7 @@ The `push` path constructor is parametrised by an element $a : A$ and an endpoin
> - The pushout of $A \leftarrow A \times B \to B$ is the **join** of $A$ and $B$, written $A * B$
> - The pushout of $1 \leftarrow A \xrightarrow{f} B$ is the **cone** or **cofiber** of $f$
The big file with all the proofs in [cubical](https://git.abby.how/abby/cubical) features a proof that the suspension $\Sigma A$ defined directly as a HIT is the same as the one defined by the pushout of $1 \leftarrow A \to 1$.
The big file with all the proofs in [cubical](https://git.amelia.how/amelia/cubical) features a proof that the suspension $\Sigma A$ defined directly as a HIT is the same as the one defined by the pushout of $1 \leftarrow A \to 1$.
## But Why?
@ -600,8 +600,8 @@ To define $\mathrm{comp}\ (\lambda i. \sum{(x : A(i))} B(i)\ x)\ [\phi \to u]\ p
$$\mathrm{comp}\ (\lambda i. \textstyle\sum{(x : A(i))} B(i)\ x)\ [\phi \to u]\ p = (v(i1), y\prime)$$
[here]: https://git.abby.how/abby/cubical/src/branch/master/intro.tt#L436-L460
[here]: https://git.amelia.how/amelia/cubical/src/branch/master/intro.tt#L436-L460
[line 114]: https://git.abby.how/abby/cubical/src/commit/fb87b16429fdd54f7e71b653ffaed115015066cc/intro.tt#L110-L114
[line 667]: https://git.abby.how/abby/cubical/src/commit/fb87b16429fdd54f7e71b653ffaed115015066cc/intro.tt#L667
[line 675]: https://git.abby.how/abby/cubical/src/commit/fb87b16429fdd54f7e71b653ffaed115015066cc/intro.tt#L675
[line 114]: https://git.amelia.how/amelia/cubical/src/commit/fb87b16429fdd54f7e71b653ffaed115015066cc/intro.tt#L110-L114
[line 667]: https://git.amelia.how/amelia/cubical/src/commit/fb87b16429fdd54f7e71b653ffaed115015066cc/intro.tt#L667
[line 675]: https://git.amelia.how/amelia/cubical/src/commit/fb87b16429fdd54f7e71b653ffaed115015066cc/intro.tt#L675

+ 1
- 1
pages/posts/2021-06-07-ax-j.md View File

@ -1,7 +1,7 @@
---
title: "A quickie: Axiom J"
date: June 7th, 2021
synopsys: 2
synopsys: 1
---
Hey y'all, it's been three months since my last blog post! You know what that means.. or should mean, at least. Yes, I'd quite like to have another long blog post done, but... Life is kinda trash right now, no motivation for writing, whatever. So over the coming week(s) or so, as a coping mechanism for the chaos that is the end of the semester, I'm gonna write a couple of really short posts (like this one) that might not even be coherent at all---this sentence sure isn't.


+ 2
- 2
pages/posts/2021-09-03-parsing-layout.md View File

@ -162,7 +162,7 @@ LClose
| error {- parse error generated '}' -}
```
We have unfortunately introduced some dragons, since the parser now needs to finesse the lexer state, meaning they must be interleaved _explicitly_, instead of explicitly (using a lazy list of tokens or similar). They must be in the same Monad.
We have unfortunately introduced some dragons, since the parser now needs to finesse the lexer state, meaning they must be interleaved _explicitly_, instead of being run in sequence (using a lazy list of tokens or similar). They must be in the same Monad.
So. How do we implement this?
@ -869,7 +869,7 @@ Right
})
```
I've thrown the code from this post up in an organised manner on [my Gitea](https://git.abby.how/abby/layout-parser/). The lexer worked out to be 130 lines, and the parser - just 81.
I've thrown the code from this post up in an organised manner on [my Gitea](https://git.amelia.how/amelia/layout-parser/). The lexer worked out to be 130 lines, and the parser - just 81.
Here's why I favour this approach:


+ 420
- 0
pages/posts/2021-09-05-outsidein-x.md View File

@ -0,0 +1,420 @@
---
title: Typing (GHC) Haskell in Haskell
subtitle: The OutsideIn(X) Elaboration Algorithm
date: September 5th, 2021
public: false
---
Typing Haskell in Haskell, in addition to being a solved problem, is the name of [a paper] by Mark P. Jones that constructs, in detail, a solution to that problem. The goal of that paper is noble: a complete specification of Haskell's type system as an executable Haskell program. And, indeed, in 2000, when that paper was published, it _was_ a complete specification of Haskell's type system, depending on what you mean by Haskell. However, most people do not mean "Haskell 2010" when they say Haskell, let alone Haskell 98 - what the paper implements. Further, it's been 21 years!
[a paper]: https://web.cecs.pdx.edu/~mpj/thih/thih.pdf
When I say Haskell, personally, I mean "GHC's default language", and possibly throw in some 20 extensions on top anyway. Here's a small list of conveniences 2021 Haskell programmers are used to, but were implemented in the two decades since _Typing Haskell in Haskell_ was first published - or, in the case of FunDeps, were simply not standardised:
- Rank-N types, a limited implementation of first-class polymorphism, let a Haskell programmer write `forall`s to the left of as many arrows as she wants. For a motivating example, take the ST monad, from which a value can be extracted using `runST`:
```haskell
runST :: (forall s. ST s a) -> a
```
Since the type of the state token - `s` - is universally quantified, it's not "chosen" by the ST computation, but rather by `runST` itself, making sure that the computation can't adversarially "choose" an instantiation of `s` that violates referential transparency.
Rank-N types were first implemented in GHC in November 2001, in [this commit](https://gitlab.haskell.org/ghc/ghc/-/commit/5e3f005d3012472e422d4ffd7dca5c21a80fca80).
[rankn]: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf
- Generalised algebraic data types (GADTs), which let us introduce local _equational constraints_ between types by means of pattern matching. I'm a big fan of GADTs, so much so that I paid 20 bucks to register the domain [gadt.fans](https://gadt.fans). The classic example of GADTs is a well-typed interpreter, where the type of each constructor constrains the return type of the interpreter:
```haskell
data Exp a where
Add :: Exp Int -> Exp Int -> Exp Int
IsZ :: Exp Int -> Exp Bool
If :: Exp Bool -> Exp a -> Exp a -> Exp a
Lit :: Int -> Exp Int
eval :: Exp a -> a
eval (Lit i) = i
{- most cases omitted for brevity -}
```
GADTs were first implemented in GHC in September 2004, in [this commit](https://gitlab.haskell.org/ghc/ghc/-/commit/23f40f0e9be6d4aa5cf9ea31d73f4013f8e7b4bd).
- Functional dependencies, inspired by database theory, let a programmer specify that some of the arguments to one of their type classes is entirely determined by the value of some other argument. If that's a bit abstract, a more operational reading is that functional dependencies improve inferred types by adding new equalities. The classic example is this:
```haskell
class Collects c e | c -> e where
singleton :: e -> c
union :: c -> c -> c
```
Without the functional dependency, the inferred type for the function `bagTwo` below would be `(Collects c e1, Collects c e2) => e1 -> e2 -> c`{.haskell}, implying that bagTwo is capable of placing two values of different types in the same collection `c`.
```haskell
bagTwo x y = singleton x `union` singleton y
```
With the functional dependency `c -> e` in place, the two inferered constraints `(Collects c e1, Collects c e2)` _interact_ to introduce an equality `e1 ~ e2`, improving the inferred type of the function to
```haskell
bagTwo :: Collects c a => a -> a -> c
```
Functional dependencies were first implemented in GHC in December 1999, in [this commit](https://gitlab.haskell.org/ghc/ghc/-/commit/297f714906efa8a76378c6fa6db3cd592f896749). The connection between database theory and type systems, integral in the design of functional dependencies for Haskell type classes, is made clear in [the original paper](http://web.cecs.pdx.edu/~mpj/pubs/fundeps-esop2000.pdf), section 5.
- Type families, originally introduced as _associated types_, are, as [Richard Eisenberg put it](https://gitlab.haskell.org/ghc/ghc/-/issues/11080), "non-generative, non-injective symbols whose equational theory is given by GHC". Put another way, they're almost-but-not-quite functions between types. Type families are _weird_, and complicate type checking massively. For instance, consider the following program, taken from Storalek et al.'s "Injective Type Families for Haskell":
```haskell
class Manifold a where
type Base a
project :: a -> Base a
unproject :: Base a -> a
id :: Manifold a => Base a -> Base a
id = project . unproject
```
Does this program type check? Surprisingly, the answer is no! The reason is that the type variable `a` only appears under type families, and in the set of constraints, so GHC reports the function's type as ambiguous.
To understand why this is problematic, imagine that we have two types `X`{.haskell} and `Y`{.haskell} such that `Base X = Base Y = [Double]`{.haskell}. Given a `vec :: [Double]`, what instance of `Manifold` should the call `id vec` use? We can't choose - we can only guess, and runtime behaviour that depends on a compiler guess is very much frowned upon!
Type families were originally implemented ca. 2006, but I've been unable to track down the precise commit. I believe it was done as part of the patch which changed GHC's intermediate representation to System $F_C$ (we'll get to it) - this is backed up by this sentence from the conclusion of the $F_C$ paper: "At the same time, we re-implemented GHC’s support for newtypes and GADTs to work as outlined in §2 and added support for associated (data) types".
All of these features interact with eachother in entirely non-trivial ways, creating a powerful source of GHC infelicities with $n^2$ magnitude. The interaction between GADTs and type families, for instance, mandates an elaboration algorithm which can cope with _local assumptions_ in a principled way, since GADTs can introduce equalities between existentials which interact with type family axioms non-trivially. Wobbly types just won't cut it.
That's where $\operatorname{OutsideIn}$ comes in - or, more specifically, $\operatorname{OutsideIn}(X)$, since the elaboration algorithm is parametrised over the constraint domain $X$. This post is intended as a companion to [the JFP paper introducing OutsideIn](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/jfp-outsidein.pdf), not as a replacement. The core idea is that we can record where the local assumptions are introduced in a tree of _implication constraints_, built out of the constraints in our domain $X$, and these can be reduced - outside-in - to an $X$-specific solver.
Diverging from the paper slightly, I'll implement the elaborator as a _bidirectional_ algorithm, which lets us take advantage of programmer-written type signatures. The signatures are there for a reason! It's silly to use type signatures as a source of complication (infer a type for the binding, then match it against the signature) rather than as a source of _simplification_. Plus - bidirectional type checking makes higher-rank types almost trivial - I think we can all agree that's a good thing, yeah?
# The Problem Statement
We're given a Haskell program - well, a program written in a proper subset of a proper superset of Haskell - and we want to tell whether it's type correct. Our superset extends Haskell 2010 to feature type families, GADTs, rank-N types and functional dependencies, but our subset doesn't contain most of Haskell's niceties, like definitions by equations, guards, or even `if`{.haskell}: you get `case`{.haskell}, and _you're going to like it_.
Well, more than just telling whether or not the program is type correct, we want to produce an _elaborated program_ in a simpler language - GHC calls this "Core" - if and only if the program is correct, and report a (set of) good type errors otherwise. The elaborated program also has to be type correct, and, ideally, we have a _second_, much smaller type checker over the Core language that calls the big, complicated elaborator out on its bullshit. Because of this, the elaborator has to produce _evidence_ justifying its wilder claims.
There are two kinds of evidence we need to produce: _coercions_ are inserted where the expected type of an expression is equal to its actual type in a non-trivial way. Consider the program below, and its elaboration to the right:
<div class=mathpar>
```haskell
data T1 a where
TI :: T1 Int
TB :: T1 Bool
foo :: T1 a -> a
foo x = case x of
TI -> 123
TB -> True
```
```haskell
data T1 a where
TI :: (a ~# Int) => T1 a
TB :: (a ~# Bool) => T1 a
foo :: T1 a -> a
foo x = case x of
TI phi -> 123 |> Sym phi
TB phi -> True |> Sym phi
```
</div>
This program, which uses GADTs (see `data ... where`{.haskell}), has two non-trivial equalities between types. In the `TI -> 123`{.haskell} case, we used an `Int`{.haskell}[^1] literal where a value of type `a` was expected. But in that branch, `a` is equal to `Int`{.haskell}! In the elaborated output, this non-trivial local equality is explicitly witnessed by a _coercion variable_ `phi :: a ~# Int`{.haskell}, and the use of `123 :: Int`{.haskell} at type `a` has to be mediated by a _cast_.
[^1]: Actually, if you know how numeric literals desugar, you might know the actual elaboration produced here is different: `123` becomes `fromInteger @a ($NumInt |> Num (Sym phi)) (123 :: Integer)`{.haskell}. This is because it's totally legit to cast the `Num Int`{.haskell} dictionary to a `Num a`{.haskell} dictionary using the local equality, and, since `123`{.haskell} is sugar for `fromInteger @α (123 :: Integer)`{.haskell}, `α` gets solved to `a`, not `Int`{.haskell}.
The other kind of evidence is not specific to GADTs, type families, or any other type fanciness: _dictionaries_ witness the existence of a type class `instance`{.haskell}, but, unlike coercions (which only exist to make the second type checker happy), exist at runtime. Consider the program below and its elaboration:
<div class=mathpar>
```haskell
class S a where
s :: a -> String
instance S Int where
s = show
foo :: Int -> String
foo x = s (x + 123)
```
```haskell
data $S a =
$MkS { s :: a -> String }
$dSInt :: S Int
$dSInt = $MkS @Int (show @Int $dShowInt)
foo :: Int -> String
foo x = s @Int $dSInt ((+) @Int $dNumInt x 123)
```
</div>
Type `class`{.haskell}es are elaborated to `data`{.haskell} types, and `instance`{.haskell}s are compiled to actual proper values of those data types. When you apply a function with overloaded type - like `s`, `show` and `(+)` - the compiler inserts the value corresponding to the `instance`{.haskell} that was selected to satisfy the class constraint. Further, `instance`{.haskell}s with superclass constraints become functions from dictionaries to dictionaries, and superclasses on `class`{.haskell}es become values embedded in the dictionary, just like class methods.
You'll also notice another artifact of elaboration here: the use of `s` at type `Int`{.haskell} became a _visible type application_ `s @Int`{.haskell}. This is, again, to satisfy the second type checker, but it can in principle be used as an actual implementation of polymorphism - one that doesn't box. See [Sixten](https://github.com/ollef/sixten) for a language that exploits this type passing to implement polymorphism without monomorphisation. Type applications are used in every polymorphic function application, not just those with class constraints.
## Why it's hard
GADTs complicate the problem of type inference in a way that's become rather famous: GADTs destroy the _principal types_ property. Recall: A **principal type** for a function $f$ is a type $\tau$ such that, $\Gamma \vdash f : \tau$ and, if $\Gamma \vdash f : \sigma$, then $\sigma$ is a substitution instance of $\tau$. Using less KaTeX, a principal type for a function is a _most general type_ for a type. For instance, the functions below are annotated with their principal types:
```haskell
id :: a -> a
id x = x
const :: a -> b -> a
const x _ = x
```
But now consider this program using GADTs:
```haskell
data T a where
T1 :: Int -> T Bool
T2 :: T a
test x y = case x of
T1 n -> n > 0
T2 -> y
```
One can verify - and we will - that the function test types as either `test :: forall a. T a -> Bool -> Bool`{.haskell} or as `forall a. T a -> a -> a`{.haskell}, but neither of these types is an instance of the other! Let's look at why `test` checks with either of those types, with a _lot_ of detail - mimicking by hand the execution of the algorithm. Don't worry about all the terms I'll be throwing around: they'll all be explained later, I promise!
<details class=blockquote>
<summary> **`test :: forall a. T a -> Bool -> Bool`{.haskell}** </summary>
The algorithm is in checking mode, since we have a type signature.
1. Introduce a binding `x :: T a`{.haskell} into scope. We must check the body of the function against the type `Bool -> Bool`{.haskell}
2. Introduce a binding `y :: Bool` into scope. We must check the body of the function against the type `Bool`{.haskell}.
3. Check the `case`{.haskell} expression against the type `Bool`{.haskell}. There are two branches.
* `T1 n -> n > 0`{.haskell}:
* Instantiate the type of the constructor `T1 :: forall a. (a ~ Bool) => Int -> T a`{.haskell} with `a := X`{.haskell} to get the type `T1 :: a ~ Bool => Int -> T a`{.haskell}, where `a` is a _skolem_ type variable. The type variable `a` becomes a skolem and not a unification variable because it is an _existential_ of T1.
* Introduce the local equality assumption `phi :: a ~ Bool`{.haskell} and the variable `n :: Int`{.haskell}.
* Check that `n > 0 :: Bool`{.haskell}. For brevity, we'll take this to be one atomic step, which succeeds, but the real algorithm must treat all of those subexpressions independently.
* `T2 -> y`{.haskell}. We must check that `y :: Bool`{.haskell}, which succeeds.
Since all of these steps succeed (most of them are introducing variables and can't fail) - the program is type-correct. Note that in the branch with a local equality, our assumption that `a ~ Bool`{.haskell} wasn't used.
</details>
<details class=blockquote>
<summary> **`test :: forall a. T a -> a -> a`{.haskell}** </summary>
The algorithm is in checking mode, since we have a type signature.
1. Introduce a binding `x :: T a`{.haskell} into scope. We must check the body of the function against the type `a -> a`{.haskell}
2. Introduce a binding `y :: a` into scope. We must check the body of the function against the type `a`{.haskell}.
3. Check the `case`{.haskell} expression against the type `Bool`{.haskell}. There are two branches.
* `T1 n -> n > 0`{.haskell}:
* Instantiate the type of the constructor `T1 :: forall a. (a ~ Bool) => Int -> T a`{.haskell} with `a := X`{.haskell} to get the type `T1 :: a ~ Bool => Int -> T a`{.haskell}, where `a` is a _skolem_ type variable. The type variable `a` becomes a skolem and not a unification variable because it is an _existential_ of T1.
* Introduce the local equality assumption `phi :: a ~ Bool`{.haskell} and the variable `n :: Int`{.haskell}.
* Check that `n > 0 :: a`{.haskell}. We infer that `n > 0 :: Bool`{.haskell}, and we must unify `Bool ~ a`{.haskell}. This unification succeeds because of the given equality `phi :: a ~ Bool`{.haskell}, which we are free to invert.
* `T2 -> y`{.haskell}. We must check that `y :: a`{.haskell}, which succeeds.
Since all of these steps succeed (most of them are introducing variables and can't fail) - the program is type-correct. In this typing, compared with the previous, we made use of the assumption `phi :: a ~ Bool` brought into scope by the match against the constructor `T1 n`.
</details>
The execution trace for both cases is remarkably similar - the only difference is in that if the function is typed as `T a -> a -> a`{.haskell}, we must make use of the local equality brought into scope to justify that we're allowed to use a value nominally of type `Bool`{.haskell} as one of type `a`. We are free to do this, but it's not obvious if, without a type annotation to guide us, we should. Consider now the following very minor alteration to test:
```haskell
test x y = case x of
T1 n -> n > 0
T2 -> not y
```
The only possible type for this program is `T a -> Bool -> Bool`{.haskell}, and so, we can decide without any major complications that the GADT equality should _not_ be used.
# How To Check Types
In this section we'll solve the infinitely simpler problem of elaborating a language with rank-N types and type classes - including functional dependencies - but crucially, no GADTs. To do this we'll use a _bidirectional_, _constraint-based_ elaboration algorithm.
First, bidirectional means that, unlike in a type _inference_ system, type information flows both in and out of the algorithm. Practically speaking, we have two functions to implement the case where type information is an input to the algorithm (`check`{.haskell}) and one where type information is a return from the algorithm (`infer`{.haskell}).
```haskell
infer :: Raw.Expr -> Elab (Core.Term, Core.Type)
check :: Raw.Expr -> Core.Type -> Elab Core.Term
```
If you know how to infer a type for an expression `e` but you need to check it against a known type `wanted_type`, you can do it by unification, whereas if you know how to check an expression `f` against a type but you need to infer a type for it, you can do it by inventing a new _metavariable_ and checking against that[^2]:
[^2]: In the OutsideIn(X) paper, metavariables are known as _unification variables_. The term _metavariable_ is common in the dependently-typed world, whereas _unification variable_ is more common among Haskell and ML researchers.
<div class=mathpar>
```haskell
check e wanted_type = do
(elab, actual_type) <- infer x
unify wanted_type actual_type
pure elab
```
```haskell
infer f = do
ty <- newMeta
elab <- check f ty
pure (elab, ty)
```
</div>
Constraint-based means that, at least conceptually, the algorithm works by first generating constraints by walking through the AST (we do this bidirectionally), and only later solving the generated constraints. But, as a very fruitful optimisation, there are cases where the constraints need not be stashed away for later: If we want to solve a unification problem, for instance, where a metavariable is being compared against a concrete type, and we're free to solve the variable with that type, we might as well do it inline.
_Elaboration_ is a natural extension of "type checking" in which the program is both checked and transformed into a simpler intermediate representation in the same step. The name "type checker" sort-of implies that the output is a boolean (or, more realistically, a list of errors): this is rarely true in practice, but I still prefer the use of the name "elaborator" to make clear that the output is a different _language_ from the input, and not merely a type-annotated version of the input.
I'm going to start by talking about the intermediate language we'll elaborate into, _System $F_C$, first. This is because of an assumption I'm making: I'm assuming most of my readers are familiar with Haskell - at least in passing - but not very familiar with GHC's intermediate language. That's why we start there!
## Our Target Language
System $F_C$, as the name kind-of sort-of implies, is a superset of System $F$, the _second-order_ lambda calculus. For those not in the loop, System F has all the same features of a normal typed lambda calculus (variables, lambda abstraction, application, algebraic data types, and pattern matching[^3]), but additionally features _first class polymorphism_. Roughly, this means that in System F, a `forall`{.haskell} type can appear everywhere a "normal" type can appear - you could form the type `[forall a. a -> a]`{.haskell} of "lists of identity functions", for instance.
[^3]: If you disagree with the inclusion of algebraic data types and pattern matching in the list of features of a "normal typed lambda calculus"---there's nothing you can do about it, this is my blog, lol.
Now, this doesn't mean that first class polymorphism is available to languages that elaborate into System $F_C$ - GHC, for instance, struggled with what they call "impredicative polymorphism" for years, up until very recently. Amulet did a slightly better job because, being a research toy and not a production compiler (that happens to be also be a research toy), there was less code to move around by implementing support for first-class polymorphism.
Since `forall`{.haskell} is a new type former, it also has a corresponding introduction form and elimination form. The introduction rule says that if you can build a term `e : t` in a context where `a` is a type variable of kind `k`, then the term `Λ (a :: k). e` has type `forall (a :: k). σ`{.haskell}. To stick with ASCII for "control" symbols, I'm going to write `Λ (a :: k)` as `\ @(a :: k)`{.haskell}, omitting `κ` if it is obvious - Also, I'm sticking with Haskell notation, even if `::` should really mean cons.
Similarly, the elimination rule says that to consume an expression `e :: forall (a :: k). t`{.haskell}, what we need to do is come up with a _type_ `s :: k`{.haskell}. Then we can _instantiate_ (using a different word so as to not overload "apply") `e`{.haskell} at `s`{.haskell} to get a term `e @s :: t[s/a]`{.haskell} - where `t[s/a]` denotes the substitution of `s` for `a` in `t`, avoiding capture.
Here's a simple Haskell program, and its translation into the notation I'll use for $F_C$. We'll go over it afterwards.
<div class=mathpar>
```haskell
data List a
= Nil
| Cons a (List a)
map :: (a -> b) -> List a -> List a
-- this line intentionally left blank
map f (Cons x xs) = Cons (f x) (map f xs)
map f Nil = Nil
```
```haskell
data List :: * -> * where
Nil :: forall a. List a
Cons :: forall a. a -> List a -> List a
map :: forall a b. (a -> b) -> List a -> List a
map @a @b f x = case x of
Cons x xs -> Cons @b (f x) (map @a @b f xs)
Nil -> Nil @b
```
</div>
Let's go over the differences:
* In Haskell, we allow datatype declarations using the Haskell 98 syntax, but in $F_C$ all data types are given in GADT syntax. Furthermore, `List`{.haskell} was given a kind annotation when it was elaborated - the kind of `List`{.haskell} says it maps ground types to ground types. By "ground type" I mean something that's potentially inhabited, e.g. `Int`{.haskell} or `Void`{.haskell}, but not `Maybe`.
Where does the kind annotation come from? Well, we know `List` will have a function kind since it has one argument, and we know its return kind will be `*`{.haskell} since all data types are in `*`{.haskell}. That means we kind-check the constructors with `List :: κ -> a`{.haskell} in scope, where `κ` is a fresh metavariable. The type of `Nil`{.haskell} doesn't fix `κ`, but the type of `Cons`{.haskell} - `a` is used on the left of an arrow, so it must have kind `*`.
* Haskell has definition by equations, but in $F_C$ we simply have type signatures and definitions. We can translate the equations into a case tree using a rather involved - but mechanical - process, and, to avoid that complication, the subset of Haskell our elaborator works will not support equations. It's mostly immaterial to elaboration, anyway.
* In Haskell, the type signature `map :: (a -> b) -> List a -> List b`{.haskell} is written with implicit binders for the type variables `a` and `b`, so that they're seemingly free. This is not the case, of course, and so in $F_C$ we must write out what `forall`{.haskell}s we mean. This is less relevant in this case, where there are no free type variables in the environment, but specifying `forall`{.haskell}s is essential when we have `ScopedTypeVariables`.
* Finally, all of the polymorphism implicit in the Haskell version of the program was made explicit in its elaboration into $F_C$. For instance, the type of the `map`{.haskell} function has two `forall`{.haskell}s, so its definition must begin with a corresponding number of `\@`{.haskell}s (which I moved onto the RHS for presentation purposes - don't want lines getting too wide).
Similarly, the list `Cons`{.haskell}tructors were used as expressions of type `List a` in Haskell, but their $F_C$ types start with a `forall`{.haskell}, meaning we have to instantiate them - `Nil @b`{.haskell}, `Cons @b`{.haskell} - at the return type of the `map` function.
We represent the language using a data type. Syntax productions in the language become constructors of our data type. For clarity of presentation, I'll use `Text`{.haskell}s for variable names. This is a bad idea, and it'll make a lot of you very angry - for good reason! Dealing with binders is _hard_, and using strings for identifiers is quite possibly the worst solution. It'd be more principled to use de Bruijn indices, or locally nameless, or something. But - that's a needless complication, so, in the interest of clarity, I'll just use strings.
Since our language contains type applications, we "need" to define types before expressions. Well, this is a Haskell program, so we don't _need_ to - Haskell programs are not lists of definitions, but rather _directed graphs_ of definitions, so that source order doesn't matter - but for clarity, we define the type of types before the type of expressions.
```haskell
module Core where
import qualified Data.Text as T
import Data.Text (Text)
data Kind
= TypeKi
-- ^ The kind '*'
| ConstraintKi
-- ^ The kind 'Constraint'
| FunKi Kind Kind
-- ^ κ → κ
deriving (Eq, Show)
data Type
= VarTy Text Kind
-- ^ Type variables α
| AppTy Type Type
-- ^ The type being applied is never a constructor,
-- always another AppTy or a VarTy.
| ConTy Text [Type]
-- ^ Type constructor applied to some arguments.
| ForAllTy Text Kind Type
-- ^ Polymorphic types
| FunTy Type Type
-- ^ Function types
deriving (Eq, Show)
```
Throughout the language, variables (resp. type variables) are annotated with the type (resp. kind) with which they are introduced. More, our type of expressions unifies `\ @a`{.haskell} and `\ x`{.haskell}, as well as both application forms, by delegating to `Binder`{.haskell} and `Arg`{.haskell} types.
```{.haskell style="padding-bottom: 0"}
data Binder = TypeBinder Text | ExprBinder Text
deriving (Eq, Show)
data Arg = TypeArg Type | ExprArg Expr
deriving (Eq, Show)
data Expr
= Var Text Type
| App Expr Arg
| Lam Binder Expr
-- continues
```
For `Let`{.haskell}, we introduce yet another auxiliary type. A `Bind`{.haskell} represents a _binding group_, a group of mutually recursive definitions. Binding groups do not correspond 1:1 with `let`{.haskell}s in Haskell, for instance, the Haskell program on the left is elaborated into the Core expression on the right:
<div class="mathpar">
```haskell
let quux x = bar (x - 1)
foo = 1
bar x = quux x + foo
in foo
```
```haskell
Let (NonRec "foo" (Lit 1)) $
Let (Rec [ ("quux", Lam (ExprBinder ...) ...
, ("bar", Lam (ExprBinder ...) ...) ] $
Var "foo"
```
</div>
As you can probably imagine, the way I arrived at this definition involves.. Graphs. Yes, it's unfortunate, but it's the only way to correctly describe how Haskell declaration blocks - that includes the top level - are type checked. The Haskell report mandates that declaration groups - in the top level, a `let`{.haskell} expression or a `where`{.haskell} clause - should be sorted into strongly connected components, and type-checked in dependency order. Each of these connected components becomes a `Rec`{.haskell} binding!
We define the auxiliary `Bind`{.haskell} type.. somewhere else, since we still have cases to add to the `Expr`{.haskell}. It's either a connected graph of mutually recursive binders, containing a list of pairs of names and expressions, or a single binder - in which case we unpack the pair.
```{.haskell style="padding-top: 0; padding-bottom: 0;"}
-- continued
| Let [Bind] Expr
data Bind
= NonRec Text Expr
| Rec [(Text, Expr)]
deriving (Eq, Show)
-- continues
```

+ 0
- 232
rubtmpjd3t_2bh.log View File

@ -1,232 +0,0 @@
This is pdfTeX, Version 3.141592653-2.6-1.40.22 (TeX Live 2021/Arch Linux) (preloaded format=pdflatex 2021.5.25) 26 AUG 2021 19:58