Skip to content

Commit a46d394

Browse files
author
Thomas Mahler
committed
split naive compilation in separate module
1 parent 90cee70 commit a46d394

5 files changed

Lines changed: 198 additions & 155 deletions

File tree

benchmark/ReductionBenchmarks.hs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Criterion.Main ( defaultMain, bench, nf, nfIO )
44
import Parser ( parseEnvironment, Environment, Expr(Int, App) )
55
import LambdaToSKI ( compileBracket )
66
import CCC.Compiler (compileNumExpr)
7+
--import CCC.CompilerNaive (compileNumExprNaive)
78
import CCC.Interpreter (interp)
89
import CCC.CatExpr (CatExpr)
910
import CLTerm
@@ -21,6 +22,7 @@ import System.Environment (withArgs)
2122
import System.IO (readFile')
2223
import Control.Exception (evaluate)
2324
import MhsEval
25+
--import CCC.Rewrite (simplify)
2426

2527
loadCccMainExpr :: SourceCode -> IO (Environment, Expr)
2628
loadCccMainExpr src = do
@@ -189,28 +191,28 @@ benchmarks = do
189191
--, bench "fibonacci Graph-Reduce-Lin" $ nf graphTest fibBulkLinear
190192
--, bench "fibonacci Graph-Reduce-Log" $ nf graphTest fibBulkLog
191193
--, bench "fibonacci HHI-Reduce" $ nf reducerTest fib
192-
bench "fibonacci HHI-Eta" $ nf reducerTest fibEta
194+
-- bench "fibonacci HHI-Eta" $ nf reducerTest fibEta
193195
-- , bench "fibonacci HHi-Bulk" $ nf reducerTest fibBulk
194196
-- , bench "fibonacci HHI-Bulk-Log" $ nf reducerTestLog fibBulk
195197
-- , bench "fibonacci HHI-Break-Bulk" $ nf reducerTest fibBulkLinear
196198
-- , bench "fibonacci HHI-Break-Log" $ nf reducerTestLog fibBulkLog
197-
, bench "fibonacci CCC.Compiler" $ nf cccTest fibCcc
198-
, bench "fibonacci MicroHs" $ nfIO (microHsTest mhsContext mhsFibEta)
199-
, bench "fibonacci Native" $ nf fibo 37
199+
--, bench "fibonacci CCC.Compiler" $ nf cccTest fibCcc
200+
--, bench "fibonacci MicroHs" $ nfIO (microHsTest mhsContext mhsFibEta)
201+
--, bench "fibonacci Native" $ nf fibo 37
200202
-- , bench "ackermann Graph-Reduce" $ nf graphTest akk
201203
--, bench "ackermann Graph-Reduce-Eta" $ nf graphTest akkEta
202204
-- , bench "ackermann Graph-Reduce-Lin" $ nf graphTest akkBulkLinear
203205
-- , bench "ackermann Graph-Reduce-Log" $ nf graphTest akkBulkLog
204-
, bench "fibonacci MHS Haskell" $ nfIO (microHsTest mhsContext mhsFib)
205-
, bench "ackermann HHI-Reduce" $ nf reducerTest akkEta
206+
--, bench "fibonacci MHS Haskell" $ nfIO (microHsTest mhsContext mhsFib)
207+
--, bench "ackermann HHI-Reduce" $ nf reducerTest akkEta
206208
-- , bench "ackermann HHI-Eta" $ nf reducerTest akkEta
207209
-- , bench "ackermann HHI-Bulk" $ nf reducerTest akkBulk
208210
-- , bench "ackermann HHI-Bulk-Log" $ nf reducerTestLog akkBulk
209211
-- , bench "ackermann HHI-Break-Bulk" $ nf reducerTest akkBulkLinear
210212
-- , bench "ackermann HHI-Break-Log" $ nf reducerTestLog akkBulkLog
211-
, bench "ackermann CCC.Compiler" $ nf cccTest akkCcc
212-
, bench "ackermann MicroHs" $ nfIO (microHsTest mhsContext mhsAckEta)
213-
, bench "ackermann Native" $ nf ack_3 9
213+
--, bench "ackermann CCC.Compiler" $ nf cccTest akkCcc
214+
--, bench "ackermann MicroHs" $ nfIO (microHsTest mhsContext mhsAckEta)
215+
--, bench "ackermann Native" $ nf ack_3 9
214216
-- -- , bench "gaussian Graph-Reduce" $ nf graphTest gau
215217
-- -- , bench "gaussian Graph-Reduce-Eta" $ nf graphTest gauEta
216218
-- -- , bench "gaussian HHI-Reduce" $ nf reducerTest gau
@@ -223,14 +225,14 @@ benchmarks = do
223225
-- , bench "tak Graph-Reduce-Lin" $ nf graphTest takBulkLinear
224226
-- , bench "tak Graph-Reduce-Log" $ nf graphTest takBulkLog
225227
-- , bench "tak HHI-Reduce" $ nf reducerTest tak
226-
, bench "ackermann MHS Haskell" $ nfIO (microHsTest mhsContext mhsAkk)
227-
, bench "tak HHI-Eta" $ nf reducerTest takEta
228+
--, bench "ackermann MHS Haskell" $ nfIO (microHsTest mhsContext mhsAkk)
229+
bench "tak HHI-Eta" $ nf reducerTest takEta
228230
-- , bench "tak HHI-Bulk" $ nf reducerTest takBulk
229231
-- , bench "tak HHI-Bulk-Log" $ nf reducerTestLog takBulk
230232
-- , bench "tak HHI-Break-Bulk" $ nf reducerTest takBulkLinear
231233
-- , bench "tak HHI-Break-Log" $ nf reducerTestLog takBulkLog
232234
, bench "tak CCC.Compiler" $ nf cccTest takCcc
233-
, bench "tak MicroHs" $ nfIO (run mhsContext mhsTakEta)
235+
--, bench "tak MicroHs" $ nfIO (run mhsContext mhsTakEta)
234236
, bench "tak Native" $ nf tak1 (18,6,3)
235237
, bench "tak MHS Haskell" $ nfIO (microHsTest mhsContext mhsTak)
236238
]

