dataframe-core 2.1.1.2 → 2.2.0.0
raw patch · 5 files changed
+168/−118 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- DataFrame.Typed.Schema: instance (GHC.TypeLits.KnownSymbol name, Data.Typeable.Internal.Typeable a, DataFrame.Internal.Column.Columnable a, DataFrame.Typed.Schema.KnownSchema rest) => DataFrame.Typed.Schema.KnownSchema (DataFrame.Typed.Types.Column name a : rest)
- DataFrame.Typed.Types: data Column (name :: Symbol) a
+ DataFrame.Typed.Schema: instance (GHC.TypeLits.KnownSymbol name, Data.Typeable.Internal.Typeable a, DataFrame.Internal.Column.Columnable a, DataFrame.Typed.Schema.KnownSchema rest) => DataFrame.Typed.Schema.KnownSchema ('(name, a) : rest)
- DataFrame.Typed.Freeze: freeze :: forall (cols :: [Type]). KnownSchema cols => DataFrame -> Maybe (TypedDataFrame cols)
+ DataFrame.Typed.Freeze: freeze :: forall (cols :: [(Symbol, Type)]). KnownSchema cols => DataFrame -> Maybe (TypedDataFrame cols)
- DataFrame.Typed.Freeze: freezeOrThrow :: forall (cols :: [Type]). KnownSchema cols => DataFrame -> IO (TypedDataFrame cols)
+ DataFrame.Typed.Freeze: freezeOrThrow :: forall (cols :: [(Symbol, Type)]). KnownSchema cols => DataFrame -> IO (TypedDataFrame cols)
- DataFrame.Typed.Freeze: freezeWithError :: forall (cols :: [Type]). KnownSchema cols => DataFrame -> Either Text (TypedDataFrame cols)
+ DataFrame.Typed.Freeze: freezeWithError :: forall (cols :: [(Symbol, Type)]). KnownSchema cols => DataFrame -> Either Text (TypedDataFrame cols)
- DataFrame.Typed.Freeze: thaw :: forall (cols :: [Type]). TypedDataFrame cols -> DataFrame
+ DataFrame.Typed.Freeze: thaw :: forall (cols :: [(Symbol, Type)]). TypedDataFrame cols -> DataFrame
- DataFrame.Typed.Freeze: unsafeFreeze :: forall (cols :: [Type]). DataFrame -> TypedDataFrame cols
+ DataFrame.Typed.Freeze: unsafeFreeze :: forall (cols :: [(Symbol, Type)]). DataFrame -> TypedDataFrame cols
- DataFrame.Typed.Record: type Schema a :: [Type];
+ DataFrame.Typed.Record: type Schema a :: [(Symbol, Type)];
- DataFrame.Typed.Schema: class KnownSchema (cols :: [Type])
+ DataFrame.Typed.Schema: class KnownSchema (cols :: [(Symbol, Type)])
- DataFrame.Typed.Schema: schemaColumnNames :: forall (cols :: [Type]). KnownSchema cols => [Text]
+ DataFrame.Typed.Schema: schemaColumnNames :: forall (cols :: [(Symbol, Type)]). KnownSchema cols => [Text]
- DataFrame.Typed.Schema: type family GroupKeyColumns (keys :: [Symbol]) (cols :: [Type]) :: [Type]
+ DataFrame.Typed.Schema: type family GroupKeyColumns (keys :: [Symbol]) (cols :: [(Symbol, Type)]) :: [(Symbol, Type)]
- DataFrame.Typed.Types: TDF :: DataFrame -> TypedDataFrame (cols :: [Type])
+ DataFrame.Typed.Types: TDF :: DataFrame -> TypedDataFrame (cols :: [(Symbol, Type)])
- DataFrame.Typed.Types: TExpr :: Expr a -> TExpr (cols :: [Type]) a
+ DataFrame.Typed.Types: TExpr :: Expr a -> TExpr (cols :: [(Symbol, Type)]) a
- DataFrame.Typed.Types: TGD :: GroupedDataFrame -> TypedGrouped (keys :: [Symbol]) (cols :: [Type])
+ DataFrame.Typed.Types: TGD :: GroupedDataFrame -> TypedGrouped (keys :: [Symbol]) (cols :: [(Symbol, Type)])
- DataFrame.Typed.Types: [Asc] :: forall a (cols :: [Type]). (Columnable a, Ord a) => TExpr cols a -> TSortOrder cols
+ DataFrame.Typed.Types: [Asc] :: forall a (cols :: [(Symbol, Type)]). (Columnable a, Ord a) => TExpr cols a -> TSortOrder cols
- DataFrame.Typed.Types: [Desc] :: forall a (cols :: [Type]). (Columnable a, Ord a) => TExpr cols a -> TSortOrder cols
+ DataFrame.Typed.Types: [Desc] :: forall a (cols :: [(Symbol, Type)]). (Columnable a, Ord a) => TExpr cols a -> TSortOrder cols
- DataFrame.Typed.Types: [TAggCons] :: forall a (cols :: [Type]) (keys :: [Symbol]) (aggs1 :: [Type]) (name :: Symbol). Columnable a => Text -> TExpr cols a -> TAgg keys cols aggs1 -> TAgg keys cols (Column name a ': aggs1)
+ DataFrame.Typed.Types: [TAggCons] :: forall a (cols :: [(Symbol, Type)]) (keys :: [Symbol]) (aggs1 :: [(Symbol, Type)]) (name :: Symbol). Columnable a => Text -> TExpr cols a -> TAgg keys cols aggs1 -> TAgg keys cols ('(name, a) ': aggs1)
- DataFrame.Typed.Types: [TAggNil] :: forall (keys :: [Symbol]) (cols :: [Type]). TAgg keys cols ('[] :: [Type])
+ DataFrame.Typed.Types: [TAggNil] :: forall (keys :: [Symbol]) (cols :: [(Symbol, Type)]). TAgg keys cols ('[] :: [(Symbol, Type)])
- DataFrame.Typed.Types: [unTDF] :: TypedDataFrame (cols :: [Type]) -> DataFrame
+ DataFrame.Typed.Types: [unTDF] :: TypedDataFrame (cols :: [(Symbol, Type)]) -> DataFrame
- DataFrame.Typed.Types: [unTExpr] :: TExpr (cols :: [Type]) a -> Expr a
+ DataFrame.Typed.Types: [unTExpr] :: TExpr (cols :: [(Symbol, Type)]) a -> Expr a
- DataFrame.Typed.Types: [unTGD] :: TypedGrouped (keys :: [Symbol]) (cols :: [Type]) -> GroupedDataFrame
+ DataFrame.Typed.Types: [unTGD] :: TypedGrouped (keys :: [Symbol]) (cols :: [(Symbol, Type)]) -> GroupedDataFrame
- DataFrame.Typed.Types: class ToTExpr (cols :: [Type]) e
+ DataFrame.Typed.Types: class ToTExpr (cols :: [(Symbol, Type)]) e
- DataFrame.Typed.Types: data TAgg (keys :: [Symbol]) (cols :: [Type]) (aggs :: [Type])
+ DataFrame.Typed.Types: data TAgg (keys :: [Symbol]) (cols :: [(Symbol, Type)]) (aggs :: [(Symbol, Type)])
- DataFrame.Typed.Types: data TSortOrder (cols :: [Type])
+ DataFrame.Typed.Types: data TSortOrder (cols :: [(Symbol, Type)])
- DataFrame.Typed.Types: newtype TExpr (cols :: [Type]) a
+ DataFrame.Typed.Types: newtype TExpr (cols :: [(Symbol, Type)]) a
- DataFrame.Typed.Types: newtype TypedDataFrame (cols :: [Type])
+ DataFrame.Typed.Types: newtype TypedDataFrame (cols :: [(Symbol, Type)])
- DataFrame.Typed.Types: newtype TypedGrouped (keys :: [Symbol]) (cols :: [Type])
+ DataFrame.Typed.Types: newtype TypedGrouped (keys :: [Symbol]) (cols :: [(Symbol, Type)])
- DataFrame.Typed.Types: taggToNamedExprs :: forall (keys :: [Symbol]) (cols :: [Type]) (aggs :: [Type]). TAgg keys cols aggs -> [NamedExpr]
+ DataFrame.Typed.Types: taggToNamedExprs :: forall (keys :: [Symbol]) (cols :: [(Symbol, Type)]) (aggs :: [(Symbol, Type)]). TAgg keys cols aggs -> [NamedExpr]
- DataFrame.Typed.Types: type family AsTExpr (cols :: [Type]) e
+ DataFrame.Typed.Types: type family AsTExpr (cols :: [(Symbol, Type)]) e
Files
- dataframe-core.cabal +1/−1
- src/DataFrame/Typed/Generic.hs +3/−4
- src/DataFrame/Typed/Record.hs +2/−1
- src/DataFrame/Typed/Schema.hs +153/−94
- src/DataFrame/Typed/Types.hs +9/−18
dataframe-core.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.4 name: dataframe-core-version: 2.1.1.2+version: 2.2.0.0 synopsis: Core data structures for the dataframe library. description: Minimal interchange-format types for the @dataframe@ ecosystem:
src/DataFrame/Typed/Generic.hs view
@@ -86,7 +86,6 @@ import qualified DataFrame.Internal.DataFrame as D import DataFrame.Typed.Record (requireColumn) import DataFrame.Typed.Schema (Append)-import DataFrame.Typed.Types (Column) import DataFrame.Typed.Util (camelToSnake) {- | Field-name policy applied to record selectors when computing@@ -97,15 +96,15 @@ -} data NameCase = SnakeCase | IdentityCase -{- | The schema type @[Column name ty, ...]@ derived from the 'Rep' of a+{- | The schema type @'[ '(name, ty), ...]@ derived from the 'Rep' of a record type, with the given 'NameCase' applied to each field name. -}-type family RepToSchema (nc :: NameCase) (r :: Type -> Type) :: [Type] where+type family RepToSchema (nc :: NameCase) (r :: Type -> Type) :: [(Symbol, Type)] where RepToSchema nc (M1 D _ f) = RepToSchema nc f RepToSchema nc (M1 C _ f) = RepToSchema nc f RepToSchema nc (a :*: b) = Append (RepToSchema nc a) (RepToSchema nc b) RepToSchema nc (M1 S ('MetaSel ('Just name) _ _ _) (K1 _ a)) =- '[Column (TransformName nc name) a]+ '(TransformName nc name, a) ': '[] type family TransformName (nc :: NameCase) (name :: Symbol) :: Symbol where TransformName 'SnakeCase s = CamelToSnake s
src/DataFrame/Typed/Record.hs view
@@ -38,6 +38,7 @@ import DataFrame.Internal.DataFrame (fromNamedColumns) import qualified DataFrame.Internal.DataFrame as D import DataFrame.Typed.Types (TypedDataFrame (..))+import GHC.TypeLits {- | Bridge a Haskell record type @a@ to a typed-dataframe schema. @@ -51,7 +52,7 @@ @Left err@ if a column is missing or has the wrong type. -} class HasSchema a where- type Schema a :: [Type]+ type Schema a :: [(Symbol, Type)] toColumns :: [a] -> [(T.Text, C.Column)] fromColumns :: D.DataFrame -> Either T.Text [a]
src/DataFrame/Typed/Schema.hs view
@@ -79,12 +79,11 @@ import DataFrame.Internal.Column (Columnable) import DataFrame.Internal.Types (These)-import DataFrame.Typed.Types (Column) -- | Look up the element type of a column by name.-type family Lookup (name :: Symbol) (cols :: [Type]) :: Type where- Lookup name (Column name a ': _) = a- Lookup name (Column _ _ ': rest) = Lookup name rest+type family Lookup (name :: Symbol) (cols :: [(Symbol, Type)]) :: Type where+ Lookup name ('(name, a) ': _) = a+ Lookup name (_ ': rest) = Lookup name rest Lookup name '[] = TypeError ('Text "Column '" ':<>: 'Text name ':<>: 'Text "' not found in schema")@@ -93,22 +92,25 @@ 'TypeError' when the column is not found. Use together with 'AssertPresent' so the error fires exactly once. -}-type family SafeLookup (name :: Symbol) (cols :: [Type]) :: Type where- SafeLookup name (Column name a ': _) = a- SafeLookup name (Column _ _ ': rest) = SafeLookup name rest+type family SafeLookup (name :: Symbol) (cols :: [(Symbol, Type)]) :: Type where+ SafeLookup name ('(name, a) ': _) = a+ SafeLookup name (_ ': rest) = SafeLookup name rest SafeLookup name '[] = Int -- | Unwrap a Maybe from a type after we impute values.-type family Impute (name :: Symbol) (cols :: [Type]) :: [Type] where- Impute name (Column name (Maybe a) ': rest) = Column name a ': rest- Impute name (Column name _ ': rest) =+type family Impute (name :: Symbol) (cols :: [(Symbol, Type)]) :: [(Symbol, Type)] where+ Impute name ('(name, Maybe a) ': rest) = '(name, a) ': rest+ Impute name ('(name, _) ': rest) = TypeError ('Text "Column '" ':<>: 'Text name ':<>: 'Text "' is not of kind Maybe *") Impute name (col ': rest) = col ': Impute name rest Impute name '[] = '[] -type family SetColumnType (name :: Symbol) (b :: Type) (cols :: [Type]) :: [Type] where- SetColumnType name b (Column name _ ': rest) = Column name b ': rest+type family+ SetColumnType (name :: Symbol) (b :: Type) (cols :: [(Symbol, Type)]) ::+ [(Symbol, Type)]+ where+ SetColumnType name b ('(name, _) ': rest) = '(name, b) ': rest SetColumnType name b (col ': rest) = col ': SetColumnType name b rest SetColumnType name b '[] = TypeError@@ -120,26 +122,26 @@ Snoc (y ': ys) x = y ': Snoc ys x -- | Check whether a column name exists in a schema (type-level Bool).-type family HasName (name :: Symbol) (cols :: [Type]) :: Bool where- HasName name (Column name _ ': _) = 'True- HasName name (Column _ _ ': rest) = HasName name rest+type family HasName (name :: Symbol) (cols :: [(Symbol, Type)]) :: Bool where+ HasName name ('(name, _) ': _) = 'True+ HasName name (_ ': rest) = HasName name rest HasName name '[] = 'False -- | Remove a column by name from a schema.-type family RemoveColumn (name :: Symbol) (cols :: [Type]) :: [Type] where- RemoveColumn name (Column name _ ': rest) = rest+type family RemoveColumn (name :: Symbol) (cols :: [(Symbol, Type)]) :: [(Symbol, Type)] where+ RemoveColumn name ('(name, _) ': rest) = rest RemoveColumn name (col ': rest) = col ': RemoveColumn name rest RemoveColumn name '[] = '[] -- | Select a subset of columns by a list of names.-type family SubsetSchema (names :: [Symbol]) (cols :: [Type]) :: [Type] where+type family SubsetSchema (names :: [Symbol]) (cols :: [(Symbol, Type)]) :: [(Symbol, Type)] where SubsetSchema '[] cols = '[]- SubsetSchema (n ': ns) cols = Column n (Lookup n cols) ': SubsetSchema ns cols+ SubsetSchema (n ': ns) cols = '(n, Lookup n cols) ': SubsetSchema ns cols -- | Exclude columns by a list of names.-type family ExcludeSchema (names :: [Symbol]) (cols :: [Type]) :: [Type] where+type family ExcludeSchema (names :: [Symbol]) (cols :: [(Symbol, Type)]) :: [(Symbol, Type)] where ExcludeSchema names '[] = '[]- ExcludeSchema names (Column n a ': rest) =+ ExcludeSchema names ('(n, a) ': rest) = ExcludeSchemaHelper (IsElem n names) n a names rest type family@@ -148,12 +150,12 @@ (n :: Symbol) (a :: Type) (names :: [Symbol])- (rest :: [Type]) ::- [Type]+ (rest :: [(Symbol, Type)]) ::+ [(Symbol, Type)] where ExcludeSchemaHelper 'True n a names rest = ExcludeSchema names rest ExcludeSchemaHelper 'False n a names rest =- Column n a ': ExcludeSchema names rest+ '(n, a) ': ExcludeSchema names rest -- | Type-level elem for Symbols type family IsElem (x :: Symbol) (xs :: [Symbol]) :: Bool where@@ -162,15 +164,21 @@ IsElem x (_ ': xs) = IsElem x xs -- | Rename a column in the schema.-type family RenameInSchema (old :: Symbol) (new :: Symbol) (cols :: [Type]) :: [Type] where- RenameInSchema old new (Column old a ': rest) = Column new a ': rest+type family+ RenameInSchema (old :: Symbol) (new :: Symbol) (cols :: [(Symbol, Type)]) ::+ [(Symbol, Type)]+ where+ RenameInSchema old new ('(old, a) ': rest) = '(new, a) ': rest RenameInSchema old new (col ': rest) = col ': RenameInSchema old new rest RenameInSchema old new '[] = TypeError ('Text "Cannot rename: column '" ':<>: 'Text old ':<>: 'Text "' not found") -- | Rename multiple columns.-type family RenameManyInSchema (pairs :: [(Symbol, Symbol)]) (cols :: [Type]) :: [Type] where+type family+ RenameManyInSchema (pairs :: [(Symbol, Symbol)]) (cols :: [(Symbol, Type)]) ::+ [(Symbol, Type)]+ where RenameManyInSchema '[] cols = cols RenameManyInSchema ('(old, new) ': rest) cols = RenameManyInSchema rest (RenameInSchema old new cols)@@ -181,24 +189,27 @@ Append (x ': xs) ys = x ': Append xs ys -- | Reverse a type-level list.-type family Reverse (xs :: [Type]) :: [Type] where+type family Reverse (xs :: [(Symbol, Type)]) :: [(Symbol, Type)] where Reverse xs = ReverseAcc xs '[] -type family ReverseAcc (xs :: [Type]) (acc :: [Type]) :: [Type] where+type family+ ReverseAcc (xs :: [(Symbol, Type)]) (acc :: [(Symbol, Type)]) ::+ [(Symbol, Type)]+ where ReverseAcc '[] acc = acc ReverseAcc (x ': xs) acc = ReverseAcc xs (x ': acc) -- | Extract column names as a type-level list of Symbols.-type family ColumnNames (cols :: [Type]) :: [Symbol] where+type family ColumnNames (cols :: [(Symbol, Type)]) :: [Symbol] where ColumnNames '[] = '[]- ColumnNames (Column n _ ': rest) = n ': ColumnNames rest+ ColumnNames ('(n, _) ': rest) = n ': ColumnNames rest -- | Assert that a column name is absent from the schema (for derive/insert).-type family AssertAbsent (name :: Symbol) (cols :: [Type]) :: Constraint where+type family AssertAbsent (name :: Symbol) (cols :: [(Symbol, Type)]) :: Constraint where AssertAbsent name cols = AssertAbsentHelper name (HasName name cols) cols type family- AssertAbsentHelper (name :: Symbol) (found :: Bool) (cols :: [Type]) ::+ AssertAbsentHelper (name :: Symbol) (found :: Bool) (cols :: [(Symbol, Type)]) :: Constraint where AssertAbsentHelper name 'False cols = ()@@ -211,11 +222,11 @@ ) -- | Assert that a column name is present in the schema.-type family AssertPresent (name :: Symbol) (cols :: [Type]) :: Constraint where+type family AssertPresent (name :: Symbol) (cols :: [(Symbol, Type)]) :: Constraint where AssertPresent name cols = AssertPresentHelper name (HasName name cols) cols type family- AssertPresentHelper (name :: Symbol) (found :: Bool) (cols :: [Type]) ::+ AssertPresentHelper (name :: Symbol) (found :: Bool) (cols :: [(Symbol, Type)]) :: Constraint where AssertPresentHelper name 'True cols = ()@@ -224,7 +235,7 @@ ('Text "Column '" ':<>: 'Text name ':<>: 'Text "' not found in schema") -- | Assert that a column name is present in the schema.-type family AssertAllPresent (name :: [Symbol]) (cols :: [Type]) :: Constraint where+type family AssertAllPresent (name :: [Symbol]) (cols :: [(Symbol, Type)]) :: Constraint where AssertAllPresent (name ': rest) cols = AssertAllPresentHelper (HasName name cols) name rest cols AssertAllPresent '[] cols = ()@@ -234,7 +245,7 @@ (found :: Bool) (name :: Symbol) (rest :: [Symbol])- (cols :: [Type]) ::+ (cols :: [(Symbol, Type)]) :: Constraint where AssertAllPresentHelper 'True name rest cols = AssertAllPresent rest cols@@ -249,7 +260,10 @@ error fires exactly once. -} type family- AssertKeyTypesMatch (keys :: [Symbol]) (left :: [Type]) (right :: [Type]) ::+ AssertKeyTypesMatch+ (keys :: [Symbol])+ (left :: [(Symbol, Type)])+ (right :: [(Symbol, Type)]) :: Constraint where AssertKeyTypesMatch '[] left right = ()@@ -276,12 +290,18 @@ ':<>: 'Text " in the right table" ) -type family AssertDisjoint (left :: [Type]) (right :: [Type]) :: Constraint where+type family+ AssertDisjoint (left :: [(Symbol, Type)]) (right :: [(Symbol, Type)]) ::+ Constraint+ where AssertDisjoint left right = AssertDisjointHelper (SharedNames left right) left right type family- AssertDisjointHelper (shared :: [Symbol]) (left :: [Type]) (right :: [Type]) ::+ AssertDisjointHelper+ (shared :: [Symbol])+ (left :: [(Symbol, Type)])+ (right :: [(Symbol, Type)]) :: Constraint where AssertDisjointHelper '[] left right = ()@@ -293,7 +313,10 @@ ) type family- AssertAllColumnsHaveType (names :: [Symbol]) (a :: Type) (cols :: [Type]) ::+ AssertAllColumnsHaveType+ (names :: [Symbol])+ (a :: Type)+ (cols :: [(Symbol, Type)]) :: Constraint where AssertAllColumnsHaveType '[] a cols = ()@@ -346,16 +369,16 @@ total — a non-numeric or nullable column is a compile error (with the offending column named, via 'AssertRealColumn'), not a runtime 'Left'. -}-type family AllColumnsReal (fn :: Symbol) (cols :: [Type]) :: Constraint where+type family AllColumnsReal (fn :: Symbol) (cols :: [(Symbol, Type)]) :: Constraint where AllColumnsReal fn '[] = ()- AllColumnsReal fn (Column n a ': rest) =+ AllColumnsReal fn ('(n, a) ': rest) = (AssertRealColumn fn n a, Real a, VU.Unbox a, AllColumnsReal fn rest) -- TODO: mchavinda - we can generalist to AllX-type family AllDouble (cols :: [Type]) :: Constraint where+type family AllDouble (cols :: [(Symbol, Type)]) :: Constraint where AllDouble '[] = ()- AllDouble (Column n Double ': rest) = AllDouble rest- AllDouble (Column n a ': rest) =+ AllDouble ('(n, Double) ': rest) = AllDouble rest+ AllDouble ('(n, a) ': rest) = TypeError ( 'Text "Column '" ':<>: 'Text n@@ -366,48 +389,51 @@ {- | Strip 'Maybe' from all columns. Used by 'filterAllJust'. -@Column "x" (Maybe Double)@ becomes @Column "x" Double@.-@Column "y" Int@ stays @Column "y" Int@.+@'("x", (Maybe Double)@ becomes @'("x", Double@.))+@'("y", Int@ stays @'("y", Int@.)) -}-type family StripAllMaybe (cols :: [Type]) :: [Type] where+type family StripAllMaybe (cols :: [(Symbol, Type)]) :: [(Symbol, Type)] where StripAllMaybe '[] = '[]- StripAllMaybe (Column n (Maybe a) ': rest) = Column n a ': StripAllMaybe rest- StripAllMaybe (Column n a ': rest) = Column n a ': StripAllMaybe rest+ StripAllMaybe ('(n, Maybe a) ': rest) = '(n, a) ': StripAllMaybe rest+ StripAllMaybe ('(n, a) ': rest) = '(n, a) ': StripAllMaybe rest {- | Strip 'Maybe' from a single named column. Used by 'filterJust'. -@StripMaybeAt "x" '[Column "x" (Maybe Double), Column "y" Int]@- = @'[Column "x" Double, Column "y" Int]@+@StripMaybeAt "x" '[ '("x", Maybe Double), '("y", Int)]@+ = @'[ '("x", Double), '("y", Int)]@ -}-type family StripMaybeAt (name :: Symbol) (cols :: [Type]) :: [Type] where- StripMaybeAt name (Column name (Maybe a) ': rest) = Column name a ': rest- StripMaybeAt name (Column name a ': rest) = Column name a ': rest+type family StripMaybeAt (name :: Symbol) (cols :: [(Symbol, Type)]) :: [(Symbol, Type)] where+ StripMaybeAt name ('(name, Maybe a) ': rest) = '(name, a) ': rest+ StripMaybeAt name ('(name, a) ': rest) = '(name, a) ': rest StripMaybeAt name (col ': rest) = col ': StripMaybeAt name rest StripMaybeAt name '[] = TypeError ('Text "Column '" ':<>: 'Text name ':<>: 'Text "' not found in schema") -- | Extract column names that appear in both schemas.-type family SharedNames (left :: [Type]) (right :: [Type]) :: [Symbol] where+type family SharedNames (left :: [(Symbol, Type)]) (right :: [(Symbol, Type)]) :: [Symbol] where SharedNames '[] right = '[]- SharedNames (Column n _ ': rest) right =+ SharedNames ('(n, _) ': rest) right = SharedNamesHelper (HasName n right) n rest right type family SharedNamesHelper (found :: Bool) (n :: Symbol)- (rest :: [Type])- (right :: [Type]) ::+ (rest :: [(Symbol, Type)])+ (right :: [(Symbol, Type)]) :: [Symbol] where SharedNamesHelper 'True n rest right = n ': SharedNames rest right SharedNamesHelper 'False n rest right = SharedNames rest right -- | Columns from @left@ whose names do NOT appear in @right@.-type family UniqueLeft (left :: [Type]) (rightNames :: [Symbol]) :: [Type] where+type family+ UniqueLeft (left :: [(Symbol, Type)]) (rightNames :: [Symbol]) ::+ [(Symbol, Type)]+ where UniqueLeft '[] _ = '[]- UniqueLeft (Column n a ': rest) rn =+ UniqueLeft ('(n, a) ': rest) rn = UniqueLeftHelper (IsElem n rn) n a rest rn type family@@ -415,26 +441,29 @@ (found :: Bool) (n :: Symbol) (a :: Type)- (rest :: [Type])+ (rest :: [(Symbol, Type)]) (rn :: [Symbol]) ::- [Type]+ [(Symbol, Type)] where UniqueLeftHelper 'True n a rest rn = UniqueLeft rest rn- UniqueLeftHelper 'False n a rest rn = Column n a ': UniqueLeft rest rn+ UniqueLeftHelper 'False n a rest rn = '(n, a) ': UniqueLeft rest rn type family ToMaybe (a :: Type) :: Type where ToMaybe (Maybe a) = Maybe a ToMaybe a = Maybe a -- | Wrap column types in Maybe; idempotent on already-optional columns.-type family WrapMaybe (cols :: [Type]) :: [Type] where+type family WrapMaybe (cols :: [(Symbol, Type)]) :: [(Symbol, Type)] where WrapMaybe '[] = '[]- WrapMaybe (Column n a ': rest) = Column n (ToMaybe a) ': WrapMaybe rest+ WrapMaybe ('(n, a) ': rest) = '(n, ToMaybe a) ': WrapMaybe rest -- | Wrap selected columns in Maybe by name list.-type family WrapMaybeColumns (names :: [Symbol]) (cols :: [Type]) :: [Type] where+type family+ WrapMaybeColumns (names :: [Symbol]) (cols :: [(Symbol, Type)]) ::+ [(Symbol, Type)]+ where WrapMaybeColumns names '[] = '[]- WrapMaybeColumns names (Column n a ': rest) =+ WrapMaybeColumns names ('(n, a) ': rest) = WrapMaybeColumnsHelper (IsElem n names) n a names rest type family@@ -443,18 +472,24 @@ (n :: Symbol) (a :: Type) (names :: [Symbol])- (rest :: [Type]) ::- [Type]+ (rest :: [(Symbol, Type)]) ::+ [(Symbol, Type)] where WrapMaybeColumnsHelper 'True n a names rest =- Column n (ToMaybe a) ': WrapMaybeColumns names rest+ '(n, ToMaybe a) ': WrapMaybeColumns names rest WrapMaybeColumnsHelper 'False n a names rest =- Column n a ': WrapMaybeColumns names rest+ '(n, a) ': WrapMaybeColumns names rest -- | Columns in left whose names collide with right (excluding keys).-type family CollidingColumns (left :: [Type]) (right :: [Type]) (keys :: [Symbol]) :: [Type] where+type family+ CollidingColumns+ (left :: [(Symbol, Type)])+ (right :: [(Symbol, Type)])+ (keys :: [Symbol]) ::+ [(Symbol, Type)]+ where CollidingColumns '[] _ _ = '[]- CollidingColumns (Column n a ': rest) right keys =+ CollidingColumns ('(n, a) ': rest) right keys = CollidingColumnsHelper1 (IsElem n keys) n a rest right keys type family@@ -462,10 +497,10 @@ (isKey :: Bool) (n :: Symbol) (a :: Type)- (rest :: [Type])- (right :: [Type])+ (rest :: [(Symbol, Type)])+ (right :: [(Symbol, Type)]) (keys :: [Symbol]) ::- [Type]+ [(Symbol, Type)] where CollidingColumnsHelper1 'True n a rest right keys = CollidingColumns rest right keys@@ -477,18 +512,24 @@ (inRight :: Bool) (n :: Symbol) (a :: Type)- (rest :: [Type])- (right :: [Type])+ (rest :: [(Symbol, Type)])+ (right :: [(Symbol, Type)]) (keys :: [Symbol]) ::- [Type]+ [(Symbol, Type)] where CollidingColumnsHelper2 'True n a rest right keys =- Column n (These a (Lookup n right)) ': CollidingColumns rest right keys+ '(n, These a (Lookup n right)) ': CollidingColumns rest right keys CollidingColumnsHelper2 'False n a rest right keys = CollidingColumns rest right keys -- | Inner join result schema.-type family InnerJoinSchema (keys :: [Symbol]) (left :: [Type]) (right :: [Type]) :: [Type] where+type family+ InnerJoinSchema+ (keys :: [Symbol])+ (left :: [(Symbol, Type)])+ (right :: [(Symbol, Type)]) ::+ [(Symbol, Type)]+ where InnerJoinSchema keys left right = Append (SubsetSchema keys left)@@ -501,7 +542,13 @@ ) -- | Left join result schema.-type family LeftJoinSchema (keys :: [Symbol]) (left :: [Type]) (right :: [Type]) :: [Type] where+type family+ LeftJoinSchema+ (keys :: [Symbol])+ (left :: [(Symbol, Type)])+ (right :: [(Symbol, Type)]) ::+ [(Symbol, Type)]+ where LeftJoinSchema keys left right = Append (SubsetSchema keys left)@@ -514,7 +561,13 @@ ) -- | Right join result schema.-type family RightJoinSchema (keys :: [Symbol]) (left :: [Type]) (right :: [Type]) :: [Type] where+type family+ RightJoinSchema+ (keys :: [Symbol])+ (left :: [(Symbol, Type)])+ (right :: [(Symbol, Type)]) ::+ [(Symbol, Type)]+ where RightJoinSchema keys left right = Append (SubsetSchema keys right)@@ -528,8 +581,11 @@ -- | Full outer join result schema. type family- FullOuterJoinSchema (keys :: [Symbol]) (left :: [Type]) (right :: [Type]) ::- [Type]+ FullOuterJoinSchema+ (keys :: [Symbol])+ (left :: [(Symbol, Type)])+ (right :: [(Symbol, Type)]) ::+ [(Symbol, Type)] where FullOuterJoinSchema keys left right = Append@@ -543,9 +599,12 @@ ) -- | Extract Column entries from a schema whose names appear in @keys@.-type family GroupKeyColumns (keys :: [Symbol]) (cols :: [Type]) :: [Type] where+type family+ GroupKeyColumns (keys :: [Symbol]) (cols :: [(Symbol, Type)]) ::+ [(Symbol, Type)]+ where GroupKeyColumns keys '[] = '[]- GroupKeyColumns keys (Column n a ': rest) =+ GroupKeyColumns keys ('(n, a) ': rest) = GroupKeyColumnsHelper (IsElem n keys) n a keys rest type family@@ -554,15 +613,15 @@ (n :: Symbol) (a :: Type) (keys :: [Symbol])- (rest :: [Type]) ::- [Type]+ (rest :: [(Symbol, Type)]) ::+ [(Symbol, Type)] where GroupKeyColumnsHelper 'True n a keys rest =- Column n a ': GroupKeyColumns keys rest+ '(n, a) ': GroupKeyColumns keys rest GroupKeyColumnsHelper 'False n a keys rest = GroupKeyColumns keys rest -- | Provides runtime evidence of a schema: a list of (name, TypeRep) pairs.-class KnownSchema (cols :: [Type]) where+class KnownSchema (cols :: [(Symbol, Type)]) where schemaEvidence :: [(T.Text, SomeTypeRep)] instance KnownSchema '[] where@@ -570,7 +629,7 @@ instance (KnownSymbol name, Typeable a, Columnable a, KnownSchema rest) =>- KnownSchema (Column name a ': rest)+ KnownSchema ('(name, a) ': rest) where schemaEvidence = (T.pack (symbolVal (Proxy @name)), someTypeRep (Proxy @a))
src/DataFrame/Typed/Types.hs view
@@ -14,9 +14,6 @@ -- * Core phantom-typed wrapper TypedDataFrame (..), - -- * Column phantom type (no constructors)- Column,- -- * Typed expressions (schema-validated) TExpr (..), @@ -49,11 +46,11 @@ {- | A phantom-typed wrapper over the untyped 'DataFrame'. -The type parameter @cols@ is a type-level list of @Column name ty@ entries+The type parameter @cols@ is a type-level list of @'(name, ty)@ pairs that tracks the schema at compile time. All operations delegate to the untyped core at runtime and update the phantom type at compile time. -}-newtype TypedDataFrame (cols :: [Type]) = TDF {unTDF :: D.DataFrame}+newtype TypedDataFrame (cols :: [(Symbol, Type)]) = TDF {unTDF :: D.DataFrame} instance Show (TypedDataFrame cols) where show (TDF df) = show df@@ -61,12 +58,6 @@ instance Eq (TypedDataFrame cols) where (TDF a) == (TDF b) = a == b -{- | A phantom type that pairs a type-level column name ('Symbol')-with its element type. Has no value-level constructors — used-purely at the type level to describe schemas.--}-data Column (name :: Symbol) (a :: Type)- {- | A typed expression validated against schema @cols@, producing values of type @a@. Unlike the untyped 'Expr a', a 'TExpr' can only be constructed through@@ -75,7 +66,7 @@ Use 'unTExpr' to extract the underlying 'Expr' for delegation to the untyped API. -}-newtype TExpr (cols :: [Type]) a = TExpr {unTExpr :: Expr a}+newtype TExpr (cols :: [(Symbol, Type)]) a = TExpr {unTExpr :: Expr a} -- | Shows the underlying expression; the schema phantom is type-level only. instance (Show a) => Show (TExpr cols a) where@@ -85,23 +76,23 @@ @AsTExpr cols (Expr r) = TExpr cols r@. Lets a result type follow the frame — an @Expr@ over a plain frame becomes a @TExpr@ over a typed one. -}-type family AsTExpr (cols :: [Type]) (e :: Type) :: Type where+type family AsTExpr (cols :: [(Symbol, Type)]) (e :: Type) :: Type where AsTExpr cols (Expr r) = TExpr cols r -- | Lift an untyped expression into its 'TExpr' for schema @cols@.-class ToTExpr (cols :: [Type]) e where+class ToTExpr (cols :: [(Symbol, Type)]) e where toTExpr :: e -> AsTExpr cols e instance ToTExpr cols (Expr r) where toTExpr = TExpr -- | A typed sort order validated against schema @cols@.-data TSortOrder (cols :: [Type]) where+data TSortOrder (cols :: [(Symbol, Type)]) where Asc :: (Columnable a, Ord a) => TExpr cols a -> TSortOrder cols Desc :: (Columnable a, Ord a) => TExpr cols a -> TSortOrder cols -- | A phantom-typed wrapper over 'GroupedDataFrame'.-newtype TypedGrouped (keys :: [Symbol]) (cols :: [Type])+newtype TypedGrouped (keys :: [Symbol]) (cols :: [(Symbol, Type)]) = TGD {unTGD :: D.GroupedDataFrame} {- | Internal aggregation chain. Each cons prepends a 'Column' to the@@ -115,7 +106,7 @@ . as \@\"avg_age\" (F.mean age) @ -}-data TAgg (keys :: [Symbol]) (cols :: [Type]) (aggs :: [Type]) where+data TAgg (keys :: [Symbol]) (cols :: [(Symbol, Type)]) (aggs :: [(Symbol, Type)]) where TAggNil :: TAgg keys cols '[] TAggCons :: (Columnable a) =>@@ -125,7 +116,7 @@ TExpr cols a -> -- | rest TAgg keys cols aggs ->- TAgg keys cols (Column name a ': aggs)+ TAgg keys cols ('(name, a) ': aggs) {- | Extract the runtime 'NamedExpr' list from a 'TAgg', in declaration order (reversed from the cons-built order).