Skip to content

Commit 089bed9

Browse files
committed
hm
1 parent 9ffd8f1 commit 089bed9

File tree

3 files changed

+56
-67
lines changed

3 files changed

+56
-67
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "\"Five Point Haskell\" Part 1: Total Depravity"
2+
title: "\"Five Point Haskell\": Total Depravity (Defensive Typing)"
33
categories: Haskell
44
tags: functional programming, type safety
55
create-time: 2025/12/26 15:01:46
Lines changed: 9 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "\"Five Point Haskell\" Part 2: Unconditional Election (Parametric Polymorphism)"
2+
title: "\"Five Point Haskell\": Unconditional Election (Parametric Polymorphism)"
33
categories: Haskell
44
tags: functional programming, parametric polymorphism
55
create-time: 2026/01/01 21:51:17
@@ -18,21 +18,19 @@ ran into in my time.
1818
In the last post, we talked about [Total Depravity][], which is about treating
1919
any mentally-tracked constraint or condition as inevitably leading to a
2020
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.
2321

2422
[Total Depravity]: https://blog.jle.im/entry/five-point-haskell-part-1-total-depravity.html
2523

26-
However, picking good structures, making invalid states unrepresentable, etc.
27-
can only go so far. They are, in the end, tools of human designs and human
28-
flaws.
24+
However, stopping here gives us an incomplete picture. Firstly, types aren't
25+
just about preventing bad behaviors. They're about designing good code.
26+
Secondly, there is only so much you can do by picking good structures and
27+
making invalid states unrepresentable. Human tools and human flaws.
2928

30-
Types aren't just about preventing bad behaviors. They're about designing good
31-
code. And there is one principle that helps guide this design by leveraging the
29+
The next point, to me, is about an aspect of the type system that I see little
30+
coverage of outside of Haskell and typed FP circles, but is a principle of
31+
design that I find permeating everything I write. It's about leveraging the
3232
unyielding properties of the universe _itself_ to take care of our fate, even
33-
when we are unable to structure our types well.
34-
35-
Let's jump into the second point in five-point Haskell: **Unconditional
33+
when we are unable to structure our types well. It's **Unconditional
3634
Election**!
3735

3836
> Unconditional Election: The power of the `forall` to elect or reprobate
@@ -46,58 +44,3 @@ Election**!
4644
> things. Embrace one of Haskell's greatest unexpected strengths: the type
4745
> parameter.
4846
49-
<!-- > Unconditional Election: The _structure_ of your types fully determine the -->
50-
<!-- > values and states it will ever take. Nothing at runtime can ever circumvent -->
51-
<!-- > this. -->
52-
<!-- > -->
53-
<!-- > Therefore, take advantage and design the structure of your _types_ to -->
54-
<!-- > anticipate the logic you want to model. The program is pre-destined before -->
55-
<!-- > you even write any functions. -->
56-
57-
58-
<!-- ### Squished Pipeline -->
59-
60-
<!-- Along the same lines, there is often the temptation to squish multiple stages -->
61-
<!-- along a pipeline into the same type. -->
62-
63-
<!-- For example, your "checkout" workflow might incrementally set `Maybe` fields: -->
64-
65-
<!-- ```haskell -->
66-
<!-- data Checkout = Checkout -->
67-
<!-- { items :: [Item] -->
68-
<!-- , address :: Maybe Address -->
69-
<!-- , payment :: Maybe Token -->
70-
<!-- } -->
71-
<!-- ``` -->
72-
73-
<!-- You start with an empty `Checkout` state...then you add `[Item]`s...then you -->
74-
<!-- add `Maybe Address`...then you add `Maybe Token` for payment. However, payment -->
75-
<!-- requires an address: -->
76-
77-
<!-- ```haskell -->
78-
<!-- pay :: Checkout -> IO Checkout -->
79-
<!-- pay c = case address c of -->
80-
<!-- Just addr -> do -->
81-
<!-- tok <- processPayment (items c) addr -->
82-
<!-- pure $ c { payment = Just tok } -->
83-
<!-- Nothing -> -- uh.... -->
84-
<!-- ``` -->
85-
86-
<!-- `pay` doesn't _really_ take a `Maybe Address`, it requires an actual `Address`! -->
87-
<!-- Its input type is too "big". This is a subtle manifestation of the same -->
88-
<!-- problems as shotgun parsing: there is no indication in the type about the -->
89-
<!-- actual stage it is in and what operations can legally be done. -->
90-
91-
<!-- To fix this, we can just...not keep them all as the same type. -->
92-
93-
<!-- ```haskell -->
94-
<!-- data PreCheckout = PreCheckout [Item] -->
95-
<!-- data PrePayment = PrePayment [Item] Address -->
96-
<!-- data PostPayment = PostPayment [Item] Address Token -->
97-
98-
<!-- pay :: PrePayment -> IO PostPayment -->
99-
<!-- pay (PrePayment items addr) = do -->
100-
<!-- tok <- processPayment items addr -->
101-
<!-- pure $ PostPayment items addr tok -->
102-
<!-- ``` -->
103-

copy/entries/five-point-haskell.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,49 @@ Idea: Benefits of Immutability
216216
<!-- * Property tests to help it all out -->
217217

218218

219+
<!-- ### Squished Pipeline -->
220+
221+
<!-- Along the same lines, there is often the temptation to squish multiple stages -->
222+
<!-- along a pipeline into the same type. -->
223+
224+
<!-- For example, your "checkout" workflow might incrementally set `Maybe` fields: -->
225+
226+
<!-- ```haskell -->
227+
<!-- data Checkout = Checkout -->
228+
<!-- { items :: [Item] -->
229+
<!-- , address :: Maybe Address -->
230+
<!-- , payment :: Maybe Token -->
231+
<!-- } -->
232+
<!-- ``` -->
233+
234+
<!-- You start with an empty `Checkout` state...then you add `[Item]`s...then you -->
235+
<!-- add `Maybe Address`...then you add `Maybe Token` for payment. However, payment -->
236+
<!-- requires an address: -->
237+
238+
<!-- ```haskell -->
239+
<!-- pay :: Checkout -> IO Checkout -->
240+
<!-- pay c = case address c of -->
241+
<!-- Just addr -> do -->
242+
<!-- tok <- processPayment (items c) addr -->
243+
<!-- pure $ c { payment = Just tok } -->
244+
<!-- Nothing -> -- uh.... -->
245+
<!-- ``` -->
246+
247+
<!-- `pay` doesn't _really_ take a `Maybe Address`, it requires an actual `Address`! -->
248+
<!-- Its input type is too "big". This is a subtle manifestation of the same -->
249+
<!-- problems as shotgun parsing: there is no indication in the type about the -->
250+
<!-- actual stage it is in and what operations can legally be done. -->
251+
252+
<!-- To fix this, we can just...not keep them all as the same type. -->
253+
254+
<!-- ```haskell -->
255+
<!-- data PreCheckout = PreCheckout [Item] -->
256+
<!-- data PrePayment = PrePayment [Item] Address -->
257+
<!-- data PostPayment = PostPayment [Item] Address Token -->
258+
259+
<!-- pay :: PrePayment -> IO PostPayment -->
260+
<!-- pay (PrePayment items addr) = do -->
261+
<!-- tok <- processPayment items addr -->
262+
<!-- pure $ PostPayment items addr tok -->
263+
<!-- ``` -->
264+

0 commit comments

Comments
 (0)