Skip to content

Commit bffed48

Browse files
Simplify and document
1 parent 4ed8414 commit bffed48

19 files changed

+166
-188
lines changed

README.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1-
# hoas
1+
# HOAS
2+
3+
A simple tagless final style compiler from a simple lambda calculus
4+
using Higher Order Abstract Syntax to first a point free/categorical
5+
representation and then to a simple call by push value representation
6+
using call by name.

app/Main.hs

+4-12
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import qualified Id
1010
import Lambda
1111
import Lambda.AsBound
1212
import Lambda.AsPointFree
13-
import Lambda.AsReified
1413
import Lambda.AsView
1514
import Lambda.Exp
1615
import Lambda.Hoas
@@ -31,17 +30,13 @@ main = do
3130
putStrLn "Point-Free Program"
3231
putStrLn (view (compiled y))
3332

34-
putStrLn ""
35-
putStrLn "Program Result"
36-
putStrLn (show (result z))
37-
3833
putStrLn ""
3934
putStrLn "Cbpv Program"
4035
putStrLn (AsViewCbpv.view (cbpv w))
4136

4237
putStrLn ""
43-
putStrLn "Cbpv Result"
44-
putStrLn (show (cbpvResult u))
38+
putStrLn "Result"
39+
putStrLn (show (result u))
4540

4641
program :: (Lambda k, Hoas k) => k Unit U64
4742
program =
@@ -56,11 +51,8 @@ bound str = bindPoints str program
5651
compiled :: Lambda k => Id.Stream -> k Unit U64
5752
compiled str = pointFree (bound str)
5853

59-
result :: Id.Stream -> Word64
60-
result str = reify (compiled str)
61-
6254
cbpv :: Cbpv c d => Id.Stream -> d (Cbpv.Sort.U (Cbpv.Sort.F Cbpv.Sort.Unit)) (Cbpv.Sort.U (AsAlgebra U64))
6355
cbpv str = toCbpv (compiled str)
6456

65-
cbpvResult :: Id.Stream -> Word64
66-
cbpvResult str = AsEval.reify (cbpv str)
57+
result :: Id.Stream -> Word64
58+
result str = AsEval.reify (cbpv str)

hoas.cabal

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ cabal-version: 1.12
44
--
55
-- see: https://github.com/sol/hpack
66
--
7-
-- hash: a75cb00ad3e76787779dab2e87dc7f48cba6cfc779ecf4b1784f08e14b351d94
7+
-- hash: aad680befe2c5d1871e747b2a2d9b568b2a2da878057a3ed205b4918b62dc6c6
88

99
name: hoas
1010
version: 0.1.0.0
@@ -36,7 +36,6 @@ library
3636
Lambda
3737
Lambda.AsBound
3838
Lambda.AsPointFree
39-
Lambda.AsReified
4039
Lambda.AsView
4140
Lambda.Exp
4241
Lambda.Hoas

src/AsCallByName.hs

+20-9
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import qualified Lambda.Exp as Exp
1717
import qualified Lambda.Product as Product
1818
import qualified Lambda.Sum as Sum
1919
import qualified Lambda.Type as Type
20-
import Prelude hiding (id, (.), (<*>))
20+
import Prelude hiding (curry, id, return, uncurry, (.), (<*>))
2121

2222
newtype Expr c a b = Expr (c (U (AsAlgebra a)) (U (AsAlgebra b)))
2323

@@ -37,23 +37,34 @@ instance Cbpv c d => Category (Expr d) where
3737
Expr f . Expr g = Expr (f . g)
3838

3939
instance Cbpv c d => Product.Product (Expr d) where
40-
unit = Expr (thunk (returns unit))
40+
unit = Expr (thunk (return unit))
4141

4242
first = Expr (thunk (force first . force id))
4343
second = Expr (thunk (force second . force id))
44-
Expr f # Expr g = Expr (thunk (returns f `to` ((returns g . returns first) `to` returns ((second . first) # second))))
44+
Expr f &&& Expr g = Expr (thunk (return f `to` ((return g . return first) `to` return ((second . first) &&& second))))
4545

4646
instance Cbpv c d => Sum.Sum (Expr d) where
4747
absurd = Expr (thunk (force absurd . force id))
4848

