Last active
January 15, 2021 22:45
-
-
Save mstewartgallus/f42f6d726d3e45f277c3a338e857df8a to your computer and use it in GitHub Desktop.
Latest Cesk kappa calculus interpreter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# LANGUAGE DataKinds #-} | |
{-# LANGUAGE FlexibleInstances #-} | |
{-# LANGUAGE GADTs #-} | |
{-# LANGUAGE KindSignatures #-} | |
{-# LANGUAGE PolyKinds #-} | |
{-# LANGUAGE QuantifiedConstraints #-} | |
{-# LANGUAGE RankNTypes #-} | |
{-# LANGUAGE Strict #-} | |
{-# LANGUAGE StrictData #-} | |
{-# LANGUAGE TypeFamilies #-} | |
{-# LANGUAGE TypeOperators #-} | |
{-# LANGUAGE UndecidableInstances #-} | |
module Main (main) where | |
import Control.Applicative | |
import Control.Monad | |
import Control.Monad.State.Strict hiding (State (..), state) | |
import qualified Control.Monad.State.Strict as State | |
import Data.Array.Unboxed (UArray) | |
import qualified Data.Array.Unboxed as Array | |
import Data.Kind | |
import Data.Map.Strict (Map) | |
import qualified Data.Map.Strict as Map | |
import Data.Type.Equality | |
import Type.Reflection | |
import Prelude hiding (fst, id, snd) | |
-- Strict everything is used mostly to show that semantics don't rely | |
-- on laziness. Performance isn't really a worry. A similar thing with | |
-- data families applies. Data families are mostly used to enforce we | |
-- don't rely on discriminating on a GADT. | |
sample :: Hoas k => k Unit Z | |
sample = | |
push (z 5) | |
>>> pop | |
( \x -> | |
push x | |
>>> push (z 4) | |
>>> add | |
) | |
main :: IO () | |
main = do | |
loop (inject sample) 0 | |
let y = eval (inject sample) | |
display y | |
display :: KnownT a => State a -> IO () | |
display (State control env store val kont) = do | |
putStrLn $ "\tip\t" ++ show control | |
putStrLn $ "\tenv\t" ++ show env | |
putStrLn $ "\theap\t" ++ show store | |
putStrLn $ "\tstack" | |
putStrLn $ "\t\t" ++ show val ++ " ⊢" | |
prettyPrint kont | |
-- fixme.. find a better method of halting | |
loop :: KnownT a => State a -> Int -> IO () | |
loop x n = do | |
putStrLn $ show n ++ ":" | |
display x | |
case step x of | |
Just y -> loop y (n + 1) | |
Nothing -> do | |
putStrLn "STUCK" | |
prettyPrint :: (KnownT a, KnownT b) => Path a b -> IO () | |
prettyPrint IdPath = pure () | |
prettyPrint (ComposePath h t) = do | |
putStrLn $ "\t\t" ++ show h | |
prettyPrint t | |
---------- | |
data T = Unit | T :*: T | Z | |
infixr 0 :*: | |
class KnownT a where | |
inferTag :: Tagged t => t a | |
class Tagged t where | |
zTag :: t Z | |
unitTag :: t Unit | |
pairTag :: (KnownT a, KnownT b) => t a -> t b -> t (a :*: b) | |
instance Tagged TypeRep where | |
unitTag = typeRep | |
zTag = typeRep | |
pairTag a b = a `withTypeable` (b `withTypeable` typeRep) | |
instance KnownT Unit where | |
inferTag = unitTag | |
instance KnownT Z where | |
inferTag = zTag | |
instance (KnownT a, KnownT b) => KnownT (a :*: b) where | |
inferTag = pairTag inferTag inferTag | |
---------- | |
class Category k where | |
id :: KnownT a => k a a | |
(>>>) :: (KnownT a, KnownT b, KnownT c) => k a b -> k b c -> k a c | |
class Category k => Common k where | |
bang :: KnownT a => k a Unit | |
push :: (KnownT a, KnownT b) => k Unit a -> k b (a :*: b) | |
z :: Int -> k Unit Z | |
add :: k (Z :*: Z :*: Unit) Z | |
class Common k => Hoas k where | |
pop :: (KnownT a, KnownT b, KnownT c) => (k Unit a -> k b c) -> k (a :*: b) c | |
class Common k => Named k where | |
load :: KnownT a => Var a -> k Unit a | |
popVar :: (KnownT a, KnownT b, KnownT c) => Var a -> k b c -> k (a :*: b) c | |
----------------- | |
type StateMonad x = State.State x | |
newtype Namer k a b = P {name :: StateMonad Layout (k a b)} | |
instance Category k => Category (Namer k) where | |
id = P (pure id) | |
P f >>> P g = P (pure (>>>) <*> f <*> g) | |
instance Common k => Common (Namer k) where | |
bang = P (pure bang) | |
push (P x) = P (pure push <*> x) | |
z n = P (pure (z n)) | |
add = P (pure add) | |
instance Named k => Hoas (Namer k) where | |
pop f = P $ do | |
v <- fresh | |
y <- name (f (P $ pure (load v))) | |
pure (popVar v y) | |
fresh :: KnownT a => StateMonad Layout (Var a) | |
fresh = fresh' inferTag | |
fresh' :: KnownT a => TypeRep a -> StateMonad Layout (Var a) | |
fresh' t = do | |
l <- get | |
let (v, mp') = Map.insertLookupWithKey (\_ -> (+)) (TypeKey t) 1 (vars l) | |
put (Layout mp') | |
let n = case v of | |
Just x -> x | |
Nothing -> 0 | |
pure (Var n) | |
--- | |
data TypeKey = forall x. KnownT x => TypeKey (TypeRep x) | |
instance Eq TypeKey where | |
TypeKey x == TypeKey y = SomeTypeRep x == SomeTypeRep y | |
instance Ord TypeKey where | |
compare (TypeKey x) (TypeKey y) = compare (SomeTypeRep x) (SomeTypeRep y) | |
instance Show TypeKey where | |
show (TypeKey x) = show x | |
----- | |
data State c = forall a b v env. | |
(Env env, KnownT a, KnownT b) => | |
State | |
{ control :: Control env a b, | |
env :: env, | |
store :: Store, | |
arg :: Val a, | |
kont :: Path b c | |
} | |
data family Val (a :: T) | |
data instance Val Unit = BangVal | |
newtype instance Val Z = ZVal Int | |
data instance Val (a :*: b) = TupleVal (Val a) (Val b) | |
-- kind of like pure ? | |
inject :: KnownT a => (forall k. Hoas k => k Unit a) -> State a | |
inject c = case runState (name c) emptyLayout of | |
(c', layout) -> State c' (allocEnv layout) emptyStore BangVal id | |
eval :: KnownT a => State a -> State a | |
eval x = case step x of | |
Nothing -> x | |
Just y -> eval y | |
step :: (KnownT a, MonadFail m) => State a -> m (State a) | |
step (State control env store x k) = step' control env store x k | |
---- | |
newtype Push a = Push { push' :: forall e b. (Env e, KnownT a, KnownT b) => Control e Unit a -> Control e b (a :*: b) } | |
instance Tagged Push where | |
unitTag = Push { | |
push' = \_ -> Control | |
{ fold = push bang, | |
step' = \env store value kont -> | |
pure (State bang env store BangVal (pushK value >>> kont)) | |
} | |
} | |
zTag = Push { | |
push' = pushGeneric | |
} | |
pairTag _ _ = Push { | |
push' = pushGeneric | |
} | |
pushGeneric :: (Env env, KnownT a, KnownT b) => Control env Unit a -> Control env b (a :*: b) | |
pushGeneric x = | |
Control | |
{ fold = push (fold x), | |
step' = \env store value kont -> | |
pure (State x env store BangVal (pushK value >>> kont)) | |
} | |
instance Category (Control env) where | |
id = | |
Control | |
{ fold = id, | |
step' = \_ store value kont -> | |
applyKont kont value store | |
} | |
f >>> g = | |
Control | |
{ fold = fold f >>> fold g, | |
step' = \env store param kont -> | |
pure (State f env store param (composeK env g >>> kont)) | |
} | |
instance Env env => Common (Control env) where | |
bang = | |
Control | |
{ fold = bang, | |
step' = \_ store _ kont -> | |
applyKont kont BangVal store | |
} | |
push = pushGeneric | |
z n = | |
Control | |
{ fold = z n, | |
step' = \_ store _ kont -> | |
applyKont kont (ZVal n) store | |
} | |
add = | |
Control | |
{ fold = add, | |
step' = \_ store (TupleVal (ZVal x) (TupleVal (ZVal y) _)) kont -> | |
applyKont kont (ZVal (x + y)) store | |
} | |
instance Env env => Named (Control env) where | |
load v = | |
Control | |
{ fold = load v, | |
step' = \env store _ kont -> do | |
let Just val = getEnv env v | |
applyKont kont val store | |
} | |
popVar v body = | |
Control | |
{ fold = popVar v (fold body), | |
step' = \env store (TupleVal h t) kont -> | |
pure (State body (putEnv env v h) store t kont) | |
} | |
----- | |
class Show env => Env env where | |
getEnv :: KnownT a => env -> Var a -> Maybe (Val a) | |
putEnv :: KnownT a => env -> Var a -> Val a -> env | |
data EmptyEnv = EmptyEnv | |
instance Show EmptyEnv where | |
show EmptyEnv = "!" | |
instance Env EmptyEnv where | |
getEnv EmptyEnv _ = Nothing | |
putEnv EmptyEnv _ _ = EmptyEnv | |
emptyEnv :: EmptyEnv | |
emptyEnv = EmptyEnv | |
data SomeEnv s = SomeEnv | |
{ zValues :: ValArray Z, | |
tupleMap :: Map TypeKey SomeArray | |
} | |
instance Env (SomeEnv s) where | |
getEnv = getEnv' inferTag | |
putEnv = putEnv' inferTag | |
data SomeArray = forall a. KnownT a => SomeArray (ValArray a) | |
newtype GetEnv s a = GetEnv { getEnv' :: KnownT a => SomeEnv s -> Var a -> Maybe (Val a) } | |
instance Tagged (GetEnv s) where | |
unitTag = GetEnv $ \_ _ -> pure BangVal | |
zTag = GetEnv $ \env n -> getValArray (zValues env) n | |
pairTag _ _ = GetEnv getEnvGeneric | |
newtype PutEnv s a = PutEnv { putEnv' :: KnownT a => SomeEnv s -> Var a -> Val a -> SomeEnv s } | |
instance Tagged (PutEnv s) where | |
unitTag = PutEnv $ \env _ -> pure env | |
zTag = PutEnv $ \env n x -> env { zValues = putValArray (zValues env) n x } | |
pairTag _ _ = PutEnv putEnvGeneric | |
allocEnv :: Layout -> SomeEnv () | |
allocEnv (Layout mp0) = | |
let zs = case Map.lookup (TypeKey zTag) mp0 of | |
Nothing -> 0 | |
Just n -> n | |
mp1 = Map.delete (TypeKey unitTag) mp0 | |
mp2 = Map.delete (TypeKey zTag) mp1 | |
mp3 = Map.mapWithKey (\(TypeKey t) n -> SomeArray (doAlloc t n)) mp2 | |
doAlloc :: KnownT a => TypeRep a -> Int -> ValArray a | |
doAlloc _ n = allocArray n | |
in SomeEnv { zValues = allocArray zs, tupleMap = mp3 } | |
getEnvGeneric :: KnownT a => SomeEnv s -> Var a -> Maybe (Val a) | |
getEnvGeneric = getEnvGeneric' inferTag | |
-- fixme... handle ... maybes better | |
putEnvGeneric :: KnownT a => SomeEnv s -> Var a -> Val a -> SomeEnv s | |
putEnvGeneric env v x = case putEnvGeneric' inferTag env v x of | |
Nothing -> undefined | |
Just y -> y | |
getEnvGeneric' :: KnownT a => TypeRep a -> SomeEnv s -> Var a -> Maybe (Val a) | |
getEnvGeneric' t mp n = do | |
SomeArray arr <- Map.lookup (TypeKey t) (tupleMap mp) | |
arr' <- fromArray arr | |
val <- getValArray arr' n | |
pure val | |
-- fixme... lookup then insert is dumb, do one pass | |
putEnvGeneric' :: KnownT a => TypeRep a -> SomeEnv s -> Var a -> Val a -> Maybe (SomeEnv s) | |
putEnvGeneric' t env n x = do | |
let t' = TypeKey t | |
let mp = tupleMap env | |
SomeArray arr <- Map.lookup t' mp | |
arr' <- fromArray arr | |
let arr'' = putValArray arr' n x | |
let mp' = Map.insert t' (SomeArray arr'') mp | |
pure (env {tupleMap = mp'}) | |
fromArray :: (KnownT a, KnownT b) => ValArray a -> Maybe (ValArray b) | |
fromArray = fromArray' inferTag inferTag | |
fromArray' :: TypeRep a -> TypeRep b -> f a -> Maybe (f b) | |
fromArray' t t' arr = do | |
Refl <- testEquality t t' | |
pure arr | |
instance Show (SomeEnv s) where | |
showsPrec _ env = ("{" ++) . shows (zValues env) . ("," ++) . shows (Map.assocs (tupleMap env)) . ("}" ++) | |
data family ValArray (a :: T) | |
data instance ValArray Unit = EmptyArray | |
newtype instance ValArray Z = ZArray (UArray Int Int) | |
data instance ValArray (a :*: b) = PairArray (ValArray a) (ValArray b) | |
allocArray :: KnownT a => Int -> ValArray a | |
allocArray = alloc inferTag | |
newtype Alloc a = Alloc { alloc :: Int -> ValArray a } | |
instance Tagged Alloc where | |
unitTag = Alloc $ \n -> EmptyArray | |
zTag = Alloc $ \n -> ZArray (Array.listArray (0, n) (repeat (0))) | |
pairTag a b = Alloc $ \n -> PairArray (alloc a n) (alloc b n) | |
newtype GetVal a = GetVal { getVal :: ValArray a -> Int -> Maybe (Val a) } | |
instance Tagged GetVal where | |
unitTag = GetVal $ \EmptyArray n -> pure BangVal | |
zTag = GetVal $ \(ZArray arr) n -> do | |
let val = arr Array.! n | |
pure (ZVal val) | |
pairTag a b = GetVal $ \(PairArray x y) n -> do | |
x' <- getVal a x n | |
y' <- getVal b y n | |
pure (TupleVal x' y') | |
newtype PutVal a = PutVal { putVal :: ValArray a -> Int -> Val a -> ValArray a } | |
instance Tagged PutVal where | |
unitTag = PutVal $ \EmptyArray _ BangVal -> | |
EmptyArray | |
zTag = PutVal $ \(ZArray arr) n (ZVal v) -> | |
ZArray (arr Array.// [(n, v)]) | |
pairTag a b = PutVal $ \(PairArray x y) n (TupleVal l r) -> | |
PairArray (putVal a x n l) (putVal b y n r) | |
newtype ShowArray a = ShowArray { showArray :: ValArray a -> String -> String } | |
instance Tagged ShowArray where | |
unitTag = ShowArray $ \EmptyArray -> ("!" ++) | |
zTag = ShowArray $ \(ZArray arr) -> | |
shows (Array.elems arr) | |
pairTag a b = ShowArray $ \(PairArray x y) -> | |
("<" ++) . showArray a x . (", " ++) . showArray b y . (">" ++) | |
getValArray :: KnownT a => ValArray a -> Var n -> Maybe (Val a) | |
getValArray arr (Var n) = getVal inferTag arr n | |
putValArray :: KnownT a => ValArray a -> Var a -> Val a -> ValArray a | |
putValArray arr (Var n) x = putVal inferTag arr n x | |
instance KnownT a => Show (ValArray a) where | |
showsPrec _ = showArray inferTag | |
instance Show SomeArray where | |
showsPrec _ (SomeArray arr) = showArray inferTag arr | |
data Store = Store | |
instance Show Store where | |
show Store = "{}" | |
emptyStore :: Store | |
emptyStore = Store | |
data Var (a :: T) = Var Int | |
instance KnownT a => Show (Var a) where | |
showsPrec _ (Var n) = ("#" ++) . shows n | |
newtype Layout = Layout {vars :: Map TypeKey Int} | |
emptyLayout :: Layout | |
emptyLayout = Layout {vars = Map.empty} | |
class Category k => K k where | |
pushK :: (KnownT a, KnownT b) => Val a -> k b (b :*: a) | |
composeK :: (Env env, KnownT a, KnownT b) => env -> Control env a b -> k a b | |
data Kont a b = Kont | |
{ foldK :: forall k. K k => k a b, | |
applyKont' :: forall m. (KnownT a, KnownT b, MonadFail m) => Val a -> Store -> m (State b) | |
} | |
data Path a b where | |
IdPath :: KnownT a => Path a a | |
ComposePath :: (KnownT a, KnownT b, KnownT c) => Kont a b -> Path b c -> Path a c | |
instance Category Path where | |
id = IdPath | |
IdPath >>> f = f | |
ComposePath f g >>> h = ComposePath f (g >>> h) | |
path :: (KnownT a, KnownT b) => Kont a b -> Path a b | |
path x = ComposePath x IdPath | |
instance K Path where | |
pushK h = | |
path $ | |
Kont | |
{ foldK = pushK h, | |
applyKont' = \value store -> | |
pure (State id emptyEnv store (TupleVal value h) id) | |
} | |
composeK env f = | |
path $ | |
Kont | |
{ foldK = composeK env f, | |
applyKont' = \value store -> | |
pure (State f env store value id) | |
} | |
applyKont :: (KnownT a, KnownT b, MonadFail m) => Path a b -> Val a -> Store -> m (State b) | |
applyKont k value store = case k of | |
IdPath -> fail "halt" | |
ComposePath h t -> do | |
-- this part doesn't seem quite right to me but IDK | |
State c e s x k <- applyKont' h value store | |
pure (State c e s x (k >>> t)) | |
instance Show (Kont a b) where | |
showsPrec _ x = view (foldK x) 0 | |
instance KnownT a => Show (Val a) where | |
showsPrec _ = showValue inferTag | |
newtype ShowValue a = ShowValue {showValue :: Val a -> String -> String} | |
instance Tagged ShowValue where | |
unitTag = ShowValue $ \BangVal -> ("!" ++) | |
zTag = ShowValue $ \(ZVal n) -> shows n | |
pairTag a b = ShowValue $ \(TupleVal x y) -> | |
("<" ++) | |
. showValue a x | |
. (", " ++) | |
. showValue b y | |
. (">" ++) | |
data Control env a b = Control | |
{ fold :: forall k. Named k => k a b, | |
step' :: forall m c. (Env env, KnownT a, KnownT b, KnownT c, MonadFail m) => env -> Store -> Val a -> Path b c -> m (State c) | |
} | |
instance Show env => Show (Control env a b) where | |
showsPrec _ control = view (fold control) 0 | |
newtype View (a :: T) (b :: T) = V {view :: Int -> String -> String} | |
instance K View where | |
pushK x = V $ \p -> | |
showParen (p > 10) $ ("push " ++) . shows x | |
composeK env f = V $ \p -> | |
("<" ++) . shows f . (", " ++) . shows env . (">" ++) | |
instance Category View where | |
id = V $ \p -> ("id" ++) | |
g >>> f = V $ \p -> | |
showParen (p > 9) $ | |
view f 10 . (" ∘ " ++) . view g 10 | |
instance Common View where | |
bang = V $ \p -> ("!" ++) | |
push x = V $ \p -> | |
showParen (p > 10) $ ("push " ++) . view x 11 | |
z n = V $ \_ -> shows n | |
add = V $ \_ -> ("add" ++) | |
instance Named View where | |
load v = V $ \_ -> shows v | |
popVar v body = V $ \p -> | |
showParen (p > 11) $ | |
("κ " ++) . shows v . (". " ++) . view body 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment