1module Type.Unify (unify) where
3import Control.Applicative ((<|>))
4import Control.Monad.Except (ExceptT, throwError, runExceptT)
5import Control.Monad.State as State
6import qualified Data.List as List
7import qualified Data.Map as Map
8import qualified Data.UnionFind.IO as UF
9import Text.PrettyPrint (render)
11import qualified AST.Type as AstType
12import qualified AST.Variable as Var
13import qualified Reporting.Region as R
14import qualified Reporting.Error.Type as Error
15import qualified Type.State as TS
16import Type.Type as Type
17import Type.PrettyPrint
18import Elm.Utils ((|>))
26 -> StateT TS.SolverState IO ()
27unify hint region variable1 variable2 =
28 do result <- runExceptT (unifyHelp variable1 variable2)
33 Left (RawMismatch (leftType, rightType, note)) ->
34 TS.addError region $ Error.Mismatch $
35 Error.MismatchInfo hint leftType rightType note
38-- ACTUALLY UNIFY STUFF
41 ExceptT RawMismatch (StateT TS.SolverState IO)
45 RawMismatch (AstType.Canonical, AstType.Canonical, Maybe String)
49 :: UF.Point Descriptor
50 -> UF.Point Descriptor
53typeError 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))
62unifyHelp :: Variable -> Variable -> Unify ()
63unifyHelp variable1 variable2 =
64 do equivalent <- liftIO $ UF.equivalent variable1 variable2
67 else actuallyUnify variable1 variable2
70actuallyUnify :: Variable -> Variable -> Unify ()
71actuallyUnify variable1 variable2 = do
72 desc1 <- liftIO $ UF.descriptor variable1
73 desc2 <- liftIO $ UF.descriptor variable2
74 let name' = pickName desc1 desc2
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
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
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
263 (EmptyRecord1, Record1 fields ext) | Map.null fields -> unifyHelp variable1 ext
265 (Record1 _ _, Record1 _ _) ->
266 recordUnify fresh variable1 variable2
268 _ -> typeError variable1 variable2 Nothing
273rigidErrorMessage :: String -> String
274rigidErrorMessage var =
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."
286 :: (Maybe (Term1 Variable) -> Unify Variable)
290recordUnify 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 =
297 fresh (Just (Record1 fields ext))
299 let uniqueFields1 = diffFields fields1 fields2
300 let uniqueFields2 = diffFields fields2 fields1
302 let addFieldMismatchError missingFields =
303 typeError variable1 variable2 $ Just $
304 fieldMismatchError missingFields
307 (Empty _, Empty _) ->
308 case Map.null uniqueFields1 && Map.null uniqueFields2 of
310 False -> typeError variable1 variable2 Nothing
312 (Empty var1, Extension var2) ->
313 case (Map.null uniqueFields1, Map.null uniqueFields2) of
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) ->
321 case (Map.null uniqueFields1, Map.null uniqueFields2) of
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) ->
329 case (Map.null uniqueFields1, Map.null uniqueFields2) of
334 do subRecord <- freshRecord uniqueFields2 var2
335 unifyHelp var1 subRecord
338 do subRecord <- freshRecord uniqueFields1 var1
339 unifyHelp subRecord var2
342 do record1' <- freshRecord uniqueFields1 =<< fresh Nothing
343 record2' <- freshRecord uniqueFields2 =<< fresh Nothing
344 unifyHelp record1' var2
345 unifyHelp var1 record2'
348unifyOverlappingFields
349 :: Map.Map String [Variable]
350 -> Map.Map String [Variable]
352unifyOverlappingFields fields1 fields2 =
353 Map.intersectionWith (zipWith unifyHelp) fields1 fields2
359diffFields :: Map.Map String [a] -> Map.Map String [a] -> Map.Map String [a]
360diffFields fields1 fields2 =
361 let eat (_:xs) (_:ys) = eat xs ys
364 Map.union (Map.intersectionWith eat fields1 fields2) fields1
365 |> Map.filter (not . null)
368data ExpandedRecord = ExpandedRecord
369 { _fields :: Map.Map String [Variable]
370 , _extension :: Extension
373data Extension = Empty Variable | Extension Variable
376gatherFields :: Variable -> IO ExpandedRecord
378 do desc <- UF.descriptor var
379 case structure desc of
380 (Just (Record1 fields ext)) ->
381 do (ExpandedRecord deeperFields rootExt) <- gatherFields ext
382 return (ExpandedRecord (Map.unionWith (++) fields deeperFields) rootExt)
384 (Just EmptyRecord1) ->
385 return (ExpandedRecord Map.empty (Empty var))
388 return (ExpandedRecord Map.empty (Extension var))
391-- assumes that one of the dicts has stuff in it
392fieldMismatchError :: Map.Map String a -> String
393fieldMismatchError missingFields =
394 case Map.keys missingFields of
399 "Looks like a record is missing the field `" ++ key ++ "`"
402 "Looks like a record is missing fields "
403 ++ List.intercalate ", " (init keys) ++ ", and " ++ last keys
407pickAlias :: Descriptor -> Descriptor -> Alias Variable
408pickAlias desc1 desc2 =
409 alias desc1 <|> alias desc2
412pickRank :: Descriptor -> Descriptor -> Int
413pickRank desc1 desc2 =
414 min (rank desc1) (rank desc2)
417pickFlex :: Descriptor -> Descriptor -> Flex
418pickFlex desc1 desc2 =
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
428pickName :: Descriptor -> Descriptor -> Maybe Var.Canonical
429pickName desc1 desc2 =
430 case (name desc1, name desc2) of
431 (Nothing, Nothing) ->
434 (Just name1, Nothing) ->
437 (Nothing, Just name2) ->
440 (Just name1, Just name2) ->
441 case (flex desc1, flex desc2) of
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
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