Skip to content

Commit a41dccf

Browse files
committed
more changes
1 parent bc934d2 commit a41dccf

File tree

5 files changed

+138
-9
lines changed

5 files changed

+138
-9
lines changed

copy/entries/five-point-haskell-1.md

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ With a new age of software development coming, what does it even mean to write
1818
good, robust, correct code? It is long overdue to clarify exactly the mindset
1919
on which we approach and define "good" coding principles.
2020

21-
In this series, [*Five-Point Haskell*][Five-Point Haskell], let's set out to
22-
establish a five-point unified framework of the "Typed Functional Programming"
23-
(and Haskell-derived) programming philosophy aimed to create code that is
21+
In this series, *[Five-Point Haskell][]*, let's set out to establish a
22+
five-point unified framework of the "Typed Functional Programming" (and
23+
Haskell-derived) programming philosophy aimed to create code that is
2424
maintainable, correct, long-lasting, extensible, and beautiful to write and
2525
work with. We'll try to reference real world case studies with and actual
2626
examples when we can, and also attempt to dispel thought leader sound-bytes
@@ -338,10 +338,25 @@ and has been the source of many frustrating bug hunts.
338338
[sock_sendpage]: https://www.rapid7.com/db/modules/exploit/linux/local/sock_sendpage/
339339
[gcp]: https://www.thousandeyes.com/blog/google-cloud-outage-analysis-june-12-2025
340340

341-
Why do we do this to ourselves? Because it is convenient. It's not easy to make
342-
a "integer or not found" type in C or javascript without some sort of
343-
side-channel. Imagine if javascript's `String.indexOf()` instead expected
344-
continuations on success and failure and became much less usable as a result:
341+
There's also [CVE-2008-5077][], because [EVP_VerifyInit][] returns `0` for
342+
false, `1` for true, and `-1` for error! So some OpenSSL code did a simple
343+
if-then-else check (`result != 0`) and treated error and true the same way.
344+
Whoops.
345+
346+
[CVE-2008-5077]: https://www.invicti.com/web-application-vulnerabilities/openssl-improper-input-validation-vulnerability-cve-2008-5077
347+
[EVP_VerifyInit]: https://docs.openssl.org/1.1.1/man3/EVP_VerifyInit/
348+
349+
Why do we do this to ourselves? Because it is convenient. In the case of
350+
`EVP_VerifyInit`, we can define an enum instead...
351+
352+
```haskell
353+
data VerifyResult = Success | Failure | Error
354+
```
355+
356+
However, it's not easy to make a "integer or not found" type in C or javascript
357+
without some sort of side-channel. Imagine if javascript's `String.indexOf()`
358+
instead expected continuations on success and failure and became much less
359+
usable as a result:
345360

