module Type.Type where
import qualified Data.Char as Char
import qualified Data.List as List
import qualified Data.Map as Map
import qualified Data.UnionFind.IO as UF
import Type.PrettyPrint
import Text.PrettyPrint as P
import System.IO.Unsafe
import Control.Applicative ((<$>),(<*>))
import Control.Monad.State (StateT, get, put, execStateT, runStateT)
import Control.Monad.Error (ErrorT, Error, liftIO)
import Data.Traversable (traverse)
import AST.Annotation
import qualified AST.PrettyPrint as PP
import qualified AST.Type as T
import qualified AST.Variable as Var
data Term1 a
= App1 a a
| Fun1 a a
| Var1 a
| EmptyRecord1
| Record1 (Map.Map String [a]) a
deriving Show
data TermN a
= VarN (Maybe Var.Canonical) a
| TermN (Maybe Var.Canonical) (Term1 (TermN a))
deriving Show
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)
type Type = TermN Variable
type Variable = UF.Point Descriptor
type SchemeName = String
type TypeName = Var.Canonical
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
deriving Show
data Scheme a b = Scheme {
rigidQuantifiers :: [b],
flexibleQuantifiers :: [b],
constraint :: Constraint a b,
header :: Map.Map String a
} deriving Show
type TypeConstraint = Constraint Type Variable
type TypeScheme = Scheme Type Variable
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])
infixr 9 ==>
(==>) :: Type -> Type -> Type
a ==> b = termN (Fun1 a b)
(<|) :: TermN a -> TermN a -> TermN a
f <| a = termN (App1 f a)
data Descriptor = Descriptor
{ structure :: Maybe (Term1 Variable)
, rank :: Int
, flex :: Flex
, name :: Maybe TypeName
, copy :: Maybe Variable
, mark :: Int
, alias :: Maybe Var.Canonical
} deriving Show
noRank :: Int
noRank = 1
outermostRank :: Int
outermostRank = 0
noMark :: Int
noMark = 0
initialMark :: Int
initialMark = 1
data Flex = Rigid | Flexible | Constant | Is SuperType
deriving (Show, Eq)
data SuperType = Number | Comparable | Appendable
deriving (Show, Eq)
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
}
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 Show a => Show (UF.Point a) where
show point = unsafePerformIO $ fmap show (UF.descriptor point)
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 | P.render px == "_List" -> P.brackets (pretty Never x)
| otherwise -> 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
VarN alias x -> either alias (pretty when x)
TermN alias t1 -> either alias (pretty when t1)
where
either maybeAlias doc =
case maybeAlias of
Just alias -> PP.pretty alias
Nothing -> doc
instance PrettyType Descriptor where
pretty when desc = do
case (alias desc, structure desc, name desc) of
(Just name, _, _) -> PP.pretty name
(_, 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
extraPretty :: (PrettyType t, Crawl t) => t -> IO Doc
extraPretty t = pretty Never <$> addNames t
addNames :: (Crawl t) => t -> IO t
addNames value = do
(rawVars, _, _, _) <- execStateT (crawl getNames value) ([], 0, 0, 0)
let vars = map head . List.group $ List.sort rawVars
suffix s = map (++s) (map (:[]) ['a'..'z'])
allVars = concatMap suffix $ ["","'","_"] ++ map show [ (0 :: Int) .. ]
okayVars = filter (`notElem` vars) allVars
runStateT (crawl rename value) (okayVars, 0, 0, 0)
return value
where
getNames desc state@(vars, a, b, c) =
let name' = name desc in
case name' of
Just (Var.Canonical _ var) -> (name', (var:vars, a, b, c))
_ -> (name', state)
rename desc state@(vars, a, b, c) =
case name desc of
Just var -> (Just var, state)
Nothing ->
case flex desc of
Is Number -> (local $ "number" ++ replicate a '\'', (vars, a+1, b, c))
Is Comparable -> (local $ "comparable" ++ replicate b '\'', (vars, a, b+1, c))
Is Appendable -> (local $ "appendable" ++ replicate c '\'', (vars, a, b, c+1))
_ -> (local $ head vars, (tail vars, a, b, c))
where
local v = Just (Var.local v)
type CrawlState = ([String], Int, Int, Int)
class Crawl t where
crawl :: (Descriptor -> CrawlState -> (Maybe TypeName, CrawlState))
-> t
-> StateT CrawlState IO t
instance Crawl e => Crawl (Annotated a e) where
crawl nextState (A ann e) = A ann <$> crawl nextState e
instance (Crawl t, Crawl v) => Crawl (BasicConstraint t v) where
crawl nextState constraint =
let rnm = crawl nextState in
case constraint of
CTrue -> return CTrue
CSaveEnv -> return CSaveEnv
CEqual a b -> CEqual <$> rnm a <*> rnm b
CAnd cs -> CAnd <$> crawl nextState cs
CLet schemes c -> CLet <$> crawl nextState schemes <*> crawl nextState c
CInstance name tipe -> CInstance name <$> rnm tipe
instance Crawl a => Crawl [a] where
crawl nextState list = mapM (crawl nextState) list
instance (Crawl t, Crawl v) => Crawl (Scheme t v) where
crawl nextState (Scheme rqs fqs c headers) =
let rnm = crawl nextState in
Scheme <$> rnm rqs <*> rnm fqs <*> crawl nextState c <*> return headers
instance Crawl t => Crawl (TermN t) where
crawl nextState tipe =
case tipe of
VarN a x -> VarN a <$> crawl nextState x
TermN a term -> TermN a <$> crawl nextState term
instance Crawl t => Crawl (Term1 t) where
crawl nextState term =
let rnm = crawl nextState in
case term of
App1 a b -> App1 <$> rnm a <*> rnm b
Fun1 a b -> Fun1 <$> rnm a <*> rnm b
Var1 a -> Var1 <$> rnm a
EmptyRecord1 -> return EmptyRecord1
Record1 fields ext ->
Record1 <$> traverse (mapM rnm) fields <*> rnm ext
instance Crawl a => Crawl (UF.Point a) where
crawl nextState point = do
desc <- liftIO $ UF.descriptor point
desc' <- crawl nextState desc
liftIO $ UF.setDescriptor point desc'
return point
instance Crawl Descriptor where
crawl nextState desc = do
state <- get
let (name', state') = nextState desc state
structure' <- traverse (crawl nextState) (structure desc)
put state'
return $ desc { name = name', structure = structure' }
toSrcType :: Variable -> IO T.CanonicalType
toSrcType var =
go =<< addNames var
where
go v = do
desc <- UF.descriptor v
srcType <- maybe (backupSrcType desc) termToSrcType (structure desc)
return $ maybe srcType (\name -> T.Aliased name srcType) (alias desc)
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 var) ]
termToSrcType term =
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 (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"
dealias :: T.CanonicalType -> T.CanonicalType
dealias t =
case t of
T.Aliased _ t' -> t'
_ -> t
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