@@ -1,12 +1,10 @@
1+{-# OPTIONS_GHC -Wall #-}
1module Type.Unify (unify) where
2module Type.Unify (unify) where
3−import Control.Applicative ((<|>))
4+import Control.Monad (zipWithM_)
4−import Control.Monad.Except (ExceptT, throwError, runExceptT)
5+import Control.Monad.Except (ExceptT, lift, liftIO, throwError, runExceptT)
5−import Control.Monad.State as State
6−import qualified Data.List as List
7import qualified Data.Map as Map
6import qualified Data.Map as Map
8import qualified Data.UnionFind.IO as UF
7import qualified Data.UnionFind.IO as UF
9−import Text.PrettyPrint (render)
11import qualified AST.Type as AstType
9import qualified AST.Type as AstType
12import qualified AST.Variable as Var
10import qualified AST.Variable as Var
@@ -14,435 +12,654 @@ import qualified Reporting.Region as R
14import qualified Reporting.Error.Type as Error
12import qualified Reporting.Error.Type as Error
15import qualified Type.State as TS
13import qualified Type.State as TS
16import Type.Type as Type
14import Type.Type as Type
17−import Type.PrettyPrint
15+
18−import Elm.Utils ((|>))
16+
20−
18+-- KICK OFF UNIFICATION
23− -> R.Region
21+unify :: Error.Hint -> R.Region -> Variable -> Variable -> TS.Solver ()
26− -> StateT TS.SolverState IO ()
27−unify hint region variable1 variable2 =
22+unify hint region expected actual =
28− do result <- runExceptT (unifyHelp variable1 variable2)
23+ do result <- runExceptT (guardedUnify ExpectedActual expected actual)
29 case result of
24 case result of
30 Right state ->
25 Right state ->
31 return state
26 return state
33− Left (RawMismatch (leftType, rightType, note)) ->
28+ Left (Mismatch leftType rightType maybeReason) ->
29+ do rootType <- liftIO (Type.toSrcType expected)
34− TS.addError region $ Error.Mismatch $
30+ TS.addError region $ Error.Mismatch $
35− Error.MismatchInfo hint leftType rightType note
31+ Error.MismatchInfo hint rootType leftType rightType maybeReason
38−-- ACTUALLY UNIFY STUFF
35+-- UNIFICATION HELPERS
40type Unify =
38type Unify =
41− ExceptT RawMismatch (StateT TS.SolverState IO)
39+ ExceptT Mismatch TS.Solver
42+data Context = Context
43+ { _orientation :: Orientation
45+ , _firstDesc :: Descriptor
46+ , _second :: Variable
47+ , _secondDesc :: Descriptor
51+data Orientation = ExpectedActual | ActualExpected
54+reorient :: Context -> Context
55+reorient (Context orientation var1 desc1 var2 desc2) =
59+ ExpectedActual -> ActualExpected
60+ ActualExpected -> ExpectedActual
62+ Context otherOrientation var2 desc2 var1 desc1
70+ = Mismatch AstType.Canonical AstType.Canonical (Maybe Error.Reason)
73+mismatch :: Context -> Maybe Error.Reason -> Unify a
74+mismatch (Context orientation first _ second _) maybeReason =
84+ do errorMsg <- liftIO $
85+ do expectedSrcType <- Type.toSrcType expected
86+ actualSrcType <- Type.toSrcType actual
87+ return (Mismatch expectedSrcType actualSrcType maybeReason)
88+ mergeHelp expected actual Error
92+rigidityError :: Context -> Maybe String -> Unify ()
93+rigidityError context maybeName =
94+ mismatch context (Just (Error.Rigid maybeName))
101+merge :: Context -> Content -> Unify ()
102+merge (Context _ first _ second _) content =
103+ mergeHelp first second content
106+mergeHelp :: Variable -> Variable -> Content -> Unify ()
107+mergeHelp first second content =
108+ liftIO $ UF.union' first second $ \desc1 desc2 ->
111+ { _content = content
112+ , _rank = min (_rank desc1) (_rank desc2)
45− RawMismatch (AstType.Canonical, AstType.Canonical, Maybe String)
118+fresh :: Context -> Content -> Unify Variable
119+fresh (Context _ _ desc1 _ desc2) content =
123+ { _content = content
124+ , _rank = min (_rank desc1) (_rank desc2)
128+ lift (TS.register freshVariable)
49− :: UF.Point Descriptor
50− -> UF.Point Descriptor
53−typeError leftType rightType note =
54− do leftSrcType <- liftIO (Type.toSrcType leftType)
55− rightSrcType <- liftIO (Type.toSrcType rightType)
57− UF.setDescriptor leftType (Type.descriptor Error)
58− UF.setDescriptor rightType (Type.descriptor Error)
59− throwError (RawMismatch (leftSrcType, rightSrcType, note))
132+-- ACTUALLY UNIFY THINGS
62−unifyHelp :: Variable -> Variable -> Unify ()
135+guardedUnify :: Orientation -> Variable -> Variable -> Unify ()
63−unifyHelp variable1 variable2 =
136+guardedUnify orientation left right =
64− do equivalent <- liftIO $ UF.equivalent variable1 variable2
137+ do equivalent <- liftIO $ UF.equivalent left right
65 if equivalent
138 if equivalent
66− then return ()
139+ then return ()
67− else actuallyUnify variable1 variable2
140+ else
70−actuallyUnify :: Variable -> Variable -> Unify ()
71−actuallyUnify variable1 variable2 = do
72− desc1 <- liftIO $ UF.descriptor variable1
141+ do leftDesc <- liftIO $ UF.descriptor left
73− desc2 <- liftIO $ UF.descriptor variable2
142+ rightDesc <- liftIO $ UF.descriptor right
74− let name' = pickName desc1 desc2
143+ actuallyUnify (Context orientation left leftDesc right rightDesc)
75− flex' = pickFlex desc1 desc2
76− rank' = pickRank desc1 desc2
77− alias' = pickAlias desc1 desc2
82− if rank desc1 < rank desc2
83− then UF.union variable2 variable1
84− else UF.union variable1 variable2
85− UF.modifyDescriptor variable1 $ \desc ->
86− desc { structure = structure desc1
95− if rank desc1 < rank desc2
96− then UF.union variable2 variable1
97− else UF.union variable1 variable2
98− UF.modifyDescriptor variable2 $ \desc ->
99− desc { structure = structure desc2
106− if rank desc1 < rank desc2 then merge1 else merge2
108− fresh :: Maybe (Term1 Variable) -> Unify Variable
110− do v <- liftIO . UF.fresh $ Descriptor
111− { structure = structure
119− lift (TS.register v)
122− do liftIO $ UF.modifyDescriptor v $ \desc -> desc { flex = Flexible }
123− unifyHelp variable1 variable2
125− unifyNumber svar (Var.Canonical home name) =
127− Var.BuiltIn | name `elem` ["Int","Float"] ->
130− Var.Local | List.isPrefixOf "number" name ->
133− Var.TopLevel _ | List.isPrefixOf "number" name ->
137− typeError variable1 variable2 Nothing
139− comparableError maybeHint =
140− typeError variable1 variable2 $ Just $
141− "Note: comparable types include Int, Float, Char, String, lists, and tuples."
142− ++ maybe "" ("\n"++) maybeHint
145− typeError variable1 variable2 $ Just $
146− "Note: appendable types include only Strings, Lists, and Text."
148− unifyComparable v (Var.Canonical home name) =
150− Var.BuiltIn | name `elem` ["Int","Float","Char","String"] ->
152− Var.Local | List.isPrefixOf "comparable" name ->
155− comparableError Nothing
157− unifyComparableStructure varSuper varFlex =
158− do struct <- liftIO $ collectApps varFlex
161− comparableError Nothing
164− do flexAndUnify varSuper
165− unifyHelp v =<< liftIO (variable $ Is Comparable)
168− if length vs > 6 then
169− comparableError $ Just $
170− "Furthermore, it cannot be a tuple with more than 6 elements."
172− do flexAndUnify varSuper
173− cmpVars <- liftIO $ forM [1..length vs] $ \_ -> variable (Is Comparable)
174− zipWithM_ unifyHelp vs cmpVars
176− unifyAppendable v cvar =
179− Var.isPrim "String" cvar
181− || Var.isLocal (List.isPrefixOf "comparable") cvar
182− || Var.isLocal (List.isPrefixOf "appendable") cvar
189− unifyAppendableStructure varSuper varFlex =
190− do struct <- liftIO $ collectApps varFlex
192− List _ -> flexAndUnify varSuper
193− _ -> appendableError
196− typeError variable1 variable2 $ Just $
197− rigidErrorMessage (render (pretty Never var))
200− case (flex desc1, flex desc2, name desc1, name desc2) of
201− (Is super1, Is super2, _, _)
202− | super1 == super2 -> merge
203− (Is Number, Is Comparable, _, _) -> merge1
204− (Is Comparable, Is Number, _, _) -> merge2
206− (Is Number, _, _, Just name) -> unifyNumber variable1 name
207− (_, Is Number, Just name, _) -> unifyNumber variable2 name
209− (Is Appendable, _, _, Just name) -> unifyAppendable variable1 name
210− (_, Is Appendable, Just name, _) -> unifyAppendable variable2 name
211− (Is Appendable, _, _, _) -> unifyAppendableStructure variable1 variable2
212− (_, Is Appendable, _, _) -> unifyAppendableStructure variable2 variable1
214− (Is Comparable, _, _, Just name) -> unifyComparable variable1 name
215− (_, Is Comparable, Just name, _) -> unifyComparable variable2 name
216− (Is Comparable, _, _, _) -> unifyComparableStructure variable1 variable2
217− (_, Is Comparable, _, _) -> unifyComparableStructure variable2 variable1
219− (Rigid, _, _, _) -> rigidError variable1
220− (_, Rigid, _, _) -> rigidError variable2
221− _ -> typeError variable1 variable2 Nothing
223− case (structure desc1, structure desc2) of
225− | flex desc1 == Flexible && flex desc1 == Flexible ->
229− | flex desc1 == Flexible -> merge2
230− | flex desc1 == Error -> return ()
233− | flex desc2 == Flexible -> merge1
234− | flex desc2 == Error -> return ()
236− (Just (Var1 v), _) ->
237− unifyHelp v variable2
239− (_, Just (Var1 v)) ->
240− unifyHelp variable1 v
248− (Just type1, Just type2) ->
249− case (type1,type2) of
250− (App1 term1 term2, App1 term1' term2') ->
252− unifyHelp term1 term1'
253− unifyHelp term2 term2'
254− (Fun1 term1 term2, Fun1 term1' term2') ->
256− unifyHelp term1 term1'
257− unifyHelp term2 term2'
259− (EmptyRecord1, EmptyRecord1) ->
262− (Record1 fields ext, EmptyRecord1) | Map.null fields -> unifyHelp ext variable2
146+subUnify :: Context -> Variable -> Variable -> Unify ()
263− (EmptyRecord1, Record1 fields ext) | Map.null fields -> unifyHelp variable1 ext
147+subUnify context var1 var2 =
148+ guardedUnify (_orientation context) var1 var2
265− (Record1 _ _, Record1 _ _) ->
151+actuallyUnify :: Context -> Unify ()
266− recordUnify fresh variable1 variable2
152+actuallyUnify context@(Context _ _ firstDesc _ secondDesc) =
154+ secondContent = _content secondDesc
156+ case _content firstDesc of
158+ -- If there was an error, just pretend it is okay. This lets us avoid
159+ -- "cascading" errors where one problem manifests as multiple message.
268− _ -> typeError variable1 variable2 Nothing
162+ Var Flex Nothing _ ->
163+ unifyFlex context secondContent
165+ Var Flex (Just super) _ ->
166+ unifySuper context super secondContent
271−-- RIGID ERROR
168+ Var Rigid maybeSuper maybeName ->
169+ unifyRigid context maybeSuper maybeName secondContent
273−rigidErrorMessage :: String -> String
171+ Atom name ->
274−rigidErrorMessage var =
172+ unifyAtom context name secondContent
275− "Could not unify user provided type variable `" ++ var ++ "`. The most likely cases are:\n\n"
276− ++ " 1. The type you wrote down is too general. It says any type can go through\n"
277− ++ " but in fact only a certain type can.\n"
278− ++ " 2. A type variable is probably being shared between two type annotations.\n"
279− ++ " They are treated as different things in the current implementation, so\n"
280− ++ " Try commenting out some type annotations and see what happens."
174+ Alias name args realVar ->
175+ unifyAlias context name args realVar secondContent
283−-- RECORD UNIFICATION
177+ Structure term ->
178+ unifyStructure context term secondContent
286− :: (Maybe (Term1 Variable) -> Unify Variable)
290−recordUnify fresh variable1 variable2 =
291− do (ExpandedRecord fields1 ext1) <- liftIO (gatherFields variable1)
292− (ExpandedRecord fields2 ext2) <- liftIO (gatherFields variable2)
294− unifyOverlappingFields fields1 fields2
296− let freshRecord fields ext =
182+-- UNIFY FLEXIBLE VARIABLES
297− fresh (Just (Record1 fields ext))
299− let uniqueFields1 = diffFields fields1 fields2
300− let uniqueFields2 = diffFields fields2 fields1
302− let addFieldMismatchError missingFields =
185+unifyFlex :: Context -> Content -> Unify ()
303− typeError variable1 variable2 $ Just $
186+unifyFlex context otherContent =
304− fieldMismatchError missingFields
187+ case otherContent of
306− case (ext1, ext2) of
191+ Var Flex _ _ ->
307− (Empty _, Empty _) ->
192+ merge context otherContent
308− case Map.null uniqueFields1 && Map.null uniqueFields2 of
310− False -> typeError variable1 variable2 Nothing
312− (Empty var1, Extension var2) ->
194+ Var Rigid _ _ ->
313− case (Map.null uniqueFields1, Map.null uniqueFields2) of
195+ merge context otherContent
314− (_, False) -> addFieldMismatchError uniqueFields2
315− (True, True) -> unifyHelp var1 var2
317− do subRecord <- freshRecord uniqueFields1 var1
318− unifyHelp subRecord var2
320− (Extension var1, Empty var2) ->
197+ Atom _ ->
321− case (Map.null uniqueFields1, Map.null uniqueFields2) of
198+ merge context otherContent
322− (False, _) -> addFieldMismatchError uniqueFields1
323− (True, True) -> unifyHelp var1 var2
325− do subRecord <- freshRecord uniqueFields2 var2
326− unifyHelp var1 subRecord
328− (Extension var1, Extension var2) ->
200+ Alias _ _ _ ->
329− case (Map.null uniqueFields1, Map.null uniqueFields2) of
201+ merge context otherContent
204+ merge context otherContent
208+-- UNIFY RIGID VARIABLES
211+unifyRigid :: Context -> Maybe Super -> Maybe String -> Content -> Unify ()
212+unifyRigid context maybeSuper maybeName otherContent =
213+ case otherContent of
217+ Var Flex otherMaybeSuper _ ->
218+ if maybeSuper == otherMaybeSuper then
219+ merge context (Var Rigid maybeSuper maybeName)
221+ rigidityError context maybeName
224+ rigidityError context maybeName
227+ rigidityError context maybeName
230+ rigidityError context maybeName
233+ rigidityError context maybeName
236+-- UNIFY SUPER VARIABLES
239+unifySuper :: Context -> Super -> Content -> Unify ()
240+unifySuper context super otherContent =
241+ case otherContent of
243+ unifySuperStructure context super term
246+ if atomMatchesSuper super name then
247+ merge context otherContent
249+ mismatch context Nothing
251+ Var Rigid Nothing maybeName ->
252+ rigidityError context maybeName
254+ Var Rigid (Just otherSuper) maybeName ->
255+ if super == otherSuper then
256+ merge context otherContent
258+ rigidityError context maybeName
260+ Var Flex Nothing _ ->
261+ merge context (Var Flex (Just super) Nothing)
263+ Var Flex (Just otherSuper) _ ->
264+ case combineSupers super otherSuper of
266+ mismatch context Nothing
269+ merge context (Var Flex (Just newSuper) Nothing)
271+ Alias _ _ realVar ->
272+ subUnify context (_first context) realVar
278+combineSupers :: Super -> Super -> Maybe Super
279+combineSupers firstSuper secondSuper =
280+ case (firstSuper, secondSuper) of
281+ (Number , Number ) -> Just Number
282+ (Comparable, Number ) -> Just Number
283+ (Number , Comparable) -> Just Number
285+ (Comparable, Comparable) -> Just Comparable
286+ (Appendable, Appendable) -> Just Appendable
288+ (Appendable, Comparable) -> Just CompAppend
289+ (Comparable, Appendable) -> Just CompAppend
291+ (CompAppend, CompAppend) -> Just CompAppend
292+ (CompAppend, Comparable) -> Just CompAppend
293+ (Comparable, CompAppend) -> Just CompAppend
294+ (CompAppend, Appendable) -> Just CompAppend
295+ (Appendable, CompAppend) -> Just CompAppend
300+isPrimitiveFrom :: [String] -> Var.Canonical -> Bool
301+isPrimitiveFrom prims var =
302+ any (\p -> Var.isPrim p var) prims
305+atomMatchesSuper :: Super -> Var.Canonical -> Bool
306+atomMatchesSuper super name =
309+ isPrimitiveFrom ["Int", "Float"] name
312+ isPrimitiveFrom ["Int", "Float", "Char", "String"] name
315+ Var.isPrim "String" name || Var.isText name
318+ Var.isPrim "String" name
321+unifySuperStructure :: Context -> Super -> Term1 Variable -> Unify ()
322+unifySuperStructure context super term =
323+ do appStructure <- liftIO (collectApps (Structure term))
324+ case appStructure of
326+ mismatch context Nothing
331+ mismatch context Nothing
334+ merge context (Structure term)
337+ do merge context (Structure term)
338+ unifyComparableRecursive (_orientation context) variable
341+ do merge context (Structure term)
342+ unifyComparableRecursive (_orientation context) variable
347+ mismatch context Nothing
350+ mismatch context Nothing
353+ if length entries > 6 then
354+ mismatch context (Just (Error.TooLongComparableTuple (length entries)))
357+ do merge context (Structure term)
358+ mapM_ (unifyComparableRecursive (_orientation context)) entries
361+ mismatch context Nothing
364+unifyComparableRecursive :: Orientation -> Variable -> Unify ()
365+unifyComparableRecursive orientation var =
368+ do desc <- UF.descriptor var
371+ { _content = Var Flex (Just Comparable) Nothing
372+ , _rank = _rank desc
377+ guardedUnify orientation compVar var
386+collectApps :: Content -> IO AppStructure
387+collectApps content =
388+ collectAppsHelp [] content
391+collectAppsHelp :: [Variable] -> Content -> IO AppStructure
392+collectAppsHelp args content =
393+ case (content, args) of
394+ (Structure (App1 func arg), _) ->
395+ collectAppsHelp (args ++ [arg]) =<< getContent func
397+ (Atom name, [arg]) | Var.isList name ->
400+ (Atom name, _) | Var.isTuple name ->
407+getContent :: Variable -> IO Content
408+getContent variable =
409+ _content <$> UF.descriptor variable
416+unifyAtom :: Context -> Var.Canonical -> Content -> Unify ()
417+unifyAtom context name otherContent =
418+ case otherContent of
422+ Var Flex Nothing _ ->
423+ merge context (Atom name)
425+ Var Flex (Just super) _ ->
426+ if atomMatchesSuper super name then
427+ merge context (Atom name)
430+ mismatch context Nothing
432+ Var Rigid _ maybeName ->
433+ rigidityError context maybeName
436+ if name == otherName then
437+ merge context otherContent
440+ mismatch context Nothing
442+ Alias _ _ realVar ->
443+ subUnify context (_first context) realVar
446+ mismatch context Nothing
452+unifyAlias :: Context -> Var.Canonical -> [(String, Variable)] -> Variable -> Content -> Unify ()
453+unifyAlias context name args realVar otherContent =
454+ case otherContent of
458+ Var Flex Nothing _ ->
459+ merge context (Alias name args realVar)
462+ subUnify context realVar (_second context)
465+ subUnify context realVar (_second context)
468+ Alias otherName otherArgs otherRealVar ->
469+ if name == otherName then
470+ do merge context otherContent
471+ zipWithM_ (subUnify context) (map snd args) (map snd otherArgs)
474+ subUnify context realVar otherRealVar
477+ subUnify context realVar (_second context)
484+unifyStructure :: Context -> Term1 Variable -> Content -> Unify ()
485+unifyStructure context term otherContent =
486+ case otherContent of
490+ Var Flex Nothing _ ->
491+ merge context (Structure term)
493+ Var Flex (Just super) _ ->
494+ unifySuper (reorient context) super (Structure term)
496+ Var Rigid _ maybeName ->
497+ rigidityError context maybeName
500+ mismatch context Nothing
502+ Alias _ _ realVar ->
503+ subUnify context (_first context) realVar
505+ Structure otherTerm ->
506+ case (term, otherTerm) of
507+ (App1 func arg, App1 otherFunc otherArg) ->
508+ do merge context otherContent
509+ subUnify context func otherFunc
510+ subUnify context arg otherArg
512+ (Fun1 arg result, Fun1 otherArg otherResult) ->
513+ do merge context otherContent
514+ subUnify context arg otherArg
515+ subUnify context result otherResult
517+ (EmptyRecord1, EmptyRecord1) ->
518+ merge context otherContent
520+ (Record1 fields ext, EmptyRecord1) | Map.null fields ->
521+ subUnify context ext (_second context)
523+ (EmptyRecord1, Record1 fields ext) | Map.null fields ->
524+ subUnify context (_first context) ext
526+ (Record1 fields extension, Record1 otherFields otherExtension) ->
527+ do firstStructure <- gatherFields context fields extension
528+ secondStructure <- gatherFields context otherFields otherExtension
529+ unifyRecord context firstStructure secondStructure
532+ mismatch context Nothing
539+unifyRecord :: Context -> RecordStructure -> RecordStructure -> Unify ()
540+unifyRecord context firstStructure secondStructure =
541+ do let (RecordStructure expFields expVar expStruct) = firstStructure
542+ let (RecordStructure actFields actVar actStruct) = secondStructure
544+ -- call after unifying extension, make sure record shape matches before
545+ -- looking into whether the particular field types match.
546+ let unifySharedFields otherFields ext =
547+ do let sharedFields = Map.intersectionWith (,) expFields actFields
548+ _ <- traverse (uncurry (subUnify context)) sharedFields
549+ let allFields = Map.union (Map.map fst sharedFields) otherFields
550+ merge context (Structure (Record1 allFields ext))
552+ let uniqueExpFields = Map.difference expFields actFields
553+ let uniqueActFields = Map.difference actFields expFields
555+ case (expStruct, actStruct) of
557+ if Map.null uniqueExpFields && Map.null uniqueActFields then
558+ do mergeHelp expVar actVar (Structure EmptyRecord1)
559+ unifySharedFields Map.empty expVar
562+ mismatch context (Just (Error.MessyFields (Map.keys uniqueExpFields) (Map.keys uniqueActFields)))
564+ (Empty, Extension) ->
565+ case (Map.null uniqueExpFields, Map.null uniqueActFields) of
333− (True, False) ->
566+ (_, False) ->
334− do subRecord <- freshRecord uniqueFields2 var2
567+ mismatch context (Just (Error.ActualFields (Map.keys uniqueActFields)))
335− unifyHelp var1 subRecord
568+
570+ do subUnify context expVar actVar
571+ unifySharedFields Map.empty expVar
337 (False, True) ->
573 (False, True) ->
338− do subRecord <- freshRecord uniqueFields1 var1
574+ do subRecord <- fresh context (Structure (Record1 uniqueExpFields expVar))
339− unifyHelp subRecord var2
575+ subUnify context subRecord actVar
576+ unifySharedFields Map.empty subRecord
578+ (Extension, Empty) ->
579+ case (Map.null uniqueExpFields, Map.null uniqueActFields) of
341− (False, False) ->
580+ (False, _) ->
342− do record1' <- freshRecord uniqueFields1 =<< fresh Nothing
581+ mismatch context (Just (Error.ExpectedFields (Map.keys uniqueExpFields)))
343− record2' <- freshRecord uniqueFields2 =<< fresh Nothing
344− unifyHelp record1' var2
345− unifyHelp var1 record2'
348−unifyOverlappingFields
349− :: Map.Map String [Variable]
350− -> Map.Map String [Variable]
352−unifyOverlappingFields fields1 fields2 =
353− Map.intersectionWith (zipWith unifyHelp) fields1 fields2
359−diffFields :: Map.Map String [a] -> Map.Map String [a] -> Map.Map String [a]
360−diffFields fields1 fields2 =
361− let eat (_:xs) (_:ys) = eat xs ys
364− Map.union (Map.intersectionWith eat fields1 fields2) fields1
365− |> Map.filter (not . null)
584+ do subUnify context expVar actVar
585+ unifySharedFields Map.empty expVar
368−data ExpandedRecord = ExpandedRecord
587+ (True, False) ->
369− { _fields :: Map.Map String [Variable]
588+ do subRecord <- fresh context (Structure (Record1 uniqueActFields actVar))
370− , _extension :: Extension
589+ subUnify context expVar subRecord
371− }
590+ unifySharedFields Map.empty subRecord
373−data Extension = Empty Variable | Extension Variable
592+ (Extension, Extension) ->
593+ case (Map.null uniqueExpFields, Map.null uniqueActFields) of
595+ do subUnify context expVar actVar
596+ unifySharedFields Map.empty expVar
599+ do subRecord <- fresh context (Structure (Record1 uniqueActFields actVar))
600+ subUnify context expVar subRecord
601+ unifySharedFields Map.empty subRecord
376−gatherFields :: Variable -> IO ExpandedRecord
603+ (False, True) ->
377−gatherFields var =
604+ do subRecord <- fresh context (Structure (Record1 uniqueExpFields expVar))
378− do desc <- UF.descriptor var
605+ subUnify context subRecord actVar
379− case structure desc of
606+ unifySharedFields Map.empty subRecord
380− (Just (Record1 fields ext)) ->
381− do (ExpandedRecord deeperFields rootExt) <- gatherFields ext
382− return (ExpandedRecord (Map.unionWith (++) fields deeperFields) rootExt)
384− (Just EmptyRecord1) ->
608+ (False, False) ->
385− return (ExpandedRecord Map.empty (Empty var))
609+ do let subFields = Map.union uniqueExpFields uniqueActFields
610+ subExt <- fresh context (Var Flex Nothing Nothing)
611+ expRecord <- fresh context (Structure (Record1 uniqueActFields subExt))
612+ actRecord <- fresh context (Structure (Record1 uniqueExpFields subExt))
613+ subUnify context expVar expRecord
614+ subUnify context actRecord actVar
615+ unifySharedFields subFields subExt
388− return (ExpandedRecord Map.empty (Extension var))
391−-- assumes that one of the dicts has stuff in it
619+-- GATHER RECORD STRUCTURE
392−fieldMismatchError :: Map.Map String a -> String
393−fieldMismatchError missingFields =
394− case Map.keys missingFields of
399− "Looks like a record is missing the field `" ++ key ++ "`"
401− keys ->
622+data RecordStructure = RecordStructure
402− "Looks like a record is missing fields "
623+ { _fields :: Map.Map String Variable
403− ++ List.intercalate ", " (init keys) ++ ", and " ++ last keys
624+ , _extVar :: Variable
625+ , _extStruct :: ExtensionStructure
629+data ExtensionStructure
407−pickAlias :: Descriptor -> Descriptor -> Alias Variable
408−pickAlias desc1 desc2 =
409− alias desc1 <|> alias desc2
634+gatherFields :: Context -> Map.Map String Variable -> Variable -> Unify RecordStructure
635+gatherFields context fields variable =
636+ do desc <- liftIO (UF.descriptor variable)
637+ case _content desc of
638+ Structure (Record1 subFields subExt) ->
639+ gatherFields context (Map.union fields subFields) subExt
412−pickRank :: Descriptor -> Descriptor -> Int
641+ Structure (EmptyRecord1) ->
413−pickRank desc1 desc2 =
642+ return (RecordStructure fields variable Empty)
414− min (rank desc1) (rank desc2)
644+ Structure (App1 _ _) ->
645+ mismatch context (Just Error.NotRecord)
417−pickFlex :: Descriptor -> Descriptor -> Flex
647+ Structure (Fun1 _ _) ->
418−pickFlex desc1 desc2 =
648+ mismatch context (Just Error.NotRecord)
419− case (flex desc1, flex desc2) of
420− ( f , Flexible ) -> f
421− ( Flexible , f ) -> f
422− ( Is Number , Is _ ) -> Is Number
423− ( Is _ , Is Number ) -> Is Number
424− ( Is super , Is _ ) -> Is super
425− ( _ , _ ) -> Flexible
651+ mismatch context (Just Error.NotRecord)
428−pickName :: Descriptor -> Descriptor -> Maybe Var.Canonical
653+ Var _ Nothing _ ->
429−pickName desc1 desc2 =
654+ return (RecordStructure fields variable Extension)
430− case (name desc1, name desc2) of
431− (Nothing, Nothing) ->
434− (Just name1, Nothing) ->
656+ Var _ (Just _) _ ->
435− Just name1
657+ mismatch context (Just Error.NotRecord)
437− (Nothing, Just name2) ->
659+ -- TODO may be dropping useful alias info here
438− Just name2
660+ Alias _ _ var ->
661+ gatherFields context fields var
440− (Just name1, Just name2) ->
663+ Error ->
441− case (flex desc1, flex desc2) of
664+ return (RecordStructure fields variable Extension)
442− ( _ , Flexible ) -> Just name1
443− ( Flexible , _ ) -> Just name2
444− ( Is Number , Is _ ) -> Just name1
445− ( Is _ , Is Number ) -> Just name2
446− ( Is _ , Is _ ) -> Just name1
447− ( _ , _ ) -> Nothing