module Top.Types.Quantification where
import Top.Types.Primitive
import Top.Types.Substitution
import Data.List
import Data.Maybe
import Utils (internalError)
newtype Quantification q a = Quantification ([Int], QuantorMap, a)
type QuantorMap = [(Int, String)]
withoutQuantors :: Quantification q a -> Bool
withoutQuantors (Quantification (is, _, _)) = null is
showQuantor :: Show q => Quantification q a -> String
showQuantor = show . f where
f :: Quantification q a -> q
f = internalError "Top.Types.Quantification" "showQuantor" "quantor unknown"
noQuantifiers :: a -> Quantification q a
noQuantifiers a = Quantification ([], [], a)
quantifiers :: Quantification q a -> [Int]
quantifiers (Quantification (is, _, _)) = is
unquantify :: Quantification q a -> a
unquantify (Quantification (_, _, a)) = a
instance Substitutable a => Substitutable (Quantification q a) where
sub |-> (Quantification (is, qmap, a)) = Quantification (is, qmap, removeDom is sub |-> a)
ftv (Quantification (is, _ , a)) = ftv a \\ is
instance HasTypes a => HasTypes (Quantification q a) where
getTypes (Quantification (_, _, a)) = getTypes a
changeTypes f (Quantification (is, qmap, a)) = Quantification (is, qmap, changeTypes f a)
introduceTypeVariables :: Substitutable a => Int -> Quantification q a -> (Int, a)
introduceTypeVariables i (Quantification (qs, _, a)) =
let sub = listToSubstitution (zip qs (map TVar [i..]))
in (i + length qs, sub |-> a)
introduceSkolemConstants :: Substitutable a => Int -> Quantification q a -> (Int, a)
introduceSkolemConstants i (Quantification (qs, _, a)) =
let sub = listToSubstitution (zip qs (map makeSkolemConstant [i..]))
in (i + length qs, sub |-> a)
bindTypeVariables :: Substitutable a => [Int] -> a -> Quantification q a
bindTypeVariables is a = Quantification (is `intersect` ftv a, [], a)
bindSkolemConstants :: HasSkolems a => [Int] -> a -> Quantification q a
bindSkolemConstants scs a =
let scs' = scs `union` allSkolems a
skMap = [ (i, TVar i) | i <- scs' ]
in Quantification (scs', [], changeSkolems skMap a)
getQuantorMap :: Quantification q a -> QuantorMap
getQuantorMap (Quantification (_, qm, _)) = qm
data Universal
type Forall = Quantification Universal
instance Show Universal where
show = const "forall"
instantiate, skolemize :: Substitutable a => Int -> Forall a -> (Int, a)
instantiate = introduceTypeVariables
skolemize = introduceSkolemConstants
generalize :: (Substitutable context, Substitutable a) => context -> a -> Forall a
generalize context a =
quantify (ftv a \\ ftv context) a
generalizeAll :: Substitutable a => a -> Forall a
generalizeAll a = quantify (ftv a) a
quantify :: Substitutable a => [Int] -> a -> Forall a
quantify = bindTypeVariables
unskolemize :: HasSkolems a => [Int] -> a -> Forall a
unskolemize = bindSkolemConstants
data Existential
type Exists = Quantification Existential
instance Show Existential where
show = const "exists"
open, reveal :: Substitutable a => Int -> Exists a -> (Int, a)
open = introduceSkolemConstants
reveal = introduceTypeVariables
close :: HasSkolems a => [Int] -> a -> Exists a
close = bindSkolemConstants
unreveal :: Substitutable a => [Int] -> a -> Exists a
unreveal = bindTypeVariables
skolemPrefix :: String
skolemPrefix = "_"
makeSkolemConstant :: Int -> Tp
makeSkolemConstant = TCon . (skolemPrefix++) . show
fromSkolemString :: String -> Maybe Int
fromSkolemString s
| skolemPrefix `isPrefixOf` s =
Just (read (drop (length skolemPrefix) s))
| otherwise = Nothing
skolemizeFTV :: Substitutable a => a -> a
skolemizeFTV a =
let sub = listToSubstitution [ (i, makeSkolemConstant i) | i <- ftv a ]
in sub |-> a
class HasSkolems a where
allSkolems :: a -> [Int]
changeSkolems :: [(Int, Tp)] -> a -> a
instance HasSkolems Tp where
allSkolems (TVar _) = []
allSkolems (TCon s) = case fromSkolemString s of
Just i -> [i]
Nothing -> []
allSkolems (TApp l r) = allSkolems l `union` allSkolems r
changeSkolems skMap = rec where
rec tp@(TVar _) = tp
rec tp@(TCon s) = case fromSkolemString s of
Just i -> fromMaybe tp (lookup i skMap)
Nothing -> tp
rec (TApp l r) = TApp (rec l) (rec r)
instance HasSkolems a => HasSkolems [a] where
allSkolems = foldr (union . allSkolems) []
changeSkolems skMap = map (changeSkolems skMap)
data ShowQuantorOptions = ShowQuantorOptions
{ showTopLevelQuantors :: Bool
, dontUseIdentifiers :: [String]
, variablePrefix :: String
, showAllTheSame :: Bool
, useTheNameMap :: Bool
}
defaultOptions :: ShowQuantorOptions
defaultOptions = ShowQuantorOptions
{ showTopLevelQuantors = False
, dontUseIdentifiers = []
, variablePrefix = "v"
, showAllTheSame = False
, useTheNameMap = True
}
showQuantors :: ShowQuantors a => a -> String
showQuantors = showQuantorsWithout (defaultOptions { showTopLevelQuantors = True })
class Show a => ShowQuantors a where
showQuantorsWithout :: ShowQuantorOptions -> a -> String
showQuantorsWithout = const show
instance ShowQuantors Tp
instance (Substitutable a, ShowQuantors a, Show q) => Show (Quantification q a) where
show = showQuantorsWithout defaultOptions
instance (Substitutable a, ShowQuantors a, Show q) => ShowQuantors (Quantification q a) where
showQuantorsWithout options q@(Quantification (is, qmap, a)) =
let
qs = is `intersect` ftv a
quantorText | null qs || not (showTopLevelQuantors options) = ""
| otherwise = unwords (showQuantor q : map (\i -> show (sub |-> TVar i)) qs ++ [". "])
dontUse = dontUseIdentifiers options
qmap1 | not (useTheNameMap options) || showAllTheSame options = []
| otherwise =
let op (rest, donts) (i,n)
| i `elem` qs = let ints = [1..] :: [Int]
s = head [ n ++ extra
| extra <- "" : map show ints
, n ++ extra `notElem` donts
]
in ((i,s):rest, s:donts)
| otherwise = (rest, donts)
in fst (foldl op ([], dontUse) qmap)
dontUse1 = map snd qmap1 ++ dontUse
qmap2 | showAllTheSame options = []
| otherwise = zip (filter (`notElem` map fst qmap1) qs) (variableList \\ dontUse1)
dontUse2 = map snd qmap2 ++ dontUse1
frees = ftv a \\ map fst (qmap1 ++ qmap2)
sub = listToSubstitution $ [ (i, TCon s) | (i,s) <- qmap1 ++ qmap2 ]
++ [ (i, TCon (variablePrefix options ++ show i)) | i <- frees ]
newOptions = options { dontUseIdentifiers = dontUse2
, showTopLevelQuantors = True
}
in
quantorText ++ showQuantorsWithout newOptions (sub |-> a)
variableList :: [String]
variableList = [ [x] | x <- ['a'..'z'] ]
++ [ x:show i | i <- [1 :: Int ..], x <- ['a'..'z'] ]