Skip to content

Commit 0fc866c

Browse files
committed
more
1 parent 1985774 commit 0fc866c

File tree

1 file changed

+91
-11
lines changed

1 file changed

+91
-11
lines changed

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

Lines changed: 91 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,16 @@ popular on Twitter --- "heresies", if you may permit the terminology.
2424

2525
[Five-Point Haskell]: https://blog.jle.im/entries/series/+five-point-haskell.html
2626

27-
Let's jump right into point 1: the doctrine of **Total Depravity**.
27+
Let's jump right into point 1: the doctrine of **Total Depravity**, and why
28+
Haskell is perfectly primed to make living with it as frictionless as possible.
2829

2930
> Total Depravity: If your code's correctness depends on keeping complicated
3031
> interconnected structure in your head, a devastating incident is not a matter
3132
> of _if_ but _when_.
3233
>
33-
> Therefore, delegate these concerns to the compiler and tooling, express
34-
> conditions in your types, and free yourself to only mentally track the actual
35-
> important things.
34+
> Therefore, delegate these concerns to tooling and a sufficiently powerful
35+
> compiler, use types to guard against errors, and free yourself to only
36+
> mentally track the actual important things.
3637
3738
Mix-ups Will Happen
3839
-------------------
@@ -55,6 +56,9 @@ This is the myth of the hero programmer. Did you have a bug? Well, you just
5556
need to upgrade your mental awareness and your context window. You just need to
5657
be better and better at keeping more in your mind.
5758

59+
Admittedly, actually _addressing_ these issues in most languages requires a lot
60+
of overhead and clunkiness. But luckily we're in Haskell!
61+
5862
### ID Mix-ups
5963

6064
The [2022 Atlassian Outage][atlassian], fundamentally, was the result of
@@ -110,18 +114,61 @@ Knowing this can happen, we can add a simple newtype wrapper so that
110114
accidentally using the wrong ID is a compile error:
111115

112116
```haskell
113-
newtype SiteId = SiteId Id
114-
newtype AppId = AppId Id
117+
newtype SiteId = SiteId String
118+
newtype AppId = AppId String
119+
120+
instance ToJSON SiteId where
121+
toJSON (SiteId x) = object [ "type" .= "Site", id" .= x ]
122+
123+
instance FromJSON SiteId where
124+
parseJSON = withObject "Id" $ \v ->
125+
tag <- v .: "type"
126+
unless (tag == "Site") $
127+
fail "Parsed wrong type of ID!"
128+
SiteId <$> (v .: "id")
129+
130+
instance ToJSON AppId where
131+
toJSON (AppId x) = object [ "type" .= "App", id" .= x ]
132+
133+
instance FromJSON AppId where
134+
parseJSON = withObject "Id" $ \v ->
135+
tag <- v .: "type"
136+
unless (tag == "App") $
137+
fail "Parsed wrong type of ID!"
138+
AppId <$> (v .: "id")
115139
```
116140

117141
And now such a mis-call will never compile! Congratulations!
118142

119143
We did have a downside now: we can no longer write code polymorphic over Id's
120144
when we want to. In the untyped situation, we could _only_ write polymorphic
121-
code, and in the new situation we can _only_ write code for one Id type. In
122-
some cases, we would like the ability to choose to do one or the other and get
123-
the best of both worlds. We can get this by using _phantom types_, types that
124-
don't refer to anything inside the actual data representation:
145+
code, and in the new situation we can _only_ write code for one Id type.
146+
147+
```haskell
148+
instance FromJSON SiteId where
149+
parseJSON = withObject "Id" $ \v ->
150+
tag <- v .: "type"
151+
unless (tag == "Site") $
152+
fail "Parsed wrong type of ID!"
153+
SiteId <$> (v .: "id")
154+
155+
instance ToJSON SiteId where
156+
toJSON (SiteId x) = object [ "type" .= "Site", id" .= x ]
157+
158+
instance FromJSON AppId where
159+
parseJSON = withObject "Id" $ \v ->
160+
tag <- v .: "type"
161+
unless (tag == "App") $
162+
fail "Parsed wrong type of ID!"
163+
AppId <$> (v .: "id")
164+
165+
instance ToJSON AppId where
166+
toJSON (AppId x) = object [ "type" .= "App", id" .= x ]
167+
```
168+
169+
However, luckily, because we're in Haskell, it's easy to get the best of both
170+
worlds with _phantom types_ (that don't refer to anything inside the actual
171+
data representation):
125172

126173
```haskell
127174
data Id a = Id { getId :: String }
@@ -657,7 +704,40 @@ flawed abstractions, you can actually think about your business logic, the flow
657704
of your program, and architecting that castle of beauty I know you are capable
658705
of.
659706

660-
Tune in next time as we discuss the next principle of [Five-Point
707+
### The LLM Elephant
708+
709+
Okay, let's address the elephant in the room. We're writing this in 2026, in
710+
the middle of one of the biggest revolutions in software engineering in the
711+
history of the field. How relevant will these issues be in the age of LLMs and
712+
agentic coding?
713+
714+
I don't have too much to say here, except that the fundamental issue being
715+
addressed here exists both in LLMs and humans: the limited "context window" and
716+
attention. Humans might be barely able to keep a dozen things in our heads,
717+
LLMs might be able to keep a dozen dozen things, but it will still be
718+
fundamentally finite. So, the more we can move concerns out of our context
719+
window (be it biological or computational), the less crowded our context
720+
windows will be, and the more productive we will be.
721+
722+
I'm not sure how quickly LLM-based agentic coding will progress, but I am sure
723+
that the accidental "dropping" of concerns will continue to be a bottleneck. If
724+
anything, it might be _the_ bottleneck. If we can provide LLMs with properly
725+
"depravity-aware" typed code --- or somehow encourage them to write it by
726+
giving them the right iterative tooling --- I (maybe naively) believe this
727+
might be the key to unlocking the full potential of agentic coding.
728+
729+
### The Next Step
730+
731+
Total depravity is all about using types to _prevent errors_. However, most
732+
discussions about the power of types stop here. Oh, how boring types would be
733+
if this was all they gave us!
734+
735+
Types are also a power tool for _program design_, helping you guide the
736+
creation of your abstractions and pushing you to more expressive and robust
737+
designs. They aren't just for preventing bad code, they're for opening your
738+
mind to new avenues of design that were impossible before.
739+
740+
Let's explore this further in the next principle of [Five-Point
661741
Haskell][Five-Point Haskell], **Unconditional Election**!
662742

663743
Special Thanks

0 commit comments

Comments
 (0)