lambda-ski.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ library
3232
CCC.CatExpr
3333
CCC.CCC
3434
CCC.Compiler
35+
CCC.CompilerNaive
3536
CCC.Hask
3637
CCC.Interpreter
3738
CCC.Rewrite

src/CCC/Compiler.hs

Lines changed: 60 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,20 @@
2929

3030
module CCC.Compiler
3131
( compileNumExpr,
32-
compileNumExprNaive,
3332
compileEnvironment,
3433
tryCompileVar,
35-
compileNumericBindings
34+
compileNumericBindings,
35+
-- * Internal: shared with CCC.CompilerNaive
36+
RVal (..),
37+
IntArgs (..),
38+
SomeIntArgs (..),
39+
compileRecBuiltin,
40+
collectLams,
41+
mkIntArgs,
42+
buildRecFun,
43+
argProjections,
44+
tupleFromExprs,
45+
expectRInt,
3646
) where
3747

3848
import CCC.CatExpr (CatExpr (..))
@@ -246,21 +256,7 @@ compileRecExpr env outerLocal local = \case
246256
applyRVal _ _ = Left "Cannot apply non-function value"
247257

248258
compileRecBuiltin :: String -> Maybe (RVal c)
249-
compileRecBuiltin "+" = Just (rIntBin Add)
250-
compileRecBuiltin "-" = Just (rIntBin Sub)
251-
compileRecBuiltin "*" = Just (rIntBin Mul)
252-
compileRecBuiltin "sub" = Just (rIntBin Sub)
253-
compileRecBuiltin "sub1" = Just (rIntUnary (\x -> Comp Sub (fanC x (IntConst 1))))
254-
compileRecBuiltin "is0" = Just (rIntToSel (\x -> Comp Eql (fanC x (IntConst 0))))
255-
compileRecBuiltin "eql" = Just (rIntCmpSel Eql)
256-
compileRecBuiltin "leq" = Just (rIntCmpSel Leq)
257-
compileRecBuiltin "geq" = Just (rIntCmpSel Geq)
258-
-- Scott-encoded booleans: TRUE = Snd (select second), FALSE = Fst (select first)
259-
compileRecBuiltin "true" = Just (rSelConst Snd)
260-
compileRecBuiltin "false" = Just (rSelConst Fst)
261-
-- if selector thenVal elseVal = Apply ∘ ⟨selector, ⟨thenVal, elseVal⟩⟩
262-
compileRecBuiltin "if" = Just rIfFun
263-
compileRecBuiltin _ = Nothing
259+
compileRecBuiltin name = builtinToRVal <$> lookupBuiltin name
264260

