module Type.Type where
import Control.Applicative ((<$>),(<*>))
import Control.Monad.State (StateT)
import qualified Control.Monad.State as State
import Control.Monad.Error (ErrorT, Error, liftIO)
import qualified Data.Char as Char
import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Data.Traversable as Traverse (traverse)
import qualified Data.UnionFind.IO as UF
import Text.PrettyPrint as P
import System.IO.Unsafe
import AST.Annotation
import qualified AST.PrettyPrint as PP
import qualified AST.Type as T
import qualified AST.Variable as Var
import Type.PrettyPrint
type Type = TermN Variable
type Variable = UF.Point Descriptor
type TypeConstraint = Constraint Type Variable
type TypeScheme = Scheme Type Variable
type Alias a = Maybe (Var.Canonical, [(String,a)])
data Term1 a
= App1 a a
| Fun1 a a
| Var1 a
| EmptyRecord1
| Record1 (Map.Map String [a]) a
data TermN a
= PlaceHolder String
| VarN (Alias (TermN a)) a
| TermN (Alias (TermN a)) (Term1 (TermN a))
varN :: a -> TermN a
varN =
VarN Nothing
termN :: (Term1 (TermN a)) -> TermN a
termN =
TermN Nothing
record :: Map.Map String [TermN a] -> TermN a -> TermN a
record fs rec =
termN (Record1 fs rec)
data Descriptor = Descriptor
{ structure :: Maybe (Term1 Variable)
, rank :: Int
, flex :: Flex
, name :: Maybe TypeName
, copy :: Maybe Variable
, mark :: Int
, alias :: Alias Variable
}
noRank :: Int
noRank = 1
outermostRank :: Int
outermostRank = 0
noMark :: Int
noMark = 0
initialMark :: Int
initialMark = 1
data Flex
= Rigid
| Flexible
| Constant
| Is SuperType
deriving (Eq)
data SuperType
= Number
| Comparable
| Appendable
deriving (Eq)
type Constraint a b =
Located (BasicConstraint a b)
data BasicConstraint a b
= CTrue
| CSaveEnv
| CEqual a a
| CAnd [Constraint a b]
| CLet [Scheme a b] (Constraint a b)
| CInstance SchemeName a
type SchemeName = String
type TypeName = Var.Canonical
data Scheme a b = Scheme
{ rigidQuantifiers :: [b]
, flexibleQuantifiers :: [b]
, constraint :: Constraint a b
, header :: Map.Map String a
}
infixr 9 ==>
(==>) :: Type -> Type -> Type
(==>) a b =
termN (Fun1 a b)
(<|) :: TermN a -> TermN a -> TermN a
(<|) f a =
termN (App1 f a)
namedVar :: Flex -> Var.Canonical -> IO Variable
namedVar flex name = UF.fresh $ Descriptor
{ structure = Nothing
, rank = noRank
, flex = flex
, name = Just name
, copy = Nothing
, mark = noMark
, alias = Nothing
}
variable :: Flex -> IO Variable
variable flex = UF.fresh $ Descriptor
{ structure = Nothing
, rank = noRank
, flex = flex
, name = Nothing
, copy = Nothing
, mark = noMark
, alias = Nothing
}
monoscheme :: Map.Map String a -> Scheme a b
monoscheme headers =
Scheme [] [] (noneNoDocs CTrue) headers
infixl 8 /\
(/\) :: Constraint a b -> Constraint a b -> Constraint a b
(/\) a@(A _ c1) b@(A _ c2) =
case (c1, c2) of
(CTrue, _) -> b
(_, CTrue) -> a
_ -> mergeOldDocs a b (CAnd [a,b])
ex :: [Variable] -> TypeConstraint -> TypeConstraint
ex fqs constraint@(A ann _) =
A ann $ CLet [Scheme [] fqs constraint Map.empty] (A ann CTrue)
fl :: [Variable] -> TypeConstraint -> TypeConstraint
fl rqs constraint@(A ann _) =
A ann $ CLet [Scheme rqs [] constraint Map.empty] (A ann CTrue)
exists :: Error e => (Type -> ErrorT e IO TypeConstraint) -> ErrorT e IO TypeConstraint
exists f =
do v <- liftIO $ variable Flexible
ex [v] <$> f (varN v)
existsNumber :: Error e => (Type -> ErrorT e IO TypeConstraint) -> ErrorT e IO TypeConstraint
existsNumber f =
do v <- liftIO $ variable (Is Number)
ex [v] <$> f (varN v)
instance PrettyType a => PrettyType (UF.Point a) where
pretty when point =
unsafePerformIO $ fmap (pretty when) (UF.descriptor point)
instance PrettyType t => PrettyType (Annotated a t) where
pretty when (A _ e) =
pretty when e
instance PrettyType a => PrettyType (Term1 a) where
pretty when term =
let prty = pretty Never in
case term of
App1 f x ->
parensIf needed (px <+> pretty App x)
where
px = prty f
needed =
case when of
App -> True
_ -> False
Fun1 arg body ->
parensIf needed (pretty Fn arg <+> P.text "->" <+> prty body)
where
needed =
case when of
Never -> False
_ -> True
Var1 x ->
prty x
EmptyRecord1 ->
P.braces P.empty
Record1 fields ext ->
P.braces (extend <+> commaSep prettyFields)
where
prettyExt = prty ext
extend
| P.render prettyExt == "{}" =
P.empty
| otherwise =
prettyExt <+> P.text "|"
mkPretty f t =
P.text f <+> P.text ":" <+> prty t
prettyFields =
concatMap (\(f,ts) -> map (mkPretty f) ts) (Map.toList fields)
instance PrettyType a => PrettyType (TermN a) where
pretty when term =
case term of
PlaceHolder _ ->
error "problem prettifying type, probably indicates a bigger problem"
VarN alias x ->
either alias (pretty when x)
TermN alias t1 ->
either alias (pretty when t1)
where
either maybeAlias doc =
case maybeAlias of
Nothing -> doc
Just (name, args) ->
P.hang (PP.pretty name) 2 (P.sep (map (pretty App . snd) args))
instance PrettyType Descriptor where
pretty when desc =
case (alias desc, structure desc, name desc) of
(Just (name, args), _, _) ->
P.hang (PP.pretty name) 4 (P.sep (map (pretty App . snd) args))
(_, Just term, _) ->
pretty when term
(_, _, Just name)
| Var.isTuple name ->
P.parens . P.text $ replicate (read (drop 6 (Var.toString name)) 1) ','
| otherwise ->
P.text (Var.toString name)
_ -> P.text "?"
instance (PrettyType a, PrettyType b) => PrettyType (BasicConstraint a b) where
pretty _ constraint =
let prty = pretty Never in
case constraint of
CTrue -> P.text "True"
CSaveEnv -> P.text "SaveTheEnvironment!!!"
CEqual a b -> prty a <+> P.text "=" <+> prty b
CAnd [] -> P.text "True"
CAnd cs ->
P.parens . P.sep $ P.punctuate (P.text " and") (map (pretty Never) cs)
CLet [Scheme [] fqs constraint header] (A _ CTrue) | Map.null header ->
P.sep [ binder, pretty Never c ]
where
mergeExists vs (A _ c) =
case c of
CLet [Scheme [] fqs' c' _] (A _ CTrue) -> mergeExists (vs ++ fqs') c'
_ -> (vs, c)
(fqs', c) = mergeExists fqs constraint
binder = if null fqs' then P.empty else
P.text "\x2203" <+> P.hsep (map (pretty Never) fqs') <> P.text "."
CLet schemes constraint ->
P.fsep [ P.hang (P.text "let") 4 (P.brackets . commaSep $ map (pretty Never) schemes)
, P.text "in", pretty Never constraint ]
CInstance name tipe ->
P.text name <+> P.text "<" <+> prty tipe
instance (PrettyType a, PrettyType b) => PrettyType (Scheme a b) where
pretty _ (Scheme rqs fqs (A _ constraint) headers) =
P.sep [ forall, cs, headers' ]
where
prty = pretty Never
forall = if null rqs && null fqs then P.empty else
P.text "\x2200" <+> frees <+> rigids
frees = P.hsep $ map prty fqs
rigids = if null rqs then P.empty else P.braces . P.hsep $ map prty rqs
cs = case constraint of
CTrue -> P.empty
CAnd [] -> P.empty
_ -> P.brackets (pretty Never constraint)
headers' = if Map.size headers > 0 then dict else P.empty
dict = P.parens . commaSep . map prettyPair $ Map.toList headers
prettyPair (n,t) = P.text n <+> P.text ":" <+> pretty Never t
toSrcType :: Variable -> IO T.CanonicalType
toSrcType variable =
do addNames variable
variableToSrcType variable variable
variableToSrcType :: Variable -> Variable -> IO T.CanonicalType
variableToSrcType rootVariable variable =
do desc <- UF.descriptor variable
srcType <- maybe (backupSrcType desc) (termToSrcType rootVariable) (structure desc)
case alias desc of
Nothing ->
return srcType
Just (name, args) ->
case srcType of
T.Type (Var.Canonical Var.BuiltIn _) ->
return srcType
_ ->
do args' <- mapM (\(arg,tvar) -> (,) arg <$> variableToSrcType rootVariable tvar) args
return (T.Aliased name args' (T.Filled srcType))
where
backupSrcType :: Descriptor -> IO T.CanonicalType
backupSrcType desc =
case name desc of
Just v@(Var.Canonical _ x@(c:_))
| Char.isLower c ->
return (T.Var x)
| otherwise ->
return $ T.Type v
_ ->
error $
concat
[ "Problem converting the following type "
, "from a type-checker type to a source-syntax type:"
, P.render (pretty Never rootVariable)
]
termToSrcType :: Variable -> Term1 Variable -> IO T.CanonicalType
termToSrcType rootVariable term =
let go = variableToSrcType rootVariable in
case term of
App1 a b ->
do a' <- go a
b' <- go b
case a' of
T.App f args ->
return $ T.App f (args ++ [b'])
_ ->
return $ T.App a' [b']
Fun1 a b ->
T.Lambda <$> go a <*> go b
Var1 a ->
go a
EmptyRecord1 ->
return $ T.Record [] Nothing
Record1 tfields extension ->
do fields' <- Traverse.traverse (mapM go) tfields
let fields = concat [ map ((,) name) ts | (name,ts) <- Map.toList fields' ]
ext' <- dealias <$> go extension
return $
case ext' of
T.Record fs ext -> T.Record (fs ++ fields) ext
T.Var _ -> T.Record fields (Just ext')
_ -> error "Used toSrcType on a type that is not well-formed"
where
dealias :: T.CanonicalType -> T.CanonicalType
dealias tipe =
case tipe of
T.Aliased _ args tipe' ->
T.dealias args tipe'
_ ->
tipe
addNames :: Variable -> IO ()
addNames variable =
do usedNames <- getVarNames variable
let freeNames = makeFreeNames usedNames
State.runStateT (varAddNames variable) (NameState freeNames 0 0 0)
return ()
makeFreeNames :: Set.Set String -> [String]
makeFreeNames usedNames =
let makeName suffix =
map (:suffix) ['a'..'z']
allNames =
concatMap makeName ("" : "'" : "_" : map show [ (0 :: Int) .. ])
in
filter (\name -> Set.notMember name usedNames) allNames
data NameState = NameState
{ freeNames :: [String]
, numberPrimes :: Int
, comparablePrimes :: Int
, appendablePrimes :: Int
}
varAddNames :: Variable -> StateT NameState IO ()
varAddNames var =
do desc <- liftIO (UF.descriptor var)
case alias desc of
Nothing ->
return ()
Just (_name, args) ->
mapM_ (\(v,t) -> (,) v <$> varAddNames t) args
case structure desc of
Just term ->
termAddNames term
Nothing ->
case name desc of
Just _ -> return ()
Nothing ->
do name' <- createName desc
liftIO $ UF.modifyDescriptor var $ \desc ->
desc { name = Just (Var.local name') }
termAddNames :: Term1 Variable -> StateT NameState IO ()
termAddNames term =
case term of
App1 a b ->
do varAddNames a
varAddNames b
Fun1 a b ->
do varAddNames a
varAddNames b
Var1 a ->
varAddNames a
EmptyRecord1 ->
return ()
Record1 fields extension ->
do mapM_ varAddNames (concat (Map.elems fields))
varAddNames extension
createName :: (Monad m) => Descriptor -> StateT NameState m String
createName desc =
case flex desc of
Is Number ->
do primes <- State.gets numberPrimes
State.modify (\state -> state { numberPrimes = primes + 1 })
return ("number" ++ replicate primes '\'')
Is Comparable ->
do primes <- State.gets comparablePrimes
State.modify (\state -> state { comparablePrimes = primes + 1 })
return ("comparable" ++ replicate primes '\'')
Is Appendable ->
do primes <- State.gets appendablePrimes
State.modify (\state -> state { appendablePrimes = primes + 1 })
return ("appendable" ++ replicate primes '\'')
_ ->
do names <- State.gets freeNames
State.modify (\state -> state { freeNames = tail names })
return (head names)
getVarNames :: Variable -> IO (Set.Set String)
getVarNames var =
do desc <- UF.descriptor var
let baseSet =
case name desc of
Just var -> Set.singleton (Var.toString var)
Nothing -> Set.empty
structureSet <-
case structure desc of
Nothing -> return Set.empty
Just term -> getVarNamesTerm term
aliasSet <-
case alias desc of
Nothing ->
return Set.empty
Just (_name, args) ->
do let set = Set.fromList (map fst args)
sets <- mapM (getVarNames . snd) args
return (Set.unions (set : sets))
return (Set.unions [ baseSet, structureSet, aliasSet ])
getVarNamesTerm :: Term1 Variable -> IO (Set.Set String)
getVarNamesTerm term =
let go = getVarNames in
case term of
App1 a b ->
Set.union <$> go a <*> go b
Fun1 a b ->
Set.union <$> go a <*> go b
Var1 a ->
go a
EmptyRecord1 ->
return Set.empty
Record1 fields extension ->
do fieldVars <- Set.unions <$> mapM go (concat (Map.elems fields))
Set.union fieldVars <$> go extension
data AppStructure
= List Variable
| Tuple [Variable]
| Other
collectApps :: Variable -> IO AppStructure
collectApps var =
go [] var
where
go vars var = do
desc <- UF.descriptor var
case (structure desc, vars) of
(Nothing, [v]) ->
case name desc of
Just n | Var.isList n -> return (List v)
_ -> return Other
(Nothing, vs) ->
case name desc of
Just n | Var.isTuple n -> return (Tuple vs)
_ -> return Other
(Just term, _) ->
case term of
App1 a b -> go (vars ++ [b]) a
_ -> return Other