g2-0.2.0.0: src/G2/Language/TypeClasses/TypeClasses.hs
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
module G2.Language.TypeClasses.TypeClasses ( TypeClasses
, Class (..)
, initTypeClasses
, insertClass
, unionTypeClasses
, isTypeClassNamed
, isTypeClass
, lookupTCDict
, lookupTCDicts
, lookupTCClass
, tcWithNameMap
, tcDicts
, typeClassInst
, satisfyingTCTypes
, toMap) where
import G2.Language.AST
import G2.Language.KnownValues (KnownValues)
import G2.Language.Naming
import G2.Language.Syntax
import G2.Language.Typing
import Data.Coerce
import Data.Data (Data, Typeable)
import Data.Hashable
import Data.List
import qualified Data.Map.Lazy as MM
import qualified Data.HashMap.Lazy as M
import Data.Maybe
import Data.Monoid ((<>))
import qualified Data.Sequence as S
import GHC.Generics (Generic)
data Class = Class { insts :: [(Type, Id)], typ_ids :: [Id], superclasses :: [(Type, Id)]}
deriving (Show, Eq, Read, Typeable, Data, Generic)
instance Hashable Class
type TCType = M.HashMap Name Class
newtype TypeClasses = TypeClasses TCType
deriving (Show, Eq, Read, Typeable, Data, Generic)
instance Hashable TypeClasses
initTypeClasses :: [(Name, Id, [Id], [(Type, Id)])] -> TypeClasses
initTypeClasses nsi =
let
ns = map (\(n, _, i, sc) -> (n, i, sc)) nsi
nsi' = filter (not . null . insts . snd)
$ map (\(n, i, sc) ->
(n, Class { insts = nub $ mapMaybe (nameIdToTypeId n) nsi
, typ_ids = i
, superclasses = sc } )) ns
in
coerce $ M.fromList nsi'
insertClass :: Name -> Class -> TypeClasses -> TypeClasses
insertClass n c (TypeClasses tc) = TypeClasses (M.insert n c tc)
unionTypeClasses :: TypeClasses -> TypeClasses -> TypeClasses
unionTypeClasses (TypeClasses tc) (TypeClasses tc') = TypeClasses (M.union tc tc')
nameIdToTypeId :: Name -> (Name, Id, [Id], [(Type, Id)]) -> Maybe (Type, Id)
nameIdToTypeId nm (n, i, _, _) =
let
t = affectedType $ returnType i
in
if n == nm then fmap (, i) t else Nothing
affectedType :: Type -> Maybe Type
affectedType (TyApp _ t) = Just t
affectedType _ = Nothing
-- | Is there a typeclass with the given `Name`?
isTypeClassNamed :: Name -> TypeClasses -> Bool
isTypeClassNamed n = M.member n . (coerce :: TypeClasses -> TCType)
-- | Is the given type a type class?
isTypeClass :: TypeClasses -> Type -> Bool
isTypeClass tc (TyCon n _) = isTypeClassNamed n tc
isTypeClass tc (TyApp t _) = isTypeClass tc t
isTypeClass _ _ = False
-- | Returns the dictionary for the given typeclass and Type,
-- if one exists.
lookupTCDict :: TypeClasses
-> Name -- ^ `Name` of the typeclass to look for
-> Type -- ^ The `Type` you want a dictionary for
-> Maybe Id
lookupTCDict tc n t =
case fmap insts $ M.lookup n (toMap tc) of
Just c -> fmap snd $ find (\(t', _) -> PresType t .:: t') c
Nothing -> Nothing
-- | Given a typeclass `Name`, gives an association list of
-- `Type`s that have instances of that typeclass, and the
-- typeclass dictionaries.
lookupTCDicts :: Name -> TypeClasses -> Maybe [(Type, Id)]
lookupTCDicts n = fmap insts . M.lookup n . coerce
lookupTCClass :: Name -> TypeClasses -> Maybe Class
lookupTCClass n = M.lookup n . coerce
tcWithNameMap :: Name -> [Id] -> M.HashMap Name Id
tcWithNameMap n =
M.fromList
. map (\i -> (forType $ typeOf i, i))
. filter (isTC . typeOf)
where
forType :: Type -> Name
forType (TyApp _ (TyVar (Id n' _))) = n'
forType _ = error "Bad type in forType"
isTC :: Type -> Bool
isTC t = case tyAppCenter t of
TyCon n' _ -> n == n'
_ -> False
-- tcDicts
tcDicts :: TypeClasses -> [Id]
tcDicts = map snd . concatMap insts . M.elems . coerce
tyConAppName :: Type -> Maybe Name
tyConAppName (TyCon n _) = Just n
tyConAppName _ = Nothing
-- Given a TypeClass name, a type that you want an instance of that typeclass
-- for, and a mapping of TyVar name's to Id's for those types instances of
-- the typeclass, returns an instance of the typeclass, if possible
typeClassInst :: TypeClasses -> M.HashMap Name Id -> Name -> Type -> Maybe Expr
typeClassInst tc m tcn t
| tca@(TyCon _ _) <- tyAppCenter t
, ts <- tyAppArgs t
, tcs <- map (typeClassInst tc m tcn) ts
, all isJust tcs =
case lookupTCDict tc tcn tca of
Just i -> Just (foldl' App (Var i) $ map Type ts ++ map fromJust tcs)
Nothing -> Nothing
| (TyVar (Id n _)) <- tyAppCenter t
, ts <- tyAppArgs t
, tcs <- map (typeClassInst tc m tcn) ts
, all (isJust) tcs =
case M.lookup n m of
Just i -> Just (foldl' App (Var i) $ map Type ts ++ map fromJust tcs)
Nothing -> Nothing
typeClassInst _ _ _ _ = Nothing
-- | Finds `Type`s that satisfy the given typeclass requirements for the given polymorphic argument.
-- Returns "Int" by default if there are no typeclass requirements.
satisfyingTCTypes :: KnownValues
-> TypeClasses
-> Id -- ^ The type variable to look for possible instantiations of
-> [Type] -- ^ Function arguments to satisfy typeclass requirements for
-> [Type]
satisfyingTCTypes kv tc i ts =
let
tcReq = satisfyTCReq tc i ts
in
case mapMaybe lookupTCDictsTypes tcReq of
[] -> [tyInt kv]
xs -> substKind i $ foldr1 intersect xs
where
lookupTCDictsTypes (TyApp t1 t2) =
fmap (mapMaybe (\t' -> MM.lookup (idName i) =<< specializes t' t2))
. fmap (map fst)
. flip lookupTCDicts tc
=<< (tyConAppName . tyAppCenter $ t1)
lookupTCDictsTypes _ = Nothing
substKind :: Id -> [Type] -> [Type]
substKind (Id _ t) ts = map (\t' -> case t' of
TyCon n _ -> TyCon n (tyFunToTyApp t)
t'' -> t'') ts
tyFunToTyApp :: Type -> Type
tyFunToTyApp (TyFun t1 (TyFun t2 t3)) = TyApp (TyApp (tyFunToTyApp t1) (tyFunToTyApp t2)) (tyFunToTyApp t3)
tyFunToTyApp t = modifyChildren tyFunToTyApp t
-- | Finds the names of the required typeclasses for a TyVar Id
satisfyTCReq :: TypeClasses -> Id -> [Type] -> [Type]
satisfyTCReq tc (Id n _) = filter isFor . filter (isTypeClass tc)
where
isFor :: Type -> Bool
isFor (TyVar (Id n' _)) = n == n'
isFor (TyApp a1 a2) = isFor a1 || isFor a2
isFor _ = False
toMap :: TypeClasses -> M.HashMap Name Class
toMap = coerce
instance ASTContainer TypeClasses Expr where
containedASTs _ = []
modifyContainedASTs _ = id
instance ASTContainer TypeClasses Type where
containedASTs = containedASTs . (coerce :: TypeClasses -> TCType)
modifyContainedASTs f =
coerce . modifyContainedASTs f . (coerce :: TypeClasses -> TCType)
instance ASTContainer Class Expr where
containedASTs _ = []
modifyContainedASTs _ = id
instance ASTContainer Class Type where
containedASTs c = (containedASTs . insts $ c) ++ containedASTs (superclasses c)
modifyContainedASTs f c = Class { insts = modifyContainedASTs f $ insts c
, typ_ids = modifyContainedASTs f $ typ_ids c
, superclasses = modifyContainedASTs f $ superclasses c}
instance Named TypeClasses where
names (TypeClasses tc) = S.fromList (M.keys tc) <> names tc
rename old new (TypeClasses m) =
coerce $ M.mapKeys (rename old new) $ rename old new m
renames hm (TypeClasses m) =
coerce $ M.mapKeys (renames hm) $ renames hm m
instance Named Class where
names (Class { insts = i, typ_ids = tids, superclasses = sc }) = names i <> names tids <> names sc
rename old new c = Class { insts = rename old new $ insts c
, typ_ids = rename old new $ typ_ids c
, superclasses = rename old new $ superclasses c }
renames hm c = Class { insts = renames hm $ insts c
, typ_ids = renames hm $ typ_ids c
, superclasses = renames hm $ superclasses c }