1module Type.Unify (unify) where
3import Control.Applicative ((<|>))
4import Control.Monad.State
5import qualified Data.List as List
6import qualified Data.Map as Map
7import qualified Data.Maybe as Maybe
8import qualified Data.UnionFind.IO as UF
9import qualified AST.Annotation as A
10import qualified AST.Variable as Var
11import qualified Type.State as TS
13import Type.PrettyPrint
14import Text.PrettyPrint (render)
15import Elm.Utils ((|>))
18unify :: A.Region -> Variable -> Variable -> StateT TS.SolverState IO ()
19unify region variable1 variable2 = do
20 equivalent <- liftIO $ UF.equivalent variable1 variable2
23 else actuallyUnify region variable1 variable2
26actuallyUnify :: A.Region -> Variable -> Variable -> StateT TS.SolverState IO ()
27actuallyUnify region variable1 variable2 = do
28 desc1 <- liftIO $ UF.descriptor variable1
29 desc2 <- liftIO $ UF.descriptor variable2
30 let unify' = unify region
32 (name', flex', rank', alias') = combinedDescriptors desc1 desc2
34 merge1 :: StateT TS.SolverState IO ()
36 if rank desc1 < rank desc2 then UF.union variable2 variable1
37 else UF.union variable1 variable2
38 UF.modifyDescriptor variable1 $ \desc ->
39 desc { structure = structure desc1
45 merge2 :: StateT TS.SolverState IO ()
47 if rank desc1 < rank desc2 then UF.union variable2 variable1
48 else UF.union variable1 variable2
49 UF.modifyDescriptor variable2 $ \desc ->
50 desc { structure = structure desc2
56 merge = if rank desc1 < rank desc2 then merge1 else merge2
58 fresh :: Maybe (Term1 Variable) -> StateT TS.SolverState IO Variable
60 v <- liftIO . UF.fresh $ Descriptor
61 { structure = structure
72 liftIO $ UF.modifyDescriptor v $ \desc -> desc { flex = Flexible }
73 unify' variable1 variable2
75 unifyNumber svar (Var.Canonical home name) =
77 Var.BuiltIn | name `elem` ["Int","Float"] -> flexAndUnify svar
78 Var.Local | List.isPrefixOf "number" name -> flexAndUnify svar
79 _ -> let hint = "A number must be an Int or Float."
80 in TS.addError region (Just hint) variable1 variable2
82 comparableError maybe =
83 TS.addError region (Just $ Maybe.fromMaybe msg maybe) variable1 variable2
84 where msg = "A comparable must be an Int, Float, Char, String, list, or tuple."
86 appendableError maybe =
87 TS.addError region (Just $ Maybe.fromMaybe msg maybe) variable1 variable2
88 where msg = "An appendable must be of type String, List, or Text."
90 unifyComparable v (Var.Canonical home name) =
92 Var.BuiltIn | name `elem` ["Int","Float","Char","String"] -> flexAndUnify v
93 Var.Local | List.isPrefixOf "comparable" name -> flexAndUnify v
94 _ -> comparableError Nothing
96 unifyComparableStructure varSuper varFlex =
97 do struct <- liftIO $ collectApps varFlex
99 Other -> comparableError Nothing
100 List v -> do flexAndUnify varSuper
101 unify' v =<< liftIO (variable $ Is Comparable)
104 comparableError $ Just "Cannot compare a tuple with more than 6 elements."
106 do flexAndUnify varSuper
107 cmpVars <- liftIO $ forM [1..length vs] $ \_ -> variable (Is Comparable)
108 zipWithM_ unify' vs cmpVars
110 unifyAppendable varSuper varFlex =
111 do struct <- liftIO $ collectApps varFlex
113 List _ -> flexAndUnify varSuper
114 _ -> appendableError Nothing
116 rigidError var = TS.addError region (Just hint) variable1 variable2
118 var' = "'" ++ render (pretty Never var) ++ "'"
119 hint = "Cannot unify rigid type variable " ++ var' ++
120 ".\nThe problem probably relates to a type annotation. Note that rigid type\n\
121 \variables are not shared between a top-level and let-bound type annotations."
124 case (flex desc1, flex desc2, name desc1, name desc2) of
125 (Is super1, Is super2, _, _)
126 | super1 == super2 -> merge
127 (Is Number, Is Comparable, _, _) -> merge1
128 (Is Comparable, Is Number, _, _) -> merge2
130 (Is Number, _, _, Just name) -> unifyNumber variable1 name
131 (_, Is Number, Just name, _) -> unifyNumber variable2 name
133 (Is Comparable, _, _, Just name) -> unifyComparable variable1 name
134 (_, Is Comparable, Just name, _) -> unifyComparable variable2 name
135 (Is Comparable, _, _, _) -> unifyComparableStructure variable1 variable2
136 (_, Is Comparable, _, _) -> unifyComparableStructure variable2 variable1
138 (Is Appendable, _, _, Just name)
139 | Var.isText name || Var.isPrim "String" name -> flexAndUnify variable1
140 (_, Is Appendable, Just name, _)
141 | Var.isText name || Var.isPrim "String" name -> flexAndUnify variable2
142 (Is Appendable, _, _, _) -> unifyAppendable variable1 variable2
143 (_, Is Appendable, _, _) -> unifyAppendable variable2 variable1
145 (Rigid, _, _, _) -> rigidError variable1
146 (_, Rigid, _, _) -> rigidError variable2
147 _ -> TS.addError region Nothing variable1 variable2
149 case (structure desc1, structure desc2) of
150 (Nothing, Nothing) | flex desc1 == Flexible && flex desc1 == Flexible -> merge
151 (Nothing, _) | flex desc1 == Flexible -> merge2
152 (_, Nothing) | flex desc2 == Flexible -> merge1
154 (Just (Var1 v), _) -> unify' v variable2
155 (_, Just (Var1 v)) -> unify' v variable1
157 (Nothing, _) -> superUnify
158 (_, Nothing) -> superUnify
160 (Just type1, Just type2) ->
161 case (type1,type2) of
162 (App1 term1 term2, App1 term1' term2') ->
166 (Fun1 term1 term2, Fun1 term1' term2') ->
171 (EmptyRecord1, EmptyRecord1) ->
174 (Record1 fields ext, EmptyRecord1) | Map.null fields -> unify' ext variable2
175 (EmptyRecord1, Record1 fields ext) | Map.null fields -> unify' ext variable1
177 (Record1 fields1 ext1, Record1 fields2 ext2) ->
178 do Map.intersectionWith (zipWith unify') fields1 fields2
183 let mkRecord fs ext =
184 fresh (Just (Record1 fs ext))
186 case (Map.null fields1', Map.null fields2') of
191 do record2' <- mkRecord fields2' ext2
195 do record1' <- mkRecord fields1' ext1
199 do record1' <- mkRecord fields1' =<< fresh Nothing
200 record2' <- mkRecord fields2' =<< fresh Nothing
204 fields1' = unmerged fields1 fields2
205 fields2' = unmerged fields2 fields1
208 Map.union (Map.intersectionWith eat a b) a
209 |> Map.filter (not . null)
211 eat (_:xs) (_:ys) = eat xs ys
214 _ -> TS.addError region Nothing variable1 variable2
216combinedDescriptors :: Descriptor -> Descriptor
217 -> (Maybe Var.Canonical, Flex, Int, Maybe Var.Canonical)
218combinedDescriptors desc1 desc2 =
219 (name', flex', rank', alias')
222 rank' = min (rank desc1) (rank desc2)
224 alias' :: Maybe Var.Canonical
225 alias' = alias desc1 <|> alias desc2
227 name' :: Maybe Var.Canonical
228 name' = case (name desc1, name desc2) of
229 (Just name1, Just name2) ->
230 case (flex desc1, flex desc2) of
231 (_, Flexible) -> Just name1
232 (Flexible, _) -> Just name2
233 (Is Number, Is _) -> Just name1
234 (Is _, Is Number) -> Just name2
235 (Is _, Is _) -> Just name1
237 (Just name1, _) -> Just name1
238 (_, Just name2) -> Just name2
242 flex' = case (flex desc1, flex desc2) of
245 (Is Number, Is _) -> Is Number
246 (Is _, Is Number) -> Is Number
247 (Is super, Is _) -> Is super
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