forked from luqui/vatican
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Naive.hs
92 lines (76 loc) · 2.56 KB
/
Naive.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE Rank2Types #-}
-- A naive, lazy interpreter. It has a terrible constant overhead,
-- but, perhaps surprisingly, it passes the tower of interpreters
-- test.
module Naive where
import Control.Monad
import Control.Monad.Trans.Class
import Control.Monad.Trans.Reader
import Data.IntSet (IntSet)
import System.IO.Unsafe (unsafePerformIO)
import qualified Data.IntSet as I
import qualified Data.Supply as Supply
import HOAS
data Exp a = Var Int
| Lam Int (Exp a)
| Exp a `App` Exp a
| Prim a
deriving Show
newtype Env a = Env { runEnv :: Supply.Supply Int -> a }
instance Functor Env where
fmap f (Env s) = Env (f . s)
instance Monad Env where
return = Env . const
Env s >>= f = Env $ \sup ->
let (sup1, sup2) = Supply.split2 sup in
runEnv (f (s sup1)) sup2
newtype Naive a = Naive { unNaive :: Env (Exp a) }
fresh :: Env Int
fresh = Env Supply.supplyValue
instance Term (Naive a) where
Naive left % Naive right = Naive $ liftM2 App left right
fun f = Naive $ do
x <- fresh
Lam x `liftM` (unNaive . f . Naive . return $ Var x)
instance PrimTerm a (Naive a) where
prim = Naive . return . Prim
freeVars :: Exp a -> IntSet
freeVars (Var v) = I.singleton v
freeVars (Lam v e) = I.delete v $ freeVars e
freeVars (App f a) = freeVars f `I.union` freeVars a
freeVars _ = I.empty
subst :: Int -> Exp a -> Exp a -> Env (Exp a)
subst x s b = sub b
where sub e@(Var v) | v == x = return s
| otherwise = return e
sub e@(Lam v e') | v == x = return e
| v `I.member` fvs = do
v' <- fresh
e'' <- sub =<< subst v (Var v') e'
return $ Lam v' e''
| otherwise = Lam v `liftM` sub e'
sub (App f a) = liftM2 App (sub f) (sub a)
sub e = return e
fvs = freeVars s
reduce :: Primitive a => Exp a -> Env (Exp a)
reduce (Lam x e) = Lam x `liftM` reduce e
reduce (App e1 e2) = do
e1' <- reduce e1
e2' <- reduce e2
case e1' of
Lam x e -> reduce =<< subst x e2' e
Prim a -> case e2' of
Prim b -> return . Prim $ a `apply` b
_ -> return $ App e1' e2'
_ -> return $ App e1' e2'
reduce e = return e
eval :: Primitive a => Naive a -> a
eval m = case e of
Prim a -> a
_ -> error "Not a prim!"
where
e = unsafePerformIO $ do
supply <- Supply.newSupply 0 succ
return $ runEnv (reduce =<< unNaive m) supply