1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
5import Control.Monad (when)
6import Control.Monad.State.Strict (StateT, liftIO)
7import qualified Control.Monad.State.Strict as State
8import qualified Data.Char as Char
9import qualified Data.Map as Map
10import Data.Monoid ((<>))
11import qualified Data.Text as Text
12import Data.Text (Text)
14import qualified AST.Type as T
15import qualified AST.Variable as Var
16import qualified Reporting.Annotation as A
17import qualified Reporting.Error.Type as Error
18import qualified Reporting.Region as R
19import qualified Type.UnionFind as UF
31 = App1 Variable Variable
32 | Fun1 Variable Variable
34 | Record1 (Map.Map Text Variable) Variable
39 | AliasN Var.Canonical [(Text, Type)] Type
44 | RecordN (Map.Map Text Type) Type
56 , _copy :: Maybe Variable
63 | Var Flex (Maybe Super) (Maybe Text)
64 | Alias Var.Canonical [(Text,Variable)] Variable
102getVarNamesMark :: Int
114 | CEqual Error.Hint R.Region Type Type
116 | CLet [Scheme] Constraint
117 | CInstance R.Region SchemeName Type
120type SchemeName = Text
125 { _rigidQuantifiers :: [Variable]
126 , _flexibleQuantifiers :: [Variable]
127 , _constraint :: Constraint
128 , _header :: Map.Map Text (A.Located Type)
140(==>) :: Type -> Type -> Type
146(<|) :: Type -> Type -> Type
155mkDescriptor :: Content -> Descriptor
156mkDescriptor content =
165mkAtom :: Var.Canonical -> IO Variable
167 UF.fresh $ mkDescriptor (Atom name)
170mkVar :: Maybe Super -> IO Variable
172 UF.fresh $ mkDescriptor (Var Flex maybeSuper Nothing)
175mkNamedVar :: Flex -> Text -> IO Variable
176mkNamedVar flex name =
177 UF.fresh $ mkDescriptor $ Var flex (toSuper name) (Just name)
180toSuper :: Text -> Maybe Super
182 if Text.isPrefixOf "number" name then
185 else if Text.isPrefixOf "comparable" name then
188 else if Text.isPrefixOf "appendable" name then
199monoscheme :: Map.Map Text (A.Located Type) -> Scheme
201 Scheme [] [] CTrue headers
207(/\) :: Constraint -> Constraint -> Constraint
215-- ex qs constraint == exists qs. constraint
216ex :: [Variable] -> Constraint -> Constraint
218 CLet [Scheme [] fqs constraint Map.empty] CTrue
221forall :: [Variable] -> Constraint -> Constraint
222forall rqs constraint =
223 CLet [Scheme rqs [] constraint Map.empty] CTrue
226exists :: (Type -> IO Constraint) -> IO Constraint
228 do v <- mkVar Nothing
229 ex [v] <$> f (VarN v)
232existsNumber :: (Type -> IO Constraint) -> IO Constraint
234 do v <- mkVar (Just Number)
235 ex [v] <$> f (VarN v)
239-- CONVERT TO SOURCE TYPES
242-- TODO: Attach resulting type to the descriptor so that you
243-- never have to do extra work, particularly nice for aliased types
244toSrcType :: Variable -> IO T.Canonical
246 do takenNames <- State.execStateT (getVarNames variable) Map.empty
247 State.evalStateT (variableToSrcType variable) (makeNameState takenNames)
250variableToSrcType :: Variable -> StateT NameState IO T.Canonical
251variableToSrcType variable =
252 do descriptor <- liftIO $ UF.descriptor variable
253 let mark = _mark descriptor
254 if mark == occursMark
259 do liftIO $ UF.modifyDescriptor variable (\desc -> desc { _mark = occursMark })
260 srcType <- contentToSrcType variable (_content descriptor)
261 liftIO $ UF.modifyDescriptor variable (\desc -> desc { _mark = mark })
265contentToSrcType :: Variable -> Content -> StateT NameState IO T.Canonical
266contentToSrcType variable content =
274 Var _ _ (Just name) ->
277 Var flex maybeSuper Nothing ->
278 do freshName <- getFreshName maybeSuper
279 liftIO $ UF.modifyDescriptor variable $ \desc ->
280 desc { _content = Var flex maybeSuper (Just freshName) }
281 return (T.Var freshName)
283 Alias name args realVariable ->
284 do srcArgs <- traverse (traverse variableToSrcType) args
285 srcType <- variableToSrcType realVariable
286 return (T.Aliased name srcArgs (T.Filled srcType))
292termToSrcType :: FlatType -> StateT NameState IO T.Canonical
296 do srcFunc <- variableToSrcType func
297 srcArg <- variableToSrcType arg
300 return (T.App f (args ++ [srcArg]))
303 return (T.App srcFunc [srcArg])
307 <$> variableToSrcType a
308 <*> variableToSrcType b
311 return $ T.Record [] Nothing
313 Record1 fields extension ->
314 do srcFields <- Map.toList <$> traverse variableToSrcType fields
315 srcExt <- T.iteratedDealias <$> variableToSrcType extension
318 T.Record subFields subExt ->
319 T.Record (subFields ++ srcFields) subExt
322 T.Record srcFields (Just srcExt)
325 error "Used toSrcType on a type that is not well-formed"
329-- MANAGE FRESH VARIABLE NAMES
334 { _taken :: TakenNames
337 , _comparables :: Int
338 , _appendables :: Int
339 , _compAppends :: Int
343type TakenNames = Map.Map Text Variable
346makeNameState :: TakenNames -> NameState
348 NameState taken 0 0 0 0 0
351getFreshName :: (Monad m) => Maybe Super -> StateT NameState m Text
352getFreshName maybeSuper =
355 do index <- State.gets _normals
356 taken <- State.gets _taken
357 let (uniqueName, newIndex) = getFreshNormal index taken
358 State.modify (\state -> state { _normals = newIndex })
362 getFreshSuper "number" _numbers (\index state -> state { _numbers = index })
365 getFreshSuper "comparable" _comparables (\index state -> state { _comparables = index })
368 getFreshSuper "appendable" _appendables (\index state -> state { _appendables = index })
371 getFreshSuper "compappend" _compAppends (\index state -> state { _compAppends = index })
374getFreshNormal :: Int -> TakenNames -> (Text, Int)
375getFreshNormal index taken =
381 Char.chr (97 + letter)
384 Text.pack (if postfix <= 0 then [char] else char : show postfix)
386 if Map.member name taken then
387 getFreshNormal (index + 1) taken
396 -> (NameState -> Int)
397 -> (Int -> NameState -> NameState)
398 -> StateT NameState m Text
399getFreshSuper name getter setter =
400 do index <- State.gets getter
401 taken <- State.gets _taken
402 let (uniqueName, newIndex) = getFreshSuperHelp name index taken
403 State.modify (setter newIndex)
407getFreshSuperHelp :: Text -> Int -> TakenNames -> (Text, Int)
408getFreshSuperHelp name index taken =
411 if index <= 0 then name else name <> Text.pack (show index)
413 if Map.member newName taken then
414 getFreshSuperHelp name (index + 1) taken
421-- GET ALL VARIABLE NAMES
424getVarNames :: Variable -> StateT TakenNames IO ()
426 do desc <- liftIO $ UF.descriptor var
427 case _mark desc == getVarNamesMark of
432 do liftIO $ UF.setDescriptor var (desc { _mark = getVarNamesMark })
433 case _content desc of
443 Var flex maybeSuper (Just name) ->
444 do oldTaken <- State.get
445 newTaken <- liftIO $ addVarName 0 name var flex maybeSuper oldTaken
448 Alias _ args realVar ->
449 do mapM_ (getVarNames . snd) args
452 Structure (App1 func arg) ->
456 Structure (Fun1 arg body) ->
460 Structure EmptyRecord1 ->
463 Structure (Record1 fields extension) ->
464 do mapM_ getVarNames fields
465 getVarNames extension
468addVarName :: Int -> Text -> Variable -> Flex -> Maybe Super -> TakenNames -> IO TakenNames
469addVarName index givenName var flex maybeSuper taken =
472 makeIndexedName givenName index
474 case Map.lookup name taken of
476 do when (name /= givenName) $ UF.modifyDescriptor var $ \desc ->
477 desc { _content = Var flex maybeSuper (Just name) }
478 return $ Map.insert name var taken
481 do same <- UF.equivalent var otherVar
484 else addVarName (index + 1) givenName var flex maybeSuper taken
487makeIndexedName :: Text -> Int -> Text
488makeIndexedName name index =
492 else if Char.isDigit (Text.last name) then
493 name <> Text.pack ('_' : show index)
496 name <> Text.pack (show index)
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