@@ -79,7 +79,7 @@ compileIntExpr env expr = do
7979-- \x. e | curry (absCCC (\(x,y). e)) | Lam p body -> SFun (\v -> compile body[p:=v])
8080-- f g | apply . (f' △ g') | applySVal (compile f) (compile g) at meta-level
8181-- 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)
82+ -- fixOp (\f a1...an. b) | Fix step . (v1 △ ... △ vn) | compileFixGeneric -> Fix with context c = (f, input)
8383
8484compileExpr :: Environment -> [(String , SVal )] -> Expr -> Either String SVal
8585compileExpr env localEnv = \ case
@@ -95,8 +95,10 @@ compileExpr env localEnv = \case
9595 case lookup name env of
9696 Just expr -> compileExpr env localEnv expr
9797 Nothing -> compileBuiltin name
98- -- y is compiled structurally to Fix rather than treated as a generic function
99- App (Var " y" ) stepExpr -> compileY env localEnv stepExpr
98+ -- Structural recursion is detected by a fixpoint operator head (y/fix/alias)
99+ -- and compiled to CatExpr Fix instead of being interpreted as a regular function.
100+ App fixHead stepExpr
101+ | isFixOperator env localEnv fixHead -> compileFix env localEnv stepExpr
100102 -- λx. e ↦ Haskell closure; beta-reduction is deferred to apply time (NBE)
101103 -- absCCC (λx. λy. e) = curry (absCCC (λ(x,y). e))
102104 Lam param body -> Right $ SFun $ \ argVal -> compileExpr env ((param, argVal) : localEnv) body
@@ -133,40 +135,40 @@ data IntArgs input where
133135data SomeIntArgs where
134136 SomeIntArgs :: IntArgs input -> SomeIntArgs
135137
136- compileY :: Environment -> [(String , SVal )] -> Expr -> Either String SVal
137- compileY env outerLocal = \ case
138+ compileFix :: Environment -> [(String , SVal )] -> Expr -> Either String SVal
139+ compileFix env outerLocal = \ case
138140 Lam fName stepExpr ->
139141 case collectLams stepExpr of
140142 (params, body) ->
141143 case mkIntArgs (length params) of
142- Just (SomeIntArgs args) -> compileYGeneric env outerLocal args fName params body
143- Nothing -> Left " y expects at least one integer argument"
144- _ -> Left " y expects a lambda step function"
144+ Just (SomeIntArgs args) -> compileFixGeneric env outerLocal args fName params body
145+ Nothing -> Left " fix expects at least one integer argument"
146+ _ -> Left " fix expects a lambda step function"
145147
146- compileYGeneric ::
148+ compileFixGeneric ::
147149 Environment ->
148150 [(String , SVal )] ->
149151 IntArgs input ->
150152 String ->
151153 [String ] ->
152154 Expr ->
153155 Either String SVal
154- compileYGeneric env outerLocal args fName params body = buildCurried args []
156+ compileFixGeneric env outerLocal args fName params body = buildCurried args []
155157 where
156158 buildCurried :: IntArgs remaining -> [Closed Integer ] -> Either String SVal
157159 buildCurried OneArg acc =
158160 Right $ SFun $ \ case
159161 SInt arg -> do
160162 applied <- applyFix (acc ++ [arg])
161163 Right (SInt applied)
162- _ -> Left " y expects Integer argument"
164+ _ -> Left " fix expects Integer argument"
163165 buildCurried (MoreArgs rest) acc =
164166 Right $ SFun $ \ case
165167 SInt arg -> buildCurried rest (acc ++ [arg])
166- _ -> Left " y expects Integer argument"
168+ _ -> Left " fix expects Integer argument"
167169
168170 -- Encodes the Fix rule:
169- -- y (λf a₁…aₙ. body) at inputs (v₁,…,vₙ)
171+ -- fix (λf a₁…aₙ. body) at inputs (v₁,…,vₙ)
170172 -- = Comp (Fix stepBody) (v₁ △ … △ vₙ)
171173 -- Context for stepBody is c = (CatExpr input Integer, input):
172174 -- f ↦ buildRecFun → Apply ∘ fanC Fst (a₁ △ … △ aₙ)
@@ -182,6 +184,26 @@ compileYGeneric env outerLocal args fName params body = buildCurried args []
182184 stepBody <- compileRecExpr env outerLocal local body >>= expectRInt " Recursive body must compile to Integer"
183185 Right (Closed (Comp (Fix stepBody) paramTuple))
184186
187+ -- Detects whether an expression head denotes the structural fixpoint operator.
188+ -- Recognized forms:
189+ -- - direct builtins: y, fix
190+ -- - environment aliases that resolve to those names
191+ -- Local bindings shadow fixpoint names.
192+ isFixOperator :: Environment -> [(String , SVal )] -> Expr -> Bool
193+ isFixOperator env localEnv = go []
194+ where
195+ go _ (Int _) = False
196+ go _ (Lam _ _) = False
197+ go _ (App _ _) = False
198+ go seen (Var name)
199+ | name `elem` map fst localEnv = False
200+ | name == " y" || name == " fix" = True
201+ | name `elem` seen = False
202+ | otherwise =
203+ case lookup name env of
204+ Just expr -> go (name : seen) expr
205+ Nothing -> False
206+
185207compileRecExpr :: Environment -> [(String , SVal )] -> [(String , RVal c )] -> Expr -> Either String (RVal c )
186208compileRecExpr env outerLocal local = \ case
187209 -- n ↦ IntConst n
@@ -348,7 +370,8 @@ compileBuiltin "geq" = Right (sIntCompare Geq)
348370compileBuiltin " if" = Right sIfFun
349371compileBuiltin " true" = Right (SBool (Closed T ))
350372compileBuiltin " false" = Right (SBool (Closed F ))
351- compileBuiltin " y" = Left " Recursion via y is not yet structurally compiled"
373+ compileBuiltin " y" = Left " Recursion via fixpoint operators is structurally compiled"
374+ compileBuiltin " fix" = Left " Recursion via fixpoint operators is structurally compiled"
352375compileBuiltin " /" = Left " Division is not currently supported in CatExpr compilation"
353376compileBuiltin name = Left (" Unbound variable: " ++ name)
354377
0 commit comments