346361
```haskell
347362
unsafeIndexOf :: String -> String -> Int
@@ -546,6 +561,54 @@ getUser conn uid = do
546561
Pushing it to the driver level will also unify everything with the driver's
547562
error-handling system.
548563

564+
### Boolean Blindness
565+
566+
At the heart of it, the previous examples' cardinal sin was "boolean
567+
blindness". If we have a predicate like `validUsername :: String -> Bool`, we
568+
will branch on that `Bool` once and throw it away. Instead, by having a
569+
function like `mkUsername :: String -> Maybe Username`, we _keep_ the proof
570+
alongside the value for the entire lifetime of the value. We basically pair
571+
the string with its proof forever, making them inseparable.
572+
573+
There was another example of such a thing earlier: instead of using `null ::
574+
[a] -> Bool` and gating a call to `mean` with `null`, we instead use
575+
`nonEmpty :: [a] -> Maybe (NonEmpty a)`, and pass along the proof of
576+
non-emptiness alongside the value itself. And, for the rest of that list's
577+
life, it will always be paired with its non-emptiness proof.
578+
579+
Embracing total depravity means always keeping these proofs together, with the
580+
witnesses bundled with the value itself, because if you don't, someone is going
581+
to assume it exists when it doesn't, or drop it unnecessarily.
582+
583+
Boolean blindness also has another facet, which is where `Bool` itself is not a
584+
semantically meaningful type. This is "semantic boolean blindness".
585+
586+
The classic example is `filter :: (a -> Bool) -> [a] -> [a]`. It might sound
587+
silly until it happens to you, but it is pretty easy to mix up if `True` means
588+
"keep" or "discard". After all, a "water filter" only lets water through, but a
589+
"profanity filter" only rejects profanity. Instead, how about `mapMaybe :: (a
590+
-> Maybe b) -> [a] -> [b]`? In that case, it is clear that `Just` results are
591+
kept, and the `Nothing` results are discarded.
592+
593+
Sometimes, the boolean is ambiguous to what it means. You can sort of interpret
594+
the [1999 Mars Polar Lander][polar] crash this way. Its functions took a
595+
boolean based on the state of the legs:
596+
597+
[polar]: https://en.wikipedia.org/wiki/Mars_Polar_Lander
598+
599+
```haskell
600+
deployThrusters :: Bool -> IO ()
601+
```
602+
603+
and `True` and `False` were misinterpreted. Instead, they could have considered
604+
semantically meaningful types: (simplified)
605+
606+
```haskell
607+
data LegState = Extended | Retracted
608+
609+
deployThrustrs :: LegState -> IO ()
610+
```
611+
549612
### Resource Cleanup
550613

551614
Clean-up of finite system resources is another area that is very easy to assume

copy/entries/five-point-haskell-2.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,41 @@
11
---
22
title: "\"Five Point Haskell\" Part 2: Unconditional Election"
33
categories: Haskell
4-
tags: functional programming, type safety
4+
tags: functional programming, type driven development
55
create-time: 2026/01/01 21:51:17
66
identifier: five-point-haskell-2
77
slug: five-point-haskell-part-2-unconditional-election
88
series: five-point-haskell
99
---
1010

11+
Welcome back to my series, *[Five-Point Haskell][]*! This is my attempt to
12+
codify principles of writing robust, maintainable, correct, clear, and
13+
effective code in Haskell and to dispel common bad practices or heresies I have
14+
ran into in my time.
15+
16+
[Five-Point Haskell]: https://blog.jle.im/entries/series/+five-point-haskell.html
17+
18+
In the last post, we talked about [Total Depravity][], which is about treating
19+
any mentally-tracked constraint or condition as inevitably leading to a
20+
catastrophe and denouncing the reliance on our flawed mental context windows.
21+
Embracing the doctrine of total depravity helps us eliminate bugs and potential
22+
errors through our types.
23+
24+
[Total Depravity]: https://blog.jle.im/entry/five-point-haskell-part-1-total-depravity.html
25+
26+
But, types don't only prevent bugs: they also help us in the process of
27+
designing and structure our programs! So, let's jump right into our second
28+
point in five-point Haskell: **Unconditional Election**!
29+
30+
> Unconditional Election: The _structure_ of your types fully determine the
31+
> values and states it will ever take. Nothing at runtime can ever circumvent
32+
> this.
33+
>
34+
> Therefore, take advantage and design the structure of your _types_ to
35+
> anticipate the logic you want to model. The program is pre-destined before
36+
> you even write any functions.
37+
38+
1139
<!-- ### Squished Pipeline -->
1240

1341
<!-- Along the same lines, there is often the temptation to squish multiple stages -->
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
title: "\"Five Point Haskell\" Part 3: Limited Atonement"
3+
categories: Haskell
4+
tags: functional programming, type safety
5+
create-time: 2026/01/03 16:17:50
6+
identifier: five-point-haskell-3
7+
slug: five-point-haskell-part-3-limited-atonement
8+
series: five-point-haskell
9+
---
10+
11+
> Limited Atonement: The goal in Haskell is not "universal purity", but the
12+
> correct partition of pure and impure.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
title: "\"Five Point Haskell\" Part 4: Irresistible Grace"
3+
categories: Haskell
4+
tags: functional programming, type safety
5+
create-time: 2026/01/03 16:37:16
6+
identifier: five-point-haskell-4
7+
slug: five-point-haskell-part-4-irresistible-grace
8+
series: five-point-haskell
9+
---
10+
11+
> Irresistible Grace: When you set up your type safety correctly, the compiler
12+
> forces you to handle all things appropriately. Create irresistible contracts,
13+
> and consumers are forced to be safe. Also mention auto-deriving instances
14+
> somehow I guess.
15+

copy/entries/five-point-haskell.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,19 @@ Theme: "Postmortems" of real world accidents, programming gore.
110110
* Shotgun validation/parser inside database, accidentally save unvalidated
111111
data
112112

113+
TODO:
114+
115+
* Boolean blindness
116+
* Crash early: you're not going to catch every exception. don't use
117+
`SomeException` either.
118+
113119
Unconditional Election
114120
----------------------
115121

116122
Idea: The choice of the type's structure will fully determine the values
117123
allowed. Bad states are unrepresentable.
118124

119-
Theme: Parse, don't validate type things
125+
Theme: Type-Driven Development, Prase Don't Validate
120126

121127
* Boolean blindness/multiple Maybe issues
122128
* NonEmpty lists
@@ -125,6 +131,8 @@ Theme: Parse, don't validate type things
125131
* Higher-kinded data
126132
* Sized vectors
127133

134+
* Can we do parametric polymorphism here?
135+
128136
Limited Atonement
129137
-----------------
130138

@@ -151,6 +159,9 @@ Irresistible Grace
151159
Idea: When you set up your type-safety correctly, the compiler forces you to
152160
handle things appropriately
153161

162+
Distinction from Unconditional election: happens when writing functions, not
163+
when writing types. I guess?
164+
154165
Theme: Sum type branches, GADTs and witnsses, handler based programming,
155166
church encodings?
156167

0 commit comments

Comments
 (0)