Skip to content

Commit ed08a66

Browse files
author
Thomas Mahler
committed
add some comments to compiler
1 parent cacb498 commit ed08a66

File tree

1 file changed

+82
-7
lines changed

1 file changed

+82
-7
lines changed

src/CCC/Compiler.hs

Lines changed: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,26 @@
55

66
{-- | Compilation from lambda calculus expressions (Expr) and environments
77
to CatExpr categorical morphisms.
8-
9-
The compiler handles:
10-
- Variable lookup in environments
11-
- Integer constants
12-
- Lambda abstractions (converted to curried morphisms)
13-
- Function applications
8+
9+
Two compilation strategies are used:
10+
11+
1. Normalization by Evaluation (NBE) for closed/non-recursive terms.
12+
Terms are interpreted as 'SVal' in a Haskell semantic domain.
13+
Lambdas become Haskell closures; application is Haskell application.
14+
This implicitly encodes the CCC abstraction rules
15+
16+
absCCC (λx. x) = id
17+
absCCC (λx. c) = const c (x ∉ fv c)
18+
absCCC (λx. p q) = apply ∘ (absCCC (λx.p) △ absCCC (λx.q))
19+
20+
without building explicit Curry/Uncurry/Apply nodes —
21+
beta-reduction happens at the Haskell metalevel instead.
22+
23+
2. Direct CatExpr construction for recursive Fix bodies.
24+
Inside a Fix step, 'RVal c' carries CatExpr nodes indexed by a
25+
fixed context c = (recursive_fn, input_tuple), making tuple
26+
projections and composition explicit.
27+
1428
--}
1529

1630
module CCC.Compiler
@@ -25,8 +39,13 @@ import CCC.Cat (fanC)
2539
import CCC.Rewrite (simplify)
2640
import Parser (Environment, Expr (..))
2741

42+
-- A closed morphism: valid in any input context z, i.e. a global element z → a.
43+
-- RankN quantification ensures the expression is truly context-independent.
2844
newtype Closed a = Closed (forall z. CatExpr z a)
2945

46+
-- Semantic value domain for NBE compilation.
47+
-- SInt/SBool wrap closed morphisms (constants in the CCC sense).
48+
-- SFun is a Haskell-level function modelling an arrow without fixing a context type.
3049
data SVal
3150
= SInt (Closed Integer)
3251
| SBool (Closed Bool)
@@ -51,36 +70,66 @@ compileIntExpr env expr = do
5170
SInt compiled -> Right compiled
5271
_ -> Left "Expected integer expression"
5372

73+
-- Core lambda-to-CCC correspondence implemented by compileExpr:
74+
--
75+
-- Lambda term | CCC rule | Compiler mechanism
76+
-- n | absCCC (\x. n) = IntConst n | Int i -> Closed (IntConst i)
77+
-- x | absCCC (\x. x) = id | Var p -> lookup p localEnv returns same SVal
78+
-- y (free) | absCCC (\x. y) = const y | same lookup path, yielding a Closed morphism
79+
-- \x. e | curry (absCCC (\(x,y). e)) | Lam p body -> SFun (\v -> compile body[p:=v])
80+
-- f g | apply . (f' △ g') | applySVal (compile f) (compile g) at meta-level
81+
-- a ⊕ b | op . (a' △ b') | Comp op (fanC (compile a) (compile b))
82+
-- y (\f a1...an. b) | Fix step . (v1 △ ... △ vn) | compileYGeneric -> Fix with context c = (f, input)
83+
5484
compileExpr :: Environment -> [(String, SVal)] -> Expr -> Either String SVal
5585
compileExpr env localEnv = \case
86+
-- n ↦ IntConst n (absCCC (λx. n) = const n)
5687
Int i -> Right (SInt (Closed (IntConst i)))
88+
-- x ↦ the SVal bound to x
89+
-- absCCC (λx. x) = id — the arg passed in is returned unchanged
90+
-- absCCC (λx. y) = const y — a Closed morphism, independent of x
5791
Var name ->
5892
case lookup name localEnv of
5993
Just value -> Right value
6094
Nothing ->
6195
case lookup name env of
6296
Just expr -> compileExpr env localEnv expr
6397
Nothing -> compileBuiltin name
98+
-- y is compiled structurally to Fix rather than treated as a generic function
6499
App (Var "y") stepExpr -> compileY env localEnv stepExpr
100+
-- λx. e ↦ Haskell closure; beta-reduction is deferred to apply time (NBE)
101+
-- absCCC (λx. λy. e) = curry (absCCC (λ(x,y). e))
65102
Lam param body -> Right $ SFun $ \argVal -> compileExpr env ((param, argVal) : localEnv) body
103+
-- f g ↦ (compile f) applied to (compile g) at the Haskell level
104+
-- absCCC (λx. p q) = apply ∘ (absCCC (λx.p) △ absCCC (λx.q))
66105
App f x -> do
67106
fVal <- compileExpr env localEnv f
68107
xVal <- compileExpr env localEnv x
69108
applySVal fVal xVal
70109