49-
left = Expr (thunk (returns left))
50-
right = Expr (thunk (returns right))
51-
Expr f ! Expr g = Expr (thunk (force id . force (thunk (returns f) ! thunk (returns g)) . force id))
49+
left = Expr (thunk (return left))
50+
right = Expr (thunk (return right))
51+
Expr f ||| Expr g = Expr (thunk (force id . force (thunk (return f) ||| thunk (return g)) . force id))
5252

5353
instance Cbpv c d => Exp.Exp (Expr d) where
54-
lambda (Expr f) = Expr (thunk (lambda (force f . returns (thunk id))))
55-
Expr f <*> Expr x = Expr (thunk (force f <*> x))
54+
curry (Expr f) = Expr (thunk (lambda (force f . return (thunk id))))
55+
uncurry (Expr f) = Expr (thunk (eval (force f) . return shuffle . force id))
56+
where
57+
shuffle :: Cbpv c d => d (b * a) (a * b)
58+
shuffle = second &&& first
59+
60+
eval :: Cbpv stk d => stk (F env) (a ~> b) -> stk (F (a * env)) b
61+
eval f = uncurry f . assocIn
62+
63+
lambda :: Cbpv stk d => stk (F (env * a)) b -> stk (F env) (a ~> b)
64+
lambda f = curry (f . return shuffle . assocOut)
65+
where
66+
shuffle = second &&& first
5667

5768
instance Cbpv c d => Lambda.Lambda (Expr d) where
58-
u64 x = Expr (thunk (returns (u64 x)))
69+
u64 x = Expr (thunk (return (u64 x)))
5970
add = Expr (thunk (addLazy . force id))

src/Cbpv.hs

+21-9
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,17 @@ module Cbpv (Cbpv (..)) where
99
import Cbpv.Sort
1010
import Control.Category
1111
import Data.Word (Word64)
12-
import Prelude hiding (id, (.), (<*>))
13-
12+
import Prelude hiding (curry, id, return, uncurry, (.), (<*>))
13+
14+
-- |
15+
-- As opposed to the usual monadic interface call by push value is based
16+
-- around adjoint functors on two different categories.
17+
--
18+
-- There is a different formulation using oblique morphisms and an
19+
-- indexed category instead of using the asymmetric tensor but was
20+
-- difficult to work with.
21+
--
22+
-- Paul Blain Levy. "Call-by-Push-Value: A Subsuming Paradigm".
1423
class (Category stk, Category dta) => Cbpv stk dta | stk -> dta, dta -> stk where
1524
thunk ::
1625
stk (F x) y ->
@@ -19,7 +28,7 @@ class (Category stk, Category dta) => Cbpv stk dta | stk -> dta, dta -> stk wher
1928
dta x (U y) ->
2029
stk (F x) y
2130

22-
returns ::
31+
return ::
2332
dta env a ->
2433
stk (F env) (F a)
2534
to ::
@@ -28,24 +37,27 @@ class (Category stk, Category dta) => Cbpv stk dta | stk -> dta, dta -> stk wher
2837
stk (env & k) b
2938

