inferno-types-0.1.0.0: src/Inferno/Types/Type.hs
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Inferno.Types.Type
( BaseType (..),
ImplType (..),
Namespace (..),
TCScheme (..),
TV (..),
InfernoType (..),
TypeClass (..),
TypeClassShape (..),
TypeMetadata (..),
Substitutable (..),
Subst (..),
Scheme (..),
(.->),
sch,
var,
tySig,
namespaceToIdent,
typeInt,
typeBool,
typeDouble,
typeWord16,
typeWord32,
typeWord64,
typeText,
typeResolution,
typeTimeDiff,
typeTime,
punctuate',
)
where
import Data.Aeson (FromJSON, ToJSON)
import Data.Data (Data)
import Data.List (intercalate)
import Data.Map (Map, empty)
import qualified Data.Map as Map
import Data.Set (Set)
import qualified Data.Set as Set
-- import Data.String (IsString)
import Data.Text (Text, unpack)
import GHC.Generics (Generic)
import Inferno.Types.Syntax (BaseType (..), Expr, ExtIdent (..), Ident (..), InfernoType (..), ModuleName (..), TV (..), punctuate')
import Inferno.Utils.Prettyprinter (renderPretty)
import Prettyprinter
( Doc,
Pretty (pretty),
align,
comma,
enclose,
encloseSep,
hsep,
lbrace,
lparen,
rbrace,
rparen,
-- sep,
-- tupled,
(<+>),
)
import Test.QuickCheck (Arbitrary (..), oneof)
import Test.QuickCheck.Arbitrary.ADT (ToADTArbitrary)
data ImplType = ImplType (Map ExtIdent InfernoType) InfernoType
deriving (Show, Eq, Ord, Data, Generic, ToJSON, FromJSON)
data Scheme = Forall [TV] ImplType
deriving (Show, Eq, Ord, Data, Generic, ToJSON, FromJSON)
typeInt, typeBool, typeDouble, typeWord16, typeWord32, typeWord64, typeText, typeResolution, typeTimeDiff, typeTime :: InfernoType
typeInt = TBase TInt
typeBool = TBase $ TEnum "bool" $ Set.fromList ["true", "false"]
typeDouble = TBase TDouble
typeWord16 = TBase TWord16
typeWord32 = TBase TWord32
typeWord64 = TBase TWord64
typeText = TBase TText
typeTimeDiff = TBase TTimeDiff
typeTime = TBase TTime
typeResolution = TBase TResolution
sch :: InfernoType -> Scheme
sch = Forall [] . ImplType empty
infixr 3 .->
(.->) :: InfernoType -> InfernoType -> InfernoType
x .-> y = TArr x y
var :: Int -> InfernoType
var x = TVar (TV x)
data TypeClass = TypeClass
{ className :: Text,
params :: [InfernoType]
}
deriving (Show, Eq, Ord, Data, Generic, ToJSON, FromJSON)
data TCScheme = ForallTC [TV] (Set TypeClass) ImplType
deriving (Show, Eq, Ord, Data, Generic, ToJSON, FromJSON)
tySig :: [Doc ann] -> [Doc ann]
tySig [] = []
tySig [d] = [":" <+> d]
tySig (d : ds) = (":" <+> d) : go ds
where
go [] = []
go (d' : ds') = ("|" <+> d') : go ds'
instance Pretty ImplType where
pretty (ImplType impl ty)
| Map.null impl = pretty ty
| otherwise =
encloseSep
lbrace
rbrace
comma
(map (\(ExtIdent idt, t) -> "implicit" <+> case idt of { Left i -> "var$" <> pretty i; Right i -> pretty i } <+> ":" <+> align (pretty t)) $ Map.toList impl)
<+> "⇒"
<+> pretty ty
instance Pretty Scheme where
pretty (Forall _ implType) = pretty implType
instance Pretty TypeClass where
pretty = \case
TypeClass nm tys -> pretty nm <+> "on" <+> (hsep $ map bracketPretty tys)
where
bracketPretty ty = case ty of
TVar _ -> pretty ty
TBase _ -> pretty ty
_ -> enclose lparen rparen $ pretty ty
newtype TypeClassShape = TypeClassShape TypeClass
instance Pretty TypeClassShape where
pretty = \case
TypeClassShape (TypeClass nm tys) -> pretty nm <+> "on" <+> (hsep $ map bracketPretty tys)
where
bracketPretty ty = case ty of
TVar _ -> "_"
TBase _ -> pretty ty
_ -> enclose lparen rparen $ pretty ty
instance Pretty TCScheme where
pretty (ForallTC _ tcs (ImplType impl ty))
| Map.null impl && null tcs = pretty ty
| otherwise =
encloseSep
lbrace
rbrace
comma
( (map (("requires" <+>) . pretty) $ Set.toList tcs)
++ (map (\(ExtIdent idt, t) -> "implicit" <+> case idt of { Left i -> "var$" <> pretty i; Right i -> pretty i } <+> ":" <+> align (pretty t)) $ Map.toList impl)
)
<+> "⇒"
<+> pretty ty
newtype Subst = Subst (Map.Map TV InfernoType)
deriving stock (Eq, Ord)
deriving newtype (Semigroup, Monoid)
instance Show Subst where
show (Subst m) = intercalate "\n" $ map (\(x, t) -> unpack $ renderPretty x <> " ~> " <> renderPretty t) $ Map.toList m
class Substitutable a where
apply :: Subst -> a -> a
ftv :: a -> Set.Set TV
instance Substitutable InfernoType where
apply _ (TBase a) = TBase a
apply (Subst s) t@(TVar a) = Map.findWithDefault t a s
apply s (t1 `TArr` t2) = apply s t1 `TArr` apply s t2
apply s (TArray t) = TArray $ apply s t
apply s (TSeries t) = TSeries $ apply s t
apply s (TOptional t) = TOptional $ apply s t
apply s (TTuple ts) = TTuple $ fmap (apply s) ts
apply s (TRep t) = TRep $ apply s t
ftv TBase {} = Set.empty
ftv (TVar a) = Set.singleton a
ftv (t1 `TArr` t2) = ftv t1 `Set.union` ftv t2
ftv (TArray t) = ftv t
ftv (TSeries t) = ftv t
ftv (TOptional t) = ftv t
ftv (TTuple ts) = foldr (Set.union . ftv) Set.empty ts
ftv (TRep t) = ftv t
instance Substitutable ImplType where
apply s (ImplType impl t) =
ImplType (Map.map (apply s) impl) $ apply s t
ftv (ImplType impl t) = (foldr Set.union Set.empty $ map (ftv . snd) $ Map.toList impl) `Set.union` ftv t
instance Substitutable TypeClass where
apply s (TypeClass n tys) = TypeClass n $ map (apply s) tys
ftv (TypeClass _ tys) = Set.unions $ map ftv tys
instance Substitutable TCScheme where
apply (Subst s) (ForallTC as tcs t) = ForallTC as (Set.map (apply s') tcs) (apply s' t)
where
s' = Subst $ foldr Map.delete s as
ftv (ForallTC as tcs t) = ((ftv t) `Set.union` (Set.unions $ Set.elems $ Set.map ftv tcs)) `Set.difference` Set.fromList as
instance Substitutable a => Substitutable [a] where
apply = map . apply
ftv = foldr (Set.union . ftv) Set.empty
data Namespace
= FunNamespace Ident
| OpNamespace Ident
| EnumNamespace Ident
| ModuleNamespace ModuleName
| TypeNamespace Ident
deriving (Eq, Show, Ord, Generic, ToJSON, FromJSON, ToADTArbitrary)
instance Pretty Namespace where
pretty = \case
FunNamespace (Ident i) -> pretty i
OpNamespace (Ident i) -> pretty i
EnumNamespace (Ident i) -> "#" <> pretty i
ModuleNamespace (ModuleName m) -> pretty m
TypeNamespace (Ident i) -> pretty i
instance Arbitrary Namespace where
arbitrary =
oneof
[ FunNamespace <$> arbitrary,
OpNamespace <$> arbitrary,
EnumNamespace <$> arbitrary,
ModuleNamespace <$> arbitrary,
TypeNamespace <$> arbitrary
]
namespaceToIdent :: Namespace -> Ident
namespaceToIdent = \case
FunNamespace i -> i
OpNamespace i -> i
EnumNamespace i -> i
TypeNamespace i -> i
ModuleNamespace _ -> error "namespaceToIdent undefined for ModuleNamespace"
data TypeMetadata ty = TypeMetadata
{ identExpr :: Expr () (),
docs :: Maybe Text,
ty :: ty
}
deriving (Eq, Show, Generic, Data, ToJSON, FromJSON)