110+
-- NBE application at the Haskell metalevel.
111+
-- Encodes apply ∘ (compile f △ compile x) without building CatExpr nodes;
112+
-- the result CatExpr is produced directly by the Haskell closure inside SFun.
71113
applySVal :: SVal -> SVal -> Either String SVal
72114
applySVal (SFun f) x = f x
73115
applySVal _ _ = Left "Cannot apply non-function value"
74116

117+
-- Context-indexed values for direct Fix-body compilation.
118+
-- Unlike SVal (which abstracts over context via universal quantification),
119+
-- RVal c carries CatExpr nodes for a fixed context c, so projections
120+
-- Fst/Snd can be composed explicitly to construct the step morphism.
75121
data RVal c
76122
= RInt (CatExpr c Integer)
77123
| RBool (CatExpr c Bool)
78124
| RFun (RVal c -> Either String (RVal c))
79125

126+
-- Encodes the arity shape of a recursive function as a type-level structure.
127+
-- n arguments map to the right-nested tuple type (Integer, (Integer, … Integer)).
80128
data IntArgs input where
81-
OneArg :: IntArgs Integer
129+
OneArg :: IntArgs Integer
82130
MoreArgs :: IntArgs rest -> IntArgs (Integer, rest)
83131

132+
-- Existential wrapper for IntArgs, used when arity is determined at runtime.
84133
data SomeIntArgs where
85134
SomeIntArgs :: IntArgs input -> SomeIntArgs
86135

@@ -116,6 +165,12 @@ compileYGeneric env outerLocal args fName params body = buildCurried args []
116165
SInt arg -> buildCurried rest (acc ++ [arg])
117166
_ -> Left "y expects Integer argument"
118167

168+
-- Encodes the Fix rule:
169+
-- y (λf a₁…aₙ. body) at inputs (v₁,…,vₙ)
170+
-- = Comp (Fix stepBody) (v₁ △ … △ vₙ)
171+
-- Context for stepBody is c = (CatExpr input Integer, input):
172+
-- f ↦ buildRecFun → Apply ∘ fanC Fst (a₁ △ … △ aₙ)
173+
-- aᵢ ↦ i-th projection of Snd (see argProjections)
119174
applyFix :: [Closed Integer] -> Either String (Closed Integer)
120175
applyFix actualArgs = do
121176
recFun <- buildRecFun args
@@ -129,19 +184,25 @@ compileYGeneric env outerLocal args fName params body = buildCurried args []
129184