265261
rIntUnary :: (CatExpr c Integer -> CatExpr c Integer) -> RVal c
266262
rIntUnary op = RFun $ \case
@@ -306,6 +302,48 @@ rIfFun = RFun $ \case
306302
_ -> Left "if: then branch must be integer"
307303
_ -> Left "if: condition must be a Scott boolean (selector)"
308304

305+
-- Builtin descriptor: captures the shape of each primitive operation once.
306+
-- Interpreted into both SVal (NBE) and RVal (direct) domains by
307+
-- builtinToSVal and builtinToRVal respectively.
308+
data Builtin where
309+
BinOp :: CatExpr (Integer, Integer) Integer -> Builtin
310+
UnaryOp :: (forall c. CatExpr c Integer -> CatExpr c Integer) -> Builtin
311+
Predicate :: (forall c. CatExpr c Integer -> CatExpr c (CatExpr (Integer, Integer) Integer)) -> Builtin
312+
CmpOp :: (forall b. CatExpr (Integer, Integer) (CatExpr (b, b) b)) -> Builtin
313+
SelConst :: CatExpr (Integer, Integer) Integer -> Builtin
314+
IfOp :: Builtin
315+
316+
lookupBuiltin :: String -> Maybe Builtin
317+
lookupBuiltin "+" = Just (BinOp Add)
318+
lookupBuiltin "-" = Just (BinOp Sub)
319+
lookupBuiltin "*" = Just (BinOp Mul)
320+
lookupBuiltin "sub" = Just (BinOp Sub)
321+
lookupBuiltin "sub1" = Just (UnaryOp (\x -> Comp Sub (fanC x (IntConst 1))))
322+
lookupBuiltin "is0" = Just (Predicate (\x -> Comp Eql (fanC x (IntConst 0))))
323+
lookupBuiltin "eql" = Just (CmpOp Eql)
324+
lookupBuiltin "leq" = Just (CmpOp Leq)
325+
lookupBuiltin "geq" = Just (CmpOp Geq)
326+
lookupBuiltin "true" = Just (SelConst Snd)
327+
lookupBuiltin "false" = Just (SelConst Fst)
328+
lookupBuiltin "if" = Just IfOp
329+
lookupBuiltin _ = Nothing
330+
331+
builtinToRVal :: Builtin -> RVal c
332+
builtinToRVal (BinOp op) = rIntBin op
333+
builtinToRVal (UnaryOp op) = rIntUnary op
334+
builtinToRVal (Predicate p) = rIntToSel p
335+
builtinToRVal (CmpOp op) = rIntCmpSel op
336+
builtinToRVal (SelConst sel) = rSelConst sel
337+
builtinToRVal IfOp = rIfFun
338+
339+
builtinToSVal :: Builtin -> SVal
340+
builtinToSVal (BinOp op) = sIntBinOp op
341+
builtinToSVal (UnaryOp op) = sIntUnaryOp op
342+
builtinToSVal (Predicate p) = sIntToSel p
343+
builtinToSVal (CmpOp op) = sIntCmpSel op
344+
builtinToSVal (SelConst sel) = SSel (ClosedSel (Lift (const sel)))
345+
builtinToSVal IfOp = sIfFun
346+
309347
sValToRVal :: SVal -> Maybe (RVal c)
310348
sValToRVal (SInt (Closed c)) = Just (RInt c)
311349
sValToRVal (SSel (ClosedSel c)) = Just (RSel c)
@@ -381,23 +419,12 @@ collectLams = go []
381419
go params expr = (params, expr)
382420

