packages feed

hindley-milner-type-check 0.1.0.0 → 0.1.1.0

raw patch · 9 files changed

+491/−227 lines, 9 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Type.Check.HM.Infer: [unContext] :: Context loc v -> Map v (Signature loc v)
- Type.Check.HM.Infer: instance Control.Monad.Error.Class.MonadError (Type.Check.HM.TypeError.TypeError loc (Type.Check.HM.Infer.Name var)) (Type.Check.HM.Infer.InferM loc var)
- Type.Check.HM.Infer: newtype Context loc v
- Type.Check.HM.Pretty: class IsVar v => HasPrefix v
- Type.Check.HM.Pretty: getFixity :: HasPrefix v => v -> Maybe OpFix
- Type.Check.HM.Pretty: instance (Prettyprinter.Internal.Pretty v, Type.Check.HM.Pretty.PrintCons v, Type.Check.HM.Pretty.HasPrefix v) => Prettyprinter.Internal.Pretty (Type.Check.HM.Type.Signature loc v)
- Type.Check.HM.Pretty: instance (Type.Check.HM.Pretty.HasPrefix v, Type.Check.HM.Pretty.PrintCons v, Prettyprinter.Internal.Pretty v) => Prettyprinter.Internal.Pretty (Type.Check.HM.Type.Type loc v)
- Type.Check.HM.Pretty: instance (Type.Check.HM.Pretty.HasPrefix v, Type.Check.HM.Pretty.PrintCons v, Prettyprinter.Internal.Pretty v, Prettyprinter.Internal.Pretty prim) => Prettyprinter.Internal.Pretty (Type.Check.HM.Term.Term prim loc v)
- Type.Check.HM.Pretty: instance Type.Check.HM.Pretty.HasPrefix Data.Text.Internal.Text
- Type.Check.HM.Pretty: instance Type.Check.HM.Pretty.HasPrefix GHC.Base.String
- Type.Check.HM.Pretty: instance Type.Check.HM.Pretty.HasPrefix GHC.Types.Int
- Type.Check.HM.Term: [caseAlt'constrType] :: CaseAlt loc v a -> Type loc v
- Type.Check.HM.Term: instance (Data.Data.Data loc, Data.Data.Data var, Data.Data.Data r) => Data.Data.Data (Type.Check.HM.Term.Bind loc var r)
- Type.Check.HM.Term: instance (GHC.Classes.Eq loc, GHC.Classes.Eq var, GHC.Classes.Eq r) => GHC.Classes.Eq (Type.Check.HM.Term.Bind loc var r)
- Type.Check.HM.Term: instance (GHC.Show.Show loc, GHC.Show.Show var, GHC.Show.Show r) => GHC.Show.Show (Type.Check.HM.Term.Bind loc var r)
+ Type.Check.HM.Infer: [context'binds] :: Context loc v -> Map v (Signature loc v)
+ Type.Check.HM.Infer: [context'constructors] :: Context loc v -> Map v (Signature loc v)
+ Type.Check.HM.Infer: data Context loc v
+ Type.Check.HM.Infer: inferTermList :: Lang q => ContextOf q -> [Bind (Src q) (Var q) (TermOf q)] -> Either [ErrorOf q] [Bind (Src q) (Var q) (TyTermOf q)]
+ Type.Check.HM.Infer: inferTypeList :: Lang q => ContextOf q -> [Bind (Src q) (Var q) (TermOf q)] -> Either [ErrorOf q] [Bind (Src q) (Var q) (TypeOf q)]
+ Type.Check.HM.Infer: insertConstructorCtx :: Ord v => v -> Signature loc v -> Context loc v -> Context loc v
+ Type.Check.HM.Infer: instance Control.Monad.Writer.Class.MonadWriter [Type.Check.HM.TypeError.TypeError loc (Type.Check.HM.Infer.Name var)] (Type.Check.HM.Infer.InferM loc var)
+ Type.Check.HM.Infer: lookupConstructorCtx :: Ord v => v -> Context loc v -> Maybe (Signature loc v)
+ Type.Check.HM.Infer: printInfer :: PrettyLang q => Either [ErrorOf q] (TypeOf q) -> IO ()
+ Type.Check.HM.Lang: type PrettyLang q = (Lang q, PrettyVar (Var q), Pretty (Src q))
+ Type.Check.HM.Pretty: FixityCtx :: (var -> Maybe OpFix) -> a -> FixityCtx var a
+ Type.Check.HM.Pretty: [fixity'context] :: FixityCtx var a -> var -> Maybe OpFix
+ Type.Check.HM.Pretty: [fixity'data] :: FixityCtx var a -> a
+ Type.Check.HM.Pretty: class Pretty a
+ Type.Check.HM.Pretty: data FixityCtx var a
+ Type.Check.HM.Pretty: instance (Prettyprinter.Internal.Pretty loc, Type.Check.HM.Pretty.PrettyVar var) => Prettyprinter.Internal.Pretty (Type.Check.HM.Pretty.FixityCtx var (Type.Check.HM.TypeError.TypeError loc var))
+ Type.Check.HM.Pretty: instance (Prettyprinter.Internal.Pretty loc, Type.Check.HM.Pretty.PrettyVar var) => Prettyprinter.Internal.Pretty (Type.Check.HM.TypeError.TypeError loc var)
+ Type.Check.HM.Pretty: instance (Type.Check.HM.Pretty.PrettyVar v, Prettyprinter.Internal.Pretty prim) => Prettyprinter.Internal.Pretty (Type.Check.HM.Pretty.FixityCtx v (Type.Check.HM.Term.Term prim loc v))
+ Type.Check.HM.Pretty: instance (Type.Check.HM.Pretty.PrettyVar v, Prettyprinter.Internal.Pretty prim) => Prettyprinter.Internal.Pretty (Type.Check.HM.Term.Term prim loc v)
+ Type.Check.HM.Pretty: instance Type.Check.HM.Pretty.PrettyVar v => Prettyprinter.Internal.Pretty (Type.Check.HM.Pretty.FixityCtx v (Type.Check.HM.Type.Signature loc v))
+ Type.Check.HM.Pretty: instance Type.Check.HM.Pretty.PrettyVar v => Prettyprinter.Internal.Pretty (Type.Check.HM.Pretty.FixityCtx v (Type.Check.HM.Type.Type loc v))
+ Type.Check.HM.Pretty: instance Type.Check.HM.Pretty.PrettyVar v => Prettyprinter.Internal.Pretty (Type.Check.HM.Type.Signature loc v)
+ Type.Check.HM.Pretty: instance Type.Check.HM.Pretty.PrettyVar v => Prettyprinter.Internal.Pretty (Type.Check.HM.Type.Type loc v)
+ Type.Check.HM.Pretty: instance Type.Check.HM.Pretty.PrintCons GHC.Base.String
+ Type.Check.HM.Pretty: instance Type.Check.HM.Pretty.PrintCons GHC.Types.Int
+ Type.Check.HM.Pretty: pretty :: Pretty a => a -> Doc ann
+ Type.Check.HM.Pretty: prettyList :: Pretty a => [a] -> Doc ann
+ Type.Check.HM.Pretty: type PrettyVar a = (Pretty a, PrintCons a, IsVar a)
+ Type.Check.HM.Term: instance (Data.Data.Data loc, Data.Data.Data var, Data.Data.Data a) => Data.Data.Data (Type.Check.HM.Term.Bind loc var a)
+ Type.Check.HM.Term: instance (GHC.Classes.Eq loc, GHC.Classes.Eq var, GHC.Classes.Eq a) => GHC.Classes.Eq (Type.Check.HM.Term.Bind loc var a)
+ Type.Check.HM.Term: instance (GHC.Show.Show loc, GHC.Show.Show var, GHC.Show.Show a) => GHC.Show.Show (Type.Check.HM.Term.Bind loc var a)
+ Type.Check.HM.Term: sortDeps :: Ord v => [(v, Term prim loc v)] -> [(v, Term prim loc v)]
+ Type.Check.HM.TyTerm: termSignature :: (Ord v, Eq loc) => TyTerm prim loc v -> Signature loc v
+ Type.Check.HM.TypeError: ConsArityMismatch :: loc -> var -> Int -> Int -> TypeError loc var
+ Type.Check.HM.TypeError: [consArityMismatch'actual] :: TypeError loc var -> Int
+ Type.Check.HM.TypeError: [consArityMismatch'expected] :: TypeError loc var -> Int
+ Type.Check.HM.TypeError: [consArityMismatch'loc] :: TypeError loc var -> loc
+ Type.Check.HM.TypeError: [consArityMismatch'tag] :: TypeError loc var -> var
- Type.Check.HM.Infer: Context :: Map v (Signature loc v) -> Context loc v
+ Type.Check.HM.Infer: Context :: Map v (Signature loc v) -> Map v (Signature loc v) -> Context loc v
- Type.Check.HM.Infer: inferTerm :: Lang q => ContextOf q -> TermOf q -> Either (ErrorOf q) (TyTermOf q)
+ Type.Check.HM.Infer: inferTerm :: Lang q => ContextOf q -> TermOf q -> Either [ErrorOf q] (TyTermOf q)
- Type.Check.HM.Infer: inferType :: Lang q => ContextOf q -> TermOf q -> Either (ErrorOf q) (TypeOf q)
+ Type.Check.HM.Infer: inferType :: Lang q => ContextOf q -> TermOf q -> Either [ErrorOf q] (TypeOf q)
- Type.Check.HM.Lang: getPrimType :: Lang q => Prim q -> TypeOf q
+ Type.Check.HM.Lang: getPrimType :: Lang q => Src q -> Prim q -> TypeOf q
- Type.Check.HM.Term: Bind :: loc -> var -> r -> Bind loc var r
+ Type.Check.HM.Term: Bind :: loc -> var -> a -> Bind loc var a
- Type.Check.HM.Term: CaseAlt :: loc -> v -> [Typed loc v (loc, v)] -> Type loc v -> a -> CaseAlt loc v a
+ Type.Check.HM.Term: CaseAlt :: loc -> v -> [(loc, v)] -> a -> CaseAlt loc v a
- Type.Check.HM.Term: Constr :: loc -> Type loc v -> v -> TermF prim loc v r
+ Type.Check.HM.Term: Constr :: loc -> v -> TermF prim loc v r
- Type.Check.HM.Term: [bind'lhs] :: Bind loc var r -> var
+ Type.Check.HM.Term: [bind'lhs] :: Bind loc var a -> var
- Type.Check.HM.Term: [bind'loc] :: Bind loc var r -> loc
+ Type.Check.HM.Term: [bind'loc] :: Bind loc var a -> loc
- Type.Check.HM.Term: [bind'rhs] :: Bind loc var r -> r
+ Type.Check.HM.Term: [bind'rhs] :: Bind loc var a -> a
- Type.Check.HM.Term: [caseAlt'args] :: CaseAlt loc v a -> [Typed loc v (loc, v)]
+ Type.Check.HM.Term: [caseAlt'args] :: CaseAlt loc v a -> [(loc, v)]
- Type.Check.HM.Term: constrE :: loc -> Type loc v -> v -> Term prim loc v
+ Type.Check.HM.Term: constrE :: loc -> v -> Term prim loc v
- Type.Check.HM.Term: data Bind loc var r
+ Type.Check.HM.Term: data Bind loc var a
- Type.Check.HM.Term: freeVars :: Ord v => Term lprim oc v -> Set v
+ Type.Check.HM.Term: freeVars :: Ord v => Term prim loc v -> Set v

Files

hindley-milner-type-check.cabal view
@@ -1,5 +1,5 @@ name:                   hindley-milner-type-check-version:                0.1.0.0+version:                0.1.1.0 synopsis:               Type inference for Hindley-Milner based languages description:     This package contains an implemention of Hindley-Milner inference algorithm.@@ -55,6 +55,7 @@   default-language:     Haskell2010    default-extensions:+    ConstraintKinds     DeriveDataTypeable     DeriveFunctor,     DeriveFoldable,@@ -73,9 +74,10 @@     TemplateHaskell,     TupleSections,     TypeFamilies,+    TypeApplications     TypeSynonymInstances -test-suite hindley-milner-tests+test-suite hindley-milner-type-check-tests   Type:                exitcode-stdio-1.0   Ghc-options:         -Wall -threaded -rtsopts   Default-Language:    Haskell2010
src/Type/Check/HM/Infer.hs view
@@ -11,7 +11,7 @@ -- >   type Src  TestLang = ()              -- ^ define type for source code locations -- >   type Var  TestLang = Text            -- ^ define type for variables -- >   type Prim TestLang = NoPrim          -- ^ define type for primitive operators--- >   getPrimType _ = error "No primops"   -- ^ reports types for primitives+-- >   getPrimType _ _ = error "No primops"   -- ^ reports types for primitives -- -- Also we define context for type inference that holds types for all known variables -- Often it defines types for all global variables or functions that are external.@@ -40,17 +40,23 @@     Context(..)   , insertCtx   , lookupCtx+  , insertConstructorCtx+  , lookupConstructorCtx   , ContextOf   -- * Inference   , inferType   , inferTerm+  , inferTypeList+  , inferTermList   , subtypeOf   , unifyTypes   -- * Utils   , closeSignature+  , printInfer ) where  import Control.Monad.Identity+import Control.Monad.Writer.Strict  import Control.Applicative import Control.Arrow (second)@@ -69,29 +75,49 @@ import Type.Check.HM.Type import Type.Check.HM.TypeError import Type.Check.HM.TyTerm+import Type.Check.HM.Pretty  import qualified Data.Map.Strict as M import qualified Data.Set as S import qualified Data.List as L  -- | Context holds map of proven signatures for free variables in the expression.-newtype Context loc v = Context { unContext :: Map v (Signature loc v) }-  deriving (Show, Eq, Semigroup, Monoid)+data Context loc v = Context+  { context'binds         :: Map v (Signature loc v)   -- ^ known binds+  , context'constructors  :: Map v (Signature loc v)   -- ^ known constructors for user-defined types+  }+  deriving (Show, Eq) +instance Ord v => Semigroup (Context loc v) where+  (<>) (Context bs1 cs1) (Context bs2 cs2) = Context (bs1 <> bs2) (cs1 <> cs2)++instance Ord v => Monoid (Context loc v) where+  mempty = Context mempty mempty+ -- | Type synonym for context. type ContextOf q = Context (Src q) (Var q)  instance CanApply Context where-  apply subst = Context . fmap (apply subst) . unContext+  apply subst ctx = ctx+      { context'binds = fmap (apply subst) $ context'binds ctx+      }  -- | Insert signature into context insertCtx :: Ord v => v -> Signature loc v ->  Context loc v -> Context loc v-insertCtx v sign (Context ctx) = Context $ M.insert v sign ctx+insertCtx v sign (Context binds cons) = Context (M.insert v sign binds) cons  -- | Lookup signature by name in the context of inferred terms. lookupCtx :: Ord v => v -> Context loc v -> Maybe (Signature loc v)-lookupCtx v (Context ctx) = M.lookup v ctx+lookupCtx v (Context binds _cons) = M.lookup v binds +-- | Insert signature into context+insertConstructorCtx :: Ord v => v -> Signature loc v ->  Context loc v -> Context loc v+insertConstructorCtx v sign (Context binds cons) = Context binds (M.insert v sign cons)++-- | Lookup signature by name in the context of inferred terms.+lookupConstructorCtx :: Ord v => v -> Context loc v -> Maybe (Signature loc v)+lookupConstructorCtx v (Context _binds cons) = M.lookup v cons+ -- | Wrapper with ability to generate fresh names data Name v   = Name v@@ -127,20 +153,24 @@ -- To check the term we need only variables that are free in the term. -- So we can safely remove everything else and speed up lookup times. restrictContext :: Ord v => Term prim loc v -> Context loc v -> Context loc v-restrictContext t (Context ctx) = Context $ M.intersection ctx fv+restrictContext t (Context binds cons) = Context (M.intersection binds fv) cons   where     fv = M.fromList $ fmap (, ()) $ S.toList $ freeVars t  wrapContextNames :: Ord v => Context loc v -> Context loc (Name v) wrapContextNames = fmapCtx Name   where-    fmapCtx f (Context m) = Context $ M.mapKeys f $ M.map (fmap f) m+    fmapCtx f (Context bs cs) = Context (wrap bs)  (wrap cs)+      where+        wrap = M.mapKeys f . M.map (fmap f)  wrapTermNames :: Term prim loc v -> Term prim loc (Name v) wrapTermNames = fmap Name  markProven :: Context loc v -> Context (Origin loc) v-markProven = Context . M.map (mapLoc Proven) . unContext+markProven (Context bs cs) = Context (mark bs) (mark cs)+  where+    mark = M.map (mapLoc Proven)  markUserCode :: Term prim loc v -> Term prim (Origin loc) v markUserCode = mapLoc UserCode@@ -180,33 +210,58 @@  -- | Type-inference monad. -- Contains integer counter for fresh variables and possibility to report type-errors.-newtype InferM loc var a = InferM (StateT Int (Except (TypeError loc (Name var))) a)-  deriving (Functor, Applicative, Monad, MonadState Int, MonadError (TypeError loc (Name var)))+newtype InferM loc var a = InferM (StateT Int (Writer [TypeError loc (Name var)]) a)+  deriving (Functor, Applicative, Monad, MonadState Int, MonadWriter [TypeError loc (Name var)])  -- | Runs inference monad.-runInferM :: InferM loc var a -> Either (TypeError loc (Name var)) a-runInferM (InferM m) = runExcept $ evalStateT m 0+runInferM :: InferM loc var a -> Either [TypeError loc (Name var)] a+runInferM (InferM m) = case runWriter $ evalStateT m 0 of+  (res, []) -> Right res+  (_, errs) -> Left errs  type InferOf q = InferM (Src q) (Var q) (Out (Prim q) (Src q) (Var q))  -- | Type-inference function. -- We provide a context of already proven type-signatures and term to infer the type.-inferType :: Lang q => ContextOf q -> TermOf q -> Either (ErrorOf q) (TypeOf q)+inferType :: Lang q => ContextOf q -> TermOf q -> Either [ErrorOf q] (TypeOf q) inferType ctx term = fmap termType $ inferTerm ctx term  -- | Infers types for all subexpressions of the given term. -- We provide a context of already proven type-signatures and term to infer the type.-inferTerm :: Lang q => ContextOf q -> TermOf q -> Either (ErrorOf q) (TyTermOf q)-inferTerm ctx term = join $-  bimap (fromTypeErrorNameVar . normaliseType) ((\(_, tyTerm) -> toTyTerm tyTerm)) $-    runInferM $ infer (wrapContextNames $ markProven $ restrictContext term ctx) (wrapTermNames $ markUserCode term)+inferTerm :: Lang q => ContextOf q -> TermOf q -> Either [ErrorOf q] (TyTermOf q)+inferTerm ctx term =+  case runInferM $ infer (wrapContextNames $ markProven $ restrictContext term ctx) (wrapTermNames $ markUserCode term) of+    Right (_, tyTerm) -> toTyTerm tyTerm+    Left errs -> Left $ fmap (fromTypeErrorNameVar . normaliseType) errs   where-    toTyTerm = fromTyTermNameVar . normaliseType . mapLoc fromOrigin+    toTyTerm = either (Left . pure) Right . fromTyTermNameVar . normaliseType . mapLoc fromOrigin  type Out prim loc var = ( Subst (Origin loc) (Name var)                         , TyTerm prim (Origin loc) (Name var)                         ) +-- | Infers types for bunch of terms. Terms can be recursive and not-sorted by depndencies.+inferTermList :: Lang q => ContextOf q -> [Bind (Src q) (Var q) (TermOf q)] -> Either [ErrorOf q] [Bind (Src q) (Var q) (TyTermOf q)]+inferTermList ctx defs = case defs of+  []  -> pure []+  d:_ ->+    let topLoc = bind'loc d+    in  fmap fromLetExpr $ inferTerm ctx (toLetExpr topLoc defs)+  where+    toLetExpr loc ds = letRecE loc (fmap toBind ds) (bottomE loc)++    fromLetExpr = \case+      (TyTerm (Fix (Ann _ (LetRec _ bs _)))) -> fmap fromBind bs+      _                                      -> error "Imposible happened. Found non let-rec expression"++    toBind = id+    fromBind = fmap TyTerm++-- | Infers types for bunch of terms. Terms can be recursive and not-sorted by depndencies.+-- It returns only top-level types for all terms.+inferTypeList :: Lang q => ContextOf q -> [Bind (Src q) (Var q) (TermOf q)] -> Either [ErrorOf q] [Bind (Src q) (Var q) (TypeOf q)]+inferTypeList ctx defs = fmap (fmap (fmap termType)) $ inferTermList ctx defs+ infer :: Lang q => ContextOf' q -> TermOf' q -> InferOf q infer ctx (Term (Fix x)) = case x of   Var loc v           -> inferVar ctx loc v@@ -216,14 +271,19 @@   Let loc v a         -> inferLet ctx loc (fmap Term v) (Term a)   LetRec loc vs a     -> inferLetRec ctx loc (fmap (fmap Term) vs) (Term a)   AssertType loc a ty -> inferAssertType ctx loc (Term a) ty-  Constr loc ty tag   -> inferConstr loc ty tag+  Constr loc tag      -> inferConstr ctx loc tag   Case loc e alts     -> inferCase ctx loc (Term e) (fmap (fmap Term) alts)   Bottom loc          -> inferBottom loc +retryWithBottom :: Lang q => TypeError (Src q) (Name (Var q)) -> Origin (Src q) -> InferOf q+retryWithBottom err loc = do+  tell [err]+  inferBottom loc+ inferVar :: Lang q => ContextOf' q -> Origin (Src q) -> Name (Var q) -> InferOf q inferVar ctx loc v = {- trace (unlines ["VAR", ppShow ctx, ppShow v]) $ -}-  case M.lookup v (unContext ctx) of-    Nothing  -> throwError $ NotInScopeErr (fromOrigin loc) v+  case lookupCtx v ctx of+    Nothing  -> retryWithBottom (NotInScopeErr (fromOrigin loc) v) loc     Just sig -> do ty <- newInstance $ setLoc loc sig                    return (mempty, tyVarE ty loc v) @@ -231,17 +291,19 @@ inferPrim loc prim =   return (mempty, tyPrimE ty loc prim)   where-    ty = fmap Name $ mapLoc UserCode $ getPrimType prim+    ty = fmap Name $ mapLoc UserCode $ getPrimType (fromOrigin loc) prim  inferApp :: Lang q => ContextOf' q -> Origin (Src q) -> TermOf' q -> TermOf' q -> InferOf q inferApp ctx loc f a = {- fmap (\res -> trace (unlines ["APP", ppCtx ctx, ppShow' f, ppShow' a, ppShow' $ snd res]) res) $-} do   tvn <- fmap (varT loc) $ freshVar   res <- inferTerms ctx [f, a]   case res of-    (phi, [(tf, f'), (ta, a')]) -> fmap (\subst ->-                                            let ty   = apply subst tvn-                                                term = tyAppE ty loc (apply subst f') (apply subst a')-                                            in  (subst, term)) $ unify phi tf (arrowT loc ta tvn)+    (phi, [(tf, f'), (ta, a')]) -> case unify phi tf (arrowT loc ta tvn) of+      Left err    -> retryWithBottom err loc+      Right subst -> let ty   = apply subst tvn+                         term = tyAppE ty loc (apply subst f') (apply subst a')+                     in  pure (subst, term)+     _               -> error "Impossible has happened!"  inferLam :: Lang q => ContextOf' q -> Origin (Src q) -> Name (Var q) -> TermOf' q -> InferOf q@@ -270,10 +332,11 @@   -> InferOf q inferLetRec ctx topLoc vs body = do   lhsCtx <- getTypesLhs vs-  (phi, rhsTyTerms) <- inferTerms (ctx <> Context (M.fromList lhsCtx)) exprBinds+  (phi, rhsTyTerms) <- inferTerms (ctx <> Context (M.fromList lhsCtx) mempty) exprBinds   let (tBinds, bindsTyTerms) = unzip rhsTyTerms-  (ctx1, lhsCtx1, subst) <- unifyRhs ctx lhsCtx phi tBinds-  inferBody bindsTyTerms ctx1 lhsCtx1 subst body+  case unifyRhs ctx lhsCtx phi tBinds of+    Right (ctx1, lhsCtx1, subst) -> inferBody bindsTyTerms ctx1 lhsCtx1 subst body+    Left err                     -> retryWithBottom err topLoc   where     exprBinds = fmap bind'rhs vs     locBinds  = fmap bind'loc vs@@ -303,60 +366,75 @@ inferAssertType :: Lang q => ContextOf' q -> Origin (Src q) -> TermOf' q -> TypeOf' q -> InferOf q inferAssertType ctx loc a ty = do   (phi, aTyTerm) <- infer ctx a-  subst <- genSubtypeOf phi ty (termType aTyTerm)-  let subst' = phi <> subst-  return (subst', apply subst' $ tyAssertTypeE loc aTyTerm ty)+  case genSubtypeOf phi ty (termType aTyTerm) of+    Right subst -> do+      let subst' = phi <> subst+      return (subst', apply subst' $ tyAssertTypeE loc aTyTerm ty)+    Left err -> retryWithBottom err loc -inferConstr :: Lang q => Origin (Src q) -> TypeOf' q -> Name (Var q) -> InferOf q-inferConstr loc ty tag = do-  vT <- newInstance $ typeToSignature ty-  return (mempty, tyConstrE loc vT tag)+inferConstr :: Lang q => ContextOf' q -> Origin (Src q) -> Name (Var q) -> InferOf q+inferConstr ctx loc tag = do+  case lookupConstructorCtx tag ctx of+    Just ty -> do+      vT <- newInstance ty+      return (mempty, tyConstrE loc vT tag)+    Nothing -> retryWithBottom (NotInScopeErr (fromOrigin loc) tag) loc  inferCase :: forall q . Lang q   => ContextOf' q -> Origin (Src q) -> TermOf' q -> [CaseAltOf' q (TermOf' q)]   -> InferOf q inferCase ctx loc e caseAlts = do   (phi, tyTermE) <- infer ctx e-  (psi, tRes, tyAlts) <- inferAlts phi (termType tyTermE) $ caseAlts-  return ( psi-         , apply psi $ tyCaseE tRes loc (apply psi tyTermE) $ fmap (applyAlt psi) tyAlts)+  mAlts <- inferAlts phi (termType tyTermE) $ caseAlts+  case mAlts of+    Just (psi, tRes, tyAlts) -> return ( psi+                                       , apply psi $ tyCaseE tRes loc (apply psi tyTermE) tyAlts)+    Nothing -> retryWithBottom (EmptyCaseExpr (fromOrigin loc)) loc   where-    inferAlts :: SubstOf' q -> TypeOf' q -> [CaseAltOf' q (TermOf' q)] -> InferM (Src q) (Var q) (SubstOf' q, TypeOf' q, [CaseAltOf' q (TyTermOf' q)])-    inferAlts substE tE alts =-      fmap (\(subst, _, tRes, as) -> (subst, tRes, L.reverse as)) $ foldM go (substE, tE, tE, []) alts+    inferAlts :: SubstOf' q -> TypeOf' q -> [CaseAltOf' q (TermOf' q)] -> InferM (Src q) (Var q) (Maybe (SubstOf' q, TypeOf' q, [CaseAltOf' q (TyTermOf' q)]))+    inferAlts substE tE alts = do+      (subst, _, mTRes, as) <- foldM go (substE, tE, Nothing, []) alts+      pure $ case mTRes of+        Just tRes -> Just (subst, tRes, L.reverse as)+        Nothing   -> Nothing       where-        go (subst, tyTop, _, res) alt = do-          (phi, tRes, alt1) <- inferAlt (applyAlt subst alt)-          let subst1 = subst <> phi-          subst2 <- unify subst1 (apply subst1 tyTop) (apply subst1 $ caseAlt'constrType alt1)-          return (subst2, apply subst2 tyTop, apply subst2 tRes, applyAlt subst2 alt1 : res)---    inferAlt :: CaseAltOf' q (TermOf' q) -> InferM (Src q) (Var q) (SubstOf' q, TypeOf' q, CaseAltOf' q (TyTermOf' q))-    inferAlt preAlt = do-      alt <- newCaseAltInstance preAlt-      let argVars = fmap  (\ty -> (snd $ typed'value ty, (fst $ typed'value ty, typed'type ty))) $ caseAlt'args alt-          ctx1 = Context (M.fromList $ fmap (second $ monoT . snd) argVars) <> ctx-      (subst, tyTermRhs) <- infer ctx1 $ caseAlt'rhs alt-      let args = fmap (\(v, (argLoc, tv)) -> Typed (apply subst tv) (argLoc, v)) argVars-          alt' = alt-                  { caseAlt'rhs = tyTermRhs-                  , caseAlt'args = args-                  , caseAlt'constrType = apply subst $ caseAlt'constrType alt-                  }-      return (subst, termType tyTermRhs, alt')--    newCaseAltInstance :: CaseAltOf' q (TermOf' q) -> InferM (Src q) (Var q) (CaseAltOf' q (TermOf' q))-    newCaseAltInstance alt = do-      tv <- newInstance $ typeToSignature $ getCaseType alt-      let (argsT, resT)= splitFunT tv-      return $ alt-        { caseAlt'constrType = resT-        , caseAlt'args = zipWith (\aT ty -> ty { typed'type = aT }) argsT $ caseAlt'args alt-        }+        go initSt@(subst, tyTop, mTyPrevRhs, res) alt = do+          mRes <- inferAlt (applyAlt subst alt)+          case mRes of+            Just (phi, tyRhs, tResExpected, alt1) -> do+              let subst1 = subst <> phi+              case unify subst1 (apply subst1 tyTop) (apply subst1 tResExpected) of+                Right subst2 -> do+                  case mTyPrevRhs of+                    Nothing -> pure (subst2, apply subst2 tyTop, Just $ apply subst2 tyRhs, applyAlt subst2 alt1 : res)+                    Just tyPrevRhs -> do+                      case unify subst2 (apply subst2 tyRhs) (apply subst2 tyPrevRhs) of+                        Right subst3 -> pure (subst3, apply subst3 tyTop, Just $ apply subst3 tyRhs, applyAlt subst3 alt1 : res)+                        Left err     -> do+                          tell [err]+                          pure initSt+                Left err     -> do+                  tell [err]+                  pure initSt+            Nothing -> do+              tell [NotInScopeErr (fromOrigin $ caseAlt'loc alt) (caseAlt'tag alt)]+              pure initSt -    getCaseType :: CaseAltOf' q (TermOf' q) -> TypeOf' q-    getCaseType CaseAlt{..} = funT (fmap typed'type caseAlt'args) caseAlt'constrType+    inferAlt :: CaseAltOf' q (TermOf' q) -> InferM (Src q) (Var q) (Maybe (SubstOf' q, TypeOf' q, TypeOf' q, CaseAltOf' q (TyTermOf' q)))+    inferAlt alt =+      case lookupConstructorCtx (caseAlt'tag alt) ctx of+        Just ctxConTy -> do+          conTy <- newInstance ctxConTy+          let (tyArgs, tyRes) = splitFunT conTy+              expectedArity = length tyArgs+              actualArity   = length $ caseAlt'args alt+          when (expectedArity /= actualArity) $ tell [ConsArityMismatch (fromOrigin $ caseAlt'loc alt) (caseAlt'tag alt) expectedArity actualArity]+          let argVars = zipWith (\ty (src, arg) -> (arg, (src, ty))) tyArgs (caseAlt'args alt)+              ctx1 = Context (M.fromList $ fmap (second $ monoT . snd) argVars) mempty <> ctx+          (subst, tyTermRhs) <- infer ctx1 $ caseAlt'rhs alt+          let alt' = alt { caseAlt'rhs = tyTermRhs }+          return $ Just (subst, termType tyTermRhs, tyRes, alt')+        Nothing -> pure Nothing      splitFunT :: TypeOf' q -> ([TypeOf' q], TypeOf' q)     splitFunT arrT = go [] arrT@@ -365,18 +443,9 @@           ArrowT _loc a b -> go (Type a : argsT) (Type b)           other           -> (reverse argsT, Type $ Fix other) +    applyAlt subst alt = alt { caseAlt'rhs = apply subst $ caseAlt'rhs alt } -    funT :: [TypeOf' q] -> TypeOf' q -> TypeOf' q-    funT argsT resT = foldr (\a b -> arrowT (getLoc a) a b) resT argsT -    applyAlt subst alt@CaseAlt{..} = alt-      { caseAlt'constrType = apply subst caseAlt'constrType-      , caseAlt'args       = fmap applyTyped caseAlt'args-      , caseAlt'rhs        = apply subst caseAlt'rhs-      }-      where-        applyTyped ty@Typed{..} = ty { typed'type = apply subst $ typed'type }- inferBottom :: Lang q => Origin (Src q) -> InferOf q inferBottom loc = do   ty <- fmap (varT loc) freshVar@@ -414,11 +483,11 @@  -- | Unification function. Checks weather two types unify. -- First argument is current substitution.-unify :: (IsVar v, Show loc, MonadError (TypeError loc (Name v)) m)+unify :: (IsVar v, Show loc)   => Subst' loc v   -> Type' loc v   -> Type' loc v-  -> m (Subst' loc v)+  -> Either (TypeError loc (Name v)) (Subst' loc v) unify phi (Type (Fix x)) (Type (Fix y)) = {- trace (unlines ["UNIFY", ppShow tx, ppShow ty]) $ -}   case (x, y) of     (VarT loc tvn, t) ->@@ -459,11 +528,11 @@   | memberVarSet tvn (tyVars ty)  = throwError $ OccursErr (fromOrigin loc) (mapLoc fromOrigin ty)   | otherwise                     = return $ phi <> delta tvn ty -unifyl :: (IsVar v, Show loc, MonadError (TypeError loc (Name v)) m)+unifyl :: (IsVar v, Show loc)   => Subst' loc v   -> [Type' loc v]   -> [Type' loc v]-  -> m (Subst' loc v)+  -> Either (TypeError loc (Name v)) (Subst' loc v) unifyl subst as bs = foldr go (return subst) $ zip as bs   where     go (a, b) eSubst = (\t -> unify t a b) =<< eSubst@@ -475,11 +544,11 @@   join $ bimap (fromTypeErrorNameVar . normaliseType) (fromSubstNameVar . fromSubstOrigin) $     genSubtypeOf mempty (fmap Name $ mapLoc Proven a) (fmap Name $ mapLoc UserCode b) -genSubtypeOf :: (IsVar v, Show loc, MonadError (TypeError loc (Name v)) m)+genSubtypeOf :: (IsVar v, Show loc)   => Subst' loc v   -> Type' loc v   -> Type' loc v-  -> m (Subst' loc v)+  -> Either (TypeError loc (Name v)) (Subst' loc v) genSubtypeOf phi tx@(Type (Fix x)) ty@(Type (Fix y)) = case (x, y) of   (_, VarT _ _) -> unify phi tx ty   (ConT locA n xs, ConT locB m ys) ->@@ -498,11 +567,11 @@     subtypeErr locA locB = throwError       $ SubtypeErr (chooseUserOrigin locA locB) (mapLoc fromOrigin tx) (mapLoc fromOrigin ty) -subtypeOfL :: (IsVar v, Show loc, MonadError (TypeError loc (Name v)) m)+subtypeOfL :: (IsVar v, Show loc)   => Subst' loc v   -> [Type' loc v]   -> [Type' loc v]-  -> m (Subst' loc v)+  -> Either (TypeError loc (Name v)) (Subst' loc v) subtypeOfL subst as bs = foldr go (return subst) $ zip as bs   where     go (a, b) eSubst = (\t -> genSubtypeOf t a b) =<< eSubst@@ -514,7 +583,7 @@ addDecls vs ctx =   foldM  (\c b -> addDecl unknowns b c) ctx vs   where-    unknowns = foldMap tyVars $ unContext ctx+    unknowns = foldMap tyVars $ context'binds ctx  addDecl :: forall loc v . IsVar v   => VarSet' loc v@@ -523,7 +592,8 @@   -> InferM loc v (Context' loc v) addDecl unknowns b ctx = do   scheme <- toScheme unknowns (bind'rhs b)-  return $ Context . M.insert (bind'lhs b) scheme . unContext $ ctx+  return $ ctx+    { context'binds = M.insert (bind'lhs b) scheme . context'binds $ ctx }   where     toScheme :: VarSet' loc v -> Type' loc v -> InferM loc v (Signature' loc v)     toScheme uVars ty = do@@ -566,6 +636,7 @@     SubtypeErr loc tA tB -> liftA2 (SubtypeErr loc) (fromTypeNameVar tA) (fromTypeNameVar tB)     NotInScopeErr loc v  -> fmap (NotInScopeErr loc) $ fromNameVar v     EmptyCaseExpr loc    -> pure $ EmptyCaseExpr loc+    ConsArityMismatch loc v expected actual -> fmap (\x -> ConsArityMismatch loc x expected actual) (fromNameVar v)     FreshNameFound       -> pure FreshNameFound  fromTypeNameVar :: Type loc (Name var) -> Either (TypeError loc var) (Type loc var)@@ -590,19 +661,18 @@       Let loc bind a      -> fmap (\b -> Let loc b a) $ fromBind bind       LetRec loc binds a  -> fmap (\bs -> LetRec loc bs a) $ mapM fromBind binds       AssertType loc a ty -> fmap (AssertType loc a) $ fromTypeNameVar ty-      Constr loc t v      -> liftA2 (Constr loc) (fromTypeNameVar t) (fromNameVar v)+      Constr loc v        -> fmap (Constr loc) (fromNameVar v)       Bottom loc          -> pure $ Bottom loc       Case loc e alts     -> fmap (Case loc e) $ mapM fromAlt alts      fromBind b = fmap (\a -> b { bind'lhs = a }) $ fromNameVar $ bind'lhs b      fromAlt alt@CaseAlt{..} =-      liftA3 (\tag args constrType -> alt { caseAlt'tag = tag, caseAlt'args = args, caseAlt'constrType = constrType })+      liftA2 (\tag args -> alt { caseAlt'tag = tag, caseAlt'args = args })         (fromNameVar caseAlt'tag)-        (mapM fromTyped caseAlt'args)-        (fromTypeNameVar caseAlt'constrType)+        (mapM fromAltArg caseAlt'args) -    fromTyped Typed{..} = liftA2 Typed (fromTypeNameVar typed'type) (mapM fromNameVar typed'value)+    fromAltArg (loc, v) = fmap (loc , ) (fromNameVar v)  fromSubstNameVar :: Ord v => Subst loc (Name v) -> Either (TypeError loc v) (Subst loc v) fromSubstNameVar (Subst m) = fmap (Subst . M.fromList) $ mapM uncover $ M.toList m@@ -617,4 +687,13 @@ closeSignature argTys sig = apply (Subst $ M.fromList $ zip argNames argTys) monoTy   where     (argNames, monoTy) = splitSignature sig++----------------------------------------------------------------------------------++-- | Pretty printer for result of type-inference+printInfer :: (PrettyLang q) => (Either [ErrorOf q] (TypeOf q)) -> IO ()+printInfer = \case+  Right ty  -> putStrLn $ show $ pretty ty+  Left errs -> mapM_ (putStrLn . (++ "\n") . show . pretty) errs+ 
src/Type/Check/HM/Lang.hs view
@@ -8,8 +8,10 @@   , TyTermOf   , SubstOf   , ErrorOf+  , PrettyLang ) where +import Type.Check.HM.Pretty import Type.Check.HM.Term import Type.Check.HM.Subst import Type.Check.HM.Type@@ -48,7 +50,7 @@   type Prim q    -- | Reports type for primitive.-  getPrimType :: Prim q -> TypeOf q+  getPrimType :: Src q -> Prim q -> TypeOf q  -- | Types of our language type TypeOf q = Type (Src q) (Var q)@@ -64,5 +66,7 @@  -- | Type substitutions type SubstOf q = Subst (Src q) (Var q)++type PrettyLang q = (Lang q, PrettyVar (Var q), Pretty (Src q))  
src/Type/Check/HM/Pretty.hs view
@@ -1,10 +1,12 @@ {-# OPTIONS_GHC -Wno-orphans #-} -- | Pretty printer for types and terms. module Type.Check.HM.Pretty(-    HasPrefix(..)+    PrettyVar+  , FixityCtx(..)   , PrintCons(..)   , OpFix(..)   , Fixity(..)+  , Pretty(..) ) where  import Data.Bool@@ -15,101 +17,116 @@  import Type.Check.HM.Type import Type.Check.HM.Term---- | Class to querry fixity of infix operations.-class IsVar v => HasPrefix v where-  getFixity :: v -> Maybe OpFix--instance HasPrefix Text where-  getFixity = const Nothing+import Type.Check.HM.TypeError -instance HasPrefix String where-  getFixity = const Nothing+-- | Type to querry fixity of infix operations in type variables.+data FixityCtx var a = FixityCtx+  { fixity'context :: var -> Maybe OpFix   -- ^ Function that provides fixity-type for a given variable+  , fixity'data    :: a                    -- ^ content+  } -instance HasPrefix Int where-  getFixity = const Nothing+-- | Ignores fixity information+noFixity :: forall v a . a -> FixityCtx v a+noFixity = FixityCtx (const Nothing)  -- | This class is useful to define the way to print special cases -- like constructors for tuples or lists. class PrintCons v where   printCons :: v -> [Doc ann] -> Doc ann +instance PrintCons Int where+  printCons name args = hsep $ pretty name : args++instance PrintCons String where+  printCons name args = hsep $ pretty name : args+ instance PrintCons Text where   printCons name args = hsep $ pretty name : args -isPrefix :: HasPrefix v => v -> Bool-isPrefix = isNothing . getFixity+isPrefix :: (v -> Maybe OpFix) -> v -> Bool+isPrefix getFixity = isNothing . getFixity -isInfix :: HasPrefix v => v -> Bool-isInfix  = not . isPrefix+isInfix :: (v -> Maybe OpFix) -> v -> Bool+isInfix a = not . isPrefix a -instance (Pretty v, PrintCons v, HasPrefix v) => Pretty (Signature loc v) where-  pretty = foldFix go . unSignature+type PrettyVar a = (Pretty a, PrintCons a, IsVar a)++instance (PrettyVar v) => Pretty (Signature loc v) where+  pretty = pretty . noFixity @v++instance (PrettyVar v) => Pretty (FixityCtx v (Signature loc v)) where+  pretty (FixityCtx getFixity sign) = foldFix go $ unSignature sign     where       go = \case         ForAllT _ _ r -> r-        MonoT ty      -> pretty ty+        MonoT ty      -> pretty (FixityCtx getFixity ty) -instance (HasPrefix v, PrintCons v, Pretty v) => Pretty (Type loc v) where-  pretty = go False initCtx . unType+instance (PrettyVar v) => Pretty (Type loc v) where+  pretty = pretty . noFixity @v++instance (PrettyVar v) => Pretty (FixityCtx v (Type loc v)) where+  pretty (FixityCtx getFixity ty) = go False initCtx $ unType ty     where       go :: Bool -> FixityContext v -> Fix (TypeF loc v) -> Doc ann       go isArrPrev ctx (Fix expr) = case expr of         VarT _ name   -> pretty name-        ConT _ name [a, b] | isInfix name -> fromBin name a b+        ConT _ name [a, b] | isInfix getFixity name -> fromBin name a b         ConT _ name as -> fromCon isArrPrev name as         ArrowT _ a b -> fromArrow a b         TupleT _ as -> fromTuple as         ListT _ a -> fromList a         where-          fromCon isArr name args = maybeParens (not (null args) && not isArr && needsParens ctx OpFunAp) $+          fromCon isArr name args = maybeParens (not (null args) && not isArr && needsParens getFixity ctx OpFunAp) $             printCons name $ fmap (go False (FcRight OpFunAp)) args -          fromBin op a b = maybeParens (needsParens ctx (Op op)) $ hsep+          fromBin op a b = maybeParens (needsParens getFixity ctx (Op op)) $ hsep             [ go True (FcLeft $ Op op) a             , pretty op             , go True (FcRight $ Op op) b             ] -          fromArrow a b = maybeParens (needsParens ctx ArrowOp) $ hsep+          fromArrow a b = maybeParens (needsParens getFixity ctx ArrowOp) $ hsep             [ go True (FcLeft ArrowOp ) a             , "->"             , go True (FcRight ArrowOp) b             ] -          fromTuple as = parens $ hsep $ punctuate comma $ fmap (pretty . Type) as+          fromTuple as = parens $ hsep $ punctuate comma $ fmap (pretty . FixityCtx getFixity . Type) as -          fromList a = brackets $ pretty $ Type a+          fromList a = brackets $ pretty $ FixityCtx getFixity $ Type a        initCtx = FcNone  maybeParens :: Bool -> Doc ann -> Doc ann maybeParens cond = bool id parens cond -needsParens :: HasPrefix v => FixityContext v -> Operator v -> Bool-needsParens = \case+needsParens :: (v -> Maybe OpFix) -> FixityContext v -> Operator v -> Bool+needsParens getFixity = \case   FcNone      -> const False   FcLeft ctx  -> fcLeft ctx   FcRight ctx -> fcRight ctx   where     fcLeft ctxt op-      | comparePrec ctxt op == PoLT = False-      | comparePrec ctxt op == PoGT = True-      | comparePrec ctxt op == PoNC = True+      | comparePrec' ctxt op == PoLT = False+      | comparePrec' ctxt op == PoGT = True+      | comparePrec' ctxt op == PoNC = True       -- otherwise the two operators have the same precedence-      | fixity ctxt /= fixity op = True-      | fixity ctxt == FixLeft = False+      | fixity' ctxt /= fixity' op = True+      | fixity' ctxt == FixLeft = False       | otherwise = True      fcRight ctxt op-      | comparePrec ctxt op == PoLT = False-      | comparePrec ctxt op == PoGT = True-      | comparePrec ctxt op == PoNC = True+      | comparePrec' ctxt op == PoLT = False+      | comparePrec' ctxt op == PoGT = True+      | comparePrec' ctxt op == PoNC = True       -- otherwise the two operators have the same precedence-      | fixity ctxt /= fixity op = True-      | fixity ctxt == FixRight = False+      | fixity' ctxt /= fixity' op = True+      | fixity' ctxt == FixRight = False       | otherwise = True +    comparePrec' = comparePrec getFixity+    fixity' = fixity getFixity+ data PartialOrdering = PoLT | PoGT | PoEQ | PoNC   deriving Eq @@ -136,14 +153,14 @@   [ (Op "->", OpFix FixRight 2) ] -} -getFixityEnv :: HasPrefix v => Operator v -> Maybe OpFix-getFixityEnv = \case+getFixityEnv :: (v -> Maybe OpFix) -> Operator v -> Maybe OpFix+getFixityEnv getFixity = \case   OpFunAp -> Nothing   Op v    -> getFixity v   ArrowOp -> Just $ OpFix FixRight 2 -comparePrec :: HasPrefix v => Operator v -> Operator v -> PartialOrdering-comparePrec a b = case (getFixityEnv a, getFixityEnv b) of+comparePrec :: (v -> Maybe OpFix) -> Operator v -> Operator v -> PartialOrdering+comparePrec getFixity a b = case (getFixityEnv getFixity a, getFixityEnv getFixity b) of   (Just opA, Just opB) -> toPo (opFix'prec opA) (opFix'prec opB)   _                    -> PoNC   where@@ -153,13 +170,17 @@       | otherwise = PoEQ  -fixity :: HasPrefix v => Operator v -> Fixity-fixity op = maybe FixNone opFix'fixity $ getFixityEnv op+fixity :: (v -> Maybe OpFix) -> Operator v -> Fixity+fixity getFixity op = maybe FixNone opFix'fixity $ getFixityEnv getFixity op ----------------------------------------+-----------------------------------------------------------------+-- pretty terms -instance (HasPrefix v, PrintCons v, Pretty v, Pretty prim) => Pretty (Term prim loc v) where-  pretty (Term x) = foldFix prettyTermF x+instance (PrettyVar v, Pretty prim) => Pretty (Term prim loc v) where+  pretty = pretty . noFixity @v++instance (PrettyVar v, Pretty prim) => Pretty (FixityCtx v (Term prim loc v)) where+  pretty (FixityCtx getFixity (Term x)) = foldFix prettyTermF x     where       prettyTermF = \case         Var _ v            -> pretty v@@ -168,8 +189,8 @@         Lam _ v a          -> parens $ hsep [hcat ["\\", pretty v], "->", a]         Let _ v a          -> onLet [v] a         LetRec _ vs a      -> onLet vs a-        AssertType _ r sig -> parens $ hsep [r, "::", pretty sig]-        Constr _ _ tag     -> pretty tag+        AssertType _ r sig -> parens $ hsep [r, "::", pretty $ FixityCtx getFixity sig]+        Constr _ tag       -> pretty tag         Case _ e alts      -> vcat [ hsep ["case", e, "of"], indent 4 $ vcat $ fmap onAlt alts]         Bottom _           -> "_|_"         where@@ -178,7 +199,28 @@                  , hsep ["in ", body]]            onAlt CaseAlt{..} = hsep-            [ pretty caseAlt'tag, hsep $ fmap (pretty . snd . typed'value) caseAlt'args+            [ pretty caseAlt'tag, hsep $ fmap (pretty . snd) caseAlt'args             , "->"             , caseAlt'rhs ]++-----------------------------------------------------------------+-- pretty errors++instance (Pretty loc, PrettyVar var) => Pretty (TypeError loc var) where+  pretty = pretty . noFixity @var++instance (Pretty loc, PrettyVar var) => Pretty (FixityCtx var (TypeError loc var)) where+  pretty (FixityCtx getFixity tyErr) = case tyErr of+    OccursErr src name     -> err src $ hsep ["Occurs error", prettyTy name]+    UnifyErr src tyA tyB   -> err src $ hsep ["Type mismatch got", inTicks $ prettyTy tyB, "expected", inTicks $ prettyTy tyA]+    NotInScopeErr src name -> err src $ hsep ["Not in scope", pretty name]+    SubtypeErr src tyA tyB -> err src $ hsep ["Subtype error", inTicks $ prettyTy tyB, "expected", inTicks $ prettyTy tyA]+    EmptyCaseExpr src      -> err src $ "Case-expression should have at least one alternative case"+    FreshNameFound         -> "Impossible happened: failed to eliminate fresh name on type-checker stage"+    ConsArityMismatch src tag expected actual -> err src $ hsep ["Case-expression arguments mismatch for ", pretty tag, ". Expected ", pretty expected, " arguments, but got ", pretty actual]+    where+      err src msg = vcat [hcat [pretty src, ": error: "], indent 4 msg]+      inTicks x = hcat ["'", x, "'"]+      prettyTy = pretty . FixityCtx getFixity+ 
src/Type/Check/HM/Term.hs view
@@ -15,12 +15,15 @@   , constrE   , bottomE   , freeVars+  , sortDeps ) where  import Control.Arrow  import Data.Data+import Data.Graph import Data.Fix+import Data.Foldable import Data.Set (Set) import Data.Eq.Deriving import Data.Ord.Deriving@@ -30,6 +33,7 @@ import Type.Check.HM.Type  import qualified Data.Set as S+import qualified Data.Sequence as Seq  -- | Term functor. The arguments are -- @loc@ for source code locations, @v@ for variables, @r@ for recurion.@@ -42,8 +46,7 @@     | LetRec loc [Bind loc v r] r     -- ^ Recursive  let bindings     | AssertType loc r (Type loc v)   -- ^ Assert type.     | Case loc r [CaseAlt loc v r]    -- ^ case alternatives-    | Constr loc (Type loc v) v       -- ^ constructor with tag and arity, also we should provide the type-                                      --   of constructor as a function for a type-checker+    | Constr loc v                    -- ^ constructor with tag     | Bottom loc                      -- ^ value of any type that means failed program.     deriving (Show, Eq, Functor, Foldable, Traversable, Data) @@ -53,10 +56,8 @@   -- ^ source code location   , caseAlt'tag   :: v   -- ^ tag of the constructor-  , caseAlt'args  :: [Typed loc v (loc, v)]+  , caseAlt'args  :: [(loc, v)]   -- ^ arguments of the pattern matching-  , caseAlt'constrType :: Type loc v-  -- ^ type of the result expression, they should be the same for all cases   , caseAlt'rhs   :: a   -- ^ right-hand side of the case-alternative   }@@ -65,10 +66,10 @@ -- | Local variable definition. -- -- > let lhs = rhs in ...-data Bind loc var r = Bind+data Bind loc var a = Bind   { bind'loc :: loc             -- ^ Source code location   , bind'lhs :: var             -- ^ Variable name-  , bind'rhs :: r               -- ^ Definition (right-hand side)+  , bind'rhs :: a               -- ^ Definition (right-hand side)   } deriving (Show, Eq, Functor, Foldable, Traversable, Data)  $(deriveShow1 ''TermF)@@ -97,17 +98,14 @@         LetRec loc vs a -> Fix $ LetRec loc (fmap (\b ->  b { bind'lhs = f $ bind'lhs b }) vs) a         AssertType loc r sig -> Fix $ AssertType loc r (fmap f sig)         Case loc a alts -> Fix $ Case loc a $ fmap (mapAlt f) alts-        Constr loc ty v -> Fix $ Constr loc (fmap f ty) (f v)+        Constr loc v -> Fix $ Constr loc (f v)         Bottom loc -> Fix $ Bottom loc        mapAlt g alt@CaseAlt{..} = alt         { caseAlt'tag  = f caseAlt'tag-        , caseAlt'args = fmap (mapTyped g) caseAlt'args-        , caseAlt'constrType = fmap f caseAlt'constrType+        , caseAlt'args = fmap (second g) caseAlt'args         } -      mapTyped g Typed{..} = Typed (fmap f typed'type) (second g typed'value)- -- | 'varE' @loc x@ constructs a variable whose name is @x@ with source code at @loc@. varE :: loc -> var -> Term prim loc var varE loc = Term . Fix . Var loc@@ -142,8 +140,8 @@ caseE loc (Term e) alts = Term $ Fix $ Case loc e $ fmap (fmap unTerm) alts  -- | 'constrE' @loc ty tag arity@ constructs constructor tag expression.-constrE :: loc -> Type loc v -> v -> Term prim loc v-constrE loc ty tag = Term $ Fix $ Constr loc ty tag+constrE :: loc -> v -> Term prim loc v+constrE loc tag = Term $ Fix $ Constr loc tag  -- | 'bottomE' @loc@ constructs bottom value. bottomE :: loc -> Term prim loc v@@ -162,7 +160,7 @@     Let loc _ _ -> loc     LetRec loc _ _ -> loc     AssertType loc _ _ -> loc-    Constr loc _ _ -> loc+    Constr loc _ -> loc     Case loc _ _ -> loc     Bottom loc -> loc @@ -177,20 +175,17 @@         Let loc v a  -> Fix $ Let (f loc) (v { bind'loc = f $ bind'loc v }) a         LetRec loc vs a -> Fix $ LetRec (f loc) (fmap (\b ->  b { bind'loc = f $ bind'loc b }) vs) a         AssertType loc r sig -> Fix $ AssertType (f loc) r (mapLoc f sig)-        Constr loc ty v -> Fix $ Constr (f loc) (mapLoc f ty) v+        Constr loc v -> Fix $ Constr (f loc) v         Case loc e alts -> Fix $ Case (f loc) e (fmap mapAlt alts)         Bottom loc -> Fix $ Bottom (f loc)        mapAlt alt@CaseAlt{..} = alt         { caseAlt'loc  = f caseAlt'loc-        , caseAlt'args = fmap mapTyped caseAlt'args-        , caseAlt'constrType = mapLoc f caseAlt'constrType+        , caseAlt'args = fmap (first f) caseAlt'args         } -      mapTyped (Typed ty val) = Typed (mapLoc f ty) (first f val)- -- | Get free variables of the term.-freeVars :: Ord v => Term lprim oc v -> Set v+freeVars :: Ord v => Term prim loc v -> Set v freeVars = foldFix go . unTerm   where     go = \case@@ -205,28 +200,37 @@                              in  (mappend (freeBinds binds) body) `S.difference` lhs       AssertType _ a _    -> a       Case _ e alts       -> mappend e (foldMap freeVarAlts alts)-      Constr _ _ _        -> mempty+      Constr _ _          -> mempty       Bottom _            -> mempty      freeBinds = foldMap bind'rhs -    freeVarAlts CaseAlt{..} = caseAlt'rhs `S.difference` (S.fromList $ fmap (snd . typed'value) caseAlt'args)+    freeVarAlts CaseAlt{..} = caseAlt'rhs `S.difference` (S.fromList $ fmap snd caseAlt'args)  instance TypeFunctor (Term prim) where   mapType f (Term term) = Term $ foldFix go term     where       go = \case-        Constr loc ty cons       -> Fix $ Constr loc (f ty) cons-        Case loc e alts          -> Fix $ Case loc e $ fmap applyAlt alts-        other                    -> Fix other--      applyAlt alt@CaseAlt{..} = alt-        { caseAlt'args       = fmap applyTyped caseAlt'args-        , caseAlt'constrType = f caseAlt'constrType-        }--      applyTyped ty@Typed{..} = ty { typed'type = f typed'type }+        AssertType loc r ty  -> Fix $ AssertType loc r (f ty)+        other                 -> Fix other  instance CanApply (Term prim) where   apply subst term = mapType (apply subst) term++-------------------------------------------------------------------------+-- sort terms by dependency order (it ignores cyclic depepndencies)++sortDeps :: Ord v => [(v, Term prim loc v)] -> [(v, Term prim loc v)]+sortDeps = fromDepGraph . stronglyConnComp . toDepGraph+  where+    toDepGraph = fmap (\(name, term) -> ((name, term), name, S.toList $ freeVars term))++    fromDepGraph = toList . foldMap getVertex+      where+        getVertex = \case+          AcyclicSCC v -> Seq.singleton v+          CyclicSCC vs -> Seq.fromList vs+++ 
src/Type/Check/HM/TyTerm.hs view
@@ -3,6 +3,7 @@     Ann(..)   , TyTerm(..)   , termType+  , termSignature   , tyVarE   , tyPrimE   , tyAppE@@ -50,6 +51,9 @@ termType :: TyTerm prim loc v -> Type loc v termType (TyTerm (Fix (Ann ty _))) = ty +termSignature :: (Ord v, Eq loc) => TyTerm prim loc v -> Signature loc v+termSignature = typeToSignature . termType+ -- tyTerm :: Type loc v -> TermF loc var (Ann () ) -> TyTerm loc var tyTerm :: Type loc v -> TermF prim loc v (Fix (Ann (Type loc v) (TermF prim loc v))) -> TyTerm prim loc v tyTerm ty x = TyTerm $ Fix $ Ann ty x@@ -89,7 +93,7 @@  -- | 'constrE' @loc ty tag arity@ constructs constructor tag expression. tyConstrE :: loc -> Type loc v -> v -> TyTerm prim loc v-tyConstrE loc ty tag = tyTerm ty $ Constr loc ty tag+tyConstrE loc ty tag = tyTerm ty $ Constr loc tag  -- | 'bottomE' @loc@ constructs bottom value. tyBottomE :: Type loc v -> loc -> TyTerm prim loc v@@ -106,33 +110,22 @@         Let loc v a  -> Let (f loc) (v { bind'loc = f $ bind'loc v }) a         LetRec loc vs a -> LetRec (f loc) (fmap (\b ->  b { bind'loc = f $ bind'loc b }) vs) a         AssertType loc r sig -> AssertType (f loc) r (mapLoc f sig)-        Constr loc ty v -> Constr (f loc) (mapLoc f ty) v+        Constr loc v -> Constr (f loc) v         Case loc e alts -> Case (f loc) e (fmap (mapAlt f) alts)         Bottom loc -> Bottom (f loc)        mapAlt g alt@CaseAlt{..} = alt         { caseAlt'loc  = g caseAlt'loc-        , caseAlt'args = fmap (mapTyped g) caseAlt'args-        , caseAlt'constrType = mapLoc g caseAlt'constrType+        , caseAlt'args = fmap (first g) caseAlt'args         } -      mapTyped g (Typed ty val) = Typed (mapLoc g ty) (first g val)- instance TypeFunctor (TyTerm prim) where   mapType f (TyTerm x) = TyTerm $ foldFix go x     where       go (Ann ty term) = Fix $ Ann (f ty) $         case term of-          Constr loc cty cons -> Constr loc (f cty) cons-          Case loc e alts          -> Case loc e $ fmap applyAlt alts-          other                    -> other--      applyAlt alt@CaseAlt{..} = alt-        { caseAlt'args       = fmap applyTyped caseAlt'args-        , caseAlt'constrType = f caseAlt'constrType-        }--      applyTyped ty@Typed{..} = ty { typed'type = f typed'type }+          AssertType loc r t -> AssertType loc r (f t)+          other              -> other  instance CanApply (TyTerm prim) where   apply subst term = mapType (apply subst) term
src/Type/Check/HM/TypeError.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE DeriveAnyClass     #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DerivingStrategies #-}--- | This module contains types for structured type errors.+-- | This module contains types for type errors. module Type.Check.HM.TypeError where  import Control.DeepSeq (NFData)@@ -19,8 +19,14 @@   | UnifyErr   loc (Type loc var) (Type loc var)  -- ^ Unification error   | SubtypeErr loc (Type loc var) (Type loc var)  -- ^ Subtype error (happens on explicit type assertions)   | NotInScopeErr loc var                         -- ^ Missing signature in context for free-variable.+  | ConsArityMismatch+      { consArityMismatch'loc :: loc+      , consArityMismatch'tag :: var+      , consArityMismatch'expected :: Int+      , consArityMismatch'actual   :: Int+      } -- ^ mismatch of arity in pattern-matching   | EmptyCaseExpr loc                             -- ^ no case alternatives in the case expression-  | FreshNameFound                                -- ^ internal error with fresh name substitution+  | FreshNameFound                                -- ^ internal error with fresh name substitution. Should not normally occur if algorithm is correct.   deriving stock    (Show, Eq, Functor, Generic, Data)   deriving anyclass (NFData) @@ -30,6 +36,7 @@     UnifyErr loc tA tB   -> UnifyErr (f loc) (mapLoc f tA) (mapLoc f tB)     SubtypeErr loc tA tB -> SubtypeErr (f loc) (mapLoc f tA) (mapLoc f tB)     NotInScopeErr loc v  -> NotInScopeErr (f loc) v+    ConsArityMismatch loc tag expect actual -> ConsArityMismatch (f loc)  tag expect actual     EmptyCaseExpr loc    -> EmptyCaseExpr (f loc)     FreshNameFound       -> FreshNameFound @@ -39,6 +46,7 @@     UnifyErr _ a b     -> tyVars a <> tyVars b     SubtypeErr _ a b   -> tyVars a <> tyVars b     NotInScopeErr _ _  -> mempty+    ConsArityMismatch _ _ _ _ -> mempty     EmptyCaseExpr _    -> mempty     FreshNameFound     -> mempty @@ -49,6 +57,7 @@     NotInScopeErr _ _  -> mempty     EmptyCaseExpr _    -> mempty     FreshNameFound     -> mempty+    ConsArityMismatch _ _ _ _ -> mempty  instance CanApply TypeError where   apply f = \case
test/TM/NumLang.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeFamilies #-} -- | Tests for language with lambda calculus with numbers and booleans. module TM.NumLang where@@ -12,6 +13,8 @@ import qualified Type.Check.HM as T import qualified Data.Map.Strict as M +import Data.Text.Prettyprint.Doc+ infixr ~>  data CodeLoc = CodeLoc@@ -20,13 +23,21 @@   }   deriving (Show, Eq) +instance Pretty CodeLoc where+  pretty (CodeLoc row col) = hcat [pretty row, ":", pretty col]+ -- | Primitives of our language. -- We support integers and booleans data Prim-  = PInt CodeLoc Int     -- ^ integers-  | PBool CodeLoc Bool   -- ^ booleans+  = PInt Int     -- ^ integers+  | PBool Bool   -- ^ booleans   deriving (Show, Eq) +instance Pretty Prim where+  pretty = \case+    PInt  n -> pretty n+    PBool b -> pretty b+ -- | Type for variables type Var = String @@ -51,9 +62,9 @@   type Prim NumLang = Prim      -- ^ primitives    -- what type is assigned to primitive literals of the language-  getPrimType = \case-    PInt  loc _ -> T.conT loc "Int"  []-    PBool loc _ -> T.conT loc "Bool" []+  getPrimType loc = \case+    PInt  _ -> T.conT loc "Int"  []+    PBool _ -> T.conT loc "Bool" []  -- | Expressions for our language newtype Expr = Expr { unExpr :: T.Term Prim CodeLoc Var }@@ -67,11 +78,11 @@  -- | constructor for integer literals int :: Int -> Expr-int = Expr . T.primE defLoc . PInt defLoc+int = Expr . T.primE defLoc . PInt  -- | constructor for boolean literals bool :: Bool -> Expr-bool = Expr . T.primE defLoc . PBool defLoc+bool = Expr . T.primE defLoc . PBool  -- numeric expressions @@ -152,36 +163,75 @@ -- custom constructors  -- types for custom types-pointT, circleT, rectT :: Ty+pointT, circleT, rectT, colorT :: Ty pointT  = T.conT defLoc "Point" [] circleT = T.conT defLoc "Circle" [] rectT = T.conT defLoc "Rect" []+colorT = T.conT defLoc "Color" [] +maybeT :: Ty -> Ty+maybeT a = T.conT defLoc "Maybe" [a]+ -- | Point constructor point :: Expr -> Expr -> Expr-point = app2 (Expr $ T.constrE defLoc (intT ~> intT ~> pointT) "Point")+point = app2 (Expr $ T.constrE defLoc "Point")  circle :: Expr -> Expr -> Expr-circle = app2 (Expr $ T.constrE defLoc (pointT ~> intT ~> circleT) "Circle")+circle = app2 (Expr $ T.constrE defLoc "Circle")  rect :: Expr -> Expr -> Expr-rect = app2 (Expr $ T.constrE defLoc (pointT ~> pointT ~> rectT) "Rect")+rect = app2 (Expr $ T.constrE defLoc "Rect") +red, blue, green :: Expr+red   = Expr $ T.constrE defLoc "Red"+blue  = Expr $ T.constrE defLoc "Blue"+green = Expr $ T.constrE defLoc "Green"++just :: Expr -> Expr+just = app (Expr $ T.constrE defLoc "Just")++nothing :: Expr+nothing = Expr $ T.constrE defLoc "Nothing"+ casePoint :: Expr -> (Var, Var) -> Expr -> Expr casePoint (Expr e) (x, y) (Expr body) = Expr $ T.caseE defLoc e-  [T.CaseAlt defLoc "Point" [tyVar intT x, tyVar intT y] pointT body]+  [T.CaseAlt defLoc "Point" (caseArgs [x, y]) body]  caseCircle :: Expr -> (Var, Var) -> Expr -> Expr caseCircle (Expr e) (x, y) (Expr body) = Expr $ T.caseE defLoc e-  [T.CaseAlt defLoc "Circle" [tyVar pointT x, tyVar intT y] circleT body]+  [T.CaseAlt defLoc "Circle" (caseArgs [x, y]) body]  caseRect :: Expr -> (Var, Var) -> Expr -> Expr caseRect (Expr e) (x, y) (Expr body) = Expr $ T.caseE defLoc e-  [T.CaseAlt defLoc "Rect" [tyVar pointT x, tyVar pointT y] rectT body]+  [T.CaseAlt defLoc "Rect" (caseArgs [x, y]) body] -tyVar :: Ty -> Var -> T.Typed CodeLoc Var (CodeLoc, Var)-tyVar ty v = T.Typed ty (defLoc, v)+caseArgs :: [Var] -> [(CodeLoc, Var)]+caseArgs = fmap (\x -> (defLoc, x)) +data CaseColor = CaseColor+  { case'red   :: Expr+  , case'blue  :: Expr+  , case'green :: Expr+  }++caseColor :: Expr -> CaseColor -> Expr+caseColor (Expr e) CaseColor{..} = Expr $ T.caseE defLoc e+  [ T.CaseAlt defLoc "Red"   [] (unExpr $ case'red)+  , T.CaseAlt defLoc "Blue"  [] (unExpr $ case'blue)+  , T.CaseAlt defLoc "Green" [] (unExpr $ case'green)+  ]++data CaseMaybe = CaseMaybe+  { case'just    :: Expr -> Expr+  , case'nothing :: Expr+  }++caseMaybe :: Expr -> CaseMaybe -> Expr+caseMaybe (Expr e) CaseMaybe{..} = Expr $ T.caseE defLoc e+  [ T.CaseAlt defLoc "Just"    [(defLoc, "$justArg")] (unExpr $ case'just "$justArg")+  , T.CaseAlt defLoc "Nothing" []              (unExpr case'nothing)+  ]+ ---------------------------------------------------------- -- Type inference context --@@ -190,13 +240,29 @@  -- | Context contains types for all known definitions defContext :: T.Context CodeLoc Var-defContext = T.Context $ M.fromList $ mconcat-  [ booleans-  , nums-  , comparisons-  , [("if", forA $ T.monoT $ boolT ~> aT ~> aT ~> aT)]-  ]+defContext = T.Context+  { T.context'binds        = binds+  , T.context'constructors = cons+  }   where+    binds = M.fromList $ mconcat+      [ booleans+      , nums+      , comparisons+      , [("if", forA $ T.monoT $ boolT ~> aT ~> aT ~> aT)]+      ]++    cons = M.fromList+      [ "Point"  `is` (intT ~> intT ~> pointT)+      , "Circle" `is` (pointT ~> intT ~> circleT)+      , "Rect"   `is` (pointT ~> pointT ~> rectT)+      , "Red"    `is` colorT+      , "Blue"   `is` colorT+      , "Green"  `is` colorT+      , ("Just"    , forA $ T.monoT $ aT ~> maybeT aT)+      , ("Nothing" , forA $ T.monoT $ maybeT aT)+      ]+     booleans =       [ "&&"  `is` (boolT ~> boolT ~> boolT)       , "||"  `is` (boolT ~> boolT ~> boolT)@@ -230,6 +296,52 @@ boolExpr1 :: Expr boolExpr1 = andB (andB (notB ((intExpr1 `lte` 1000) `orB` (2 `gt` 0))) (bool True)) (5 `neq` (2 + 2)) +colorFun :: Expr+colorFun = lam "c" $ caseColor "c" CaseColor+  { case'red   = 1+  , case'blue  = 2+  , case'green = 3+  }++colorFun2 :: Expr+colorFun2 = lam "c" $ caseColor "c" CaseColor+  { case'red   = nothing+  , case'blue  = just $ bool True+  , case'green = just $ bool False+  }++colorFun3 :: Expr+colorFun3 = lam "c" $ caseColor "c" CaseColor+  { case'blue  = just $ bool True+  , case'green = just $ bool False+  , case'red   = nothing+  }++colorFun4 :: Expr+colorFun4 = lam "mc" $ caseMaybe "mc" CaseMaybe+  { case'just    = id+  , case'nothing = red+  }++colorFunFail :: Expr+colorFunFail = lam "c" $ caseColor "c" CaseColor+  { case'red   = 1+  , case'blue  = bool True+  , case'green = 3+  }++mapMaybe :: Expr+mapMaybe = lam "f" $ lam "ma" $ caseMaybe "ma" CaseMaybe+  { case'nothing = nothing+  , case'just    = \a -> just $ app "f" a+  }++bindMaybe :: Expr+bindMaybe = lam "ma" $ lam "mf" $ caseMaybe "ma" CaseMaybe+  { case'nothing = nothing+  , case'just    = \a -> app "mf" a+  }+ failExpr1 :: Expr failExpr1 = lam "x" $ 2 + "x" `eq` (bool True) @@ -319,9 +431,28 @@   , check "negate point"    (pointT ~> pointT)              negatePointLam   , check "rect square"     (rectT ~> intT)                 rectSquare   , check "inside circle 2" (circleT ~> pointT ~> boolT)    insideCircle2+  , check "color fun"       (colorT ~> intT)                colorFun+  , check "color fun 2"     (colorT ~> maybeT boolT)        colorFun2+  , check "color fun 3"     (colorT ~> maybeT boolT)        colorFun3+  , check "color fun 4"     (maybeT colorT ~> colorT)       colorFun4+  , check "map maybe"       ((aT ~> bT) ~> maybeT aT ~> maybeT bT) mapMaybe+  , check "bind maybe"      (maybeT aT ~> (aT ~> maybeT bT) ~> maybeT bT) bindMaybe+  , fails "color fun fail"  colorFunFail+  , checkList "list of expressions" [("a", intExpr1), ("b", boolExpr1), ("c", intFun1)]   ]   where+    aT = T.varT defLoc "a"+    bT = T.varT defLoc "b"+     infer = T.inferType defContext . unExpr     check msg ty expr = testCase msg $ Right ty @=? (infer expr)     fails msg expr = testCase msg $ assertBool "Detected wrong type" $ isLeft (infer expr)++    checkList msg exprs = testCase msg $ assertBool msg $ isRight (T.inferTermList defContext $ fmap (uncurry toBind) exprs)++----------------------------------------------------------++-- | Prints result of type-inference+printInfer :: Expr -> IO ()+printInfer (Expr e) = T.printInfer $ T.inferType defContext e 
test/TM/SKI.hs view
@@ -35,7 +35,7 @@   type Src  TestLang = ()   type Var  TestLang = Text   type Prim TestLang = NoPrim-  getPrimType _ = error "No primops"+  getPrimType _ _ = error "No primops"  -- I combinator termI,termK :: Term NoPrim () Text