bottom up

This commit is contained in:
crumbtoo
2024-04-03 16:03:17 -06:00
parent 0f9c179f20
commit 7c8dae9813
5 changed files with 258 additions and 502 deletions

View File

@@ -89,7 +89,9 @@ For the time being, I just disabled the memoisation. This is very, very bad.
** TODO update architecture diagram :docs: ** TODO update architecture diagram :docs:
** TODO pattern support; everywhere :feature: ** TODO pattern support; everywhere [0%] :feature:
- [ ] in the type-checker
- [ ] in the desugarer
** TODO G-machine visualiser :docs: ** TODO G-machine visualiser :docs:

View File

@@ -16,6 +16,7 @@ tested-with: GHC==9.6.2
common warnings common warnings
-- ghc-options: -Wall -Wno-incomplete-uni-patterns -Wno-unused-top-binds -- ghc-options: -Wall -Wno-incomplete-uni-patterns -Wno-unused-top-binds
ghc-options: -fdefer-typed-holes
library library
import: warnings import: warnings

View File

@@ -4,6 +4,7 @@ module Rlp.AltSyntax
-- * AST -- * AST
Program(..), Decl(..), ExprF(..), Pat(..) Program(..), Decl(..), ExprF(..), Pat(..)
, RlpExprF, RlpExpr, Binding(..), Alter(..) , RlpExprF, RlpExpr, Binding(..), Alter(..)
, RlpExpr', RlpExprF', AnnotatedRlpExpr', Type'
, DataCon(..), Type(..), Kind , DataCon(..), Type(..), Kind
, pattern IntT, pattern TypeT , pattern IntT, pattern TypeT
, Core.Rec(..) , Core.Rec(..)
@@ -54,6 +55,11 @@ import Compiler.Types
import Core.Syntax qualified as Core import Core.Syntax qualified as Core
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
type RlpExpr' = RlpExpr PsName
type RlpExprF' = RlpExprF PsName
type AnnotatedRlpExpr' = Cofree (RlpExprF PsName)
type Type' = Type PsName
type AnnotatedRlpExpr b = Cofree (RlpExprF b) type AnnotatedRlpExpr b = Cofree (RlpExprF b)
type TypedRlpExpr b = Cofree (RlpExprF b) (Type b) type TypedRlpExpr b = Cofree (RlpExprF b) (Type b)

View File