130185
compileRecExpr :: Environment -> [(String, SVal)] -> [(String, RVal c)] -> Expr -> Either String (RVal c)
131186
compileRecExpr env outerLocal local = \case
187+
-- n ↦ IntConst n
132188
Int i -> Right (RInt (IntConst i))
189+
-- x ↦ the RVal projection bound to x (Snd, Fst∘Snd, Fst∘Snd∘Snd, …)
133190
Var name ->
134191
case lookup name local of
135192
Just v -> Right v
136193
Nothing -> compileEnvVar name
194+
-- λx. e ↦ Haskell closure (same NBE trick as compileExpr)
137195
Lam param expr -> Right (RFun (\arg -> compileRecExpr env outerLocal ((param, arg) : local) expr))
196+
-- if c t e ↦ IfVal ∘ (compile c △ (compile t △ compile e))
138197
App (App (App (Var "if") cond) thenExpr) elseExpr -> do
139198
condV <- compileRecExpr env outerLocal local cond
140199
thenV <- compileRecExpr env outerLocal local thenExpr
141200
elseV <- compileRecExpr env outerLocal local elseExpr
142201
case (condV, thenV, elseV) of
143202
(RBool c, RInt t, RInt e) -> Right (RInt (Comp IfVal (fanC c (fanC t e))))
144203
_ -> Left "if expects (Bool, Int, Int)"
204+
-- f g ↦ (compile f) `applyRVal` (compile g)
205+
-- RFun closures build Comp/fanC nodes, so apply ∘ (compile f △ compile g) emerges in output.
145206
App f x -> do
146207
fVal <- compileRecExpr env outerLocal local f
147208
xVal <- compileRecExpr env outerLocal local x
@@ -181,6 +242,8 @@ rIntUnary op = RFun $ \case
181242
RInt x -> Right (RInt (op x))
182243
_ -> Left "Expected integer argument"
183244

245+
-- Encodes: compile(a ⊕ b) = Comp op (fanC (compile a) (compile b))
246+
-- i.e. op ∘ (compile a △ compile b) :: CatExpr c Integer
184247
rIntBin :: CatExpr (Integer, Integer) Integer -> RVal c
185248
rIntBin op = RFun $ \left -> Right $ RFun $ \right ->
186249
case (left, right) of
@@ -214,6 +277,11 @@ expectRInt :: String -> RVal c -> Either String (CatExpr c Integer)
214277
expectRInt _ (RInt out) = Right out
215278
expectRInt msg _ = Left msg
216279

280+
-- Builds the RVal for the recursive function 'f' inside the Fix body.
281+
-- Context is c = (CatExpr input Integer, input), so:
282+
-- Fst :: c → CatExpr input Integer (the step function itself)
283+
-- Snd :: c → input (the argument tuple)
284+
-- A recursive call f a₁…aₙ is: Apply ∘ fanC Fst (a₁ △ … △ aₙ)
217285
buildRecFun :: forall input. IntArgs input -> Either String (RVal (CatExpr input Integer, input))
218286
buildRecFun args = build args []
219287
where
@@ -229,6 +297,10 @@ buildRecFun args = build args []
229297
RInt arg -> build rest (acc ++ [arg])
230298
_ -> Left "Recursive call expects Integer argument"
231299

300+
-- Extracts individual integer argument projections from a right-nested tuple.
301+
-- For OneArg: the tuple is the integer itself → [tupleExpr]
302+
-- For MoreArgs: first arg = Fst ∘ tuple, rest = projections on Snd ∘ tuple
303+
-- Called as 'argProjections args Snd', yielding: Snd, Fst∘Snd, Fst∘Snd∘Snd, …
232304
argProjections :: IntArgs input -> CatExpr c input -> [CatExpr c Integer]
233305
argProjections OneArg tupleExpr = [tupleExpr]
234306
argProjections (MoreArgs rest) tupleExpr = Comp Fst tupleExpr : argProjections rest (Comp Snd tupleExpr)
@@ -285,6 +357,9 @@ sIntUnaryOp op = SFun $ \case
285357
SInt (Closed x) -> Right (SInt (Closed (op x)))
286358
_ -> Left "Expected integer argument"
287359

360+
-- Same rule in the closed (NBE) domain:
361+
-- compile(a ⊕ b) = Comp op (fanC (compile a) (compile b))
362+
-- Both arguments are Closed (∀z), so fanC yields a valid closed morphism.
288363
sIntBinOp :: CatExpr (Integer, Integer) Integer -> SVal
289364
sIntBinOp op = SFun $ \left -> Right $ SFun $ \right ->
290365
case (left, right) of

0 commit comments

Comments
 (0)