11---
2- title : " Extreme Haskell: Typed Lambda Calculus"
2+ title : " Extreme Haskell: Typed State Machines with Typed Lambda Calculus"
33categories : Haskell
44tags : functional programming, dependent types, haskell, singletons, types
55create-time : 2026/02/07 12:30:55
6- identifier : typed-lc-1
7- slug : extreme-haskell-typed-lambda-calculus
6+ identifier : typed-sm-lc
7+ slug : extreme-haskell-typed-state-machines-with-typed- lambda-calculus
88---
99
1010I always say, inside every Haskeller there are two wolves, living on both ends
@@ -59,10 +59,10 @@ monstrosity we are about to explore in this post?
5959All of the code here is [ available online] [ code samples ] , and if you check out
6060the repo and run ` nix develop ` you should be able to load it all in ghci:
6161
62- !!![ code samples] : typed-lc /flake.nix
62+ !!![ code samples] : typed-sm- lc /flake.nix
6363
6464``` bash
65- $ cd code-samples/typed-lc
65+ $ cd code-samples/typed-sm- lc
6666$ nix develop
6767$ ghci
6868ghci> :load Stage1.hs
7979One basic thing we can do is start with:
8080
8181``` haskell
82- !!! typed- lc/ Stage1. hs " data Prim" " data Op" " data Expr"
82+ !!! typed- sm - lc/ Stage1. hs " data Prim" " data Op" " data Expr"
8383```
8484
8585And you can write ` (\x -> x * 3) 5 ` as:
8686
8787
8888``` haskell
89- !!! typed- lc/ Stage1. hs " fifteen ::"
89+ !!! typed- sm - lc/ Stage1. hs " fifteen ::"
9090```
9191
9292You can definitely easily render this in a graph, but what happens when you
@@ -95,7 +95,7 @@ What would the type even be? `eval :: Expr -> Maybe Prim`? Maybe just
9595` normalize :: Expr -> Expr ` and hope that the result is ` Prim ` ?
9696
9797``` haskell
98- !!! typed- lc/ Stage1. hs " normalize ::"
98+ !!! typed- sm - lc/ Stage1. hs " normalize ::"
9999```
100100
101101This would properly evaluate:
@@ -112,7 +112,7 @@ But this isn't type-safe...we have undefined branches still. We could make the
112112entire thing monadic by returning ` Maybe ` :
113113
114114``` haskell
115- !!! typed- lc/ Stage2. hs " normalize ::"
115+ !!! typed- sm - lc/ Stage2. hs " normalize ::"
116116```
117117
118118This kind of works if you remember to thread everything through ` Maybe ` (or
@@ -137,7 +137,7 @@ The next step you'll see in posts online is to add a phantom index type to
137137` Expr ` :
138138
139139``` haskell
140- !!! typed- lc/ Stage3. hs " type data Ty" " data STy" " data Prim" " data Op" " data Expr"
140+ !!! typed- sm - lc/ Stage3. hs " type data Ty" " data STy" " data Prim" " data Op" " data Expr"
141141```
142142
143143Here we use ` -XTypeData ` to define a data kind, ` Ty ` is a kind with types
@@ -148,7 +148,7 @@ domain's `Bool`, or our domain's `String`. At least, now, it is impossible to
148148create an ` Expr ` that doesn't type check:
149149
150150``` haskell
151- !!! typed- lc/ Stage3. hs " fifteen ::"
151+ !!! typed- sm - lc/ Stage3. hs " fifteen ::"
152152```
153153
154154We also need a [ singleton] [ ] for our ` Ty ` type, ` STy ` ...this makes a whole lot
@@ -167,7 +167,7 @@ we still have issues here.
167167But now at least we can write ` eval ` :
168168
169169``` haskell
170- !!! typed- lc/ Stage3. hs " data EValue" " data SomeValue" " sameTy ::" " eval ::"
170+ !!! typed- sm - lc/ Stage3. hs " data EValue" " data SomeValue" " sameTy ::" " eval ::"
171171```
172172
173173What did we gain here? We have a type-safe ` eval ` now that will create a
@@ -189,7 +189,7 @@ environment.
189189We'll have:
190190
191191``` haskell
192- !!! typed- lc/ Stage4. hs " data Expr ::" 1 " type (:::)"
192+ !!! typed- sm - lc/ Stage4. hs " data Expr ::" 1 " type (:::)"
193193```
194194
195195So a value of type ` Expr '["x" ::: TInt, "y" ::: TBool] ` is an expression with
@@ -200,14 +200,14 @@ an `Expr` of a function type: (and `KnownSymbol` instance so that we can debug
200200print the variable name)
201201
202202``` haskell
203- !!! typed- lc/ Stage4. hs " ELambda ::"
203+ !!! typed- sm - lc/ Stage4. hs " ELambda ::"
204204```
205205
206206So how do we implement ` Var ` ? We have to gate it on whether or not the free
207207variable is available in the environment. For that, we can use ` Index ` :
208208
209209``` haskell
210- !!! typed- lc/ Stage4. hs " data Index ::"
210+ !!! typed- sm - lc/ Stage4. hs " data Index ::"
211211```
212212
213213I have this in [ functor-products] [ ] , but it's also ` CoRec Proxy ` from [ vinyl] [ ]
@@ -225,15 +225,15 @@ b`, and `IS (IS IZ) :: Index '[a,b,c] c`. So, if we require `Var` to take an
225225variable list and at that given index:
226226
227227``` haskell
228- !!! typed- lc/ Stage4. hs " EVar ::"
228+ !!! typed- sm - lc/ Stage4. hs " EVar ::"
229229```
230230
231231So it is legal to have ` EVar IZ :: Expr '["x" ::: TInt, "y" ::: TBool] TInt ` , and
232232also it is automatically inferred to be a ` TInt ` . But we could _ not_ write
233233` EVar IZ :: Expr '[] TInt ` .
234234
235235``` haskell
236- !!! typed- lc/ Stage4. hs " data Expr ::" " eLambda ::" " fifteen ::"
236+ !!! typed- sm - lc/ Stage4. hs " data Expr ::" " eLambda ::" " fifteen ::"
237237```
238238
239239In GHC 9.12 we can write ` eLambda ` using ` RequiredTypeArguments ` and so can
@@ -252,5 +252,5 @@ these variables, and for this we can use `Rec` (from [vinyl][]) or `NP` from
252252[ sop-core] :
253253
254254``` haskell
255- !!! typed- lc/ Stage4. hs " data Rec" " indexRec ::" " eval ::"
255+ !!! typed- sm - lc/ Stage4. hs " data Rec" " indexRec ::" " eval ::"
256256```
0 commit comments