3039
unit :: dta x Unit
31-
(#) :: dta env a -> dta env b -> dta env (a * b)
40+
(&&&) :: dta env a -> dta env b -> dta env (a * b)
3241
first :: dta (a * b) a
3342
second :: dta (a * b) b
3443

3544
absurd :: dta Void x
36-
(!) :: dta a c -> dta b c -> dta (a + b) c
45+
(|||) :: dta a c -> dta b c -> dta (a + b) c
3746
left :: dta a (a + b)
3847
right :: dta b (a + b)
3948

40-
lambda :: stk (F (env * a)) b -> stk (F env) (a ~> b)
41-
(<*>) :: stk (F env) (a ~> b) -> dta env a -> stk (F env) b
49+
assocOut :: stk (a & (b & c)) ((a * b) & c)
50+
assocIn :: stk ((a * b) & c) (a & (b & c))
51+
52+
curry :: stk (a & env) b -> stk env (a ~> b)
53+
uncurry :: stk env (a ~> b) -> stk (a & env) b
4254

4355
u64 :: Word64 -> dta x U64
4456

4557
-- fixme.. have optimized version...
4658
add :: dta Unit (U (U64 ~> F (U (U64 ~> F U64))))
4759
addLazy :: stk (F Unit) (U (F U64) ~> U (F U64) ~> F U64)
4860

49-
infixl 9 #
61+
infixl 9 &&&
5062

51-
infixl 9 !
63+
infixl 9 |||

src/Cbpv/AsEval.hs

+38-34
Original file line numberDiff line numberDiff line change
@@ -7,80 +7,84 @@
77
{-# LANGUAGE TypeOperators #-}
88
{-# LANGUAGE NoStarIsType #-}
99

10-
module Cbpv.AsEval (reify, Stack, DataM, Code, Data) where
10+
module Cbpv.AsEval (reify, Stack, Code, Action, Data) where
1111

1212
import Cbpv
1313
import Control.Category
1414
import Data.Word
1515
import Cbpv.Sort
1616
import Prelude hiding ((.), id)
1717

18-
reify :: DataM (U (F Unit)) (U (F U64)) -> Word64
19-
reify (D f) = case f (Thunk $ \w -> Unit :& Effect w) of
18+
reify :: Code (U (F Unit)) (U (F U64)) -> Word64
19+
reify (C f) = case f (Thunk $ \w -> Unit :& Effect w) of
2020
Thunk t -> case t 0 of
2121
(U64 y :& _) -> y
2222

23-
newtype DataM a b = D (Data a -> Data b)
23+
newtype Code a b = C (Data a -> Data b)
2424

25-
newtype Stack a b = C (Code a -> Code b)
25+
newtype Stack a b = S (Action a -> Action b)
2626

2727
data family Data (a :: Set)
2828

29-
data instance Data (U a) = Thunk (Int -> Code a)
29+
data instance Data (U a) = Thunk (Int -> Action a)
3030

3131
data instance Data Unit = Unit
3232
data instance Data Void
3333
data instance Data (a * b) = Pair { firstOf :: (Data a), secondOf :: (Data b) }
3434
data instance Data (a + b) = L (Data a) | R (Data b)
3535
newtype instance Data U64 = U64 Word64
3636

37-
data family Code (a :: Algebra)
38-
data instance Code Initial = Effect Int
39-
data instance Code (a & b) = Data a :& Code b
40-
newtype instance Code (a ~> b) = Lam (Data a -> Code b)
37+
-- | Actions are CBPVs computations but we use a different name for brevity
38+
data family Action (a :: Algebra)
39+
data instance Action Initial = Effect Int
40+
data instance Action (a & b) = Data a :& Action b
41+
newtype instance Action (a ~> b) = Lam (Data a -> Action b)
4142

42-
instance Category DataM where
43-
id = D id
44-
D f . D g = D (f . g)
45-
46-
instance Category Stack where
43+
instance Category Code where
4744
id = C id
4845
C f . C g = C (f . g)
4946

50-
instance Cbpv Stack DataM where
51-
to (C f) (C g) = C $ \x@(env :& _) -> case f x of
47+
instance Category Stack where
48+
id = S id
49+
S f . S g = S (f . g)
50+
51+
instance Cbpv Stack Code where
52+
to (S f) (S g) = S $ \x@(env :& _) -> case f x of
5253
(y :& k) -> g ((Pair env y) :& k)
53-
returns (D f) = C $ \(x :& w) -> f x :& w
54+
return (C f) = S $ \(x :& w) -> f x :& w
5455

55-
thunk (C f) = D $ \x -> Thunk $ \w -> f (x :& Effect w)
56-
force (D f) = C $ \(x :& Effect w) -> case f x of
56+
thunk (S f) = C $ \x -> Thunk $ \w -> f (x :& Effect w)
57+
force (C f) = S $ \(x :& Effect w) -> case f x of
5758
Thunk t -> t w
5859

59-
absurd = D $ \x -> case x of {}
60-
D x ! D y = D $ \env -> case env of
60+
absurd = C $ \x -> case x of {}
61+
C x ||| C y = C $ \env -> case env of
6162
L l -> x l
6263
R r -> y r
63-
left = D L
64-
right = D R
64+
left = C L
65+
right = C R
66+
67+
unit = C $ const Unit
68+
C x &&& C y = C $ \env -> Pair (x env) (y env)
69+
first = C firstOf
70+
second = C secondOf
6571

66-
unit = D $ const Unit
67-
D x # D y = D $ \env -> Pair (x env) (y env)
68-
first = D firstOf
69-
second = D secondOf
72+
curry (S f) = S $ \env -> Lam $ \x -> f (x :& env)
73+
uncurry (S f) = S $ \(x :& env) -> case f env of
74+
Lam y -> y x
7075

71-
lambda (C f) = C $ \(env :& w) -> Lam $ \x -> f ((Pair env x) :& w)
72-
C f <*> D x = C $ \(env :& w) -> case f (env :& w) of
73-
Lam y -> y (x env)
76+
assocOut = S $ \(a :& (b :& c)) -> (Pair a b) :& c
77+
assocIn = S $ \((Pair a b) :& c) -> a :& (b :& c)
7478

75-
u64 x = D $ const (U64 x)
76-
add = D $ \Unit ->
79+
u64 x = C $ const (U64 x)
80+
add = C $ \Unit ->
7781
Thunk $ \w0 ->
7882
Lam $ \(U64 x) ->
7983
(:& Effect w0) $ Thunk $ \w1 ->
8084
Lam $ \(U64 y) ->
8185
U64 (x + y) :& Effect w1
8286

83-
addLazy = C $ \(Unit :& Effect w0) ->
87+
addLazy = S $ \(Unit :& Effect w0) ->
8488
Lam $ \(Thunk x) -> Lam $ \(Thunk y) -> case x w0 of
8589
U64 x' :& Effect w1 -> case y w1 of
8690
U64 y' :& Effect w2 -> U64 (x' + y') :& Effect w2

src/Cbpv/AsView.hs

+8-5
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,26 @@ instance Category Code where
2626

2727
instance Cbpv Stack Code where
2828
to (Stack f) (Stack x) = Stack ("(to " ++ f ++ " " ++ x ++ ")")
29-
returns (Code f) = Stack ("(return " ++ f ++ ")")
29+
return (Code f) = Stack ("(return " ++ f ++ ")")
3030

3131
thunk (Stack f) = Code ("(thunk " ++ f ++ ")")
3232
force (Code f) = Stack ("(force " ++ f ++ ")")
3333

3434
unit = Code "unit"
35-
Code f # Code x = Code ("" ++ f ++ " , " ++ x ++ "")
35+
Code f &&& Code x = Code ("" ++ f ++ " , " ++ x ++ "")
3636
first = Code "π₁"
3737
second = Code "π₂"
3838

3939
absurd = Code "absurd"
40-
Code f ! Code x = Code ("[" ++ f ++ " , " ++ x ++ "]")
40+
Code f ||| Code x = Code ("[" ++ f ++ " , " ++ x ++ "]")
4141
left = Code "i₁"
4242
right = Code "i₂"
4343

44-
lambda (Stack f) = Stack ("" ++ f ++ ")")
45-
Stack f <*> Code x = Stack ("(" ++ f ++ " " ++ x ++ ")")
44+
assocOut = Stack "out"
45+
assocIn = Stack "in"
46+
47+
curry (Stack f) = Stack ("" ++ f ++ ")")
48+
uncurry (Stack f) = Stack ("(! " ++ f ++ ")")
4649

4750
u64 x = Code (show x)
4851
add = Code "add"

src/Cbpv/Sort.hs

-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
{-# LANGUAGE DataKinds #-}
2-
{-# LANGUAGE GADTs #-}
32
{-# LANGUAGE TypeOperators #-}
43
{-# LANGUAGE NoStarIsType #-}
54

@@ -19,8 +18,6 @@ module Cbpv.Sort
1918
)
2019
where
2120

22-
import Data.Typeable ((:~:) (..))
23-
2421
type Set = SetImpl
2522

2623
type Unit = 'Unit

src/Id.hs

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
-- |
2+
--
3+
-- Use the trick from functional pearl "On generating unique names by
4+
-- Lennart Augustsson, Mikael Rittri and Dan Synek" to generate a unique
5+
-- stream of ids.
16
module Id (Id, stream, Stream (..)) where
27

38
import Data.Atomics.Counter

src/Lambda.hs

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
{-# LANGUAGE TypeOperators #-}
22

3+
-- |
4+
--
5+
-- Export the final type class of the simple lambda calculus language.
6+
-- Here we finish the Lambda type class off with some basic operations on
7+
-- integers.
38
module Lambda (Lambda (..)) where
49

510
import Data.Word (Word64)
@@ -9,5 +14,5 @@ import Lambda.Sum
914
import Lambda.Type
1015

1116
class (Sum k, Product k, Exp k) => Lambda k where
12-
u64 :: Word64 -> Value k U64
13-
add :: Value k (U64 ~> U64 ~> U64)
17+
u64 :: Word64 -> k Unit U64
18+
add :: k Unit (U64 ~> U64 ~> U64)

0 commit comments

Comments
 (0)