infer
authorMart Lubbers <mart@martlubbers.net>
Tue, 5 Mar 2019 13:10:25 +0000 (14:10 +0100)
committerMart Lubbers <mart@martlubbers.net>
Tue, 5 Mar 2019 13:10:25 +0000 (14:10 +0100)
check.icl
parse.icl
tests/preamble.mfp

index e332b59..7d02871 100644 (file)
--- a/check.icl
+++ b/check.icl
@@ -4,202 +4,141 @@ import StdEnv
 
 import Data.Either
 import Data.List
+import Data.Functor
+import Data.Func
+import Data.Maybe
+import Data.Tuple
 import Control.Monad
+import Control.Monad.Trans
+import Control.Monad.State
+
+import qualified Data.Map
+from Data.Map import instance Functor (Map k)
 
 import ast
 
+import StdDebug
 check :: [Function] -> Either [String] Expression
 check fs
        # dups = filter (\x->length x > 1) (groupBy (\(Function i _ _) (Function j _ _)->i == j) fs)
        | length dups > 0 = Left ["Duplicate functions: ":[toString n\\[(Function n _ _):_]<-dups]]
        = case partition (\a->a=:(Function ['start'] _ _)) fs of
                ([], _) = Left ["No start function defined"]
-               ([Function _ [] e], fs) = Right (foldr (\(Function i a e)->Let i a e) e fs)
+               ([Function _ [] e], fs)
+                       # e = foldr (\(Function i a e)->Let i a e) e fs
+                       = case runInfer (infer 'Data.Map'.newMap e) of
+                               Left e = Left e
+                               Right s
+                                       = Left [printToString s]
                ([Function _ _ _], _) = Left ["Start cannot have arguments"]
 
