git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
Infer types with class constraints (work in progress)
commit3ed6f052016064ef16285d1b5031960390e0751d authorPhil Freeman <paf31@cantab.net> dateTue 1 Mar 2016, 21:24 (11 years ago) parent38618a5
2 files changed+46−28
src/Language/PureScript/TypeChecker/Entailment.hs+35−21 src/Language/PureScript/TypeChecker/Types.hs+11−7
src/Language/PureScript/TypeChecker/Entailment.hs+35−21
@@ -18,6 +18,7 @@ import Control.Arrow (Arrow(..))
1818import Control.Monad.State
1919import Control.Monad.Error.Class (MonadError(..))
2020import Control.Monad.Writer.Class (MonadWriter(..))
21+import Control.Monad.Supply.Class (MonadSupply(..))
2122
2223import Language.PureScript.Crash
2324import Language.PureScript.AST
@@ -34,11 +35,11 @@ import qualified Language.PureScript.Constants as C
3435--
3536entails
3637 :: forall m
37− . (Functor m, Applicative m, MonadError MultipleErrors m, MonadWriter MultipleErrors m)
38+ . (Functor m, Applicative m, MonadError MultipleErrors m, MonadWriter MultipleErrors m, MonadSupply m)
3839 => ModuleName
3940 -> M.Map (Maybe ModuleName) (M.Map (Qualified (ProperName 'ClassName)) (M.Map (Qualified Ident) TypeClassDictionaryInScope))
4041 -> Constraint
41− -> m Expr
42+ -> m (Expr, [(Ident, Constraint)])
4243entails moduleName context = solve
4344 where
4445 forClassName :: Qualified (ProperName 'ClassName) -> [Type] -> [TypeClassDictionaryInScope]
@@ -54,12 +55,12 @@ entails moduleName context = solve
5455 findDicts :: Qualified (ProperName 'ClassName) -> Maybe ModuleName -> [TypeClassDictionaryInScope]
5556 findDicts cn = maybe [] M.elems . (>>= M.lookup cn) . flip M.lookup context
5657
57− solve :: Constraint -> m Expr
58+ solve :: Constraint -> m (Expr, [(Ident, Constraint)])
5859 solve (className, tys) = do
59− dict <- go 0 className tys
60− return $ dictionaryValueToValue dict
60+ (dict, unsolved) <- go 0 className tys
61+ return (dictionaryValueToValue dict, unsolved)
6162 where
62− go :: Int -> Qualified (ProperName 'ClassName) -> [Type] -> m DictionaryValue
63+ go :: Int -> Qualified (ProperName 'ClassName) -> [Type] -> m (DictionaryValue, [(Ident, Constraint)])
6364 go work className' tys' | work > 1000 = throwError . errorMessage $ PossiblyInfiniteInstance className' tys'
6465 go work className' tys' = do
6566 let instances = do
@@ -67,21 +68,33 @@ entails moduleName context = solve
6768 -- Make sure the type unifies with the type in the type instance definition
6869 subst <- maybeToList . (>>= verifySubstitution) . fmap concat $ zipWithM (typeHeadsAreEqual moduleName) tys' (tcdInstanceTypes tcd)
6970 return (subst, tcd)
70− (subst, tcd) <- unique instances
71− -- Solve any necessary subgoals
72− args <- solveSubgoals subst (tcdDependencies tcd)
73− return $ foldr (\(superclassName, index) dict -> SubclassDictionaryValue dict superclassName index)
74− (mkDictionary (tcdName tcd) args)
75− (tcdPath tcd)
71+ solution <- unique instances
72+ case solution of
73+ Left (subst, tcd) -> do
74+ -- Solve any necessary subgoals
75+ (args, unsolved) <- solveSubgoals subst (tcdDependencies tcd)
76+ let match = foldr (\(superclassName, index) dict -> SubclassDictionaryValue dict superclassName index)
77+ (mkDictionary (tcdName tcd) args)
78+ (tcdPath tcd)
79+ return (match, unsolved)
80+ Right unsolved@(Qualified _ (ProperName unsolvedClassName), _) -> do
81+ ident <- freshIdent ("dict" ++ unsolvedClassName)
82+ return (LocalDictionaryValue (Qualified Nothing ident), [(ident, unsolved)])
7683 where
7784
78− unique :: [(a, TypeClassDictionaryInScope)] -> m (a, TypeClassDictionaryInScope)
79− unique [] = throwError . errorMessage $ NoInstanceFound className' tys'
80− unique [a] = return a
85+ unique :: [(a, TypeClassDictionaryInScope)] -> m (Either (a, TypeClassDictionaryInScope) Constraint)
86+ unique [] | all canBeGeneralized tys' = return $ Right (className, tys)
87+ | otherwise = throwError . errorMessage $ NoInstanceFound className' tys'
88+ unique [a] = return $ Left a
8189 unique tcds | pairwise overlapping (map snd tcds) = do
8290 tell . errorMessage $ OverlappingInstances className' tys' (map (tcdName . snd) tcds)
83− return (head tcds)
84− | otherwise = return (minimumBy (compare `on` length . tcdPath . snd) tcds)
91+ return $ Left (head tcds)
92+ | otherwise = return $ Left (minimumBy (compare `on` length . tcdPath . snd) tcds)
93+
94+ canBeGeneralized :: Type -> Bool
95+ canBeGeneralized TUnknown{} = True
96+ canBeGeneralized Skolem{} = True
97+ canBeGeneralized _ = False
8598
8699 -- |
87100 -- Check if two dictionaries are overlapping
@@ -98,11 +111,12 @@ entails moduleName context = solve
98111 -- Create dictionaries for subgoals which still need to be solved by calling go recursively
99112 -- E.g. the goal (Show a, Show b) => Show (Either a b) can be satisfied if the current type
100113 -- unifies with Either a b, and we can satisfy the subgoals Show a and Show b recursively.
101− solveSubgoals :: [(String, Type)] -> Maybe [Constraint] -> m (Maybe [DictionaryValue])
102− solveSubgoals _ Nothing = return Nothing
114+ solveSubgoals :: [(String, Type)] -> Maybe [Constraint] -> m (Maybe [DictionaryValue], [(Ident, Constraint)])
115+ solveSubgoals _ Nothing = return (Nothing, [])
103116 solveSubgoals subst (Just subgoals) = do
104− dict <- traverse (uncurry (go (work + 1)) . second (map (replaceAllTypeVars subst))) subgoals
105− return $ Just dict
117+ zipped <- traverse (uncurry (go (work + 1)) . second (map (replaceAllTypeVars subst))) subgoals
118+ let (dicts, unsolved) = unzip zipped
119+ return (Just dicts, concat unsolved)
106120
107121 -- Make a dictionary from subgoal dictionaries by applying the correct function
108122 mkDictionary :: Qualified Ident -> Maybe [DictionaryValue] -> DictionaryValue
src/Language/PureScript/TypeChecker/Types.hs+11−7
@@ -39,6 +39,7 @@ import Control.Monad.State.Class (MonadState(..), gets)
3939import Control.Monad.Supply.Class (MonadSupply)
4040import Control.Monad.Error.Class (MonadError(..))
4141import Control.Monad.Writer.Class (MonadWriter(..))
42+import Control.Monad.Trans.Writer (WriterT(..))
4243
4344import Language.PureScript.Crash
4445import Language.PureScript.AST
@@ -74,13 +75,16 @@ typesOf moduleName vals = do
7475
7576 forM tys $ \(ident, (val, ty)) -> do
7677 -- Replace type class dictionary placeholders with actual dictionaries
77− val' <- replaceTypeClassDictionaries moduleName val
78+ (val', unsolved) <- replaceTypeClassDictionaries moduleName val
7879 -- Check skolem variables did not escape their scope
7980 skolemEscapeCheck val'
8081 -- Check rows do not contain duplicate labels
8182 checkDuplicateLabels val'
82− return (ident, (val', varIfUnknown ty))
83+ return (ident, (foldr (Abs . Left) val' (map fst unsolved), varIfUnknown (constrain unsolved ty)))
8384 where
85+ -- | Generalize over any unsolved constraints
86+ constrain [] = id
87+ constrain cs = ConstrainedType (map snd cs)
8488 -- Apply the substitution that was returned from runUnify to both types and (type-annotated) values
8589 tidyUp (ts, sub) = map (\(i, (val, ty)) -> (i, (overTypes (substituteType sub) val, substituteType sub ty))) ts
8690 -- Replace all the wildcards types with their inferred types
@@ -168,16 +172,16 @@ overTypes f = let (_, f', _) = everywhereOnValues id g id in f'
168172
169173-- | Replace type class dictionary placeholders with inferred type class dictionaries
170174replaceTypeClassDictionaries ::
171− (Functor m, Applicative m, MonadState CheckState m, MonadError MultipleErrors m, MonadWriter MultipleErrors m) =>
175+ (Functor m, Applicative m, MonadState CheckState m, MonadError MultipleErrors m, MonadWriter MultipleErrors m, MonadSupply m) =>
172176 ModuleName ->
173177 Expr ->
174− m Expr
178+ m (Expr, [(Ident, Constraint)])
175179replaceTypeClassDictionaries mn =
176− let (_, f, _) = everywhereOnValuesTopDownM return go return
177− in f
180+ let (_, f, _) = everywhereOnValuesTopDownM return (WriterT . go) return
181+ in runWriterT . f
178182 where
179183 go (TypeClassDictionary constraint dicts) = entails mn dicts constraint
180− go other = return other
184+ go other = return (other, [])
181185
182186-- | Check the kind of a type, failing if it is not of kind *.
183187checkTypeKind ::