@@ -1,12 +1,7 @@
{-# LANGUAGE PartialTypeSignatures #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE OverloadedLists #-}
module Rlp.HindleyMilner module Rlp.HindleyMilner
( typeCheckRlpProgR ( typeCheckRlpProgR
, annotate
, TypeError(..) , TypeError(..)
, runHM'
, liftHM
, HM
, renamePrettily , renamePrettily
) )
where where
@@ -49,6 +44,8 @@ import Data.Fix hiding (cata, para)
import Control.Comonad.Cofree import Control.Comonad.Cofree
import Control.Comonad import Control.Comonad
import Effectful
import Compiler.RLPC import Compiler.RLPC
import Compiler.RlpcError import Compiler.RlpcError
import Rlp.AltSyntax as Rlp import Rlp.AltSyntax as Rlp
@@ -57,439 +54,181 @@ import Core.Syntax (ExprF(..), Lit(..))
import Rlp.HindleyMilner.Types import Rlp.HindleyMilner.Types
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
fixCofree :: (Functor f, Functor g) -- | Annotate a structure with the result of a catamorphism at each level.
=> Iso (Fix f) (Fix g) (Cofree f ()) (Cofree g b) --
fixCofree = iso sa bt where -- Pretentious etymology: 'dendr-' means 'tree'
sa = foldFix (() :<)
bt (_ :< as) = Fix $ bt <$> as
lookupVar :: PsName -> Context -> HM (Type PsName) dendroscribe :: (Functor f, Base t ~ f, Recursive t)
lookupVar n g = case g ^. contextVars . at n of => (f (Cofree f a) -> a) -> t -> Cofree f a
Just t -> pure t dendroscribe c (project -> f) = c f' :< f'
Nothing -> addFatal $ TyErrUntypedVariable n where f' = dendroscribe c <$> f
gather :: RlpExpr PsName -> HM (Type PsName, PartialJudgement) dendroscribeM :: (Traversable f, Monad m, Base t ~ f, Recursive t)
gather e = use hmMemo >>= (H.lookup e >>> maybe memoise pure) => (f (Cofree f a) -> m a) -> t -> m (Cofree f a)
dendroscribeM c (project -> f) = do
as <- dendroscribeM c `traverse` f
a <- c as
pure (a :< as)
--------------------------------------------------------------------------------
assume :: Name -> Type' -> Judgement
assume n t = mempty & assumptions .~ H.singleton n [t]
equal :: Type' -> Type' -> Judgement
equal a b = mempty & constraints .~ [Equality a b]
elim :: Name -> Type' -> Judgement -> Judgement
elim n t j = j & assumptions %~ H.delete n
& constraints <>~ cs
where where
memoise = do cs = j & foldMapOf (assumptions . at n . each . each) \t' ->
r <- gather' e [Equality t t']
hmMemo <>= H.singleton e r
pure r
gather' :: RlpExpr PsName -> HM (Type PsName, PartialJudgement) elimGenerally :: Name -> Type' -> Judgement -> Judgement
gather' = \case elimGenerally n t j = j & assumptions %~ H.delete n
Finl (LitF (IntL _)) -> pure (IntT, mempty) & constraints <>~ cs
where
cs = j & foldMapOf (assumptions . at n . each . each) \t' ->
[ImplicitInstance mempty t' t]
Finl (VarF n) -> do monomorphise :: Type' -> Judgement -> Judgement
t <- freshTv monomorphise n = constraints . each . _ImplicitInstance . _1 %~ S.insert n
let j = mempty & assumptions .~ H.singleton n [t]
pure (t,j)
Finl (AppF f x) -> do withoutPatterns :: [Binding b a] -> [(b, a)]
tfx <- freshTv withoutPatterns bs = bs ^.. each . singular _VarB
(tf,jf) <- gather f & each . _1 %~ view (singular _VarP)
(tx,jx) <- gather x
let jtfx = mempty & constraints .~ [Equality tf (tx :-> tfx)]
pure (tfx, jf <> jx <> jtfx)
Finl (LamF bs e) -> do --------------------------------------------------------------------------------
tbs <- for bs (const freshTv)
(te,je) <- gather e
cs <- bs `zip` tbs
& concatMapM (uncurry $ elimAssumptions (je ^. assumptions))
let as = foldr H.delete (je ^. assumptions) bs
j = mempty & constraints .~ (je ^. constraints <> cs)
& assumptions .~ as
t = foldr (:->) te tbs
pure (t,j)
Finr (LetEF NonRec bs e) -> do gather :: (Unique :> es)
let ks = bs ^.. each . singular _VarB . _1 . singular _VarP => RlpExprF' (Type', Judgement) -> Eff es (Type', Judgement)
(txs,jxs) <- unzip <$> generaliseGatherBinds bs gather (InL (LitF (IntL _))) = pure (IntT, mempty)
(te,je) <- gather e
(cs,m) <- fmap fold . for (ks `zip` txs) $ \ (k,t) ->
elimAssumptionsMap (je ^. assumptions) k t
let jxcs = jxs ^. each . constraints
& each . constraintTypes %~ substMap m
as = foldr H.delete (je ^. assumptions) ks
j = mempty & constraints .~ je ^. constraints <> jxcs <> cs
& assumptions .~ foldOf (each . assumptions) jxs <> as
pure (te, j)
Finr (LetEF Rec bs e) -> do gather (InL (VarF n)) = do
let ks = bs ^.. each . singular _VarB . _1 . singular _VarP t <- freshTv
(txs,txs',jxs) <- unzip3 <$> gatherBinds bs pure (t, assume n t)
let jxsa = foldOf (each . assumptions) jxs
jxcs <- elimWithBinds (ks `zip` txs) jxsa
(te,je) <- gather e
-- ... why don't we need the map?
(cs,_) <- fmap fold . for (ks `zip` txs') $ \ (k,t) ->
elimAssumptionsMap (je ^. assumptions) k t
let as = deleteKeys ks (je ^. assumptions <> jxsa)
j = mempty & constraints .~ je ^. constraints <> jxcs <> cs
& assumptions .~ as
pure (te,j)
deleteKeys :: (Eq k, Hashable k) => [k] -> HashMap k v -> HashMap k v gather (InL (AppF (tf,jf) (tx,jx))) = do
deleteKeys ks h = foldr H.delete h ks tfx <- freshTv
pure (tfx, jf <> jx <> equal tf (tx :-> tfx))
gatherBinds :: [Binding PsName (RlpExpr PsName)] gather (InL (LamF xs (te,je))) = do
-> HM [( Type PsName -- inferred type bs <- for xs (\x -> (x,) <$> freshTv)
, Type PsName -- generalised type let j = je & forBinds elim bs
, PartialJudgement )] & forBinds (const monomorphise) bs
gatherBinds bs = for bs $ \ (VarB (VarP k) x) -> do t = foldr (:->) te (bs ^.. each . _2)
((tx,jx),frees) <- listenFreshTvNames $ gather x pure (t, j)
let tx' = generalise frees tx
pure (tx,tx',jx)
generaliseGatherBinds :: [Binding PsName (RlpExpr PsName)]
-> HM [(Type PsName, PartialJudgement)]
generaliseGatherBinds = traverse \b ->
b ^. singular _VarB . _2 & generaliseGather
generaliseGather :: RlpExpr PsName -> HM (Type PsName, PartialJudgement)
generaliseGather e = do
(a,frees) <- listenFreshTvNames $ gather e
pure $ a & _1 %~ generalise frees
generalise :: [PsName] -> Type PsName -> Type PsName
generalise freeTvs t = foldr ForallT t freeTvs
generaliseG :: Context -> Type PsName -> Type PsName
generaliseG g t = ifoldr (\n _ s -> ForallT n s) t vs where
vs = H.difference (freeVariables t ^. hashMap)
(g ^. contextTyVars)
instantiate :: Type PsName -> HM (Type PsName)
instantiate (ForallT x m) = do
tv <- freshTv
subst x tv <$> instantiate m
instantiate x = pure x
instantiateMap :: Type PsName -> HM (Type PsName, HashMap PsName (Type PsName))
instantiateMap (ForallT x m) = do
tv <- freshTv
instantiateMap m & mapped . _2 %~ H.insert x tv
& mapped . _1 %~ subst x tv
instantiateMap t = pure (t, mempty)
saturated :: Type PsName -> HM [Type PsName]
saturated (ConT con `AppT` as) = do
mk <- view $ contextTyCons . at con
case mk of
Nothing -> addFatal $ TyErrUntypedVariable con
Just k | lengthOf arrowStops k - 1 == lengthOf applicants1 as
-> pure (as ^.. applicants1)
| otherwise
-> undefined
unify :: [Constraint] -> HM [(PsName, Type PsName)]
unify [] = pure mempty
unify (Equality (sx :-> sy) (tx :-> ty) : cs) =
unify $ Equality sx tx : Equality sy ty : cs
unify (Equality a@(ConT ca `AppT` as) b@(ConT cb `AppT` bs) : cs)
| ca == cb = do
cs' <- liftA2 (zipWith Equality) (saturated a) (saturated b)
unify $ cs' ++ cs
-- elim
unify (Equality (ConT s) (ConT t) : cs) | s == t = unify cs
unify (Equality (VarT s) (VarT t) : cs) | s == t = unify cs
unify (Equality (VarT s) t : cs)
| occurs s t = addFatal $ TyErrRecursiveType s t
| otherwise = unify cs' <&> ((s,t):)
where where
cs' = cs & each . constraintTypes %~ subst s t forBinds :: (PsName -> Type' -> Judgement -> Judgement)
-> [(PsName, Type')] -> Judgement -> Judgement
forBinds f bs j = foldr (uncurry f) j bs
-- swap elimBind (x,tx) j1 = elim x tx j1
unify (Equality s (VarT t) : cs) = unify (Equality (VarT t) s : cs)
-- failure! gather (InR (LetEF NonRec (withoutPatterns -> bs) (te,je))) = do
unify (Equality s t : _) = addFatal $ TyErrCouldNotUnify s t let j = foldr elimBind je bs
pure (te, j)
annotate :: RlpExpr PsName
-> HM (Cofree (RlpExprF PsName) (Type PsName, PartialJudgement))
annotate = sequenceA . fixtend (gather . wrapFix)
assocs :: IndexedTraversal k [(k,v)] [(k,v')] v v'
assocs f [] = pure []
assocs f ((k,v):xs) = (\v' xs' -> (k,v') : xs')
<$> indexed f k v <*> assocs f xs
-- | @elimAssumptions as b tb@ eliminates each assumption in @as@ involving @b@
-- by translating the assumptions into constraints equating @b@'s assumed type
-- with @tb@
elimAssumptions :: Assumptions -> PsName -> Type PsName -> HM [Constraint]
elimAssumptions as b tb =
as ^. at b . non' _Empty & traverseOf each k
where k t = Equality tb <$> instantiate t
elimAssumptions' :: Assumptions -> PsName -> Type PsName -> HM [Constraint]
elimAssumptions' as b tb =
as ^. at b . non' _Empty & traverseOf each k
where k t = Equality <$> instantiate tb <*> instantiate t
elimAssumptionsMap :: Assumptions -> PsName -> Type PsName
-> HM ([Constraint], HashMap PsName (Type PsName))
elimAssumptionsMap as b tb =
runWriterT $ as ^. at b . non' _Empty & traverseOf each k
where where
k t = do elimBind (x,(tx,jx)) j1 = elimGenerally x tx (jx <> j1)
(tb',w) <- lift $ instantiateMap tb
(t',w') <- lift $ instantiateMap t
writer (Equality tb' t', w <> w')
substMap :: HashMap PsName (Type PsName) -> Type PsName -> Type PsName gather (InR (LetEF Rec (withoutPatterns -> bs) (te,je))) = do
substMap m t = ifoldr subst t m let j = foldOf (each . _2 . _2) bs
let j' = foldr elimRecBind j bs
elimAssumptionsG :: Context -> Assumptions -> HM [Constraint] pure (te, j' <> foldr elimBind je bs)
elimAssumptionsG g as
= g ^. contextVars
& itraverse (elimAssumptions' as)
& fmap (H.elems >>> concat)
finalJudgement :: Cofree (RlpExprF PsName) (Type PsName, PartialJudgement)
-> PartialJudgement
finalJudgement = foldOf (folded . _2)
infer :: RlpExpr PsName -> HM (Cofree (RlpExprF PsName) (Type PsName))
infer e = do
g0 <- ask
e' <- annotate e
let (cs,as) = finalJudgement e' ^. lensProduct constraints assumptions
cs' <- (<>cs) <$> elimAssumptionsG g0 as
-- checkUndefinedVariables e'
sub <- solve cs'
pure $ e' & fmap (sub . view _1)
& _extract %~ generaliseG g0
where where
-- intuitively, we'd return mgu(s,t) but the union is left-biased making `s` elimRecBind (x,(tx,_)) j = elim x tx j
-- the user-specified type: prioritise her. elimBind (x,(tx,_)) j = elimGenerally x tx j
unifyTypes _ s t = unify [Equality s t] $> s
solve :: [Constraint] -> HM (Type PsName -> Type PsName) unify :: (Unique :> es)
solve cs = do => [Constraint] -> ErrorfulT TypeError (Eff es) Subst
g <- unify cs unify [] = pure id
pure $ \t -> ifoldrOf (reversed . assocs) subst t g unify (c:cs) = case c of
checkUndefinedVariables Equality (ConT a) (ConT b)
:: Cofree (RlpExprF PsName) (Type PsName, PartialJudgement) | a == b
-> HM () -> unify cs
checkUndefinedVariables ((_,j) :< es)
= case j ^. assumptions of
[] -> checkUndefinedVariables `traverse_` es
as -> doErrs *> checkUndefinedVariables `traverse_` es
where doErrs = ifor as \n _ -> addWound $ TyErrUntypedVariable n
infer1 :: RlpExpr PsName -> HM (Type PsName) Equality (VarT a) (VarT b)
infer1 = fmap extract . infer | a == b
-> unify cs
occurs :: PsName -> Type PsName -> Bool Equality (VarT a) t
occurs n = cata \case | a `occurs` t
VarTF m | n == m -> True -> error "recursive type"
t -> or t | otherwise
-> unify (subst a t <$> cs) <&> (. subst a t)
subst :: PsName -> Type PsName -> Type PsName -> Type PsName Equality t (VarT a)
subst n t' = para \case -> unify (Equality (VarT a) t : cs)
VarTF m | n == m -> t'
-- shadowing
ForallTF x (pre,post) | x == n -> ForallT x pre
t -> embed $ t <&> view _2
prettyHM :: (Out a) Equality (s :-> t) (s' :-> t')
=> Either [TypeError] (a, [Constraint]) -> unify (Equality s s' : Equality t t' : cs)
-> Either [TypeError] (String, [String])
prettyHM = over (mapped . _1) rout
. over (mapped . _2 . each) rout
fixtend :: Functor f => (f (Fix f) -> b) -> Fix f -> Cofree f b ImplicitInstance m s t
fixtend c (Fix f) = c f :< fmap (fixtend c) f | null $ (freeTvs t `S.difference` freeTvs m)
`S.intersection` activeTvs cs
-> unify $ ExplicitInstance s (generalise (freeTvs m) t) : cs
buildInitialContext :: Program PsName a -> Context ExplicitInstance s t -> do
buildInitialContext = foldMapOf (programDecls . each) \case t' <- lift $ instantiate t
TySigD n t -> contextOfTySig n t unify $ Equality s t' : cs
DataD n as cs -> contextOfData n as cs
_ -> mempty
contextOfTySig :: PsName -> Type PsName -> Context Equality a b
contextOfTySig = const $ const mempty -> addFatal $ TyErrCouldNotUnify a b
contextOfData :: PsName -> [PsName] -> [DataCon PsName] -> Context _ -> error "explode (typecheckr explsiong)"
contextOfData n as cs = kindCtx <> consCtx where
kindCtx = mempty & contextTyCons . at n ?~ kind
where kind = foldr (\_ t -> TypeT :-> t) TypeT as
consCtx = foldMap contextOfCon cs activeTvs :: [Constraint] -> HashSet Name
activeTvs = foldMap \case
Equality s t -> freeTvs s <> freeTvs t
ImplicitInstance m s t -> freeTvs s <> (freeTvs m `S.intersection` freeTvs t)
ExplicitInstance s t -> freeTvs s <> freeTvs t
contextOfCon (DataCon c as) = instantiate :: (Unique :> es) => Scheme -> Eff es Type'
mempty & contextVars . at c ?~ ty instantiate (ForallT x t) = do
where ty = foralls $ foldr (:->) base as x' <- freshTv
subst x x' <$> instantiate t
instantiate t = pure t
base = foldl (\f x -> AppT f (VarT x)) (ConT n) as generalise :: HashSet Name -> Type' -> Scheme
generalise m t = foldr ForallT t as
where as = S.toList $ freeTvs t `S.difference` m
foralls t = foldr ForallT t as occurs :: (HasTypes a) => Name -> a -> Bool
occurs x t = x `elem` freeTvs t
typeCheckRlpProgR :: (Monad m) --------------------------------------------------------------------------------
=> Program PsName (RlpExpr PsName)
-> RLPCT m (Program PsName
(TypedRlpExpr PsName))
typeCheckRlpProgR p = liftHM g (inferProg p)
where
g = buildInitialContext p
inferProg :: Program PsName (RlpExpr PsName) annotate :: (Unique :> es)
-> HM (Program PsName (TypedRlpExpr PsName)) => RlpExpr' -> Eff es (Cofree RlpExprF' (Type', Judgement))
inferProg p = do annotate = dendroscribeM (gather . fmap extract)
g0 <- ask
traceM $ "g0 : " <> show g0
-- we only wipe the memo here as a temporary solution to the memo shadowing
-- problem
-- p' <- (thenWipeMemo . annotate) `traverse` etaExpandAll p
(p',csroot) <- annotateProg (etaExpandAll p)
let (cs,as) = foldMap finalJudgement p' ^. lensProduct constraints assumptions
cs' <- (\a -> cs <> csroot <> a) <$> elimAssumptionsG g0 as
sub <- solve cs'
pure $ p' & programDecls . traversed . _FunD . _3
%~ ((_extract %~ generaliseG g0) . fmap (sub . view _1))
where
etaExpandAll = programDecls . each %~ etaExpand
thenWipeMemo a = (hmMemo .= mempty) *> a
annotateProg :: Program PsName (RlpExpr PsName) orderConstraints :: [Constraint] -> [Constraint]
-> HM ( Program PsName orderConstraints cs = a <> b
(Cofree (RlpExprF PsName) (Type PsName, PartialJudgement)) where (a,b) = partition (isn't _ImplicitInstance) cs
, [Constraint] )
annotateProg p = do
let bs = funsToSimpleBinds (p ^. programDecls)
(ks,xs) = unzip bs
xs' <- annotate `traverse` xs
let jxs = foldOf (each . _extract . _2) xs'
txs = xs' ^.. each . _extract . _1
cs <- elimWithBinds (ks `zip` txs) (jxs ^. assumptions)
-- let p' = annotateDecls (ks `zip` xs') p
-- we only wipe the memo here as a temporary solution to the memo shadowing
-- problem
p' <- (thenWipeMemo . annotate) `traverse` p
p'' <- forOf (traversed . traversed . _2) p' \ j -> do
c <- elimWithBinds (ks `zip` txs) (j ^. assumptions)
pure $ j & constraints <>~ c
& assumptions %~ deleteKeys ks
-- TODO: any remaining assumptions should be errors at this point
pure (p'',cs)
where
thenWipeMemo a = (hmMemo .= mempty) *> a
-- this sucks! FunDs should probably be stored as a hashmap in Program... finalJudgement :: Cofree RlpExprF' (Type', Judgement) -> Judgement
annotateDecls :: [( PsName finalJudgement = snd . extract
, Cofree (RlpExprF PsName) (Type PsName, PartialJudgement) )]
-> Program PsName a
-> Program PsName
(Cofree (RlpExprF PsName) (Type PsName, PartialJudgement))
annotateDecls bs = programDecls . traversed . _FunD %~ \case
(n,_,_)
| Just e <- lookup n bs
-> (n,[],e)
gatherBinds' :: [(PsName, RlpExpr PsName)] solveTree :: (Unique :> es)
-> HM [(Type PsName, Type PsName, PartialJudgement)] => Cofree RlpExprF' (Type', Judgement)
gatherBinds' = gatherBinds . fmap (uncurry simpleBind) -> ErrorfulT TypeError (Eff es) (Cofree RlpExprF' Type')
solveTree e = do
sub <- unify (orderConstraints $ finalJudgement e ^. constraints . reversed)
pure $ sub . view _1 <$> e
elimWithBinds :: [(PsName, Type PsName)] typeCheckRlpProgR :: Monad m
-> Assumptions => Program PsName RlpExpr'
-> HM [Constraint] -> RLPCT m (Program PsName (Cofree RlpExprF' Type'))
elimWithBinds bs jxsa = fmap concat . for bs $ \ (k,t) -> typeCheckRlpProgR = undefined
elimAssumptions' jxsa k t
simpleBind :: b -> a -> Binding b a gatherProg :: (Unique :> es)
simpleBind k v = VarB (VarP k) v => Program PsName RlpExpr'
-> Eff es a
gatherProg = undefined
funsToSimpleBinds :: [Decl PsName (RlpExpr PsName)] --------------------------------------------------------------------------------
-> [(PsName, RlpExpr PsName)]
funsToSimpleBinds = mapMaybe \case
d@(FunD n _ _) -> Just (n, etaExpand' d)
_ -> Nothing
simpleBindsToFuns :: [(PsName, TypedRlpExpr PsName)] renamePrettily = undefined
-> [Decl PsName (TypedRlpExpr PsName)]
simpleBindsToFuns = fmap \ (n,e) -> FunD n [] e
wrapLetrec :: [(PsName, RlpExpr PsName)] -> RlpExpr PsName
wrapLetrec ds = ds & each . _1 %~ VarP
& each %~ review _VarB
& \bs -> Finr $ LetEF Rec bs (Finl . LitF . IntL $ 123)
unwrapLetrec :: TypedRlpExpr PsName -> [(PsName, TypedRlpExpr PsName)]
unwrapLetrec (_ :< InR (LetEF _ bs _))
= bs ^.. each . _VarB
& each . _1 %~ view (singular _VarP)
etaExpand' :: Decl b (RlpExpr b) -> RlpExpr b
etaExpand' (FunD _ [] e) = e
etaExpand' (FunD _ as e) = Finl . LamF as' $ e
where as' = as ^.. each . singular _VarP
etaExpand :: Decl b (RlpExpr b) -> Decl b (RlpExpr b)
etaExpand (FunD n [] e) = FunD n [] e
etaExpand (FunD n as e)
| Right as' <- allVarP as
= FunD n [] (Finl . LamF as' $ e)
where
allVarP = traverse (matching _VarP)
etaExpand a = a
liftHM :: (Monad m) => Context -> HM a -> RLPCT m a
liftHM g = liftEither . runHM g
freeVariables :: Type PsName -> HashSet PsName
freeVariables = cata \case
VarTF x -> S.singleton x
ForallTF x m -> S.delete x m
vs -> fold vs
boundVariables :: Type PsName -> HashSet PsName
boundVariables = cata \case
ForallTF x m -> S.singleton x <> m
vs -> fold vs
freeVariablesLTR :: Type PsName -> [PsName]
freeVariablesLTR = nub . cata \case
VarTF x -> [x]
ForallTF x m -> m \\ [x]
vs -> concat vs
renamePrettily' :: Type PsName -> Type PsName
renamePrettily' = join renamePrettily
-- | for some type, compute a substitution which will rename all free variables
-- for aesthetic purposes
renamePrettily :: Type PsName -> Type PsName -> Type PsName
renamePrettily root = (`evalState` alphabetNames) . (renameFree <=< renameBound)
where
renameBound :: Type PsName -> State [PsName] (Type PsName)
renameBound = cata \case
ForallTF x m -> do
n <- getName
ForallT n <$> (subst x (VarT n) <$> m)
t -> embed <$> sequenceA t
renameFree :: Type PsName -> State [PsName] (Type PsName)
renameFree t = do
subs <- forM (freeVariablesLTR root) $ \v -> do
n <- getName
pure $ Endo (subst v (VarT n))
pure . appEndo (fold subs) $ t
getName :: State [PsName] PsName
getName = state (fromJust . uncons)
alphabetNames :: [PsName]
alphabetNames = alphabet ++ concatMap appendAlphabet alphabetNames
where alphabet = [ T.pack [c] | c <- ['a'..'z'] ]
appendAlphabet c = [ c <> c' | c' <- alphabet ]

View File

@@ -11,77 +11,45 @@ import Data.HashSet qualified as S
import GHC.Generics (Generic(..), Generically(..)) import GHC.Generics (Generic(..), Generically(..))
import Data.Kind qualified import Data.Kind qualified
import Data.Text qualified as T import Data.Text qualified as T
import Control.Monad.Writer import Effectful.State.Static.Local
import Control.Monad.Accum import Effectful.Labeled
import Control.Monad.Trans.Accum import Effectful
import Control.Monad.Errorful
import Control.Monad.State
import Control.Monad.Reader
import Text.Printf import Text.Printf
import Data.Pretty import Data.Pretty
import Data.Function import Data.Function
import Control.Lens hiding (Context', Context) import Control.Lens hiding (Context', Context, para)
import Data.Functor.Foldable hiding (fold)
import Data.Foldable
import Compiler.RlpcError import Compiler.RlpcError
import Rlp.AltSyntax import Rlp.AltSyntax
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
data Context = Context -- | A polymorphic type
{ _contextVars :: HashMap PsName (Type PsName)
, _contextTyVars :: HashMap PsName (Type PsName)
, _contextTyCons :: HashMap PsName (Kind PsName)
}
deriving (Show, Generic)
deriving (Semigroup, Monoid)
via Generically Context
data Constraint = Equality (Type PsName) (Type PsName) type Scheme = Type'
| GeneralisedEquality (Type PsName) (Type PsName)
deriving (Eq, Generic, Show)
type Assumptions = HashMap PsName [Type PsName] type Subst = Type' -> Type'
data PartialJudgement = PartialJudgement data Constraint = Equality Type' Type'
{ _constraints :: [Constraint] | ImplicitInstance (HashSet Type') Type' Type'
, _assumptions :: Assumptions | ExplicitInstance Type' Scheme
} deriving Show
deriving (Generic, Show)
deriving (Monoid)
via Generically PartialJudgement
instance Semigroup PartialJudgement where instance Out Constraint where
a <> b = PartialJudgement out (Equality s t) =
{ _constraints = ((<>) `on` _constraints) a b hsep [outPrec appPrec1 s, "~", outPrec appPrec1 t]
, _assumptions = (H.unionWith (<>) `on` _assumptions) a b
}
instance Hashable Constraint --------------------------------------------------------------------------------
type Memo = HashMap (RlpExpr PsName) (Type PsName, PartialJudgement)
data HMState = HMState
{ _hmMemo :: Memo
, _hmUniq :: Int
}
deriving Show
newtype HM a = HM {
unHM :: ErrorfulT TypeError
(ReaderT Context (State HMState)) a
}
deriving (Functor, Applicative, Monad)
deriving ( MonadReader Context
, MonadState HMState
, MonadErrorful TypeError
)
-- | Type error enum. -- | Type error enum.
data TypeError data TypeError
-- | Two types could not be unified -- | Two types could not be unified
= TyErrCouldNotUnify (Type Name) (Type Name) = TyErrCouldNotUnify Type' Type'
-- | @x@ could not be unified with @t@ because @x@ occurs in @t@ -- | @x@ could not be unified with @t@ because @x@ occurs in @t@
| TyErrRecursiveType Name (Type Name) | TyErrRecursiveType Name Type'
-- | Untyped, potentially undefined variable -- | Untyped, potentially undefined variable
| TyErrUntypedVariable Name | TyErrUntypedVariable Name
| TyErrMissingTypeSig Name | TyErrMissingTypeSig Name
@@ -105,60 +73,100 @@ instance IsRlpcError TypeError where
(rout @String t) (rout @String x) (rout @String t) (rout @String x)
] ]
runHM :: Context -> HM a -> Either [TypeError] a --------------------------------------------------------------------------------
runHM g e = maybe (Left es) Right ma
where
(ma,es) = (`evalState` (HMState mempty 0))
. (`runReaderT` g) . runErrorfulT $ unHM e
runHM' :: HM a -> Either [TypeError] a type Unique = State Int
runHM' = runHM mempty
makePrisms ''PartialJudgement freshTv :: (Unique :> es) => Eff es (Type PsName)
makeLenses ''PartialJudgement freshTv = do
makeLenses ''Context n <- get
makePrisms ''Constraint modify @Int succ
makePrisms ''TypeError pure (VarT $ tvNameOfInt n)
makeLenses ''HMState
supplement :: [(PsName, Type PsName)] -> Context -> Context
supplement bs = contextVars %~ (H.fromList bs <>)
demoContext :: Context
demoContext = mempty
& contextVars .~
[ ("+#", IntT :-> IntT :-> IntT)
, ("Nil", ForallT "a" $ ConT "List" `AppT` VarT "a")
]
& contextTyCons .~
[ ("List", TypeT :-> TypeT)
]
constraintTypes :: Traversal' Constraint (Type PsName)
constraintTypes k (Equality s t) = Equality <$> k s <*> k t
constraintTypes k (GeneralisedEquality s t) =
GeneralisedEquality <$> k s <*> k t
instance Out Constraint where
out (Equality s t) =
hsep [outPrec appPrec1 s, "~", outPrec appPrec1 t]
tvNameOfInt :: Int -> PsName tvNameOfInt :: Int -> PsName
tvNameOfInt n = "$a" <> T.pack (show n) tvNameOfInt n = "$a" <> T.pack (show n)
freshTv :: HM (Type PsName) --------------------------------------------------------------------------------
freshTv = do
n <- use hmUniq
hmUniq %= succ
pure (VarT $ tvNameOfInt n)
listenFreshTvs :: HM a -> HM (a, [Type PsName]) -- | A 'Judgement' is a sort of "co-context" used in bottom-up inference. The
listenFreshTvs hm = listenFreshTvNames hm & mapped . _2 . each %~ VarT -- typical algorithms J, W, and siblings pass some context Γ to the inference
-- algorithm which is used to lookup variables and such. Here in rlpc we
-- infer a type under zero context; inference returns the assumptions made of
-- a variable which may be later eliminated and solved.
listenFreshTvNames :: HM a -> HM (a, [PsName]) data Judgement = Judgement
listenFreshTvNames hm = do { _constraints :: [Constraint]
n <- use hmUniq , _assumptions :: Assumptions
a <- hm }
n' <- use hmUniq deriving (Show)
pure (a, [ tvNameOfInt k | k <- [n .. pred n'] ])
type Assumptions = HashMap PsName [Type PsName]
instance Semigroup Judgement where
a <> b = Judgement
{ _constraints = ((<>) `on` _constraints) a b
, _assumptions = (H.unionWith (<>) `on` _assumptions) a b
}
instance Monoid Judgement where
mempty = Judgement
{ _constraints = mempty
, _assumptions = mempty
}
--------------------------------------------------------------------------------
class HasTypes a where
types :: Traversal' a Type'
freeTvs :: a -> HashSet PsName
boundTvs :: a -> HashSet PsName
subst :: Name -> Type' -> a -> a
freeTvs = foldMapOf types $ cata \case
VarTF n -> S.singleton n
t -> fold t
boundTvs = const mempty
subst k v = types %~ cata \case
VarTF n | k == n -> v
t -> embed t
instance HasTypes Constraint where
types k (Equality s t) = Equality <$> types k s <*> types k t
types k (ImplicitInstance m s t) =
ImplicitInstance <$> types k m <*> types k s <*> types k t
types k (ExplicitInstance s t) =
ExplicitInstance <$> types k s <*> types k t
instance (Hashable a, HasTypes a) => HasTypes (HashSet a) where
types k = traverseHashSetBad (types k)
instance HasTypes Type' where
types = id
freeTvs = cata \case
VarTF n -> S.singleton n
ForallTF x t -> S.delete x t
t -> fold t
boundTvs = cata \case
ForallTF x t -> S.insert x t
t -> fold t
subst k v = para \case
VarTF n | k == n -> v
ForallTF x (pre,post)
| k == x -> ForallT x pre
t -> embed $ snd <$> t
-- illegal traversal
traverseHashSetBad :: (Hashable a, Hashable b)
=> Traversal (HashSet a) (HashSet b) a b
traverseHashSetBad k s = fmap S.fromList $ traverse k (S.toList s)
--------------------------------------------------------------------------------
makePrisms ''Judgement
makeLenses ''Judgement
makePrisms ''Constraint
makePrisms ''TypeError