383421
compileBuiltin :: String -> Either String SVal
384-
compileBuiltin "+" = Right (sIntBinOp Add)
385-
compileBuiltin "-" = Right (sIntBinOp Sub)
386-
compileBuiltin "*" = Right (sIntBinOp Mul)
387-
compileBuiltin "sub" = Right (sIntBinOp Sub)
388-
compileBuiltin "sub1" = Right (sIntUnaryOp (\x -> Comp Sub (fanC x (IntConst 1))))
389-
compileBuiltin "is0" = Right (sIntToSel (\x -> Comp Eql (fanC x (IntConst 0))))
390-
compileBuiltin "eql" = Right (sIntCmpSel Eql)
391-
compileBuiltin "leq" = Right (sIntCmpSel Leq)
392-
compileBuiltin "geq" = Right (sIntCmpSel Geq)
393-
compileBuiltin "if" = Right sIfFun
394-
-- Scott-encoded booleans: TRUE = Snd (select second), FALSE = Fst (select first)
395-
compileBuiltin "true" = Right (SSel (ClosedSel (Lift (const Snd))))
396-
compileBuiltin "false" = Right (SSel (ClosedSel (Lift (const Fst))))
397-
compileBuiltin "y" = Left "Recursion via fixpoint operators is structurally compiled"
422+
compileBuiltin name
423+
| Just b <- lookupBuiltin name = Right (builtinToSVal b)
424+
compileBuiltin "y" = Left "Recursion via fixpoint operators is structurally compiled"
398425
compileBuiltin "fix" = Left "Recursion via fixpoint operators is structurally compiled"
399-
compileBuiltin "/" = Left "Division is not currently supported in CatExpr compilation"
400-
compileBuiltin name = Left ("Unbound variable: " ++ name)
426+
compileBuiltin "/" = Left "Division is not currently supported in CatExpr compilation"
427+
compileBuiltin name = Left ("Unbound variable: " ++ name)
401428

402429
sIntUnaryOp :: (forall z. CatExpr z Integer -> CatExpr z Integer) -> SVal
403430
sIntUnaryOp op = SFun $ \case
@@ -439,116 +466,6 @@ sIfFun = SFun $ \case
439466
_ -> Left "if: then branch must be integer"
440467
_ -> Left "if: condition must be a Scott boolean (selector)"
441468

