dino-0.1: src/Dino/Types.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Dino.Types
( module Dino.Types
, Inspectable
) where
import Dino.Prelude
import Data.Type.Equality ((:~:) (..), TestEquality (..))
import Data.Typeable (cast)
import Type.Reflection (typeRep)
import Dino.AST (Inspectable)
-- | Built-in Dino types
--
-- Whether or not a type is built-in is mostly an implementation detail.
type family BuiltIn a :: Bool where
BuiltIn [a] = 'True
BuiltIn (a, b) = 'True
BuiltIn a = 'False
data DinoTypeRep a where
ListType :: DinoTypeRep a -> DinoTypeRep [a]
PairType :: DinoTypeRep a -> DinoTypeRep b -> DinoTypeRep (a, b)
OtherType :: (BuiltIn a ~ 'False, DinoType a) => DinoTypeRep a
-- The `BuiltIn` constraint ensures that `DinoTypeRep` is a canonical
-- representation for built-in types. This allows us to give total definitions
-- of functions like `listElemType`.
-- The reason for having a separate constructor for list types is solely to be
-- able to implement functions like `listElemType`.
-- The constraints on `OtherType` are somewhat arbitrary. We may bring in
-- other constraints in the future if that is needed by a particular back end.
-- However, we should avoid using type classes from a back end directly.
-- Rather use generic ones, such as `Data`. We can also have different
-- constructors for different types; e.g. numeric types, enumerations, etc.
withType :: DinoTypeRep a -> (DinoType a => b) -> b
withType (ListType t) b = withType t b
withType (PairType t u) b = withType t $ withType u b
withType OtherType b = b
listTypeElem :: DinoTypeRep [a] -> DinoTypeRep a
listTypeElem (ListType t) = t
-- This function is total due to the `BuiltIn` constraint on `OtherType`.
-- | This instance is complete in the sense that if @t ~ u@, then
-- @`testEquality` (trep :: `DinoTypeRep` t) (urep :: `DinoTypeRep` u)@ returns
-- @`Just` `Refl`@.
--
-- For example, @`BoolType`@ and @`EnumType` :: `DinoTypeRep` `Bool`@ are
-- considered equal.
instance TestEquality DinoTypeRep where
testEquality :: forall t u. DinoTypeRep t -> DinoTypeRep u -> Maybe (t :~: u)
testEquality t u = withType t $ withType u $
testEquality (typeRep @t) (typeRep @u)
-- Note: Because `DinoTypeRep` is a canonical representation of Dino types,
-- testing equality via `typeRep` should correspond to structural equality.
class (Eq a, Show a, Typeable a, Inspectable a) => DinoType a where
dinoTypeRep :: DinoTypeRep a
default dinoTypeRep :: (BuiltIn a ~ 'False) => DinoTypeRep a
dinoTypeRep = OtherType
instance DinoType ()
instance DinoType Bool
instance DinoType Rational
instance DinoType Int
instance DinoType Integer
instance DinoType Float
instance DinoType Double
instance DinoType Text
instance DinoType a => DinoType (Maybe a)
instance DinoType a => DinoType [a] where
dinoTypeRep = ListType dinoTypeRep
instance (DinoType a, DinoType b) => DinoType (a, b) where
dinoTypeRep = PairType dinoTypeRep dinoTypeRep
-- | Dynamic type based on 'DinoType'
data Dinamic where
Dinamic :: DinoType a => a -> Dinamic
fromDinamic :: DinoType a => Dinamic -> Maybe a
fromDinamic (Dinamic a) = cast a