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
1630module CCC.Compiler
@@ -25,8 +39,13 @@ import CCC.Cat (fanC)
2539import CCC.Rewrite (simplify )
2640import 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.
2844newtype 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.
3049data 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+
5484compileExpr :: Environment -> [(String , SVal )] -> Expr -> Either String SVal
5585compileExpr 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.
71113applySVal :: SVal -> SVal -> Either String SVal
72114applySVal (SFun f) x = f x
73115applySVal _ _ = 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.
75121data 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)).
80128data 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.
84133data 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
130185compileRecExpr :: Environment -> [(String , SVal )] -> [(String , RVal c )] -> Expr -> Either String (RVal c )
131186compileRecExpr 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
184247rIntBin :: CatExpr (Integer , Integer ) Integer -> RVal c
185248rIntBin op = RFun $ \ left -> Right $ RFun $ \ right ->
186249 case (left, right) of
@@ -214,6 +277,11 @@ expectRInt :: String -> RVal c -> Either String (CatExpr c Integer)
214277expectRInt _ (RInt out) = Right out
215278expectRInt 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ₙ)
217285buildRecFun :: forall input . IntArgs input -> Either String (RVal (CatExpr input Integer , input ))
218286buildRecFun 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, …
232304argProjections :: IntArgs input -> CatExpr c input -> [CatExpr c Integer ]
233305argProjections OneArg tupleExpr = [tupleExpr]
234306argProjections (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.
288363sIntBinOp :: CatExpr (Integer , Integer ) Integer -> SVal
289364sIntBinOp op = SFun $ \ left -> Right $ SFun $ \ right ->
290365 case (left, right) of
0 commit comments