442-
-- ---------------------------------------------------------------------------
443-
-- Naive (explicit CatExpr) compilation — no NBE
444-
-- ---------------------------------------------------------------------------
445-
446-
-- | Naive CCC compilation: builds explicit CatExpr nodes directly.
447-
-- Unlike compileNumExpr (which uses NBE with Haskell closures for
448-
-- beta-reduction), this compiles into the RVal domain from a unit
449-
-- context, producing explicit Comp/fanC/Apply nodes at every step.
450-
--
451-
-- For first-order integer programs (no higher-order function passing),
452-
-- the output is identical to the NBE version — NBE only gains an
453-
-- advantage for higher-order terms where it can beta-reduce at compile time.
454-
compileNumExprNaive :: Environment -> Expr -> CatExpr () Integer
455-
compileNumExprNaive env expr =
456-
case compileNaive env [] expr of
457-
Right (RInt e) -> e
458-
Right _ -> error "Naive compilation: expected integer result"
459-
Left err -> error ("Naive compilation failed: " ++ err)
460-
461-
-- Direct CatExpr compilation (no NBE).
462-
-- Uses the same RVal machinery as compileRecExpr but starts from any context.
463-
-- Builtins are RFun closures (primitives), user-level code builds explicit nodes.
464-
--
465-
-- Key difference from compileExpr (NBE):
466-
-- NBE: Lam → Haskell closure; App → Haskell application (beta-reduces)
467-
-- Naive: Lam → RFun closure; App → RFun application (same node building)
468-
-- For first-order programs, both produce identical CatExpr output because
469-
-- builtins build the same Comp/fanC nodes. The difference appears only
470-
-- with higher-order terms where NBE can beta-reduce at compile time.
471-
compileNaive :: forall c. Environment -> [(String, RVal c)] -> Expr -> Either String (RVal c)
472-
compileNaive env local = \case
473-
Int i -> Right (RInt (IntConst i))
474-
Var name ->
475-
case lookup name local of
476-
Just v -> Right v
477-
Nothing -> compileNaiveVar name
478-
App fixHead stepExpr
479-
| isNaiveFixOp env local fixHead -> compileNaiveFix env stepExpr
480-
Lam param body ->
481-
Right (RFun (\arg -> compileNaive env ((param, arg) : local) body))
482-
App f x -> do
483-
fVal <- compileNaive env local f
484-
xVal <- compileNaive env local x
485-
applyNaiveVal fVal xVal
486-
where
487-
compileNaiveVar name =
488-
case lookup name env of
489-
Just expr -> compileNaive env local expr
490-
Nothing -> case compileRecBuiltin name of
491-
Just v -> Right v
492-
Nothing -> Left ("Unbound variable: " ++ name)
493-
494-
applyNaiveVal (RFun fn) x = fn x
495-
applyNaiveVal _ _ = Left "Cannot apply non-function value"
496-
497-
isNaiveFixOp :: Environment -> [(String, RVal c)] -> Expr -> Bool
498-
isNaiveFixOp env local = go []
499-
where
500-
go _ (Int _) = False
501-
go _ (Lam _ _) = False
502-
go _ (App _ _) = False
503-
go seen (Var name)
504-
| name `elem` map fst local = False
505-
| name == "y" || name == "fix" = True
506-
| name `elem` seen = False
507-
| otherwise =
508-
case lookup name env of
509-
Just expr -> go (name : seen) expr
510-
Nothing -> False
511-
512-
compileNaiveFix :: forall c. Environment -> Expr -> Either String (RVal c)
513-
compileNaiveFix env = \case
514-
Lam fName stepExpr ->
515-
case collectLams stepExpr of
516-
(params, body) ->
517-
case mkIntArgs (length params) of
518-
Just (SomeIntArgs args) -> compileNaiveFixGeneric env args fName params body
519-
Nothing -> Left "fix expects at least one integer argument"
520-
_ -> Left "fix expects a lambda step function"
521-
522-
compileNaiveFixGeneric ::
523-
forall c input.
524-
Environment ->
525-
IntArgs input ->
526-
String ->
527-
[String] ->
528-
Expr ->
529-
Either String (RVal c)
530-
compileNaiveFixGeneric env args fName params body = buildCurried args []
531-
where
532-
buildCurried :: IntArgs remaining -> [CatExpr c Integer] -> Either String (RVal c)
533-
buildCurried OneArg acc =
534-
Right $ RFun $ \case
535-
RInt arg -> applyNaiveFix (acc ++ [arg])
536-
_ -> Left "fix expects Integer argument"
537-
buildCurried (MoreArgs rest) acc =
538-
Right $ RFun $ \case
539-
RInt arg -> buildCurried rest (acc ++ [arg])
540-
_ -> Left "fix expects Integer argument"
541-
542-
applyNaiveFix :: [CatExpr c Integer] -> Either String (RVal c)
543-
applyNaiveFix actualArgs = do
544-
paramTuple <- tupleFromExprs args actualArgs
545-
recFun <- buildRecFun args
546-
let fixLocal =
547-
(fName, recFun) :
548-
zipWith (\name proj -> (name, RInt proj)) params (argProjections args Snd)
549-
stepBody <- compileNaive env fixLocal body >>= expectRInt "Recursive body must compile to Integer"
550-
Right (RInt (Comp (Fix stepBody) paramTuple))
551-
552469
-- | Compile an expression, extracting environment variables to morphisms.
553470
-- Returns a list of (name, morphism_string_representation) for inspection.
554471
compileEnvironment :: Environment -> [(String, String)]

0 commit comments

Comments
 (0)