diff --git a/assets/sass/_content.scss b/assets/sass/_content.scss
index 4ec71e9..126d000 100644
--- a/assets/sass/_content.scss
+++ b/assets/sass/_content.scss
@@ -289,6 +289,9 @@ details {
summary {
cursor: pointer;
+ position: sticky;
+ top: 0;
+ background-color: $background-color;
}
}
@@ -334,6 +337,11 @@ table.table {
background-color: $tag-color;
padding: 2px 7px;
+ &.draft {
+ background-color: orange;
+ color: black;
+ }
+
.text {
text-decoration: none;
}
@@ -456,8 +464,18 @@ table.table {
details {
border: 1px solid lightgray;
- padding: 10px 30px;
+ // padding: 10px 30px;
font-size: 0.9rem;
+ padding: 0 30px;
+ line-height: 1.5;
+
+ summary {
+ padding: 10px 0;
+ }
+
+ &[open] summary {
+ border-bottom: 1px solid lightgray;
+ }
}
hr {
diff --git a/content/posts/2023-04-20-programmable-proofs.md b/content/posts/2023-04-20-programmable-proofs.md
new file mode 100644
index 0000000..3055971
--- /dev/null
+++ b/content/posts/2023-04-20-programmable-proofs.md
@@ -0,0 +1,248 @@
++++
+title = "Programmable Proofs"
+date = 2023-04-20
+tags = ["type-theory"]
+math = true
+draft = true
++++
+
+Today I'm going to convince you that data types in programming are the same as
+[_propositions_][proposition] in the logical sense. Using this, we can build an
+entire proof system on top of a programming language and verify mathematical
+statements purely with programs.
+
+To make this idea more approachable, I'm going to stick to using [Typescript]
+for the majority of my examples, to explain the concepts. I personally love the
+way this language tackles a type system, and even though there are certain
+things it lacks, I still find it generally useful and intuitive for talking
+about basic ideas.
+
+---
+
+Let's start with a very simple data type: a class! We can make new classes in
+Typescript like this:
+
+```ts
+class Cake {}
+```
+
+That's all there is to it! `Cake` is now a full-fledged data type. What does
+being a data type mean?
+
+- We can make some `Cake` like this:
+
+ ```ts
+ let cake = new Cake();
+ ```
+
+- We can use it in function definitions, like this:
+
+ ```ts
+ function giveMeCake(cake: Cake) { ... }
+ ```
+
+- We can put it in other types, like this:
+
+ ```ts
+ type Party = { cake: Cake, balloons: Balloons, ... };
+ ```
+
+Ok that's cool. But what about classes with no constructors?
+
+```ts
+class NoCake {
+ private constructor() {}
+}
+```
+
+In Typescript and many OO languages it's based on, classes are given
+public constructors by default. This lets anyone create an instance of the
+class, and get all the great benefits we just talked about above. But when we
+lock down the constructor, we can't just freely create instances of this class
+anymore.
+
+Why might we want to have private constructors?
+
+ Let's say you had integer user IDs, and in your application whenever you wrote
+ a helper function that had to deal with user IDs, you had to first check that
+ it was a valid ID:
+
+ ```ts
+ function getUsername(id: number) {
+ // First, check if id is even a valid id...
+ if (!isIdValid(id)) throw new Error("Invalid ID");
+
+ // Ok, now we are sure that id is valid.
+ ...
+ }
+ ```
+
+ It would be really nice if we could make a type to encapsulate this, so that
+ we know that every instance of this class was a valid id, rather than passing
+ around numbers and doing the check every time.
+
+ ```ts
+ function getUsername(id: UserId) {
+ // We can just skip the first part entirely now!
+ // Because of the type of the input, we are sure that id is valid.
+ ...
+ }
+ ```
+
+ You could probably imagine what an implementation of this type looks like:
+
+ ```ts
+ class UserId {
+ private id: number;
+
+ /** @throws {Error} */
+ constructor(id: number) {
+ // First, check if id is even a valid id...
+ if (!isIdValid(id)) throw new Error("Invalid ID");
+
+ this.id = number;
+ }
+ }
+ ```
+
+ This is one way to do it. But throwing exceptions from constructors is
+ typically bad practice. This is because constructors are meant to be the final
+ step in creating a particular object and should always return successfully and
+ not have side effects. So in reality, what we want is to put this into a
+ _static method_ that can create `UserId` objects:
+
+ ```ts
+ class UserId {
+ private id: number;
+ constructor(id: number) { this.id = id; }
+
+ /** @throws {Error} */
+ fromNumberChecked() {
+ // First, check if id is even a valid id...
+ if (!isIdValid(id)) throw new Error("Invalid ID");
+ return new UserId(id);
+ }
+ }
+ ```
+
+ But this doesn't work if the constructor is also public, because someone can
+ just **bypass** this check function and call the constructor anyway with an
+ invalid id! We need to limit the constructor, so that all attempts to create
+ a `UserId` instance goes through our function:
+
+ ```ts
+ class UserId {
+ private id: number;
+ private constructor(id: number) { this.id = id; }
+
+ /** @throws {Error} */
+ fromNumberChecked() {
+ // First, check if id is even a valid id...
+ if (!isIdValid(id)) throw new Error("Invalid ID");
+ return new UserId(id);
+ }
+ }
+ ```
+
+ Now we can rest assured that no matter what, if I get passed a `UserId`
+ object, it's going to be valid. This works for all kinds of validations, as
+ long as the validation is _permanent_. So actually in our example, a user
+ could get deleted while we still have `UserId` objects lying around, so the
+ validity would not be true. But if we've checked that an email is of the
+ correct form, for example, then we know it's valid forever.
+