on master help sign in
resolve types in atoms of ConstructedAtoms

add test for unresolved constructed atom types

commitc33ea320f2e9bb6a7afdefc2c980cab4b898316c authorAgentM <agentm@themactionfaction.com> dateFri 6 Jul 2018, 13:21 (8 years ago) parent49cb34d
3 files changed+81−29
src/lib/ProjectM36/AtomType.hs+55−19 src/lib/ProjectM36/RelationalExpression.hs+13−6 test/TutorialD/Interpreter.hs+13−4
src/lib/ProjectM36/AtomType.hs+55−19
@@ -31,6 +31,7 @@ findDataConstructor dName = foldr tConsFolder Nothing
3131 [dCons] -> Just dCons
3232 _ -> error "More than one data constructor with the same name found"
3333
34+{-
3435-- | Scan the atom types and return the resultant ConstructedAtomType or error.
3536-- Used in typeFromAtomExpr to validate argument types.
3637atomTypeForDataConstructorName :: DataConstructorName -> [AtomType] -> TypeConstructorMapping -> Either RelationalError AtomType
@@ -38,8 +39,12 @@ atomTypeForDataConstructorName :: DataConstructorName -> [AtomType] -> TypeConst
3839atomTypeForDataConstructorName dConsName atomTypesIn tConsList =
3940 case findDataConstructor dConsName tConsList of
4041 Nothing -> Left (NoSuchDataConstructorError dConsName)
41− Just (tCons, dCons) ->
42− ConstructedAtomType (TCD.name tCons) <$> resolveDataConstructorTypeVars dCons atomTypesIn tConsList
42+ Just (tCons, dCons) -> do
43+ dConsVars <- resolveDataConstructorTypeVars dCons atomTypesIn tConsList
44+ let tConsVars = M.fromList (map TypeVariableType (TCD.typeVars tCons))
45+ allVars = M.union dConsVars tConsVars
46+ ConstructedAtomType (TCD.name tCons) <$> allVars
47+-}
4348
4449atomTypeForDataConstructorDefArg :: DataConstructorDefArg -> AtomType -> TypeConstructorMapping -> Either RelationalError AtomType
4550atomTypeForDataConstructorDefArg (DataConstructorDefTypeConstructorArg tCons) aType tConss = do
@@ -49,15 +54,20 @@ atomTypeForDataConstructorDefArg (DataConstructorDefTypeConstructorArg tCons) aT
4954atomTypeForDataConstructorDefArg (DataConstructorDefTypeVarNameArg _) aType _ = Right aType --any type is OK
5055
5156-- | Used to determine if the atom arguments can be used with the data constructor.
52−-- | This is the entry point for type-checking from RelationalExpression.hs.
57+-- | This is the entry point for type-checking from RelationalExpression.hs
5358atomTypeForDataConstructor :: TypeConstructorMapping -> DataConstructorName -> [AtomType] -> Either RelationalError AtomType
5459atomTypeForDataConstructor tConss dConsName atomArgTypes =
5560 --lookup the data constructor
5661 case findDataConstructor dConsName tConss of
5762 Nothing -> Left (NoSuchDataConstructorError dConsName)
58− Just (tCons, dCons) ->
63+ Just (tCons, dCons) -> do
5964 --validate that the type constructor arguments are fulfilled in the data constructor
60− ConstructedAtomType (TCD.name tCons) <$> resolveDataConstructorTypeVars dCons atomArgTypes tConss
65+ dConsVars <- resolveDataConstructorTypeVars dCons atomArgTypes tConss
66+ let tConsVars = M.fromList (map (\v -> (v, TypeVariableType v)) (TCD.typeVars tCons))
67+ allVars = M.union dConsVars tConsVars
68+ unresolvedType = ConstructedAtomType (TCD.name tCons) allVars
69+ --validateAtomType unresolvedType tConss -- do not validate here because the type may not be fully resolved at this point
70+ pure unresolvedType
6171
6272-- | Walks the data and type constructors to extract the type variable map.
6373resolveDataConstructorTypeVars :: DataConstructorDef -> [AtomType] -> TypeConstructorMapping -> Either RelationalError TypeVarMap
@@ -71,10 +81,11 @@ resolveDataConstructorTypeVars dCons@(DataConstructorDef _ defArgs) aTypeArgs tC
7181 --if any two maps have the same key and different values, this indicates a type arg mismatch
7282 let typeVarMapFolder valMap acc = case acc of
7383 Left err -> Left err
74− Right accMap -> if accMap `M.isSubmapOf` valMap then
75− Right (M.union accMap valMap)
76− else
77− Left (DataConstructorTypeVarsMismatch (DCD.name dCons) accMap valMap)
84+ Right accMap -> do
85+ case resolveAtomTypesInTypeVarMap valMap accMap of
86+ Left (TypeConstructorTypeVarMissing _) -> Left (DataConstructorTypeVarsMismatch (DCD.name dCons) accMap valMap)
87+ Left err -> Left err
88+ Right ok -> pure ok
7889 case foldr typeVarMapFolder (Right M.empty) maps of
7990 Left err -> Left err
8091 Right typeVarMaps -> pure typeVarMaps
@@ -199,6 +210,12 @@ resolveAtomType typeFromRelation unresolvedType = if typeFromRelation == unresol
199210 else
200211 Left (AtomTypeMismatchError typeFromRelation unresolvedType)
201212
213+{-
214+--walk an `AtomType` and apply the type variables in the map
215+resolveAtomTypeVars :: TypeVarMap -> AtomType -> AtomType
216+resolveAtomTypeVars tvMap typ@(TypeVariableType nam) = fromMaybe typ (M.lookup nam tvMap)
217+resolveAtomTypeVars tvMap (RelationAtomType relAttrs) =
218+-}
202219-- this could be optimized to reduce new tuple creation
203220resolveAtomTypesInTypeVarMap :: TypeVarMap -> TypeVarMap -> Either RelationalError TypeVarMap
204221resolveAtomTypesInTypeVarMap resolvedTypeMap unresolvedTypeMap = do
@@ -220,24 +237,30 @@ resolveAtomTypesInTypeVarMap resolvedTypeMap unresolvedTypeMap = do
220237 subType@(ConstructedAtomType _ _) -> do
221238 resSubType <- resolveAtomType resType subType
222239 pure (resKey, resSubType)
223− otherType -> pure (resKey, otherType)
240+ _ -> pure (resKey, resType)
224241 Nothing ->
225242 pure (resKey, resType) --swipe the missing type var from the expected map
226243 tVarList <- mapM (uncurry resolveTypePair) (M.toList resolvedTypeMap)
227244 pure (M.fromList tVarList)
228245
229246-- | See notes at `resolveTypesInTuple`. The typeFromRelation must not include any wildcards.
230−resolveTypeInAtom :: AtomType -> Atom -> Either RelationalError Atom
231−resolveTypeInAtom typeFromRelation atomIn@(ConstructedAtom dConsName _ args) = do
247+resolveTypeInAtom :: AtomType -> Atom -> TypeConstructorMapping -> Either RelationalError Atom
248+resolveTypeInAtom typeFromRelation@(ConstructedAtomType _ tvMap) atomIn@(ConstructedAtom dConsName _ args) tConss = do
232249 newType <- resolveAtomType typeFromRelation (atomTypeForAtom atomIn)
233− pure (ConstructedAtom dConsName newType args)
234−resolveTypeInAtom _ atom = Right atom
250+ case findDataConstructor dConsName tConss of
251+ Nothing -> -- the atom may have been constructed using a constructor function without a public data constructor, no further resolution is possible
252+ pure atomIn
253+ Just (_, dConsDef) -> do
254+ atomArgTypes <- resolvedAtomTypesForDataConstructorDefArgs tConss tvMap dConsDef
255+ newArgs <- mapM (\(atom, atomTyp) -> resolveTypeInAtom atomTyp atom tConss) (zip args atomArgTypes)
256+ pure (ConstructedAtom dConsName newType newArgs)
257+resolveTypeInAtom _ atom _ = Right atom
235258
236259-- | When creating a tuple, the data constructor may not complete the type constructor arguments, so the wildcard "TypeVar x" fills in the type constructor's argument. The tuple type must be resolved before it can be part of a relation, however.
237260-- Example: "Nothing" does not specify the the argument in "Maybe a", so allow delayed resolution in the tuple before it is added to the relation. Note that this resolution could cause a type error. Hardly a Hindley-Milner system.
238−resolveTypesInTuple :: Attributes -> RelationTuple -> Either RelationalError RelationTuple
239−resolveTypesInTuple resolvedAttrs (RelationTuple _ tupAtoms) = do
240− newAtoms <- mapM (\(atom, resolvedType) -> resolveTypeInAtom resolvedType atom) (zip (V.toList tupAtoms) $ map A.atomType (V.toList resolvedAttrs))
261+resolveTypesInTuple :: Attributes -> TypeConstructorMapping -> RelationTuple -> Either RelationalError RelationTuple
262+resolveTypesInTuple resolvedAttrs tConss (RelationTuple _ tupAtoms) = do
263+ newAtoms <- mapM (\(atom, resolvedType) -> resolveTypeInAtom resolvedType atom tConss) (zip (V.toList tupAtoms) $ map A.atomType (V.toList resolvedAttrs))
241264 Right (RelationTuple resolvedAttrs (V.fromList newAtoms))
242265
243266-- | Validate that the type is provided with complete type variables for type constructors.
@@ -252,7 +275,7 @@ validateAtomType typ@(ConstructedAtomType tConsName tVarMap) tConss =
252275 if not (S.null diff) then
253276 Left $ TypeConstructorTypeVarsMismatch expectedTyVarNames actualTyVarNames
254277 else
255− Right ()
278+ validateTypeVarMap tVarMap tConss
256279 _ -> Right ()
257280validateAtomType (RelationAtomType attrs) tConss = do
258281 mapM_ (\attr ->
@@ -261,8 +284,21 @@ validateAtomType (RelationAtomType attrs) tConss = do
261284validateAtomType (TypeVariableType x) _ = Left (TypeConstructorTypeVarMissing x)
262285validateAtomType _ _ = pure ()
263286
287+--ensure that all type vars are fully resolved
288+validateTypeVarMap :: TypeVarMap -> TypeConstructorMapping -> Either RelationalError ()
289+validateTypeVarMap tvMap tConss = mapM_ ((flip validateAtomType) tConss) $ (M.elems tvMap)
290+
264291validateTuple :: RelationTuple -> TypeConstructorMapping -> Either RelationalError ()
265−validateTuple (RelationTuple _ atoms) tConss = mapM_ (\a -> validateAtomType (atomTypeForAtom a) tConss) atoms
292+validateTuple (RelationTuple _ atoms) tConss = mapM_ (\a -> validateAtom a tConss) atoms
293+
294+--ensure that all types are fully resolved
295+validateAtom :: Atom -> TypeConstructorMapping -> Either RelationalError ()
296+validateAtom (RelationAtom (Relation _ tupSet)) tConss = mapM_ ((flip validateTuple) tConss) (asList tupSet)
297+validateAtom (ConstructedAtom _ dConsType atomArgs) tConss = do
298+ validateAtomType dConsType tConss
299+ mapM_ ((flip validateAtom) tConss) atomArgs
300+validateAtom _ _ = pure ()
301+
266302
267303-- | Determine if two types are equal or compatible (including special handling for TypeVar x).
268304atomTypeVerify :: AtomType -> AtomType -> Either RelationalError AtomType
src/lib/ProjectM36/RelationalExpression.hs+13−6
@@ -850,7 +850,7 @@ typeFromAtomExpr attrs (RelationAtomExpr relExpr) = runExceptT $ do
850850-- grab the type of the data constructor, then validate that the args match the expected types
851851typeFromAtomExpr attrs (ConstructedAtomExpr dConsName dConsArgs _) =
852852 runExceptT $ do
853− argsTypes <- mapM (liftE . typeFromAtomExpr attrs) dConsArgs
853+ argsTypes <- mapM (liftE . typeFromAtomExpr attrs) dConsArgs
854854 context <- fmap stateElemsContext (lift ask)
855855 either throwE pure (atomTypeForDataConstructor (typeConstructorMapping context) dConsName argsTypes)
856856
@@ -902,24 +902,31 @@ evalAttrExpr aTypes (AttributeAndTypeNameExpr attrName tCons ()) = do
902902evalAttrExpr _ (NakedAttributeExpr attr) = Right attr
903903
904904evalTupleExpr :: Maybe Attributes -> TupleExpr -> RelationalExprState (Either RelationalError RelationTuple)
905−evalTupleExpr attrs (TupleExpr tupMap) = do
905+evalTupleExpr mAttrs (TupleExpr tupMap) = do
906906 context <- fmap stateElemsContext ask
907907 runExceptT $ do
908908 -- it's not possible for AtomExprs in tuple constructors to reference other Attributes' atoms due to the necessary order-of-operations (need a tuple to pass to evalAtomExpr)- it may be possible with some refactoring of type usage or delayed evaluation- needs more thought, but not a priority
909909 -- I could adjust this logic so that when the attributes are not specified (Nothing), then I can attempt to extract the attributes from the tuple- the type resolution will blow up if an ambiguous data constructor is used (Left 4) and this should allow simple cases to "relation{tuple{a 4}}" to be processed
910+ let attrs = fromMaybe A.emptyAttributes mAttrs
910911 attrAtoms <- mapM (\(attrName, aExpr) -> do
911− newAtomType <- liftE (typeFromAtomExpr A.emptyAttributes aExpr)
912+ --provided when the relation header is available
913+ let eExpectedAtomType = A.atomTypeForAttributeName attrName attrs
914+ unresolvedType <- liftE (typeFromAtomExpr attrs aExpr)
915+ resolvedType <- case eExpectedAtomType of
916+ Left _ -> pure unresolvedType
917+ Right typeHint -> either throwE pure (resolveAtomType typeHint unresolvedType)
918+ --resolve atom typevars based on resolvedType?
912919 newAtom <- liftE (evalAtomExpr emptyTuple aExpr)
913− pure (attrName, newAtom, newAtomType)
920+ pure (attrName, newAtom, resolvedType)
914921 ) (M.toList tupMap)
915922 let tupAttrs = A.attributesFromList $ map (\(attrName, _, aType) -> Attribute attrName aType) attrAtoms
916923 atoms = V.fromList $ map (\(_, atom, _) -> atom) attrAtoms
917924 tup = mkRelationTuple tupAttrs atoms
918925 tConss = typeConstructorMapping context
919− finalAttrs = fromMaybe tupAttrs attrs
926+ finalAttrs = fromMaybe tupAttrs mAttrs
920927 --verify that the attributes match
921928 when (A.attributeNameSet finalAttrs /= A.attributeNameSet tupAttrs) $ throwE (TupleAttributeTypeMismatchError tupAttrs)
922− tup' <- either throwE pure (resolveTypesInTuple finalAttrs (reorderTuple finalAttrs tup))
929+ tup' <- either throwE pure (resolveTypesInTuple finalAttrs tConss (reorderTuple finalAttrs tup))
923930 _ <- either throwE pure (validateTuple tup' tConss)
924931 pure tup'
925932
test/TutorialD/Interpreter.hs+13−4
@@ -58,7 +58,7 @@ main = do
5858 testRelationalExprStateTupleElems,
5959 testFunctionalDependencies,
6060 testEmptyCommits,
61− testIntervalAtom,
61+-- testIntervalAtom,
6262 testListConstructedAtom,
6363 testTypeChecker,
6464 testRestrictionPredicateExprs,
@@ -70,7 +70,8 @@ main = do
7070 testDefineWithTypeVar,
7171 testIntervalType,
7272 testArbitraryRelation,
73− testNonEmptyListType
73+ testNonEmptyListType,
74+ testUnresolvedAtomTypes
7475 ]
7576 simpleRelTests = [("x:=true", Right relationTrue),
7677 ("x:=false", Right relationFalse),
@@ -163,7 +164,7 @@ main = do
163164 --test Maybe Integer
164165 ("x:=relation{tuple{a Just 3}}", mkRelationFromList simpleMaybeIntAttributes [[ConstructedAtom "Just" maybeIntegerAtomType [IntegerAtom 3]]]),
165166 --test Either Integer Text
166− ("x:=relation{tuple{a Left 3}}", Left (TypeConstructorTypeVarsMismatch (S.fromList ["a","b"]) (S.fromList ["a"]))), -- Left 3, alone is not enough information to imply the type
167+ ("x:=relation{tuple{a Left 3}}", Left (TypeConstructorTypeVarMissing "b")), -- Left 3, alone is not enough information to imply the type
167168 ("x:=relation{a Either Integer Text}{tuple{a Left 3}}", mkRelationFromList simpleEitherIntTextAttributes [[ConstructedAtom "Left" (eitherAtomType IntegerAtomType TextAtomType) [IntegerAtom 3]]]),
168169 --test datetime constructor
169170 ("x:=relation{tuple{a dateTimeFromEpochSeconds(1495199790)}}", mkRelationFromList (A.attributesFromList [Attribute "a" DateTimeAtomType]) [[DateTimeAtom (posixSecondsToUTCTime(realToFrac (1495199790 :: Int)))]]),
@@ -607,4 +608,12 @@ testNonEmptyListType = TestCase $ do
607608 IntegerAtom 3,
608609 ConstructedAtom "Cons" listType [IntegerAtom 4, ConstructedAtom "Empty" listType []]]
609610 nehead = IntegerAtom 3
610− assertEqual "non-empty list type construction" expected eX
611+ assertEqual "non-empty list type construction" expected eX
612+
613+testUnresolvedAtomTypes :: Test
614+testUnresolvedAtomTypes = TestCase $ do
615+ (sessionId, dbconn) <- dateExamplesConnection emptyNotificationCallback
616+ let err1 = "TypeConstructorTypeVarMissing"
617+ expectTutorialDErr sessionId dbconn (T.isPrefixOf err1) "x:=relation{tuple{a Empty}}"
618+ executeTutorialD sessionId dbconn "x:=relation{a List Int}{tuple{a Empty}}"
619+