1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings, Rank2Types #-}
11import Control.Monad (foldM, liftM2, zipWithM_)
12import qualified Data.Map.Strict as Map
14import qualified AST.Canonical as Can
15import qualified AST.Module.Name as ModuleName
16import qualified Elm.Name as N
17import qualified Type.Occurs as Occurs
18import Type.Type as Type
19import qualified Type.UnionFind as UF
28 | Err [Variable] Problem Can.Type Can.Type
31unify :: Variable -> Variable -> IO Answer
33 case guardedUnify ActualExpected v1 v2 of
35 k [] onSuccess $ \vars problem ->
36 do t1 <- Type.toSrcType v1
37 t2 <- Type.toSrcType v2
38 UF.union v1 v2 errorDescriptor
39 return (Err vars problem t1 t2)
42onSuccess :: [Variable] -> () -> IO Answer
47{-# NOINLINE errorDescriptor #-}
48errorDescriptor :: Descriptor
50 Descriptor (Error "?") noRank noMark Nothing
58 = BadFields [(N.Name, Maybe Reason)]
59 | MessyFields [N.Name] [N.Name] [N.Name]
62 | RigidClash N.Name N.Name
63 | NotPartOfSuper Type.SuperType
64 | RigidVarTooGeneric N.Name SpecificThing
65 | RigidSuperTooGeneric Type.SuperType N.Name SpecificThing
69 = SpecificSuper Type.SuperType
77flipReason :: Reason -> Reason
81 BadFields (map (fmap (fmap flipReason)) fields)
83 MessyFields both left right ->
84 MessyFields both right left
95 NotPartOfSuper super ->
98 RigidVarTooGeneric name specific ->
99 RigidVarTooGeneric name specific
101 RigidSuperTooGeneric super name specific ->
102 RigidSuperTooGeneric super name specific
112 -> ([Variable] -> a -> IO r)
113 -> ([Variable] -> Problem -> IO r)
118-- TODO experiment with INLINE on the Unify instances
120instance Functor Unify where
121 fmap func (Unify kv) =
122 Unify $ \vars ok err ->
125 ok vars1 (func value)
130instance Applicative Unify where
132 Unify $ \vars ok _ ->
135 (<*>) (Unify kf) (Unify kv) =
136 Unify $ \vars ok err ->
141 ok vars2 (func value)
148instance Monad Unify where
150 Unify $ \vars ok _ ->
153 (>>=) (Unify ka) callback =
154 Unify $ \vars ok err ->
158 Unify kb -> kb vars1 ok err
163register :: IO Variable -> Unify Variable
165 Unify $ \vars ok _ ->
171-- UNIFICATION HELPERS
176 { _orientation :: Orientation
178 , _firstDesc :: Descriptor
179 , _second :: Variable
180 , _secondDesc :: Descriptor
189reorient :: Context -> Context
190reorient (Context orientation var1 desc1 var2 desc2) =
194 ExpectedActual -> ActualExpected
195 ActualExpected -> ExpectedActual
197 Context otherOrientation var2 desc2 var1 desc1
205 = Reason Bool (Maybe Reason)
209mismatch :: Context -> Maybe Reason -> Unify a
210mismatch context@(Context orientation var1 _ var2 _) maybeReason =
211 Unify $ \vars _ err ->
212 do anyInfinite <- liftM2 (||) (Occurs.occurs var1) (Occurs.occurs var2)
214 then err vars Infinite
216 do args1 <- collectArgs [] var1
217 args2 <- collectArgs [] var2
220 or <$> traverse detectDecoder (args1 ++ args2)
222 if length args1 /= length args2
224 do newVars <- foldM (subUnifyAnyway context) vars (zip args1 args2)
225 err newVars $ Reason hasDecoder $ Just $
226 MissingArgs (abs (length args2 - length args1))
228 err vars $ Reason hasDecoder $
234 fmap flipReason maybeReason
237subUnifyAnyway :: Context -> [Variable] -> (Variable, Variable) -> IO [Variable]
238subUnifyAnyway context vars (arg1, arg2) =
239 case subUnify context arg1 arg2 of
241 k vars (\vs _ -> return vs) (\vs _ -> return vs)
244collectArgs :: [Variable] -> Variable -> IO [Variable]
245collectArgs revArgs variable =
246 do (Descriptor content _ _ _) <- UF.get variable
248 Structure (Fun1 arg returnType) ->
249 collectArgs (arg : revArgs) returnType
252 return $ variable : revArgs
255detectDecoder :: Variable -> IO Bool
256detectDecoder variable =
257 do (Descriptor content _ _ _) <- UF.get variable
259 Structure (App1 home name [_]) ->
260 return (home == ModuleName.jsonDecode && name == "Decoder")
270merge :: Context -> Content -> Unify ()
271merge (Context _ var1 (Descriptor _ rank1 _ _) var2 (Descriptor _ rank2 _ _)) content =
272 Unify $ \vars ok _ ->
274 UF.union var1 var2 (Descriptor content (min rank1 rank2) noMark Nothing)
277fresh :: Context -> Content -> Unify Variable
278fresh (Context _ _ (Descriptor _ rank1 _ _) _ (Descriptor _ rank2 _ _)) content =
279 register $ UF.fresh $
280 Descriptor content (min rank1 rank2) noMark Nothing
284-- ACTUALLY UNIFY THINGS
287guardedUnify :: Orientation -> Variable -> Variable -> Unify ()
288guardedUnify orientation left right =
289 Unify $ \vars ok err ->
290 do equivalent <- UF.equivalent left right
294 do leftDesc <- UF.get left
295 rightDesc <- UF.get right
296 putStrLn (toString leftDesc ++ " vs " ++ toString rightDesc)
297 case actuallyUnify (Context orientation left leftDesc right rightDesc) of
302toString :: Descriptor -> String
303toString (Descriptor content _ _ _) =
305 FlexVar _ -> "FlexVar"
306 FlexSuper _ _ -> "FlexSuper"
307 RigidVar _ -> "RigidVar"
308 RigidSuper _ _ -> "RigidSuper"
309 Alias _ _ _ _ -> "Alias"
310 Structure _ -> "Structure"
314subUnify :: Context -> Variable -> Variable -> Unify ()
315subUnify context var1 var2 =
316 guardedUnify (_orientation context) var1 var2
319actuallyUnify :: Context -> Unify ()
320actuallyUnify context@(Context _ _ (Descriptor firstContent _ _ _) _ (Descriptor secondContent _ _ _)) =
323 unifyFlex context firstContent secondContent
326 unifyFlexSuper context super firstContent secondContent
329 unifyRigid context Nothing name firstContent secondContent
331 RigidSuper super name ->
332 unifyRigid context (Just super) name firstContent secondContent
334 Alias home name args realVar ->
335 unifyAlias context home name args realVar secondContent
337 Structure flatType ->
338 unifyStructure context flatType firstContent secondContent
341 -- If there was an error, just pretend it is okay. This lets us avoid
342 -- "cascading" errors where one problem manifests as multiple message.
347-- UNIFY FLEXIBLE VARIABLES
350unifyFlex :: Context -> Content -> Content -> Unify ()
351unifyFlex context content otherContent =
356 -- TODO see if wildcarding makes a noticable perf difference
368 merge context otherContent
371 merge context otherContent
374 merge context otherContent
377 merge context otherContent
380 merge context otherContent
384-- UNIFY RIGID VARIABLES
387unifyRigid :: Context -> Maybe SuperType -> N.Name -> Content -> Content -> Unify ()
388unifyRigid context maybeSuper name content otherContent =
391 merge context content
393 FlexSuper otherSuper _ ->
396 if combineRigidSupers super otherSuper then
397 merge context content
399 mismatch context $ Just $
400 RigidSuperTooGeneric super name $ SpecificSuper otherSuper
403 mismatch context $ Just $
404 RigidVarTooGeneric name $ SpecificSuper otherSuper
406 RigidVar otherName ->
407 mismatch context $ Just $
408 RigidClash name otherName
410 RigidSuper _ otherName ->
411 mismatch context $ Just $
412 RigidClash name otherName
414 Alias _ otherName _ _ ->
415 mismatch context $ Just $
416 maybe RigidVarTooGeneric RigidSuperTooGeneric maybeSuper name $
417 SpecificType otherName
419 Structure flatType ->
420 mismatch context $ Just $
421 maybe RigidVarTooGeneric RigidSuperTooGeneric maybeSuper name $
422 flatTypeToSpecificThing flatType
428flatTypeToSpecificThing :: FlatType -> SpecificThing
429flatTypeToSpecificThing flatType =
451-- UNIFY SUPER VARIABLES
454unifyFlexSuper :: Context -> SuperType -> Content -> Content -> Unify ()
455unifyFlexSuper context super content otherContent =
457 Structure flatType ->
458 unifyFlexSuperStructure context super flatType
461 mismatch context $ Just $
462 RigidVarTooGeneric name (SpecificSuper super)
464 RigidSuper otherSuper name ->
465 if combineRigidSupers otherSuper super then
466 merge context otherContent
468 mismatch context $ Just $
469 RigidSuperTooGeneric otherSuper name (SpecificSuper super)
472 merge context content
474 FlexSuper otherSuper _ ->
478 Number -> merge context content
479 Comparable -> merge context content
480 _ -> mismatch context Nothing -- NumberAppendableClash
484 Comparable -> merge context otherContent
485 Number -> merge context otherContent
486 Appendable -> merge context (Type.unnamedFlexSuper CompAppend)
487 CompAppend -> merge context otherContent
491 Appendable -> merge context otherContent
492 Comparable -> merge context (Type.unnamedFlexSuper CompAppend)
493 CompAppend -> merge context otherContent
494 Number -> mismatch context Nothing -- NumberAppendableClash
498 Comparable -> merge context content
499 Appendable -> merge context content
500 CompAppend -> merge context content
501 Number -> mismatch context Nothing -- NumberAppendableClash
503 Alias _ _ _ realVar ->
504 subUnify context (_first context) realVar
510combineRigidSupers :: SuperType -> SuperType -> Bool
511combineRigidSupers rigid flex =
513 || (rigid == Number && flex == Comparable)
514 || (rigid == CompAppend && (flex == Comparable || flex == Appendable))
517atomMatchesSuper :: SuperType -> ModuleName.Canonical -> N.Name -> Bool
518atomMatchesSuper super home name =
519 home == ModuleName.basics
539unifyFlexSuperStructure :: Context -> SuperType -> FlatType -> Unify ()
540unifyFlexSuperStructure context super flatType =
543 if atomMatchesSuper super home name then
544 merge context (Structure flatType)
546 mismatch context $ Just $ NotPartOfSuper super
548 App1 home name [variable] | home == ModuleName.list && name == N.list ->
551 mismatch context $ Just $ NotPartOfSuper super
554 merge context (Structure flatType)
557 do comparableOccursCheck context
558 merge context (Structure flatType)
559 unifyComparableRecursive (_orientation context) variable
562 do comparableOccursCheck context
563 merge context (Structure flatType)
564 unifyComparableRecursive (_orientation context) variable
569 mismatch context $ Just $ NotPartOfSuper super
572 mismatch context $ Just $ NotPartOfSuper super
575 do comparableOccursCheck context
576 merge context (Structure flatType)
577 let orientation = _orientation context
578 unifyComparableRecursive orientation a
579 unifyComparableRecursive orientation b
585 unifyComparableRecursive orientation c
588 mismatch context $ Just $ NotPartOfSuper super
591 mismatch context $ Just $ NotPartOfSuper super
594-- TODO: is there some way to avoid doing this?
595-- Do type classes require occurs checks?
596comparableOccursCheck :: Context -> Unify ()
597comparableOccursCheck (Context _ _ _ var _) =
598 Unify $ \vars ok err ->
599 do hasOccurred <- Occurs.occurs var
601 then err vars Infinite
605unifyComparableRecursive :: Orientation -> Variable -> Unify ()
606unifyComparableRecursive orientation var =
607 do compVar <- register $
608 do (Descriptor _ rank _ _) <- UF.get var
609 UF.fresh $ Descriptor (Type.unnamedFlexSuper Comparable) rank noMark Nothing
610 guardedUnify orientation compVar var
617unifyAlias :: Context -> ModuleName.Canonical -> N.Name -> [(N.Name, Variable)] -> Variable -> Content -> Unify ()
618unifyAlias context home name args realVar otherContent =
621 merge context (Alias home name args realVar)
624 subUnify context realVar (_second context)
627 subUnify context realVar (_second context)
630 subUnify context realVar (_second context)
632 Alias otherHome otherName otherArgs otherRealVar ->
633 if name == otherName && home == otherHome then
634 do zipWithM_ (subUnify context) (map snd args) (map snd otherArgs)
635 merge context otherContent
638 subUnify context realVar otherRealVar
641 subUnify context realVar (_second context)
651unifyStructure :: Context -> FlatType -> Content -> Content -> Unify ()
652unifyStructure context flatType content otherContent =
655 merge context content
658 unifyFlexSuperStructure (reorient context) super flatType
661 mismatch context $ Just $ RigidVarTooGeneric name (flatTypeToSpecificThing flatType)
663 RigidSuper super name ->
664 mismatch context $ Just $ RigidSuperTooGeneric super name (flatTypeToSpecificThing flatType)
666 Alias _ _ _ realVar ->
667 subUnify context (_first context) realVar
669 Structure otherFlatType ->
670 case (flatType, otherFlatType) of
671 (App1 home name args, App1 otherHome otherName otherArgs) ->
672 if home == otherHome && name == otherName then
673 do zipWithM_ (subUnify context) args otherArgs
674 merge context otherContent
676 else if isIntFloat home name otherHome otherName then
677 mismatch context (Just IntFloat)
680 mismatch context Nothing
682 (Fun1 arg result, Fun1 otherArg otherResult) ->
683 do subUnify context arg otherArg
684 subUnify context result otherResult
685 merge context otherContent
687 (EmptyRecord1, EmptyRecord1) ->
688 merge context otherContent
690 (Record1 fields ext, EmptyRecord1) | Map.null fields ->
691 subUnify context ext (_second context)
693 (EmptyRecord1, Record1 fields ext) | Map.null fields ->
694 subUnify context (_first context) ext
696 (Record1 fields extension, Record1 otherFields otherExtension) ->
697 Unify $ \vars ok err ->
698 do firstStructure <- gatherFields context fields extension
699 secondStructure <- gatherFields context otherFields otherExtension
700 case unifyRecord context firstStructure secondStructure of
704 (Tuple1 a b Nothing, Tuple1 x y Nothing) ->
705 do subUnify context a x
708 (Tuple1 a b (Just c), Tuple1 x y (Just z)) ->
709 do subUnify context a x
714 mismatch context Nothing
720isIntFloat :: ModuleName.Canonical -> N.Name -> ModuleName.Canonical -> N.Name -> Bool
721isIntFloat home name otherHome otherName =
722 otherHome == ModuleName.basics
723 && home == ModuleName.basics
725 (name == N.int && otherName == N.float)
727 (name == N.float && otherName == N.int)
735unifyRecord :: Context -> RecordStructure -> RecordStructure -> Unify ()
736unifyRecord context firstStructure secondStructure =
737 do let (RecordStructure expFields expVar expStruct) = firstStructure
738 let (RecordStructure actFields actVar actStruct) = secondStructure
740 let sharedFields = Map.intersectionWith (,) expFields actFields
742 -- call after unifying extension, make sure record shape matches before
743 -- looking into whether the particular field types match.
744 let unifySharedFields otherFields ext =
745 do unifySharedFieldsHelp context sharedFields
746 let allFields = Map.union (Map.map fst sharedFields) otherFields
747 merge context (Structure (Record1 allFields ext))
749 let uniqueExpFields = Map.difference expFields actFields
750 let uniqueActFields = Map.difference actFields expFields
752 case (expStruct, Map.null uniqueExpFields, actStruct, Map.null uniqueActFields) of
753 (_, True, _, True) ->
754 do subUnify context expVar actVar
755 unifySharedFields Map.empty expVar
757 (Empty, _, _, False) ->
758 mismatch context $ Just $
759 MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
761 (_, False, Empty, _) ->
762 mismatch context $ Just $
763 MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
765 (_, False, _, True) ->
766 do subRecord <- fresh context (Structure (Record1 uniqueExpFields expVar))
767 subUnify context subRecord actVar
768 unifySharedFields Map.empty subRecord
770 (_, True, _, False) ->
771 do subRecord <- fresh context (Structure (Record1 uniqueActFields actVar))
772 subUnify context expVar subRecord
773 unifySharedFields Map.empty subRecord
775 (Extension, False, Extension, False) ->
776 do let subFields = Map.union uniqueExpFields uniqueActFields
777 subExt <- fresh context Type.unnamedFlexVar
778 expRecord <- fresh context (Structure (Record1 uniqueActFields subExt))
779 actRecord <- fresh context (Structure (Record1 uniqueExpFields subExt))
780 subUnify context expVar expRecord
781 subUnify context actRecord actVar
782 unifySharedFields subFields subExt
786unifySharedFieldsHelp :: Context -> Map.Map N.Name (Variable, Variable) -> Unify ()
787unifySharedFieldsHelp context sharedFields =
788 do badFields <- Map.traverseMaybeWithKey (unifyField context) sharedFields
789 case Map.toList badFields of
794 mismatch context (Just (BadFields badFieldPairs))
797unifyField :: Context -> N.Name -> (Variable, Variable) -> Unify (Maybe (Maybe Reason))
798unifyField context _ (actual, expected) =
799 Unify $ \vars ok _ ->
800 case subUnify context actual expected of
803 (\vs () -> ok vs Nothing)
816-- GATHER RECORD STRUCTURE
819data RecordStructure =
821 { _fields :: Map.Map N.Name Variable
822 , _extVar :: Variable
823 , _extStruct :: ExtensionStructure
827data ExtensionStructure
832gatherFields :: Context -> Map.Map N.Name Variable -> Variable -> IO RecordStructure
833gatherFields context fields variable =
834 do (Descriptor content _ _ _) <- UF.get variable
836 Structure (Record1 subFields subExt) ->
837 gatherFields context (Map.union fields subFields) subExt
839 Structure EmptyRecord1 ->
840 return (RecordStructure fields variable Empty)
843 -- TODO may be dropping useful alias info here
844 gatherFields context fields var
847 return (RecordStructure fields variable Extension)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list