g2-0.1.0.0: src/G2/Language/TypeClasses/TypeClasses.hs
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
module G2.Language.TypeClasses.TypeClasses ( TypeClasses
, Class (..)
, initTypeClasses
, insertClass
, unionTypeClasses
, isTypeClassNamed
, isTypeClass
, lookupTCDict
, lookupTCDicts
, tcDicts
, typeClassInst
, satisfyingTCTypes
, satisfyingTC
, 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.List
import qualified Data.Map as M
import Data.Maybe
data Class = Class { insts :: [(Type, Id)], typ_ids :: [Id]}
deriving (Show, Eq, Read, Typeable, Data)
type TCType = M.Map Name Class
newtype TypeClasses = TypeClasses TCType
deriving (Show, Eq, Read, Typeable, Data)
initTypeClasses :: [(Name, Id, [Id])] -> TypeClasses
initTypeClasses nsi =
let
ns = map (\(n, _, i) -> (n, i)) nsi
nsi' = filter (not . null . insts . snd)
$ map (\(n, i) -> (n, Class { insts = mapMaybe (nameIdToTypeId n) nsi, typ_ids = i } )) 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]) -> 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 (TyCon _ _) t) = Just t
affectedType _ = Nothing
isTypeClassNamed :: Name -> TypeClasses -> Bool
isTypeClassNamed n = M.member n . (coerce :: TypeClasses -> TCType)
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 -> Type -> 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
lookupTCDicts :: Name -> TypeClasses -> Maybe [(Type, Id)]
lookupTCDicts n = fmap insts . M.lookup n . coerce
lookupTCDictsTypes :: TypeClasses -> Name -> Maybe [Type]
lookupTCDictsTypes tc = fmap (map fst) . flip lookupTCDicts tc
-- 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.Map 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
-- satisfyingTCTypes
-- Finds types/dict pairs that satisfy the given TC requirements for the given polymorphic argument
-- returns a list of acceptable types
satisfyingTCTypes :: KnownValues -> TypeClasses -> Id -> [Type] -> [Type]
satisfyingTCTypes kv tc i ts =
let
tcReq = satisfyTCReq tc i ts
in
substKind i . inter kv $ mapMaybe (lookupTCDictsTypes tc) tcReq
inter :: KnownValues -> [[Type]] -> [Type]
inter kv [] = [tyInt kv]
inter _ xs = foldr1 intersect xs
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
-- satisfyingTCReq
-- Finds the names of the required typeclasses for a TyVar Id
-- See satisfyingTCTypes
satisfyTCReq :: TypeClasses -> Id -> [Type] -> [Name]
satisfyTCReq tc i =
mapMaybe (tyConAppName . tyAppCenter) . filter (isFor i) . filter (isTypeClass tc)
where
isFor :: Id -> Type -> Bool
isFor ii (TyApp (TyCon _ _) a) = ii `elem` tyVarIds a
isFor _ _ = False
-- Given a list of type arguments and a mapping of TyVar Ids to actual Types
-- Gives the required TC's to pass to any TC arguments
satisfyingTC :: TypeClasses -> [Type] -> Id -> Type -> [Expr]
satisfyingTC tc ts i t =
let
tcReq = satisfyTCReq tc i ts
in
map (\n -> case lookupTCDict tc n t of
Just i' -> Var i'
Nothing -> error "No typeclass found.") tcReq
toMap :: TypeClasses -> M.Map 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 = containedASTs . insts
modifyContainedASTs f c = Class { insts = modifyContainedASTs f $ insts c
, typ_ids = modifyContainedASTs f $ typ_ids c}
instance Named TypeClasses where
names = names . (coerce :: TypeClasses -> TCType)
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 = names . insts
rename old new c = Class { insts = rename old new $ insts c
, typ_ids = rename old new $ typ_ids c }
renames hm c = Class { insts = renames hm $ insts c
, typ_ids = renames hm $ typ_ids c }