{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} module DataFrame.Internal.Types where import Data.Int (Int16, Int32, Int64, Int8) import Data.Kind (Constraint, Type) import Data.Typeable (Typeable) import qualified Data.Vector.Unboxed as VU import Data.Word (Word16, Word32, Word64, Word8) type Columnable' a = (Typeable a, Show a, Ord a, Eq a, Read a) {- | A type with column representations used to select the "right" representation when specializing the `toColumn` function. -} data Rep = RBoxed | RUnboxed | ROptional -- | Type-level if statement. type family If (cond :: Bool) (yes :: k) (no :: k) :: k where If 'True yes _ = yes If 'False _ no = no -- | All unboxable types (according to the `vector` package). type family Unboxable (a :: Type) :: Bool where Unboxable Int = 'True Unboxable Int8 = 'True Unboxable Int16 = 'True Unboxable Int32 = 'True Unboxable Int64 = 'True Unboxable Word = 'True Unboxable Word8 = 'True Unboxable Word16 = 'True Unboxable Word32 = 'True Unboxable Word64 = 'True Unboxable Char = 'True Unboxable Bool = 'True Unboxable Double = 'True Unboxable Float = 'True Unboxable _ = 'False type family Numeric (a :: Type) :: Bool where Numeric Integer = 'True Numeric Int = 'True Numeric Int8 = 'True Numeric Int16 = 'True Numeric Int32 = 'True Numeric Int64 = 'True Numeric Word = 'True Numeric Word8 = 'True Numeric Word16 = 'True Numeric Word32 = 'True Numeric Word64 = 'True Numeric Double = 'True Numeric Float = 'True Numeric _ = 'False -- | Compute the column representation tag for any ‘a’. type family KindOf a :: Rep where KindOf (Maybe a) = 'ROptional KindOf a = If (Unboxable a) 'RUnboxed 'RBoxed -- | Type-level boolean for constraint/type comparison. data SBool (b :: Bool) where STrue :: SBool 'True SFalse :: SBool 'False -- | The runtime witness for our type-level branching. class SBoolI (b :: Bool) where sbool :: SBool b instance SBoolI 'True where sbool = STrue instance SBoolI 'False where sbool = SFalse -- | Type-level function to determine whether or not a type is unboxa sUnbox :: forall a. (SBoolI (Unboxable a)) => SBool (Unboxable a) sUnbox = sbool @(Unboxable a) sNumeric :: forall a. (SBoolI (Numeric a)) => SBool (Numeric a) sNumeric = sbool @(Numeric a) type family When (flag :: Bool) (c :: Constraint) :: Constraint where When 'True c = c When 'False c = () -- empty constraint type UnboxIf a = When (Unboxable a) (VU.Unbox a) type family IntegralTypes (a :: Type) :: Bool where IntegralTypes Integer = 'True IntegralTypes Int = 'True IntegralTypes Int8 = 'True IntegralTypes Int16 = 'True IntegralTypes Int32 = 'True IntegralTypes Int64 = 'True IntegralTypes Word = 'True IntegralTypes Word8 = 'True IntegralTypes Word16 = 'True IntegralTypes Word32 = 'True IntegralTypes Word64 = 'True IntegralTypes _ = 'False sIntegral :: forall a. (SBoolI (IntegralTypes a)) => SBool (IntegralTypes a) sIntegral = sbool @(IntegralTypes a) type IntegralIf a = When (IntegralTypes a) (Integral a) type family FloatingTypes (a :: Type) :: Bool where FloatingTypes Float = 'True FloatingTypes Double = 'True FloatingTypes _ = 'False sFloating :: forall a. (SBoolI (FloatingTypes a)) => SBool (FloatingTypes a) sFloating = sbool @(FloatingTypes a) type FloatingIf a = When (FloatingTypes a) (Real a, Fractional a)