-//import qualified Data.Map as DM
-//from Data.Map import instance Functor (Map k)
-//import qualified Data.Set as DS
-//import Data.Functor
-//import Data.Func
-//import Data.Either
-//import Data.List
-//import Data.Maybe
-//import Control.Applicative
-//import Control.Monad
-//import Control.Monad.Trans
-//import qualified Control.Monad.State as MS
-//import Control.Monad.State => qualified gets, put, modify
-//import Control.Monad.RWST => qualified put
-//
-//import ast
-//
-//check :: AST -> Either [String] (AST, [([Char], Scheme)])
-//check (AST fs) = pure (AST fs, [])/*case inferAST preamble fs of
-//     Left e = Left e
-//     Right s = Right (AST fs, 'DM'.toList s)
-//where
-//     preamble = 'DM'.fromList
-//             [(['if'], Forall [['a']] $ TFun TBool $ TFun (TVar ['a']) $ TFun (TVar ['a']) $ TVar ['a'])
-//             ,(['eq'], Forall [] $ TFun TInt $ TFun TInt TBool)
-//             ,(['mul'], Forall [] $ TFun TInt $ TFun TInt TInt)
-//             ,(['div'], Forall [] $ TFun TInt $ TFun TInt TInt)
-//             ,(['add'], Forall [] $ TFun TInt $ TFun TInt TInt)
-//             ,(['sub'], Forall [] $ TFun TInt $ TFun TInt TInt)
-//             ]
-//*/
-//
-//:: TypeEnv    :== 'DM'.Map [Char] Scheme
-//:: Constraint :== (Type, Type)
-//:: Subst      :== 'DM'.Map [Char] Type
-//:: Unifier    :== (Subst, [Constraint])
-//:: Infer a    :== RWST TypeEnv [Constraint] InferState (Either [String]) a
-//:: InferState =   { count :: Int }
-//:: Scheme     =   Forall [[Char]] Type
-//:: Solve a    :== StateT Unifier (Either [String]) a
-//
-//nullSubst :: Subst
-//nullSubst = 'DM'.newMap
-//
-//uni :: Type Type -> Infer ()
-//uni t1 t2 = tell [(t1, t2)]
-//
-//inEnv :: ([Char], Scheme) (Infer a) -> Infer a
-//inEnv (x, sc) m = local (\e->'DM'.put x sc $ 'DM'.del x e) m
-//
-//letters :: [[Char]]
-//letters = [1..] >>= flip replicateM ['a'..'z']
-//
-//fresh :: Infer Type
-//fresh = get >>= \s=:{count}->'Control.Monad.RWST'.put {s & count=count + 1} >>| pure (TVar $ letters !! count)
-//
-//class Substitutable a
-//where
-//     apply :: Subst a -> a
-//     ftv   :: a -> [[Char]]
-//
-//instance Substitutable Type
-//where
-//     apply s t=:(TVar a) = maybe t id $ 'DM'.get a s
-//     apply s (TFun t1 t2)  = TFun (apply s t1) (apply s t2)
-//     apply _ t = t
-//
-//     ftv (TVar a)     = [a]
-//     ftv (TFun t1 t2) = union (ftv t1) (ftv t2)
-//     ftv t            = []
-//
-//instance Substitutable Scheme
-//where
-//     apply s (Forall as t)   = Forall as $ apply (foldr 'DM'.del s as) t
-//     ftv (Forall as t) = difference (ftv t) as
-//
-//instance Substitutable [a] | Substitutable a
-//where
-//     apply s ls = map (apply s) ls
-//     ftv t = foldr (union o ftv) [] t
-//
-//instance Substitutable TypeEnv where
-//     apply s env = fmap (apply s) env
-//     ftv env = ftv $ 'DM'.elems env
-//
-//instance Substitutable Constraint where
-//     apply s (t1, t2) = (apply s t1, apply s t2)
-//     ftv (t1, t2) = union (ftv t1) (ftv t2)
-//
-//instantiate ::  Scheme -> Infer Type
-//instantiate (Forall as t) = mapM (const fresh) as
-//     >>= \as`->let s = 'DM'.fromList $ zip2 as as` in pure $ apply s t
-//
-//generalize :: TypeEnv Type -> Scheme
-//generalize env t = Forall (difference (ftv t) (ftv env)) t
-//
-////:: Expression
-////   = Lit Value
-////   | Var [Char]
-////   | App Expression Expression
-////   | Lambda [Char] Expression
-////   | Builtin [Char] [Expression]
-//inferExpr :: TypeEnv Expression -> Either [String] Scheme
-//inferExpr env ex = case runRWST (infer ex) env {count=0} of
-//     Left e = Left e
-//     Right (ty, st, cs) = case runStateT solver ('DM'.newMap, cs) of
-//             Left e = Left e
-//             Right (s, _) = Right (closeOver (apply s ty))
-//
-//closeOver :: Type -> Scheme
-//closeOver t = normalize (generalize 'DM'.newMap t)
-//
-//normalize :: Scheme -> Scheme
-//normalize t = t 
-//
-//inferAST :: TypeEnv [Function] -> Either [String] TypeEnv
-//inferAST env [] = Right env
-//inferAST env [Function name args body:rest] = case inferExpr env (foldr Lambda body args) of
-//     Left e = Left e
-//     Right ty = inferAST ('DM'.put name ty env) rest
-//
-//inferFunc :: [Function] -> Infer ()
-//inferFunc [] = pure () 
-//inferFunc [Function name args body:rest]
-//     =      ask
-//     >>= \en->infer (foldr Lambda body args)
-//     >>= \t1->inEnv (name, generalize en t1) (inferFunc rest)
-//     >>= \_->pure ()
-//
-//infer :: Expression -> Infer Type
-//infer (Lit v) = case v of
-//     Int  _ = pure TInt
-//     Bool _ = pure TBool
-//infer (Var s) = asks ('DM'.get s)
-//     >>= maybe (liftT $ Left ["Unbound variable " +++ toString s]) instantiate
-//infer (App e1 e2)
-//     =        infer e1
-//     >>= \t1->infer e2
-//     >>= \t2->fresh
-//     >>= \tv->uni t1 (TFun t2 tv)
-//     >>|      pure tv
-//infer (Lambda s e)
-//     =        fresh
-//     >>= \tv->inEnv (s, Forall [] tv) (infer e)
-//     >>= \t-> pure (TFun tv t)
-////infer (Let x e1 e2)
-////   =        ask
-////   >>= \en->infer e1
-////   >>= \t1->inEnv (x, generalize en t1) (infer e2)
-//
-//unifies :: Type Type -> Solve Unifier
-//unifies TInt TInt = pure ('DM'.newMap, [])
-//unifies TBool TBool = pure ('DM'.newMap, [])
-//unifies (TVar a) (TVar b) 
-//     | a == b = pure ('DM'.newMap, [])
-//unifies (TVar v) t = tbind v t
-//unifies t (TVar v) = tbind v t
-//unifies (TFun t1 t2) (TFun t3 t4) = unifyMany [t1, t2] [t3, t4]
-//unifies t1 t2 = liftT $ Left ["Cannot unify " +++ toString t1 +++ " with " +++ toString t2]
-//
-//unifyMany :: [Type] [Type] -> Solve Unifier
-//unifyMany [] [] = pure ('DM'.newMap, [])
-//unifyMany [t1:ts1] [t2:ts2] = unifies t1 t2
-//     >>= \(su1, cs1)->unifyMany (apply su1 ts1) (apply su1 ts2)
-//     >>= \(su2, cs2)->pure (su2 `compose` su1, cs1 ++ cs2)
-//unifyMany t1 t2 = liftT $ Left ["Length difference in unifyMany"]
-//
-//(`compose`) infix 1 :: Subst Subst -> Subst
-//(`compose`) s1 s2 = 'DM'.union (apply s1 <$> s2) s1
-//
-//tbind ::  [Char] Type -> Solve Unifier
-//tbind a (TVar b)
-//     | a == b = pure ('DM'.newMap, [])
-//tbind a t
-//     | occursCheck a t = liftT $ Left ["Infinite type " +++ toString a +++ toString t]
-//     = pure $ ('DM'.singleton a t, [])
-//
-//occursCheck ::  [Char] a -> Bool | Substitutable a
-//occursCheck a t = isMember a $ ftv t
-//
-//solver :: Solve Subst
-//solver = getState >>= \(su, cs)->case cs of
-//     [] = pure su
-//     [(t1, t2):cs0] = unifies t1 t2
-//             >>= \(su1, cs1)->'MS'.put (su1 `compose` su, cs1 ++ (apply su1 cs0))
-//             >>| solver
+import Text.GenPrint
+derive gPrint Scheme, Type
+
+//Polytypes
+:: Scheme = Forall [[Char]] Type
+:: TypeEnv :== 'Data.Map'.Map [Char] Scheme
+:: Subst   :== 'Data.Map'.Map [Char] Type
+nullSubst = 'Data.Map'.newMap
+
+:: Infer a :== StateT [Int] (Either [String]) a
+runInfer :: (Infer (Subst, Type)) -> Either [String] Scheme
+runInfer i = uncurry closeOver <$> evalStateT i [0..]
+where
+       closeOver :: Subst Type -> Scheme
+       closeOver sub ty = normalize (generalize 'Data.Map'.newMap (apply sub ty))
+
+       normalize :: Scheme -> Scheme
+       normalize i = i
+//     normalize (Forall ts body) = Forall (snd <$> ord) (normtype body)
+//     where
+//             ord = zip2 (removeDup $ fv body) (fmap letters)
+//
+//     fv (TVar a) = [a]
+//     fv (TFun a b) = fv a ++ fv b
+//     fv _ = []
+//
+//     normtype (TFun a b) = TFun (normtype a) (normtype b)
+//     normtype (TCon a)   = TCon a
+//     normtype (TVar a)   =
+//               case lookup a ord of
+//                     Just x = TVar x
+//                     Nothing = Left ["type variable not in signature"]
+
+fresh :: Infer Type
+fresh = getState >>= \[s:ss]->put ss >>| pure (TVar (['v':[c\\c<-:toString s]]))
+
+compose :: Subst Subst -> Subst
+compose s1 s2 = 'Data.Map'.union (apply s1 <$> s2) s1
+
+class Substitutable a where
+       apply :: Subst a -> a
+       ftv :: a -> [[Char]]
+
+instance Substitutable Type where
+       apply s t=:(TVar v) = 'Data.Map'.findWithDefault t v s
+       apply s (TFun t1 t2) = on TFun (apply s) t1 t2
+       apply _ x = x
+       
+       ftv (TVar v) = [v]
+       ftv (TFun t1 t2) = on union ftv t1 t2
+       ftv _ = []
+
+instance Substitutable Scheme where
+       apply s (Forall as t) = Forall as $ apply (foldr 'Data.Map'.del s as) t
+       ftv (Forall as t) = difference (ftv t) (removeDup as)
+
+instance Substitutable TypeEnv where
+       apply s env = apply s <$> env
+       ftv env = ftv ('Data.Map'.elems env)
+
+instance Substitutable [a] | Substitutable a where
+       apply s l = apply s <$>  l
+       ftv t = foldr (union o ftv) [] t
+
+occursCheck :: [Char] -> (a -> Bool) | Substitutable a
+occursCheck a = isMember a o ftv
+
+unify :: Type Type -> Infer Subst
+unify (TFun l r) (TFun l` r`)
+       =        unify l l`
+       >>= \s1->on unify (apply s1) r r`
+       >>= \s2->pure (compose s1 s2)
+unify (TVar a) t = bind a t
+unify t (TVar a) = bind a t
+unify TInt TInt = pure nullSubst
+unify TBool TBool = pure nullSubst
+unify t1 t2 = liftT (Left ["Cannot unify: ", toString t1, " with ", toString t2])
+
+bind :: [Char] Type -> Infer Subst
+bind a (TVar t) | a == t = pure nullSubst
+bind a t
+       | occursCheck a t = liftT (Left ["Infinite type: ", toString a, " and ", toString t])
+       = pure ('Data.Map'.singleton a t)
+
+instantiate :: Scheme -> Infer Type
+instantiate (Forall as t)
+       =         sequence [fresh\\_<-as]
+       >>= \as`->pure (apply ('Data.Map'.fromList $ zip2 as as`) t)
+
+generalize :: TypeEnv Type -> Scheme
+generalize env t = Forall (difference (ftv t) (ftv env)) t
+
+infer :: TypeEnv Expression -> Infer (Subst, Type)
+infer env (Lit (Int _)) = pure (nullSubst, TInt)
+infer env (Lit (Bool _)) = pure (nullSubst, TBool)
+infer env (Var x) = case 'Data.Map'.get x env of
+       Nothing = liftT (Left ["Unbound variable: ", toString x])
+       Just s = tuple nullSubst <$> instantiate s
+infer env (App e1 e2)
+       =              fresh
+       >>= \tv->      infer env e1
+       >>= \(s1, t1)->infer (apply s1 env) e2
+       >>= \(s2, t2)->unify (apply s2 t1) (TFun t2 tv)
+       >>= \s3->      pure (compose (compose s3 s2) s1, apply s3 tv)
+infer env (Lambda x b)
+       =              fresh
+       >>= \tv->      infer ('Data.Map'.put x (Forall [] tv) env) b
+       >>= \(s1, t1)->pure (s1, TFun (apply s1 tv) t1)
+infer env (Builtin c a) = undef
+infer env (Let i args e b) = undef
index 5bb4bac..7d4fb2e 100644 (file)
--- a/parse.icl
+++ b/parse.icl
@@ -67,7 +67,7 @@ pTop = getState >>= \s->case s.tokens of
 pEof :: Parser ()
 pEof = getState >>= \s->case s.tokens of
        [] = pure ()
-       [t:ts] = liftT (Left ["Expected EOF"])
+       [t:ts] = liftT (Left ["Expected EOF but got ":map toString [t:ts]])
 
 (?) infixl 9 :: (Parser a) (a -> Bool) -> Parser a
 (?) p f = p >>= \v->if (f v) (pure v) empty
index be7ee13..3263957 100644 (file)
@@ -4,5 +4,5 @@ $ ifxr 0 x y = x y;
 * ifxl 7 x y = mul x y;
 - ifxl 6 x y = sub x y;
 + ifxl 6 x y = add x y;
-fac i = if (i == 0) 1 (i * fac (i - 1));
+fac i = if (i == 0) 1 $ i * fac (i - 1);
 start = fac 5;