rel8 1.0.0.1 → 1.1.0.0
raw patch · 106 files changed
+4490/−5006 lines, 106 filesdep +bifunctorsdep +comonaddep +hasql-transactiondep −lifted-basedep −monad-controldep ~opaleyePVP ok
version bump matches the API change (PVP)
Dependencies added: bifunctors, comonad, hasql-transaction, mmorph, pretty, product-profunctors
Dependencies removed: lifted-base, monad-control
Dependency ranges changed: opaleye
API changes (from Hackage documentation)
- Rel8: HKDT :: a -> HKDT a
- Rel8: [unHKDT] :: HKDT a -> a
- Rel8: class Interpretation context => Labelable context
- Rel8: class (Table from a, Table to b, Congruent a b, Recontextualize from from a a, Recontextualize to to b b, Recontextualize to from b a) => Recontextualize from to a b | a -> from, b -> to, a to -> b, b from -> a
- Rel8: data Evaluate a
- Rel8: data Result a
- Rel8: eval :: Table Expr a => a -> Evaluate a
- Rel8: newtype HKDT a
- Rel8: reify :: (Table context a, Generic (Record a), Generic (Record (Unreify a)), GMappable (TTable context) (Rep (Record a)), Rep (Record (Unreify a)) ~ GMap TUnreify (Rep (Record a))) => (context :~: Reify ctx) -> Unreify a -> a
- Rel8: type Unreify a = DefaultUnreify a;
- Rel8: unreify :: (Table context a, Generic (Record a), Generic (Record (Unreify a)), GMappable (TTable context) (Rep (Record a)), Rep (Record (Unreify a)) ~ GMap TUnreify (Rep (Record a))) => (context :~: Reify ctx) -> a -> Unreify a
- Rel8: whereExists :: Query a -> Query ()
- Rel8: whereNotExists :: Query a -> Query ()
+ Rel8: -- context to the corresponding Haskell type.
+ Rel8: DoUpdate :: Upsert names -> OnConflict names
+ Rel8: [Upsert] :: (Selects names exprs, Projecting names index, excluded ~ exprs) => {index :: Projection names index " The set of conflict targets, projected from the set of columns for the whole table", set :: excluded -> exprs -> exprs " How to update each selected row.", updateWhere :: excluded -> exprs -> Expr Bool " Which rows to select for update."} -> Upsert names
+ Rel8: absent :: Query a -> Query ()
+ Rel8: aggregateEitherTable :: () => (exprs -> aggregates) -> (exprs' -> aggregates') -> EitherTable Expr exprs exprs' -> EitherTable Aggregate aggregates aggregates'
+ Rel8: aggregateMaybeTable :: () => (exprs -> aggregates) -> MaybeTable Expr exprs -> MaybeTable Aggregate aggregates
+ Rel8: aggregateTheseTable :: () => (exprs -> aggregates) -> (exprs' -> aggregates') -> TheseTable Expr exprs exprs' -> TheseTable Aggregate aggregates aggregates'
+ Rel8: biproject :: (Biprojectable p, Projecting a b, Projecting c d) => Projection a b -> Projection c d -> p a c -> p b d
+ Rel8: class Biprojectable p
+ Rel8: class Projectable f
+ Rel8: class (Transposes (Context a) (Field a) a (Transpose (Field a) a), Transposes (Context a) (Field a) b (Transpose (Field a) b)) => Projecting a b
+ Rel8: class (Table from a, Table to b, Congruent a b, b ~ Transpose to a, a ~ Transpose from b) => Transposes from to a b | a -> from, b -> to, a to -> b, b from -> a
+ Rel8: data Field table a
+ Rel8: data Upsert names
+ Rel8: indexed :: Query a -> Query (Expr Int64, a)
+ Rel8: present :: Query a -> Query ()
+ Rel8: project :: (Projectable f, Projecting a b) => Projection a b -> f a -> f b
+ Rel8: rebind :: Table Expr a => a -> Query a
+ Rel8: showDelete :: Delete a -> String
+ Rel8: showInsert :: Insert a -> String
+ Rel8: showUpdate :: Update a -> String
+ Rel8: type FromExprs a = Map TFromExprs a;
+ Rel8: type Projection a b = Transpose (Field a) a -> Transpose (Field a) b
+ Rel8: type Result = Identity
+ Rel8: type Transpose context a = Map (TTranspose context) a;
+ Rel8: unsafeDefault :: Expr a
+ Rel8.Tabulate: absent :: Tabulation k a -> Tabulation k ()
+ Rel8.Tabulate: aggregate :: forall k aggregates exprs. (EqTable k, Aggregates aggregates exprs) => Tabulation k aggregates -> Tabulation k exprs
+ Rel8.Tabulate: align :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k (TheseTable Expr a b)
+ Rel8.Tabulate: alignWith :: EqTable k => (TheseTable Expr a b -> c) -> Tabulation k a -> Tabulation k b -> Tabulation k c
+ Rel8.Tabulate: count :: EqTable k => Tabulation k a -> Tabulation k (Expr Int64)
+ Rel8.Tabulate: data Tabulation k a
+ Rel8.Tabulate: difference :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k a
+ Rel8.Tabulate: distinct :: EqTable k => Tabulation k a -> Tabulation k a
+ Rel8.Tabulate: exists :: Tabulation k a -> Tabulation k (Expr Bool)
+ Rel8.Tabulate: fromQuery :: Query (k, a) -> Tabulation k a
+ Rel8.Tabulate: infixr 1 `through`
+ Rel8.Tabulate: instance (Rel8.Table.Eq.EqTable k, Rel8.Table.Table Rel8.Expr.Expr a, GHC.Base.Semigroup a) => GHC.Base.Monoid (Rel8.Tabulate.Tabulation k a)
+ Rel8.Tabulate: instance (Rel8.Table.Eq.EqTable k, Rel8.Table.Table Rel8.Expr.Expr a, GHC.Base.Semigroup a) => GHC.Base.Semigroup (Rel8.Tabulate.Tabulation k a)
+ Rel8.Tabulate: instance Data.Bifunctor.Bifunctor Rel8.Tabulate.Tabulation
+ Rel8.Tabulate: instance Data.Functor.Contravariant.Contravariant Rel8.Tabulate.Predicate
+ Rel8.Tabulate: instance GHC.Base.Functor (Rel8.Tabulate.Tabulation k)
+ Rel8.Tabulate: instance GHC.Base.Monoid (Rel8.Tabulate.Predicate k)
+ Rel8.Tabulate: instance GHC.Base.Semigroup (Rel8.Tabulate.Predicate k)
+ Rel8.Tabulate: instance Rel8.Table.Eq.EqTable k => Data.Functor.Bind.Class.Apply (Rel8.Tabulate.Tabulation k)
+ Rel8.Tabulate: instance Rel8.Table.Eq.EqTable k => Data.Functor.Bind.Class.Bind (Rel8.Tabulate.Tabulation k)
+ Rel8.Tabulate: instance Rel8.Table.Eq.EqTable k => GHC.Base.Applicative (Rel8.Tabulate.Tabulation k)
+ Rel8.Tabulate: instance Rel8.Table.Eq.EqTable k => GHC.Base.Monad (Rel8.Tabulate.Tabulation k)
+ Rel8.Tabulate: instance Rel8.Table.Eq.EqTable k => Rel8.Table.Alternative.AltTable (Rel8.Tabulate.Tabulation k)
+ Rel8.Tabulate: instance Rel8.Table.Eq.EqTable k => Rel8.Table.Alternative.AlternativeTable (Rel8.Tabulate.Tabulation k)
+ Rel8.Tabulate: instance Rel8.Table.Projection.Biprojectable Rel8.Tabulate.Tabulation
+ Rel8.Tabulate: instance Rel8.Table.Projection.Projectable (Rel8.Tabulate.Tabulation k)
+ Rel8.Tabulate: leftAlign :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k (a, MaybeTable Expr b)
+ Rel8.Tabulate: leftAlignWith :: EqTable k => (a -> MaybeTable Expr b -> c) -> Tabulation k a -> Tabulation k b -> Tabulation k c
+ Rel8.Tabulate: liftQuery :: Query a -> Tabulation k a
+ Rel8.Tabulate: lookup :: EqTable k => k -> Tabulation k a -> Query a
+ Rel8.Tabulate: many :: (EqTable k, Table Expr a) => Tabulation k a -> Tabulation k (ListTable Expr a)
+ Rel8.Tabulate: optional :: Tabulation k a -> Tabulation k (MaybeTable Expr a)
+ Rel8.Tabulate: order :: OrdTable k => Order a -> Tabulation k a -> Tabulation k a
+ Rel8.Tabulate: present :: Tabulation k a -> Tabulation k ()
+ Rel8.Tabulate: rightAlign :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k (MaybeTable Expr a, b)
+ Rel8.Tabulate: rightAlignWith :: EqTable k => (MaybeTable Expr a -> b -> c) -> Tabulation k a -> Tabulation k b -> Tabulation k c
+ Rel8.Tabulate: similarity :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k a
+ Rel8.Tabulate: some :: (EqTable k, Table Expr a) => Tabulation k a -> Tabulation k (NonEmptyTable Expr a)
+ Rel8.Tabulate: through :: (a -> Query b) -> Tabulation k a -> Tabulation k b
+ Rel8.Tabulate: toQuery :: Table Expr k => Tabulation k a -> Query (k, a)
+ Rel8.Tabulate: zip :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k (a, b)
+ Rel8.Tabulate: zipWith :: EqTable k => (a -> b -> c) -> Tabulation k a -> Tabulation k b -> Tabulation k c
- Rel8: ($?) :: forall a b. Sql DBType b => (a -> Expr b) -> MaybeTable a -> Expr (Nullify b)
+ Rel8: ($?) :: forall a b. Sql DBType b => (a -> Expr b) -> MaybeTable Expr a -> Expr (Nullify b)
- Rel8: -- | The common context that all columns use as an interpretation.
+ Rel8: -- | The <tt>FromExprs</tt> type family maps a type in the <tt>Expr</tt>
- Rel8: Abort :: OnConflict
+ Rel8: Abort :: OnConflict names
- Rel8: DoNothing :: OnConflict
+ Rel8: DoNothing :: OnConflict names
- Rel8: [Delete] :: Selects names exprs => {from :: TableSchema names " Which table to delete from.", deleteWhere :: exprs -> Expr Bool " Which rows should be selected for deletion.", returning :: Returning names a " What to return from the @DELETE@ statement."} -> Delete a
+ Rel8: [Delete] :: Selects names exprs => {from :: TableSchema names " Which table to delete from.", using :: Query using " @USING@ clause — this can be used to join against other tables, and its results can be referenced in the @WHERE@ clause", deleteWhere :: using -> exprs -> Expr Bool " Which rows should be selected for deletion.", returning :: Returning names a " What to return from the @DELETE@ statement."} -> Delete a
- Rel8: [Insert] :: Selects names exprs => {into :: TableSchema names " Which table to insert into.", rows :: [exprs] " The rows to insert.", onConflict :: OnConflict " What to do if the inserted rows conflict with data already in the table.", returning :: Returning names a " What information to return on completion."} -> Insert a
+ Rel8: [Insert] :: Selects names exprs => {into :: TableSchema names " Which table to insert into.", rows :: Query exprs " The rows to insert. This can be an arbitrary query — use 'Rel8.values' insert a static list of rows.", onConflict :: OnConflict names " What to do if the inserted rows conflict with data already in the table.", returning :: Returning names a " What information to return on completion."} -> Insert a
- Rel8: [Projection] :: (Selects names exprs, Serializable projection a) => (exprs -> projection) -> Returning names [a]
+ Rel8: [Projection] :: (Selects names exprs, Serializable returning a) => (exprs -> returning) -> Returning names [a]
- Rel8: [Update] :: Selects names exprs => {target :: TableSchema names " Which table to update.", set :: exprs -> exprs " How to update each selected row.", updateWhere :: exprs -> Expr Bool " Which rows to select for update.", returning :: Returning names a " What to return from the @UPDATE@ statement."} -> Update a
+ Rel8: [Update] :: Selects names exprs => {target :: TableSchema names " Which table to update.", from :: Query from " @FROM@ clause — this can be used to join against other tables, and its results can be referenced in the @SET@ and @WHERE@ clauses.", set :: from -> exprs -> exprs " How to update each selected row.", updateWhere :: from -> exprs -> Expr Bool " Which rows to select for update.", returning :: Returning names a " What to return from the @UPDATE@ statement."} -> Update a
- Rel8: alignBy :: (Table Expr a, Table Expr b) => (a -> b -> Expr Bool) -> Query a -> Query b -> Query (TheseTable a b)
+ Rel8: alignBy :: () => (a -> b -> Expr Bool) -> Query a -> Query b -> Query (TheseTable Expr a b)
- Rel8: bitraverseEitherTable :: () => (a -> Query c) -> (b -> Query d) -> EitherTable a b -> Query (EitherTable c d)
+ Rel8: bitraverseEitherTable :: () => (a -> Query c) -> (b -> Query d) -> EitherTable Expr a b -> Query (EitherTable Expr c d)
- Rel8: bitraverseTheseTable :: () => (a -> Query c) -> (b -> Query d) -> TheseTable a b -> Query (TheseTable c d)
+ Rel8: bitraverseTheseTable :: () => (a -> Query c) -> (b -> Query d) -> TheseTable Expr a b -> Query (TheseTable Expr c d)
- Rel8: catListTable :: Table Expr a => ListTable a -> Query a
+ Rel8: catListTable :: Table Expr a => ListTable Expr a -> Query a
- Rel8: catMaybeTable :: MaybeTable a -> Query a
+ Rel8: catMaybeTable :: MaybeTable Expr a -> Query a
- Rel8: catNonEmptyTable :: Table Expr a => NonEmptyTable a -> Query a
+ Rel8: catNonEmptyTable :: Table Expr a => NonEmptyTable Expr a -> Query a
- Rel8: class (Generic (t Result), HTable (GColumnsADT t), GTableADT (TTable (Reify Result)) TColumns (Col (Reify Result)) (GRecord (Rep (t (Reify Result)))), GRecordable (Rep (t (Reify Result))), GMappable (TTable (Reify Result)) (Rep (t (Reify Result))), GMap TUnreify (Rep (t (Reify Result))) ~ Rep (t Result)) => ADTable t
+ Rel8: class (Generic (Record (t Result)), HTable (GColumnsADT t), GSerializeADT TSerialize TColumns (Eval (ADTRep t Expr)) (Eval (ADTRep t Result))) => ADTable t
- Rel8: class Recontextualize Aggregate Expr aggregates exprs => Aggregates aggregates exprs
+ Rel8: class Transposes Aggregate Expr aggregates exprs => Aggregates aggregates exprs
- Rel8: class (Generic a, HTable (GColumns (HKD a)), KnownAlgebra (GAlgebra (Rep a)), Eval (GGTable (GAlgebra (Rep a)) (TTable (Reify Result)) TColumns (Col (Reify Result)) (GRecord (GMap (TColumn (Reify Result)) (Rep a)))), Eval (GGContext (GAlgebra (Rep a)) TUnreifyContext (GRecord (GMap (TColumn (Reify Result)) (Rep a)))) ~ Result, GRecordable (GMap (TColumn (Reify Result)) (Rep a)), GMappable Top (Rep a), GMappable (TTable (Reify Result)) (GMap (TColumn (Reify Result)) (Rep a)), GMap TUnreify (GMap (TColumn (Reify Result)) (Rep a)) ~ GMap (TColumn Result) (Rep a)) => HKDable a
+ Rel8: class (Generic (Record a), HTable (GColumns (HKD a)), KnownAlgebra (GAlgebra (Rep a)), Eval (GGSerialize (GAlgebra (Rep a)) TSerialize TColumns (Eval (HKDRep a Expr)) (Eval (HKDRep a Result))), GRecord (GMap (TColumn Result) (Rep a)) ~ Rep (Record a)) => HKDable a
- Rel8: class Recontextualize Name Expr names exprs => Selects names exprs
+ Rel8: class Transposes Name Expr names exprs => Selects names exprs
- Rel8: class (HTable (Columns a), context ~ Context a) => Table context a | a -> context where {
+ Rel8: class (HTable (Columns a), context ~ Context a, a ~ Transpose context a) => Table context a | a -> context where {
- Rel8: createView :: Selects names exprs => TableSchema names -> Query exprs -> Connection -> IO ()
+ Rel8: createView :: Selects names exprs => TableSchema names -> Query exprs -> Statement () ()
- Rel8: data EitherTable a b
+ Rel8: data EitherTable context a b
- Rel8: data ListTable a
+ Rel8: data ListTable context a
- Rel8: data MaybeTable a
+ Rel8: data MaybeTable context a
- Rel8: data NonEmptyTable a
+ Rel8: data NonEmptyTable context a
- Rel8: data OnConflict
+ Rel8: data OnConflict names
- Rel8: data TheseTable a b
+ Rel8: data TheseTable context a b
- Rel8: delete :: Connection -> Delete a -> IO a
+ Rel8: delete :: Delete a -> Statement () a
- Rel8: eitherTable :: Table Expr c => (a -> c) -> (b -> c) -> EitherTable a b -> c
+ Rel8: eitherTable :: Table Expr c => (a -> c) -> (b -> c) -> EitherTable Expr a b -> c
- Rel8: evaluate :: Evaluate a -> Query a
+ Rel8: evaluate :: Table Expr a => a -> Query a
- Rel8: fromColumns :: (Table context a, Generic (Record a), KnownAlgebra (GAlgebra (Rep (Record a))), Eval (GGTable (GAlgebra (Rep (Record a))) (TTable context) TColumns (Col context) (Rep (Record a))), Columns a ~ Eval (GGColumns (GAlgebra (Rep (Record a))) TColumns (Rep (Record a))), Context a ~ Eval (GGContext (GAlgebra (Rep (Record a))) TContext (Rep (Record a)))) => Columns a (Col context) -> a
+ Rel8: fromColumns :: (Table context a, Generic (Record a), GTable (TTable context) TColumns (Rep (Record a)), Columns a ~ GColumns TColumns (Rep (Record a))) => Columns a context -> a
- Rel8: fromResult :: (ToExprs exprs a, Generic (Record a), KnownAlgebra (GAlgebra (Rep (Record exprs))), Eval (GGToExprs (GAlgebra (Rep (Record exprs))) TToExprs TColumns (Rep (Record exprs)) (Rep (Record a))), Columns exprs ~ Eval (GGColumns (GAlgebra (Rep (Record exprs))) TColumns (Rep (Record exprs)))) => Columns exprs (Col Result) -> a
+ Rel8: fromResult :: (Table context a, Generic (Record (FromExprs a)), GSerialize TSerialize TColumns (Rep (Record a)) (Rep (Record (FromExprs a))), Columns a ~ GColumns TColumns (Rep (Record a))) => Columns a Result -> FromExprs a
- Rel8: hasHereTable :: TheseTable a b -> Expr Bool
+ Rel8: hasHereTable :: TheseTable Expr a b -> Expr Bool
- Rel8: hasThereTable :: TheseTable a b -> Expr Bool
+ Rel8: hasThereTable :: TheseTable Expr a b -> Expr Bool
- Rel8: insert :: Connection -> Insert a -> IO a
+ Rel8: insert :: Insert a -> Statement () a
- Rel8: isJustTable :: MaybeTable a -> Expr Bool
+ Rel8: isJustTable :: MaybeTable Expr a -> Expr Bool
- Rel8: isLeftTable :: EitherTable a b -> Expr Bool
+ Rel8: isLeftTable :: EitherTable Expr a b -> Expr Bool
- Rel8: isNothingTable :: MaybeTable a -> Expr Bool
+ Rel8: isNothingTable :: MaybeTable Expr a -> Expr Bool
- Rel8: isRightTable :: EitherTable a b -> Expr Bool
+ Rel8: isRightTable :: EitherTable Expr a b -> Expr Bool
- Rel8: isThatTable :: TheseTable a b -> Expr Bool
+ Rel8: isThatTable :: TheseTable Expr a b -> Expr Bool
- Rel8: isThisTable :: TheseTable a b -> Expr Bool
+ Rel8: isThisTable :: TheseTable Expr a b -> Expr Bool
- Rel8: isThoseTable :: TheseTable a b -> Expr Bool
+ Rel8: isThoseTable :: TheseTable Expr a b -> Expr Bool
- Rel8: justHereTable :: TheseTable a b -> MaybeTable a
+ Rel8: justHereTable :: TheseTable context a b -> MaybeTable context a
- Rel8: justTable :: a -> MaybeTable a
+ Rel8: justTable :: a -> MaybeTable Expr a
- Rel8: justThereTable :: TheseTable a b -> MaybeTable b
+ Rel8: justThereTable :: TheseTable context a b -> MaybeTable context b
- Rel8: keepHereTable :: TheseTable a b -> Query (a, MaybeTable b)
+ Rel8: keepHereTable :: TheseTable Expr a b -> Query (a, MaybeTable Expr b)
- Rel8: keepLeftTable :: EitherTable a b -> Query a
+ Rel8: keepLeftTable :: EitherTable Expr a b -> Query a
- Rel8: keepRightTable :: EitherTable a b -> Query b
+ Rel8: keepRightTable :: EitherTable Expr a b -> Query b
- Rel8: keepThatTable :: TheseTable a b -> Query b
+ Rel8: keepThatTable :: TheseTable Expr a b -> Query b
- Rel8: keepThereTable :: TheseTable a b -> Query (MaybeTable a, b)
+ Rel8: keepThereTable :: TheseTable Expr a b -> Query (MaybeTable Expr a, b)
- Rel8: keepThisTable :: TheseTable a b -> Query a
+ Rel8: keepThisTable :: TheseTable Expr a b -> Query a
- Rel8: keepThoseTable :: TheseTable a b -> Query (a, b)
+ Rel8: keepThoseTable :: TheseTable Expr a b -> Query (a, b)
- Rel8: leftTable :: Table Expr b => a -> EitherTable a b
+ Rel8: leftTable :: Table Expr b => a -> EitherTable Expr a b
- Rel8: listAgg :: Aggregates aggregates exprs => exprs -> ListTable aggregates
+ Rel8: listAgg :: Aggregates aggregates exprs => exprs -> ListTable Aggregate aggregates
- Rel8: listTable :: Table Expr a => [a] -> ListTable a
+ Rel8: listTable :: Table Expr a => [a] -> ListTable Expr a
- Rel8: loseHereTable :: TheseTable a b -> Query b
+ Rel8: loseHereTable :: TheseTable Expr a b -> Query b
- Rel8: loseThatTable :: TheseTable a b -> Query (a, MaybeTable b)
+ Rel8: loseThatTable :: TheseTable Expr a b -> Query (a, MaybeTable Expr b)
- Rel8: loseThereTable :: TheseTable a b -> Query a
+ Rel8: loseThereTable :: TheseTable Expr a b -> Query a
- Rel8: loseThisTable :: TheseTable a b -> Query (MaybeTable a, b)
+ Rel8: loseThisTable :: TheseTable Expr a b -> Query (MaybeTable Expr a, b)
- Rel8: loseThoseTable :: TheseTable a b -> Query (EitherTable a b)
+ Rel8: loseThoseTable :: TheseTable Expr a b -> Query (EitherTable Expr a b)
- Rel8: many :: Table Expr a => Query a -> Query (ListTable a)
+ Rel8: many :: Table Expr a => Query a -> Query (ListTable Expr a)
- Rel8: maybeTable :: Table Expr b => b -> (a -> b) -> MaybeTable a -> b
+ Rel8: maybeTable :: Table Expr b => b -> (a -> b) -> MaybeTable Expr a -> b
- Rel8: nameEitherTable :: Name EitherTag -> a -> b -> EitherTable a b
+ Rel8: nameEitherTable :: Name EitherTag -> a -> b -> EitherTable Name a b
- Rel8: nameListTable :: Table Name a => a -> ListTable a
+ Rel8: nameListTable :: Table Name a => a -> ListTable Name a
- Rel8: nameMaybeTable :: Name (Maybe MaybeTag) -> a -> MaybeTable a
+ Rel8: nameMaybeTable :: Name (Maybe MaybeTag) -> a -> MaybeTable Name a
- Rel8: nameNonEmptyTable :: Table Name a => a -> NonEmptyTable a
+ Rel8: nameNonEmptyTable :: Table Name a => a -> NonEmptyTable Name a
- Rel8: nameTheseTable :: () => Name (Maybe MaybeTag) -> Name (Maybe MaybeTag) -> a -> b -> TheseTable a b
+ Rel8: nameTheseTable :: () => Name (Maybe MaybeTag) -> Name (Maybe MaybeTag) -> a -> b -> TheseTable Name a b
- Rel8: nonEmptyAgg :: Aggregates aggregates exprs => exprs -> NonEmptyTable aggregates
+ Rel8: nonEmptyAgg :: Aggregates aggregates exprs => exprs -> NonEmptyTable Aggregate aggregates
- Rel8: nonEmptyTable :: Table Expr a => NonEmpty a -> NonEmptyTable a
+ Rel8: nonEmptyTable :: Table Expr a => NonEmpty a -> NonEmptyTable Expr a
- Rel8: nothingTable :: Table Expr a => MaybeTable a
+ Rel8: nothingTable :: Table Expr a => MaybeTable Expr a
- Rel8: optional :: Query a -> Query (MaybeTable a)
+ Rel8: optional :: Query a -> Query (MaybeTable Expr a)
- Rel8: rightTable :: Table Expr a => b -> EitherTable a b
+ Rel8: rightTable :: Table Expr a => b -> EitherTable Expr a b
- Rel8: select :: forall exprs a. Serializable exprs a => Connection -> Query exprs -> IO [a]
+ Rel8: select :: forall exprs a. Serializable exprs a => Query exprs -> Statement () [a]
- Rel8: some :: Table Expr a => Query a -> Query (NonEmptyTable a)
+ Rel8: some :: Table Expr a => Query a -> Query (NonEmptyTable Expr a)
- Rel8: thatTable :: Table Expr a => b -> TheseTable a b
+ Rel8: thatTable :: Table Expr a => b -> TheseTable Expr a b
- Rel8: theseTable :: Table Expr c => (a -> c) -> (b -> c) -> (a -> b -> c) -> TheseTable a b -> c
+ Rel8: theseTable :: Table Expr c => (a -> c) -> (b -> c) -> (a -> b -> c) -> TheseTable Expr a b -> c
- Rel8: thisTable :: Table Expr b => a -> TheseTable a b
+ Rel8: thisTable :: Table Expr b => a -> TheseTable Expr a b
- Rel8: thoseTable :: a -> b -> TheseTable a b
+ Rel8: thoseTable :: a -> b -> TheseTable Expr a b
- Rel8: toColumns :: (Table context a, Generic (Record a), KnownAlgebra (GAlgebra (Rep (Record a))), Eval (GGTable (GAlgebra (Rep (Record a))) (TTable context) TColumns (Col context) (Rep (Record a))), Columns a ~ Eval (GGColumns (GAlgebra (Rep (Record a))) TColumns (Rep (Record a))), Context a ~ Eval (GGContext (GAlgebra (Rep (Record a))) TContext (Rep (Record a)))) => a -> Columns a (Col context)
+ Rel8: toColumns :: (Table context a, Generic (Record a), GTable (TTable context) TColumns (Rep (Record a)), Columns a ~ GColumns TColumns (Rep (Record a))) => a -> Columns a context
- Rel8: toResult :: (ToExprs exprs a, Generic (Record a), KnownAlgebra (GAlgebra (Rep (Record exprs))), Eval (GGToExprs (GAlgebra (Rep (Record exprs))) TToExprs TColumns (Rep (Record exprs)) (Rep (Record a))), Columns exprs ~ Eval (GGColumns (GAlgebra (Rep (Record exprs))) TColumns (Rep (Record exprs)))) => a -> Columns exprs (Col Result)
+ Rel8: toResult :: (Table context a, Generic (Record (FromExprs a)), GSerialize TSerialize TColumns (Rep (Record a)) (Rep (Record (FromExprs a))), Columns a ~ GColumns TColumns (Rep (Record a))) => FromExprs a -> Columns a Result
- Rel8: traverseMaybeTable :: (a -> Query b) -> MaybeTable a -> Query (MaybeTable b)
+ Rel8: traverseMaybeTable :: (a -> Query b) -> MaybeTable Expr a -> Query (MaybeTable Expr b)
- Rel8: type Columns a = Eval (GGColumns (GAlgebra (Rep (Record a))) TColumns (Rep (Record a)));
+ Rel8: type Columns a = GColumns TColumns (Rep (Record a));
- Rel8: type Context a = Eval (GGContext (GAlgebra (Rep (Record a))) TContext (Rep (Record a)));
+ Rel8: type Context a = GContext TContext (Rep (Record a));
- Rel8: type family FromExprs a
+ Rel8: type family Transpose (context' :: Context) a :: Type;
- Rel8: update :: Connection -> Update a -> IO a
+ Rel8: update :: Update a -> Statement () a
Files
- Changelog.md +66/−0
- README.md +4/−0
- rel8.cabal +26/−15
- src/Rel8.hs +33/−15
- src/Rel8/Aggregate.hs +21/−69
- src/Rel8/Aggregate.hs-boot +11/−0
- src/Rel8/Column.hs +5/−77
- src/Rel8/Column/ADT.hs +3/−68
- src/Rel8/Column/Either.hs +4/−140
- src/Rel8/Column/Lift.hs +3/−67
- src/Rel8/Column/List.hs +6/−126
- src/Rel8/Column/Maybe.hs +4/−105
- src/Rel8/Column/NonEmpty.hs +6/−127
- src/Rel8/Column/These.hs +5/−142
- src/Rel8/Expr.hs +16/−47
- src/Rel8/Expr.hs-boot +6/−8
- src/Rel8/Expr/Default.hs +35/−0
- src/Rel8/Expr/Eq.hs +1/−1
- src/Rel8/FCF.hs +3/−3
- src/Rel8/Generic/Construction.hs +146/−165
- src/Rel8/Generic/Construction/ADT.hs +31/−47
- src/Rel8/Generic/Construction/Record.hs +6/−11
- src/Rel8/Generic/Map.hs +20/−64
- src/Rel8/Generic/Reify.hs +0/−24
- src/Rel8/Generic/Rel8able.hs +170/−69
- src/Rel8/Generic/Rel8able/Test.hs +0/−157
- src/Rel8/Generic/Table.hs +24/−109
- src/Rel8/Generic/Table/ADT.hs +61/−204
- src/Rel8/Generic/Table/Record.hs +74/−99
- src/Rel8/Kind/Context.hs +8/−33
- src/Rel8/Kind/Labels.hs +0/−57
- src/Rel8/Order.hs +1/−1
- src/Rel8/Query.hs +168/−7
- src/Rel8/Query.hs-boot +3/−1
- src/Rel8/Query/Each.hs +3/−3
- src/Rel8/Query/Either.hs +10/−6
- src/Rel8/Query/Evaluate.hs +37/−51
- src/Rel8/Query/Exists.hs +15/−17
- src/Rel8/Query/Indexed.hs +34/−0
- src/Rel8/Query/List.hs +17/−15
- src/Rel8/Query/Maybe.hs +24/−29
- src/Rel8/Query/Opaleye.hs +41/−5
- src/Rel8/Query/SQL.hs +6/−59
- src/Rel8/Query/These.hs +50/−37
- src/Rel8/Schema/Context.hs +0/−20
- src/Rel8/Schema/Context/Label.hs +0/−62
- src/Rel8/Schema/Context/Nullify.hs +100/−136
- src/Rel8/Schema/Field.hs +50/−0
- src/Rel8/Schema/HTable.hs +22/−22
- src/Rel8/Schema/HTable/Either.hs +2/−3
- src/Rel8/Schema/HTable/Identity.hs +16/−28
- src/Rel8/Schema/HTable/Label.hs +37/−50
- src/Rel8/Schema/HTable/MapTable.hs +22/−12
- src/Rel8/Schema/HTable/Maybe.hs +2/−3
- src/Rel8/Schema/HTable/Nullify.hs +47/−32
- src/Rel8/Schema/HTable/These.hs +2/−3
- src/Rel8/Schema/HTable/Vectorize.hs +28/−44
- src/Rel8/Schema/Kind.hs +6/−15
- src/Rel8/Schema/Name.hs +34/−56
- src/Rel8/Schema/Name.hs-boot +11/−0
- src/Rel8/Schema/Reify.hs +0/−74
- src/Rel8/Schema/Result.hs +16/−41
- src/Rel8/Schema/Spec.hs +17/−37
- src/Rel8/Schema/Spec/ConstrainDBType.hs +0/−89
- src/Rel8/Schema/Table.hs +17/−0
- src/Rel8/Statement/Delete.hs +35/−48
- src/Rel8/Statement/Insert.hs +43/−72
- src/Rel8/Statement/OnConflict.hs +106/−0
- src/Rel8/Statement/Returning.hs +114/−10
- src/Rel8/Statement/SQL.hs +29/−0
- src/Rel8/Statement/Select.hs +116/−33
- src/Rel8/Statement/Set.hs +33/−0
- src/Rel8/Statement/Update.hs +38/−56
- src/Rel8/Statement/Using.hs +36/−0
- src/Rel8/Statement/View.hs +17/−27
- src/Rel8/Statement/Where.hs +31/−0
- src/Rel8/Table.hs +88/−252
- src/Rel8/Table/ADT.hs +32/−64
- src/Rel8/Table/Aggregate.hs +12/−13
- src/Rel8/Table/Bool.hs +5/−6
- src/Rel8/Table/Cols.hs +50/−0
- src/Rel8/Table/Either.hs +118/−123
- src/Rel8/Table/Eq.hs +12/−39
- src/Rel8/Table/HKD.hs +64/−105
- src/Rel8/Table/List.hs +53/−54
- src/Rel8/Table/Maybe.hs +103/−117
- src/Rel8/Table/Name.hs +17/−22
- src/Rel8/Table/NonEmpty.hs +54/−53
- src/Rel8/Table/Nullify.hs +184/−0
- src/Rel8/Table/Opaleye.hs +56/−26
- src/Rel8/Table/Ord.hs +14/−43
- src/Rel8/Table/Order.hs +13/−16
- src/Rel8/Table/Projection.hs +73/−0
- src/Rel8/Table/Recontextualize.hs +0/−90
- src/Rel8/Table/Rel8able.hs +29/−85
- src/Rel8/Table/Serialize.hs +18/−118
- src/Rel8/Table/Tag.hs +0/−97
- src/Rel8/Table/These.hs +146/−141
- src/Rel8/Table/Transpose.hs +46/−0
- src/Rel8/Table/Undefined.hs +5/−5
- src/Rel8/Table/Unreify.hs +0/−102
- src/Rel8/Tabulate.hs +637/−0
- src/Rel8/Type/Composite.hs +20/−19
- src/Rel8/Type/Tag.hs +1/−1
- tests/Main.hs +227/−162
- tests/Rel8/Generic/Rel8able/Test.hs +179/−0
Changelog.md view
@@ -1,3 +1,69 @@+# 1.1.0.0 (2021-07-16)++## New features++* You can derive `Rel8able` for "vanilla" higher-kinded data types - data types that don't use the `Column` type family. For example, the following is now possible:++ ```haskell+ data MyTable f = MyTable+ { myTableX :: f Int32+ , myTableY :: f Text+ } deriving (Generic, Rel8able)+ ```++* The `Projectable` and `Biprojectable` type classes have been introduced. These classes model a type of functors and bifunctors (respectively) on tables, where the mapping function is only able to rearrange columns. For example, the following is now possible:++ ```haskell+ x <- project myTableX <$> many ...+ ```++* `Insert`'s `onConflict` field now has a new option - `DoUpdate`. This maps to PostgreSQL's `ON CONFLICT DO UPDATE` syntax, which provides upsert support. ([#85](https://github.com/circuithub/rel8/pull/85))++* `Insert`, `Update` and `Delete` has all been expanded to work with `Query`s. In particular:++ * `Delete` now supports a `USING` sub-query+ * `Update` now supports a `FROM` sub-query+ * `Insert` can delete the result of any arbitrary `Query` (providing the types match)++* The new `Rel8.Tabulate` module has been added, which gives a `Map`-esque interface to writing and composing queries. ([#70](https://github.com/circuithub/rel8/pull/70))++* The new `indexed` `Query -> Query` function was added. This function augments each row in a query with it's 0-based index. ([#117](https://github.com/circuithub/rel8/pull/117))++## Breaking changes++* `Insert`, `Delete` and `Update` have all changed. In particular, for `Insert` users should now replace `rows = xs` with `rows = values xs`. ([#85](https://github.com/circuithub/rel8/pull/85))++* Rename `whereExists` and `whereNotExists` to `present` and `absent` respectively. ([#57](https://github.com/circuithub/rel8/pull/57))++* Simplify `evaluate` to run directly inside the `Query` monad, rendering the `Evaluate` monad unnecessary. ([#56](https://github.com/circuithub/rel8/pull/56))++* The `Labelable` type class has been removed as it's no longer necessary. This class was mostly internal, though was visible in some public API functions. ([#76](https://github.com/circuithub/rel8/pull/76))++* `EitherTable`, `ListTable`, `MaybeTable`, `NonEmptyTable` and `TheseTable` now all have an extra parameter which tracks the context of the table. If you manually specified any types before, you need to provide this parameter - usually `Expr` will be the correct choice. ([#101](https://github.com/circuithub/rel8/pull/101))++* We've done a lot of work to simplify the internals of Rel8, but some of these are breaking changes. However, most of these API changes shouldn't affect users as they are mostly types used in type inference, or affect classes where users don't need to define their own instances.++ * The kind of a `Rel8able` is now `(Type -> Type) -> Type`. Previously it was `(X -> Type) -> Type`.+ * `Table` has a new associated type - `FromExprs`. This was previously an open type family.+ * `Table` has a new associated type - `Transpose` - and `Recontextualise` has been renamed to `Transposes`. This `Transposes` class now operates in terms of `Transpose`.++* `select`, `insert`, `update` and `delete` now produce Hasql `Statement`s, rather than actually running the statement as IO. This allows Rel8 to be used with transaction/connection-managing monads like [`hasql-transaction`](https://hackage.haskell.org/package/hasql-transaction). ([#94](https://github.com/circuithub/rel8/pull/94))++## Bug fixes++* Fixes a bug where cartesian products of queries using `catListTable`, `catNonEmptyTable`, `catList` and `catNonEmpty` would incorrectly be zipped instead. ([#61](https://github.com/circuithub/rel8/pull/61))++* Require Opaleye 0.7.3.0. This version has better support for lateral queries, which can improve query plans, especially in `optional`/`LEFT JOIN` ([#72](https://github.com/circuithub/rel8/pull/72))++* Rel8 now compiles with GHC 9. ([#97](https://github.com/circuithub/rel8/pull/97))++## Other changes++* `exists` is now implemented in terms of the SQL `EXISTS` keyword. ([#69](https://github.com/circuithub/rel8/pull/69))++* `alignBy` no longer requires `Table`s. ([#67](https://github.com/circuithub/rel8/pull/67))++ # 1.0.0.1 (2021-06-21) This release contains various fixes for documentation.
README.md view
@@ -10,4 +10,8 @@ * *Familiar*: writing Rel8 queries should feel like normal Haskell programming. +Rel8 was presented at ZuriHac 2021. If you want to have a brief overview of what Rel8 is, and a tour of the API - check out the video below:++[](http://www.youtube.com/watch?v=3uwrtjxiq6E)+ For more details, check out the [official documentation](https://rel8.readthedocs.io/en/latest/).
rel8.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.0 name: rel8-version: 1.0.0.1+version: 1.1.0.0 synopsis: Hey! Hey! Can u rel8? license: BSD3 license-file: LICENSE@@ -21,23 +21,27 @@ build-depends: aeson , base ^>= 4.14 || ^>=4.15+ , bifunctors , bytestring , case-insensitive+ , comonad , contravariant , hasql ^>= 1.4.5.1- , opaleye ^>= 0.7.1.0+ , opaleye ^>= 0.7.3.0+ , pretty , profunctors+ , product-profunctors , scientific , semialign , semigroupoids , text , these , time- , transformers , uuid default-language: Haskell2010 ghc-options:+ -Werror=missing-methods -Werror=incomplete-patterns -Werror=missing-fields -Weverything -Wno-unsafe -Wno-safe -Wno-missing-safe-haskell-mode -Wno-missing-import-lists -Wno-prepositive-qualified-module -Wno-monomorphism-restriction@@ -49,6 +53,7 @@ Rel8.Expr.Num Rel8.Expr.Text Rel8.Expr.Time+ Rel8.Tabulate other-modules: Rel8.Aggregate@@ -66,6 +71,7 @@ Rel8.Expr.Aggregate Rel8.Expr.Array Rel8.Expr.Bool+ Rel8.Expr.Default Rel8.Expr.Eq Rel8.Expr.Function Rel8.Expr.Null@@ -79,16 +85,13 @@ Rel8.Kind.Algebra Rel8.Kind.Context- Rel8.Kind.Labels Rel8.Generic.Construction Rel8.Generic.Construction.ADT Rel8.Generic.Construction.Record Rel8.Generic.Map Rel8.Generic.Record- Rel8.Generic.Reify Rel8.Generic.Rel8able- Rel8.Generic.Rel8able.Test Rel8.Generic.Table Rel8.Generic.Table.ADT Rel8.Generic.Table.Record@@ -103,6 +106,7 @@ Rel8.Query.Evaluate Rel8.Query.Exists Rel8.Query.Filter+ Rel8.Query.Indexed Rel8.Query.Limit Rel8.Query.List Rel8.Query.Maybe@@ -114,10 +118,9 @@ Rel8.Query.These Rel8.Query.Values - Rel8.Schema.Context- Rel8.Schema.Context.Label Rel8.Schema.Context.Nullify Rel8.Schema.Dict+ Rel8.Schema.Field Rel8.Schema.HTable Rel8.Schema.HTable.Either Rel8.Schema.HTable.Identity@@ -133,24 +136,28 @@ Rel8.Schema.Kind Rel8.Schema.Name Rel8.Schema.Null- Rel8.Schema.Reify Rel8.Schema.Result Rel8.Schema.Spec- Rel8.Schema.Spec.ConstrainDBType Rel8.Schema.Table Rel8.Statement.Delete Rel8.Statement.Insert+ Rel8.Statement.OnConflict Rel8.Statement.Returning Rel8.Statement.Select+ Rel8.Statement.Set+ Rel8.Statement.SQL Rel8.Statement.Update+ Rel8.Statement.Using Rel8.Statement.View+ Rel8.Statement.Where Rel8.Table Rel8.Table.ADT Rel8.Table.Aggregate Rel8.Table.Alternative Rel8.Table.Bool+ Rel8.Table.Cols Rel8.Table.Either Rel8.Table.Eq Rel8.Table.HKD@@ -158,16 +165,16 @@ Rel8.Table.Maybe Rel8.Table.Name Rel8.Table.NonEmpty+ Rel8.Table.Nullify Rel8.Table.Opaleye Rel8.Table.Ord Rel8.Table.Order- Rel8.Table.Recontextualize+ Rel8.Table.Projection Rel8.Table.Rel8able Rel8.Table.Serialize- Rel8.Table.Tag Rel8.Table.These+ Rel8.Table.Transpose Rel8.Table.Undefined- Rel8.Table.Unreify Rel8.Type Rel8.Type.Array@@ -194,9 +201,9 @@ , case-insensitive , containers , hasql+ , hasql-transaction , hedgehog ^>=1.0.2- , lifted-base ^>=0.2.3.12- , monad-control ^>=1.0.2.3+ , mmorph , rel8 , scientific , tasty@@ -204,7 +211,11 @@ , text , time , tmp-postgres ^>=1.34.1.0+ , transformers , uuid++ other-modules:+ Rel8.Generic.Rel8able.Test main-is: Main.hs hs-source-dirs: tests
src/Rel8.hs view
@@ -43,7 +43,7 @@ , Table(..) , HTable- , Recontextualize+ , Transposes , AltTable((<|>:)) , AlternativeTable( emptyTable ) , EqTable, (==:), (/=:)@@ -59,6 +59,7 @@ , optional , catMaybeTable , traverseMaybeTable+ , aggregateMaybeTable , nameMaybeTable -- ** @EitherTable@@@ -68,6 +69,7 @@ , keepLeftTable , keepRightTable , bitraverseEitherTable+ , aggregateEitherTable , nameEitherTable -- ** @TheseTable@@@ -83,6 +85,7 @@ , keepThatTable, loseThatTable , keepThoseTable, loseThoseTable , bitraverseTheseTable+ , aggregateTheseTable , nameTheseTable -- ** @ListTable@@@ -170,6 +173,13 @@ , Query , showQuery + -- ** Projection+ , Projection+ , Projectable( project )+ , Biprojectable( biproject )+ , Projecting+ , Field+ -- ** Selecting rows , Selects , each@@ -178,8 +188,8 @@ -- ** Filtering , filter , where_- , whereExists- , whereNotExists+ , present+ , absent , distinct , distinctOn , distinctOnBy@@ -234,10 +244,15 @@ , nullsFirst , nullsLast + -- ** Window functions+ , indexed++ -- ** Bindings+ , rebind+ -- * IO , Serializable- , ToExprs(..)- , FromExprs+ , ToExprs , Result -- * Running statements@@ -249,15 +264,20 @@ -- ** @INSERT@ , Insert(..) , OnConflict(..)+ , Upsert(..) , insert+ , unsafeDefault+ , showInsert -- ** @DELETE@ , Delete(..) , delete+ , showDelete -- ** @UPDATE@- , update , Update(..)+ , update+ , showUpdate -- ** @.. RETURNING@ , Returning(..)@@ -267,14 +287,7 @@ -- ** Sequences , nextval-- , Evaluate- , eval , evaluate-- -- * Implementation details- , Labelable- , HKDT(..) ) where -- base@@ -293,6 +306,7 @@ import Rel8.Expr import Rel8.Expr.Aggregate import Rel8.Expr.Bool+import Rel8.Expr.Default import Rel8.Expr.Eq import Rel8.Expr.Function import Rel8.Expr.Null@@ -311,6 +325,7 @@ import Rel8.Query.Evaluate import Rel8.Query.Exists import Rel8.Query.Filter+import Rel8.Query.Indexed import Rel8.Query.Limit import Rel8.Query.List import Rel8.Query.Maybe@@ -320,7 +335,7 @@ import Rel8.Query.Set import Rel8.Query.These import Rel8.Query.Values-import Rel8.Schema.Context.Label+import Rel8.Schema.Field import Rel8.Schema.HTable import Rel8.Schema.Name import Rel8.Schema.Null hiding ( nullable )@@ -328,8 +343,10 @@ import Rel8.Schema.Table import Rel8.Statement.Delete import Rel8.Statement.Insert+import Rel8.Statement.OnConflict import Rel8.Statement.Returning import Rel8.Statement.Select+import Rel8.Statement.SQL import Rel8.Statement.Update import Rel8.Statement.View import Rel8.Table@@ -346,10 +363,11 @@ import Rel8.Table.NonEmpty import Rel8.Table.Ord import Rel8.Table.Order-import Rel8.Table.Recontextualize+import Rel8.Table.Projection import Rel8.Table.Rel8able () import Rel8.Table.Serialize import Rel8.Table.These+import Rel8.Table.Transpose import Rel8.Type import Rel8.Type.Composite import Rel8.Type.Eq
src/Rel8/Aggregate.hs view
@@ -1,27 +1,23 @@ {-# language DataKinds #-}-{-# language DerivingVia #-} {-# language FlexibleContexts #-} {-# language FlexibleInstances #-}-{-# language GADTs #-} {-# language MultiParamTypeClasses #-} {-# language NamedFieldPuns #-}-{-# language PolyKinds #-}+{-# language RankNTypes #-} {-# language StandaloneKindSignatures #-} {-# language TypeFamilies #-} {-# language UndecidableInstances #-}-{-# language UndecidableSuperClasses #-} module Rel8.Aggregate ( Aggregate(..), foldInputs, mapInputs , Aggregator(..), unsafeMakeAggregate , Aggregates- , Col( A, unA ) ) where -- base import Data.Functor.Const ( Const( Const ), getConst )-import Data.Functor.Identity ( Identity )+import Data.Functor.Identity ( Identity( Identity ) ) import Data.Kind ( Constraint, Type ) import Prelude @@ -32,19 +28,15 @@ -- rel8 import Rel8.Expr ( Expr )-import Rel8.Schema.Context ( Interpretation(..) )-import Rel8.Schema.Context.Label ( Labelable(..) )-import Rel8.Schema.HTable.Identity ( HIdentity(..), HType )-import Rel8.Schema.Name ( Name )+import Rel8.Schema.HTable.Identity ( HIdentity(..) )+import qualified Rel8.Schema.Kind as K import Rel8.Schema.Null ( Sql )-import Rel8.Schema.Reify ( notReify )-import Rel8.Schema.Result ( Result )-import Rel8.Schema.Spec ( Spec( Spec ) ) import Rel8.Table ( Table, Columns, Context, fromColumns, toColumns- , reify, unreify+ , FromExprs, fromResult, toResult+ , Transpose )-import Rel8.Table.Recontextualize ( Recontextualize )+import Rel8.Table.Transpose ( Transposes ) import Rel8.Type ( DBType ) @@ -53,70 +45,30 @@ -- @Aggregate@ is almost an 'Applicative' functor - but there is no 'pure' -- operation. This means 'Aggregate' is an instance of 'Apply', and you can -- combine @Aggregate@s using the @<.>@ combinator.-type Aggregate :: k -> Type-data Aggregate a where- Aggregate :: !(Opaleye.Aggregator () (Expr a)) -> Aggregate a---instance Interpretation Aggregate where- data Col Aggregate _spec where- A :: ()- => { unA :: !(Aggregate a) }- -> Col Aggregate ('Spec labels a)+type Aggregate :: K.Context+newtype Aggregate a = Aggregate (Opaleye.Aggregator () (Expr a)) instance Sql DBType a => Table Aggregate (Aggregate a) where- type Columns (Aggregate a) = HType a+ type Columns (Aggregate a) = HIdentity a type Context (Aggregate a) = Aggregate-- toColumns = HIdentity . A- fromColumns (HIdentity (A a)) = a-- reify = notReify- unreify = notReify---instance Sql DBType a =>- Recontextualize Aggregate Aggregate (Aggregate a) (Aggregate a)---instance Sql DBType a =>- Recontextualize Aggregate Expr (Aggregate a) (Expr a)---instance Sql DBType a =>- Recontextualize Aggregate Result (Aggregate a) (Identity a)---instance Sql DBType a =>- Recontextualize Aggregate Name (Aggregate a) (Name a)---instance Sql DBType a =>- Recontextualize Expr Aggregate (Expr a) (Aggregate a)---instance Sql DBType a =>- Recontextualize Result Aggregate (Identity a) (Aggregate a)---instance Sql DBType a =>- Recontextualize Name Aggregate (Name a) (Aggregate a)-+ type FromExprs (Aggregate a) = a+ type Transpose to (Aggregate a) = to a -instance Labelable Aggregate where- labeler (A aggregate) = A aggregate- unlabeler (A aggregate) = A aggregate+ toColumns = HIdentity+ fromColumns (HIdentity a) = a+ toResult = HIdentity . Identity+ fromResult (HIdentity (Identity a)) = a -- | @Aggregates a b@ means that the columns in @a@ are all 'Aggregate' 'Expr's -- for the columns in @b@. type Aggregates :: Type -> Type -> Constraint-class Recontextualize Aggregate Expr aggregates exprs => Aggregates aggregates exprs-instance Recontextualize Aggregate Expr aggregates exprs => Aggregates aggregates exprs+class Transposes Aggregate Expr aggregates exprs => Aggregates aggregates exprs+instance Transposes Aggregate Expr aggregates exprs => Aggregates aggregates exprs -foldInputs :: Monoid b+foldInputs :: forall (a :: Type) (b :: Type). Monoid b => (Maybe Aggregator -> Opaleye.PrimExpr -> b) -> Aggregate a -> b foldInputs f (Aggregate (Opaleye.Aggregator (Opaleye.PackMap agg))) = getConst $ flip agg () $ \(aggregator, a) ->@@ -126,7 +78,7 @@ Aggregator {operation, ordering, distinction} -mapInputs :: ()+mapInputs :: forall (a :: Type). () => (Opaleye.PrimExpr -> Opaleye.PrimExpr) -> Aggregate a -> Aggregate a mapInputs transform (Aggregate (Opaleye.Aggregator (Opaleye.PackMap agg))) = Aggregate $ Opaleye.Aggregator $ Opaleye.PackMap $ agg . \f input ->@@ -141,7 +93,7 @@ } -unsafeMakeAggregate :: ()+unsafeMakeAggregate :: forall (input :: Type) (output :: Type). () => (Expr input -> Opaleye.PrimExpr) -> (Opaleye.PrimExpr -> Expr output) -> Maybe Aggregator
+ src/Rel8/Aggregate.hs-boot view
@@ -0,0 +1,11 @@+{-# language PolyKinds #-}+{-# language RoleAnnotations #-}+{-# language StandaloneKindSignatures #-}++module Rel8.Aggregate where++import Data.Kind++type Aggregate :: k -> Type+type role Aggregate nominal+data Aggregate a
src/Rel8/Column.hs view
@@ -1,38 +1,21 @@ {-# language DataKinds #-}-{-# language FlexibleContexts #-}-{-# language LambdaCase #-}-{-# language MultiParamTypeClasses #-} {-# language StandaloneKindSignatures #-} {-# language TypeFamilies #-} module Rel8.Column- ( Column, AColumn(..)+ ( Column , TColumn ) where -- base import Data.Kind ( Type )-import Prelude+import Prelude () -- rel8-import Rel8.Aggregate ( Aggregate, Col( A ) )-import Rel8.Expr ( Expr, Col( E ) ) import Rel8.FCF ( Eval, Exp )-import Rel8.Kind.Context ( SContext(..), Reifiable( contextSing ) )-import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Name ( Name(..), Col( N ) )-import Rel8.Schema.Null ( Sql )-import Rel8.Schema.Reify ( Reify, Col(..) )-import Rel8.Schema.Result ( Col( R ), Result )-import Rel8.Schema.Spec ( Spec( Spec ) )-import Rel8.Table- ( Table, Columns, Context, fromColumns, toColumns- , Unreify, reify, unreify- )-import Rel8.Table.Recontextualize ( Recontextualize )-import Rel8.Type ( DBType )+import Rel8.Schema.Result ( Result ) -- | This type family is used to specify columns in 'Rel8able's. In @Column f@@ -40,63 +23,8 @@ -- 'Rel8able' definitions), and @a@ is the type of the column. type Column :: K.Context -> Type -> Type type family Column context a where- Column (Reify context) a = AColumn context a- Column Aggregate a = Aggregate a- Column Expr a = Expr a- Column Name a = Name a- Column Result a = a---type AColumn :: K.Context -> Type -> Type-newtype AColumn context a = AColumn (Column context a)---instance (Reifiable context, Sql DBType a) =>- Table (Reify context) (AColumn context a)- where- type Context (AColumn context a) = Reify context- type Columns (AColumn context a) = HIdentity ('Spec '[] a)- type Unreify (AColumn context a) = Column context a-- fromColumns (HIdentity (Reify a)) = sfromColumn contextSing a- toColumns = HIdentity . Reify . stoColumn contextSing- reify _ = AColumn- unreify _ (AColumn a) = a---instance- ( Reifiable context, Reifiable context'- , Sql DBType a- ) =>- Recontextualize- (Reify context)- (Reify context')- (AColumn context a)- (AColumn context' a)---sfromColumn :: ()- => SContext context- -> Col context ('Spec labels a)- -> AColumn context a-sfromColumn = \case- SAggregate -> \(A a) -> AColumn a- SExpr -> \(E a) -> AColumn a- SName -> \(N a) -> AColumn a- SResult -> \(R a) -> AColumn a- SReify context -> \(Reify a) -> AColumn (sfromColumn context a)---stoColumn :: ()- => SContext context- -> AColumn context a- -> Col context ('Spec labels a)-stoColumn = \case- SAggregate -> \(AColumn a) -> A a- SExpr -> \(AColumn a) -> E a- SName -> \(AColumn a) -> N a- SResult -> \(AColumn a) -> R a- SReify context -> \(AColumn a) -> Reify (stoColumn context a)+ Column Result a = a+ Column context a = context a data TColumn :: K.Context -> Type -> Exp Type
src/Rel8/Column/ADT.hs view
@@ -1,88 +1,23 @@ {-# language DataKinds #-}-{-# language LambdaCase #-}-{-# language MultiParamTypeClasses #-} {-# language StandaloneKindSignatures #-} {-# language TypeFamilies #-}-{-# language UndecidableInstances #-} module Rel8.Column.ADT- ( HADT, AHADT(..)+ ( HADT ) where -- base import Data.Kind ( Type )-import Prelude+import Prelude () -- rel8-import Rel8.Generic.Rel8able ( GColumns )-import Rel8.Kind.Context ( SContext(..), Reifiable, contextSing )-import Rel8.Schema.Context ( Col ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Reify ( Reify, hreify, hunreify ) import Rel8.Schema.Result ( Result )-import Rel8.Table- ( Table, Columns, Context, fromColumns, toColumns- , Unreify, reify, unreify- )-import Rel8.Table.ADT ( ADT( ADT ), ADTable, fromADT, toADT )-import Rel8.Table.Recontextualize ( Recontextualize )+import Rel8.Table.ADT ( ADT ) type HADT :: K.Context -> K.Rel8able -> Type type family HADT context t where- HADT (Reify context) t = AHADT context t HADT Result t = t Result HADT context t = ADT t context---type AHADT :: K.Context -> K.Rel8able -> Type-newtype AHADT context t = AHADT (HADT context t)---instance (ADTable t, Reifiable context) =>- Table (Reify context) (AHADT context t)- where- type Context (AHADT context t) = Reify context- type Columns (AHADT context t) = GColumns (ADT t)- type Unreify (AHADT context t) = HADT context t-- fromColumns = sfromColumnsADT contextSing- toColumns = stoColumnsADT contextSing- reify _ = AHADT- unreify _ (AHADT a) = a---instance- ( Reifiable context, Reifiable context'- , ADTable t, t ~ t'- )- => Recontextualize- (Reify context)- (Reify context')- (AHADT context t)- (AHADT context' t')---sfromColumnsADT :: ADTable t- => SContext context- -> GColumns (ADT t) (Col (Reify context))- -> AHADT context t-sfromColumnsADT = \case- SAggregate -> AHADT . ADT . hunreify- SExpr -> AHADT . ADT . hunreify- SName -> AHADT . ADT . hunreify- SResult -> AHADT . fromADT . ADT . hunreify- SReify context -> AHADT . sfromColumnsADT context . hunreify---stoColumnsADT :: ADTable t- => SContext context- -> AHADT context t- -> GColumns (ADT t) (Col (Reify context))-stoColumnsADT = \case- SAggregate -> hreify . (\(AHADT (ADT a)) -> a)- SExpr -> hreify . (\(AHADT (ADT a)) -> a)- SName -> hreify . (\(AHADT (ADT a)) -> a)- SResult -> hreify . (\(ADT a) -> a) . toADT . (\(AHADT a) -> a)- SReify context -> hreify . stoColumnsADT context . (\(AHADT a) -> a)
src/Rel8/Column/Either.hs view
@@ -1,162 +1,26 @@ {-# language DataKinds #-}-{-# language FlexibleContexts #-}-{-# language LambdaCase #-}-{-# language MultiParamTypeClasses #-} {-# language StandaloneKindSignatures #-}-{-# language TypeFamilies #-}-{-# language UndecidableInstances #-}+{-# language TypeFamilyDependencies #-} module Rel8.Column.Either- ( HEither, AHEither(..)+ ( HEither ) where -- base-import Control.Applicative ( liftA2 )-import Data.Bifunctor ( Bifunctor, bimap ) import Data.Kind ( Type ) import Prelude -- rel8-import Rel8.Aggregate ( Aggregate )-import Rel8.Expr ( Expr )-import Rel8.Kind.Context ( SContext(..), Reifiable( contextSing ) )-import Rel8.Schema.Context ( Col )-import Rel8.Schema.HTable.Either ( HEitherTable ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Name ( Name(..) )-import Rel8.Schema.Reify ( Reify, hreify, hunreify ) import Rel8.Schema.Result ( Result )-import Rel8.Table- ( Table, Columns, Context, fromColumns, toColumns- , Unreify, reify, unreify- ) import Rel8.Table.Either ( EitherTable )-import Rel8.Table.Recontextualize ( Recontextualize ) -- | Nest an 'Either' value within a 'Rel8able'. @HEither f a b@ will produce a -- 'EitherTable' @a b@ in the 'Expr' context, and a 'Either' @a b@ in the -- 'Result' context. type HEither :: K.Context -> Type -> Type -> Type-type family HEither context where- HEither (Reify context) = AHEither context- HEither Aggregate = EitherTable- HEither Expr = EitherTable- HEither Name = EitherTable+type family HEither context = either | either -> context where HEither Result = Either---type AHEither :: K.Context -> Type -> Type -> Type-newtype AHEither context a b = AHEither (HEither context a b)---instance Reifiable context => Bifunctor (AHEither context) where- bimap = sbimapEither contextSing---instance Reifiable context => Functor (AHEither context a) where- fmap = bimap id---instance (Reifiable context, Table (Reify context) a, Table (Reify context) b)- => Table (Reify context) (AHEither context a b)- where- type Context (AHEither context a b) = Reify context- type Columns (AHEither context a b) = HEitherTable (Columns a) (Columns b)- type Unreify (AHEither context a b) = HEither context (Unreify a) (Unreify b)-- fromColumns = sfromColumnsEither contextSing- toColumns = stoColumnsEither contextSing- reify proof = liftA2 bimap reify reify proof . AHEither- unreify proof = (\(AHEither a) -> a) . liftA2 bimap unreify unreify proof---instance- ( Reifiable context, Reifiable context'- , Recontextualize (Reify context) (Reify context') a a'- , Recontextualize (Reify context) (Reify context') b b'- ) =>- Recontextualize- (Reify context)- (Reify context')- (AHEither context a b)- (AHEither context' a' b')---sbimapEither :: ()- => SContext context- -> (a -> c)- -> (b -> d)- -> AHEither context a b- -> AHEither context c d-sbimapEither = \case- SAggregate -> \f g (AHEither a) -> AHEither (bimap f g a)- SExpr -> \f g (AHEither a) -> AHEither (bimap f g a)- SResult -> \f g (AHEither a) -> AHEither (bimap f g a)- SName -> \f g (AHEither a) -> AHEither (bimap f g a)- SReify context -> \f g (AHEither a) -> AHEither (sbimapEither context f g a)---sfromColumnsEither :: (Table (Reify context) a, Table (Reify context) b)- => SContext context- -> HEitherTable (Columns a) (Columns b) (Col (Reify context))- -> AHEither context a b-sfromColumnsEither = \case- SAggregate ->- AHEither .- bimap (fromColumns . hreify) (fromColumns . hreify) .- fromColumns .- hunreify- SExpr ->- AHEither .- bimap (fromColumns . hreify) (fromColumns . hreify) .- fromColumns .- hunreify- SResult ->- AHEither .- bimap (fromColumns . hreify) (fromColumns . hreify) .- fromColumns .- hunreify- SName ->- AHEither .- bimap (fromColumns . hreify) (fromColumns . hreify) .- fromColumns .- hunreify- SReify context ->- AHEither .- sbimapEither context (fromColumns . hreify) (fromColumns . hreify) .- sfromColumnsEither context .- hunreify---stoColumnsEither :: (Table (Reify context) a, Table (Reify context) b)- => SContext context- -> AHEither context a b- -> HEitherTable (Columns a) (Columns b) (Col (Reify context))-stoColumnsEither = \case- SAggregate ->- hreify .- toColumns .- bimap (hunreify . toColumns) (hunreify . toColumns) .- (\(AHEither a) -> a)- SExpr ->- hreify .- toColumns .- bimap (hunreify . toColumns) (hunreify . toColumns) .- (\(AHEither a) -> a)- SResult ->- hreify .- toColumns .- bimap (hunreify . toColumns) (hunreify . toColumns) .- (\(AHEither a) -> a)- SName ->- hreify .- toColumns .- bimap (hunreify . toColumns) (hunreify . toColumns) .- (\(AHEither a) -> a)- SReify context ->- hreify .- stoColumnsEither context .- sbimapEither context (hunreify . toColumns) (hunreify . toColumns) .- (\(AHEither a) -> a)+ HEither context = EitherTable context
src/Rel8/Column/Lift.hs view
@@ -1,87 +1,23 @@ {-# language DataKinds #-}-{-# language LambdaCase #-}-{-# language MultiParamTypeClasses #-} {-# language StandaloneKindSignatures #-} {-# language TypeFamilies #-} module Rel8.Column.Lift- ( Lift, ALift(..)+ ( Lift ) where -- base import Data.Kind ( Type )-import Prelude+import Prelude () -- rel8-import Rel8.Generic.Rel8able ( GColumns )-import Rel8.Kind.Context ( Reifiable(..), SContext(..) )-import Rel8.Schema.Context ( Col ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Reify ( Reify, hreify, hunreify ) import Rel8.Schema.Result ( Result )-import Rel8.Table- ( Table, Columns, Context, fromColumns, toColumns- , Unreify, reify, unreify- )-import Rel8.Table.Rel8able ()-import Rel8.Table.HKD ( HKD( HKD ), HKDable, fromHKD, toHKD )-import Rel8.Table.Recontextualize ( Recontextualize )+import Rel8.Table.HKD ( HKD ) type Lift :: K.Context -> Type -> Type type family Lift context a where- Lift (Reify context) a = ALift context a Lift Result a = a Lift context a = HKD a context---type ALift :: K.Context -> Type -> Type-newtype ALift context a = ALift- { unALift :: Lift context a- }---instance (Reifiable context, HKDable a) =>- Table (Reify context) (ALift context a)- where- type Context (ALift context a) = Reify context- type Columns (ALift context a) = GColumns (HKD a)- type Unreify (ALift context a) = Lift context a-- fromColumns = sfromColumnsLift contextSing- toColumns = stoColumnsLift contextSing- reify _ = ALift- unreify _ (ALift a) = a---instance (Reifiable context, Reifiable context', HKDable a) =>- Recontextualize- (Reify context)- (Reify context')- (ALift context a)- (ALift context' a)---sfromColumnsLift :: HKDable a- => SContext context- -> GColumns (HKD a) (Col (Reify context))- -> ALift context a-sfromColumnsLift = \case- SAggregate -> ALift . fromColumns . hunreify- SExpr -> ALift . fromColumns . hunreify- SName -> ALift . fromColumns . hunreify- SResult -> ALift . fromHKD . HKD . hunreify- SReify context -> ALift . sfromColumnsLift context . hunreify---stoColumnsLift :: HKDable a- => SContext context- -> ALift context a- -> GColumns (HKD a) (Col (Reify context))-stoColumnsLift = \case- SAggregate -> hreify . toColumns . unALift- SExpr -> hreify . toColumns . unALift- SName -> hreify . toColumns . unALift- SResult -> hreify . (\(HKD a) -> a) . toHKD . unALift- SReify context -> hreify . stoColumnsLift context . unALift
src/Rel8/Column/List.hs view
@@ -1,145 +1,25 @@ {-# language DataKinds #-}-{-# language FlexibleContexts #-}-{-# language LambdaCase #-}-{-# language MultiParamTypeClasses #-} {-# language StandaloneKindSignatures #-}-{-# language TypeFamilies #-}-{-# language UndecidableInstances #-}+{-# language TypeFamilyDependencies #-} module Rel8.Column.List- ( HList, AHList(..)+ ( HList ) where -- base import Data.Kind ( Type )-import Data.Type.Equality ( (:~:)( Refl ) )-import Prelude+import Prelude () -- rel8-import Rel8.Aggregate ( Aggregate )-import Rel8.Expr ( Expr )-import Rel8.Kind.Context ( SContext(..), Reifiable( contextSing ) )-import Rel8.Schema.Context ( Col )-import Rel8.Schema.HTable.List ( HListTable ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Name ( Name )-import Rel8.Schema.Reify ( Reify, hreify, hunreify ) import Rel8.Schema.Result ( Result )-import Rel8.Table- ( Table, Columns, Congruent, Context, fromColumns, toColumns- , Unreify, reify, unreify- )-import Rel8.Table.List ( ListTable( ListTable ) )-import Rel8.Table.Recontextualize ( Recontextualize )-import Rel8.Table.Unreify ( Unreifiability(..), Unreifiable, unreifiability )+import Rel8.Table.List ( ListTable ) -- | Nest a list within a 'Rel8able'. @HList f a@ will produce a 'ListTable' -- @a@ in the 'Expr' context, and a @[a]@ in the 'Result' context. type HList :: K.Context -> Type -> Type-type family HList context where- HList (Reify context) = AHList context- HList Aggregate = ListTable- HList Expr = ListTable- HList Name = ListTable+type family HList context = list | list -> context where HList Result = []---type AHList :: K.Context -> Type -> Type-newtype AHList context a = AHList (HList context a)---instance (Reifiable context, Unreifiable a, Table (Reify context) a) =>- Table (Reify context) (AHList context a)- where- type Context (AHList context a) = Reify context- type Columns (AHList context a) = HListTable (Columns a)- type Unreify (AHList context a) = HList context (Unreify a)-- fromColumns = sfromColumnsList contextSing- toColumns = stoColumnsList contextSing-- reify _ = sreifyList (unreifiability contextSing)- unreify _ = sunreifyList (unreifiability contextSing)---instance- ( Reifiable context, Reifiable context'- , Unreifiable a, Unreifiable a'- , Recontextualize (Reify context) (Reify context') a a'- )- => Recontextualize- (Reify context)- (Reify context')- (AHList context a)- (AHList context' a')---smapList :: Congruent a b- => SContext context- -> (a -> b)- -> (HListTable (Columns a) (Col (Context a)) -> HListTable (Columns b) (Col (Context b)))- -> AHList context a- -> AHList context b-smapList = \case- SAggregate -> \_ f (AHList (ListTable a)) -> AHList (ListTable (f a))- SExpr -> \_ f (AHList (ListTable a)) -> AHList (ListTable (f a))- SResult -> \f _ (AHList as) -> AHList (fmap f as)- SName -> \_ f (AHList (ListTable a)) -> AHList (ListTable (f a))- SReify context -> \f g (AHList as) -> AHList (smapList context f g as)---sfromColumnsList :: Table (Reify context) a- => SContext context- -> HListTable (Columns a) (Col (Reify context))- -> AHList context a-sfromColumnsList = \case- SAggregate -> AHList . ListTable- SExpr -> AHList . ListTable- SResult -> AHList . fmap (fromColumns . hreify) . fromColumns . hunreify- SName -> AHList . ListTable- SReify context ->- AHList .- smapList context (fromColumns . hreify) hreify .- sfromColumnsList context .- hunreify---stoColumnsList :: Table (Reify context) a- => SContext context- -> AHList context a- -> HListTable (Columns a) (Col (Reify context))-stoColumnsList = \case- SAggregate -> \(AHList (ListTable a)) -> a- SExpr -> \(AHList (ListTable a)) -> a- SResult ->- hreify . toColumns . fmap (hunreify . toColumns) . (\(AHList a) -> a)- SName -> \(AHList (ListTable a)) -> a- SReify context ->- hreify .- stoColumnsList context .- smapList context (hunreify . toColumns) hunreify .- (\(AHList a) -> a)---sreifyList :: Table (Reify context) a- => Unreifiability context a- -> HList context (Unreify a)- -> AHList context a-sreifyList = \case- UResult -> AHList . fmap (reify Refl)- Unreifiability context ->- smapList context (reify Refl) hreify .- AHList---sunreifyList :: Table (Reify context) a- => Unreifiability context a- -> AHList context a- -> HList context (Unreify a)-sunreifyList = \case- UResult -> fmap (unreify Refl) . (\(AHList a) -> a)- Unreifiability context ->- (\(AHList a) -> a) .- smapList context (unreify Refl) hunreify+ HList context = ListTable context
src/Rel8/Column/Maybe.hs view
@@ -1,14 +1,9 @@ {-# language DataKinds #-}-{-# language FlexibleContexts #-}-{-# language GADTs #-}-{-# language LambdaCase #-}-{-# language MultiParamTypeClasses #-} {-# language StandaloneKindSignatures #-}-{-# language TypeFamilies #-}-{-# language UndecidableInstances #-}+{-# language TypeFamilyDependencies #-} module Rel8.Column.Maybe- ( HMaybe, AHMaybe(..)+ ( HMaybe ) where @@ -17,111 +12,15 @@ import Prelude -- rel8-import Rel8.Aggregate ( Aggregate )-import Rel8.Expr ( Expr )-import Rel8.Kind.Context ( SContext(..), Reifiable( contextSing ) )-import Rel8.Schema.Context ( Col )-import Rel8.Schema.HTable.Maybe ( HMaybeTable ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Name ( Name )-import Rel8.Schema.Reify ( Reify, hreify, hunreify ) import Rel8.Schema.Result ( Result )-import Rel8.Table- ( Table, Columns, Context, fromColumns, toColumns- , Unreify, reify, unreify- ) import Rel8.Table.Maybe ( MaybeTable )-import Rel8.Table.Recontextualize ( Recontextualize ) -- | Nest a 'Maybe' value within a 'Rel8able'. @HMaybe f a@ will produce a -- 'MaybeTable' @a@ in the 'Expr' context, and a 'Maybe' @a@ in the 'Result' -- context. type HMaybe :: K.Context -> Type -> Type-type family HMaybe context where- HMaybe (Reify context) = AHMaybe context- HMaybe Aggregate = MaybeTable- HMaybe Expr = MaybeTable- HMaybe Name = MaybeTable+type family HMaybe context = maybe | maybe -> context where HMaybe Result = Maybe---type AHMaybe :: K.Context -> Type -> Type-newtype AHMaybe context a = AHMaybe (HMaybe context a)---instance Reifiable context => Functor (AHMaybe context) where- fmap = smapMaybe contextSing---instance (Reifiable context, Table (Reify context) a) =>- Table (Reify context) (AHMaybe context a)- where- type Context (AHMaybe context a) = Reify context- type Columns (AHMaybe context a) = HMaybeTable (Columns a)- type Unreify (AHMaybe context a) = HMaybe context (Unreify a)-- fromColumns = sfromColumnsMaybe contextSing- toColumns = stoColumnsMaybe contextSing- reify proof = fmap fmap reify proof . AHMaybe- unreify proof = (\(AHMaybe a) -> a) . fmap fmap unreify proof---instance- ( Reifiable context, Reifiable context'- , Recontextualize (Reify context) (Reify context') a a'- ) =>- Recontextualize- (Reify context)- (Reify context')- (AHMaybe context a)- (AHMaybe context' a')---smapMaybe :: ()- => SContext context- -> (a -> b)- -> AHMaybe context a- -> AHMaybe context b-smapMaybe = \case- SAggregate -> \f (AHMaybe a) -> AHMaybe (fmap f a)- SExpr -> \f (AHMaybe a) -> AHMaybe (fmap f a)- SResult -> \f (AHMaybe a) -> AHMaybe (fmap f a)- SName -> \f (AHMaybe a) -> AHMaybe (fmap f a)- SReify context -> \f (AHMaybe a) -> AHMaybe (smapMaybe context f a)---sfromColumnsMaybe :: Table (Reify context) a- => SContext context- -> HMaybeTable (Columns a) (Col (Reify context))- -> AHMaybe context a-sfromColumnsMaybe = \case- SAggregate -> AHMaybe . fmap (fromColumns . hreify) . fromColumns . hunreify- SExpr -> AHMaybe . fmap (fromColumns . hreify) . fromColumns . hunreify- SResult -> AHMaybe . fmap (fromColumns . hreify) . fromColumns . hunreify- SName -> AHMaybe . fmap (fromColumns . hreify) . fromColumns . hunreify- SReify context ->- AHMaybe .- smapMaybe context (fromColumns . hreify) .- sfromColumnsMaybe context .- hunreify---stoColumnsMaybe :: Table (Reify context) a- => SContext context- -> AHMaybe context a- -> HMaybeTable (Columns a) (Col (Reify context))-stoColumnsMaybe = \case- SAggregate ->- hreify . toColumns . fmap (hunreify . toColumns) . (\(AHMaybe a) -> a)- SExpr ->- hreify . toColumns . fmap (hunreify . toColumns) . (\(AHMaybe a) -> a)- SResult ->- hreify . toColumns . fmap (hunreify . toColumns) . (\(AHMaybe a) -> a)- SName ->- hreify . toColumns . fmap (hunreify . toColumns) . (\(AHMaybe a) -> a)- SReify context ->- hreify .- stoColumnsMaybe context .- smapMaybe context (hunreify . toColumns) .- (\(AHMaybe a) -> a)+ HMaybe context = MaybeTable context
src/Rel8/Column/NonEmpty.hs view
@@ -1,148 +1,27 @@ {-# language DataKinds #-}-{-# language FlexibleContexts #-}-{-# language LambdaCase #-}-{-# language MultiParamTypeClasses #-} {-# language StandaloneKindSignatures #-}-{-# language TypeFamilies #-}-{-# language UndecidableInstances #-}+{-# language TypeFamilyDependencies #-} module Rel8.Column.NonEmpty- ( HNonEmpty, AHNonEmpty(..)+ ( HNonEmpty ) where -- base import Data.Kind ( Type ) import Data.List.NonEmpty ( NonEmpty )-import Data.Type.Equality ( (:~:)( Refl ) )-import Prelude+import Prelude () -- rel8-import Rel8.Aggregate ( Aggregate )-import Rel8.Expr ( Expr )-import Rel8.Kind.Context ( SContext(..), Reifiable( contextSing ) )-import Rel8.Schema.Context ( Col )-import Rel8.Schema.HTable.NonEmpty ( HNonEmptyTable ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Name ( Name )-import Rel8.Schema.Reify ( Reify, hreify, hunreify ) import Rel8.Schema.Result ( Result )-import Rel8.Table- ( Table, Columns, Congruent, Context, fromColumns, toColumns- , Unreify, reify, unreify- )-import Rel8.Table.NonEmpty ( NonEmptyTable( NonEmptyTable ) )-import Rel8.Table.Recontextualize ( Recontextualize )-import Rel8.Table.Unreify ( Unreifiability(..), Unreifiable, unreifiability )+import Rel8.Table.NonEmpty ( NonEmptyTable ) -- | Nest a 'NonEmpty' list within a 'Rel8able'. @HNonEmpty f a@ will produce a -- 'NonEmptyTable' @a@ in the 'Expr' context, and a 'NonEmpty' @a@ in the -- 'Result' context. type HNonEmpty :: K.Context -> Type -> Type-type family HNonEmpty context where- HNonEmpty (Reify context) = AHNonEmpty context- HNonEmpty Aggregate = NonEmptyTable- HNonEmpty Expr = NonEmptyTable- HNonEmpty Name = NonEmptyTable+type family HNonEmpty context = nonEmpty | nonEmpty -> context where HNonEmpty Result = NonEmpty---type AHNonEmpty :: K.Context -> Type -> Type-newtype AHNonEmpty context a = AHNonEmpty (HNonEmpty context a)---instance (Reifiable context, Unreifiable a, Table (Reify context) a) =>- Table (Reify context) (AHNonEmpty context a)- where- type Context (AHNonEmpty context a) = Reify context- type Columns (AHNonEmpty context a) = HNonEmptyTable (Columns a)- type Unreify (AHNonEmpty context a) = HNonEmpty context (Unreify a)-- fromColumns = sfromColumnsNonEmpty contextSing- toColumns = stoColumnsNonEmpty contextSing-- reify _ = sreifyNonEmpty (unreifiability contextSing)- unreify _ = sunreifyNonEmpty (unreifiability contextSing)---instance- ( Reifiable context, Reifiable context'- , Unreifiable a, Unreifiable a'- , Recontextualize (Reify context) (Reify context') a a'- )- => Recontextualize- (Reify context)- (Reify context')- (AHNonEmpty context a)- (AHNonEmpty context' a')---smapNonEmpty :: Congruent a b- => SContext context- -> (a -> b)- -> (HNonEmptyTable (Columns a) (Col (Context a)) -> HNonEmptyTable (Columns b) (Col (Context b)))- -> AHNonEmpty context a- -> AHNonEmpty context b-smapNonEmpty = \case- SAggregate -> \_ f (AHNonEmpty (NonEmptyTable a)) -> AHNonEmpty (NonEmptyTable (f a))- SExpr -> \_ f (AHNonEmpty (NonEmptyTable a)) -> AHNonEmpty (NonEmptyTable (f a))- SResult -> \f _ (AHNonEmpty as) -> AHNonEmpty (fmap f as)- SName -> \_ f (AHNonEmpty (NonEmptyTable a)) -> AHNonEmpty (NonEmptyTable (f a))- SReify context -> \f g (AHNonEmpty as) -> AHNonEmpty (smapNonEmpty context f g as)---sfromColumnsNonEmpty :: Table (Reify context) a- => SContext context- -> HNonEmptyTable (Columns a) (Col (Reify context))- -> AHNonEmpty context a-sfromColumnsNonEmpty = \case- SAggregate -> AHNonEmpty . NonEmptyTable- SExpr -> AHNonEmpty . NonEmptyTable- SResult ->- AHNonEmpty . fmap (fromColumns . hreify) . fromColumns . hunreify- SName -> AHNonEmpty . NonEmptyTable- SReify context ->- AHNonEmpty .- smapNonEmpty context (fromColumns . hreify) hreify .- sfromColumnsNonEmpty context .- hunreify---stoColumnsNonEmpty :: Table (Reify context) a- => SContext context- -> AHNonEmpty context a- -> HNonEmptyTable (Columns a) (Col (Reify context))-stoColumnsNonEmpty = \case- SAggregate -> \(AHNonEmpty (NonEmptyTable a)) -> a- SExpr -> \(AHNonEmpty (NonEmptyTable a)) -> a- SResult ->- hreify . toColumns . fmap (hunreify . toColumns) . (\(AHNonEmpty a) -> a)- SName -> \(AHNonEmpty (NonEmptyTable a)) -> a- SReify context ->- hreify .- stoColumnsNonEmpty context .- smapNonEmpty context (hunreify . toColumns) hunreify .- (\(AHNonEmpty a) -> a)---sreifyNonEmpty :: Table (Reify context) a- => Unreifiability context a- -> HNonEmpty context (Unreify a)- -> AHNonEmpty context a-sreifyNonEmpty = \case- UResult -> AHNonEmpty . fmap (reify Refl)- Unreifiability context ->- smapNonEmpty context (reify Refl) hreify .- AHNonEmpty---sunreifyNonEmpty :: Table (Reify context) a- => Unreifiability context a- -> AHNonEmpty context a- -> HNonEmpty context (Unreify a)-sunreifyNonEmpty = \case- UResult -> fmap (unreify Refl) . (\(AHNonEmpty a) -> a)- Unreifiability context ->- (\(AHNonEmpty a) -> a) .- smapNonEmpty context (unreify Refl) hunreify+ HNonEmpty context = NonEmptyTable context
src/Rel8/Column/These.hs view
@@ -1,38 +1,19 @@ {-# language DataKinds #-}-{-# language FlexibleContexts #-}-{-# language GADTs #-}-{-# language LambdaCase #-}-{-# language MultiParamTypeClasses #-} {-# language StandaloneKindSignatures #-}-{-# language TypeFamilies #-}-{-# language UndecidableInstances #-}+{-# language TypeFamilyDependencies #-} module Rel8.Column.These- ( HThese, AHThese(..)+ ( HThese ) where -- base-import Control.Applicative ( liftA2 )-import Data.Bifunctor ( Bifunctor, bimap ) import Data.Kind ( Type )-import Prelude+import Prelude () -- rel8-import Rel8.Aggregate ( Aggregate )-import Rel8.Expr ( Expr )-import Rel8.Kind.Context ( SContext(..), Reifiable( contextSing ) )-import Rel8.Schema.Context ( Col )-import Rel8.Schema.HTable.These ( HTheseTable ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Name ( Name )-import Rel8.Schema.Reify ( Reify, hreify, hunreify ) import Rel8.Schema.Result ( Result )-import Rel8.Table- ( Table, Columns, Context, fromColumns, toColumns- , Unreify, reify, unreify- )-import Rel8.Table.Recontextualize ( Recontextualize ) import Rel8.Table.These ( TheseTable ) -- these@@ -43,124 +24,6 @@ -- 'TheseTable' @a b@ in the 'Expr' context, and a 'These' @a b@ in the -- 'Result' context. type HThese :: K.Context -> Type -> Type -> Type-type family HThese context where- HThese (Reify context) = AHThese context- HThese Aggregate = TheseTable- HThese Expr = TheseTable- HThese Name = TheseTable+type family HThese context = these | these -> context where HThese Result = These---type AHThese :: K.Context -> Type -> Type -> Type-newtype AHThese context a b = AHThese (HThese context a b)---instance Reifiable context => Bifunctor (AHThese context) where- bimap = sbimapThese contextSing---instance Reifiable context => Functor (AHThese context a) where- fmap = bimap id---instance (Reifiable context, Table (Reify context) a, Table (Reify context) b)- => Table (Reify context) (AHThese context a b)- where- type Context (AHThese context a b) = Reify context- type Columns (AHThese context a b) = HTheseTable (Columns a) (Columns b)- type Unreify (AHThese context a b) = HThese context (Unreify a) (Unreify b)-- fromColumns = sfromColumnsThese contextSing- toColumns = stoColumnsThese contextSing- reify proof = liftA2 bimap reify reify proof . AHThese- unreify proof = (\(AHThese a) -> a) . liftA2 bimap unreify unreify proof---instance- ( Reifiable context, Reifiable context'- , Recontextualize (Reify context) (Reify context') a a'- , Recontextualize (Reify context) (Reify context') b b'- ) =>- Recontextualize- (Reify context)- (Reify context')- (AHThese context a b)- (AHThese context' a' b')---sbimapThese :: ()- => SContext context- -> (a -> c)- -> (b -> d)- -> AHThese context a b- -> AHThese context c d-sbimapThese = \case- SAggregate -> \f g (AHThese a) -> AHThese (bimap f g a)- SExpr -> \f g (AHThese a) -> AHThese (bimap f g a)- SResult -> \f g (AHThese a) -> AHThese (bimap f g a)- SName -> \f g (AHThese a) -> AHThese (bimap f g a)- SReify context -> \f g (AHThese a) -> AHThese (sbimapThese context f g a)---sfromColumnsThese :: (Table (Reify context) a, Table (Reify context) b)- => SContext context- -> HTheseTable (Columns a) (Columns b) (Col (Reify context))- -> AHThese context a b-sfromColumnsThese = \case- SAggregate ->- AHThese .- bimap (fromColumns . hreify) (fromColumns . hreify) .- fromColumns .- hunreify- SExpr ->- AHThese .- bimap (fromColumns . hreify) (fromColumns . hreify) .- fromColumns .- hunreify- SResult ->- AHThese .- bimap (fromColumns . hreify) (fromColumns . hreify) .- fromColumns .- hunreify- SName ->- AHThese .- bimap (fromColumns . hreify) (fromColumns . hreify) .- fromColumns .- hunreify- SReify context ->- AHThese .- sbimapThese context (fromColumns . hreify) (fromColumns . hreify) .- sfromColumnsThese context .- hunreify---stoColumnsThese :: (Table (Reify context) a, Table (Reify context) b)- => SContext context- -> AHThese context a b- -> HTheseTable (Columns a) (Columns b) (Col (Reify context))-stoColumnsThese = \case- SAggregate ->- hreify .- toColumns .- bimap (hunreify . toColumns) (hunreify . toColumns) .- (\(AHThese a) -> a)- SExpr ->- hreify .- toColumns .- bimap (hunreify . toColumns) (hunreify . toColumns) .- (\(AHThese a) -> a)- SResult ->- hreify .- toColumns .- bimap (hunreify . toColumns) (hunreify . toColumns) .- (\(AHThese a) -> a)- SName ->- hreify .- toColumns .- bimap (hunreify . toColumns) (hunreify . toColumns) .- (\(AHThese a) -> a)- SReify context ->- hreify .- stoColumnsThese context .- sbimapThese context (hunreify . toColumns) (hunreify . toColumns) .- (\(AHThese a) -> a)+ HThese context = TheseTable context
src/Rel8/Expr.hs view
@@ -1,27 +1,20 @@ {-# language DataKinds #-} {-# language DerivingStrategies #-} {-# language FlexibleInstances #-}-{-# language GADTs #-} {-# language MultiParamTypeClasses #-}-{-# language PolyKinds #-}-{-# language RoleAnnotations #-} {-# language ScopedTypeVariables #-}-{-# language StandaloneDeriving #-} {-# language StandaloneKindSignatures #-} {-# language TypeApplications #-} {-# language TypeFamilies #-} {-# language UndecidableInstances #-}-{-# language UndecidableSuperClasses #-} module Rel8.Expr ( Expr(..)- , Col( E, unE ) ) where -- base-import Data.Functor.Identity ( Identity )-import Data.Kind ( Type )+import Data.Functor.Identity ( Identity( Identity ) ) import Data.String ( IsString, fromString ) import Prelude hiding ( null ) @@ -38,17 +31,14 @@ , zipPrimExprsWith ) import Rel8.Expr.Serialize ( litExpr )-import Rel8.Schema.Context ( Interpretation, Col )-import Rel8.Schema.Context.Label ( Labelable, labeler, unlabeler )-import Rel8.Schema.HTable.Identity ( HIdentity( HType ), HType )+import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )+import qualified Rel8.Schema.Kind as K import Rel8.Schema.Null ( Nullity( Null, NotNull ), Sql, nullable )-import Rel8.Schema.Reify ( notReify )-import Rel8.Schema.Result ( Result )-import Rel8.Schema.Spec ( Spec( Spec ) ) import Rel8.Table- ( Table, Columns, Context, fromColumns, toColumns, reify, unreify+ ( Table, Columns, Context, fromColumns, toColumns+ , FromExprs, fromResult, toResult+ , Transpose )-import Rel8.Table.Recontextualize ( Recontextualize ) import Rel8.Type ( DBType ) import Rel8.Type.Monoid ( DBMonoid, memptyExpr ) import Rel8.Type.Num ( DBFloating, DBFractional, DBNum )@@ -56,13 +46,9 @@ -- | Typed SQL expressions.-type role Expr representational-type Expr :: k -> Type-data Expr a where- Expr :: k ~ Type => !Opaleye.PrimExpr -> Expr (a :: k)---deriving stock instance Show (Expr a)+type Expr :: K.Context+newtype Expr a = Expr Opaleye.PrimExpr+ deriving stock Show instance Sql DBSemigroup a => Semigroup (Expr a) where@@ -126,30 +112,13 @@ atanh = function "atanh" -instance Interpretation Expr where- data Col Expr _spec where- E :: {unE :: !(Expr a)} -> Col Expr ('Spec labels a)-- instance Sql DBType a => Table Expr (Expr a) where- type Columns (Expr a) = HType a+ type Columns (Expr a) = HIdentity a type Context (Expr a) = Expr-- toColumns a = HType (E a)- fromColumns (HType (E a)) = a- reify = notReify- unreify = notReify---instance Sql DBType a => Recontextualize Expr Expr (Expr a) (Expr a)---instance Sql DBType a => Recontextualize Expr Result (Expr a) (Identity a)---instance Sql DBType a => Recontextualize Result Expr (Identity a) (Expr a)-+ type FromExprs (Expr a) = a+ type Transpose to (Expr a) = to a -instance Labelable Expr where- labeler (E a) = E a- unlabeler (E a) = E a+ toColumns a = HIdentity a+ fromColumns (HIdentity a) = a+ toResult a = HIdentity (Identity a)+ fromResult (HIdentity (Identity a)) = a
src/Rel8/Expr.hs-boot view
@@ -1,6 +1,4 @@-{-# language GADTs #-}-{-# language PolyKinds #-}-{-# language RoleAnnotations #-}+{-# language DataKinds #-} {-# language StandaloneKindSignatures #-} module Rel8.Expr@@ -9,14 +7,14 @@ where -- base-import Data.Kind ( Type ) import Prelude () -- opaleye import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye +-- rel8+import Rel8.Schema.Kind ( Context ) -type role Expr representational-type Expr :: k -> Type-data Expr a where- Expr :: k ~ Type => !Opaleye.PrimExpr -> Expr (a :: k)++type Expr :: Context+newtype Expr a = Expr Opaleye.PrimExpr
+ src/Rel8/Expr/Default.hs view
@@ -0,0 +1,35 @@+module Rel8.Expr.Default+ ( unsafeDefault+ )+where++-- base+import Prelude ()++-- opaleye+import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye++-- rel8+import Rel8.Expr ( Expr )+import Rel8.Expr.Opaleye ( fromPrimExpr )+++-- | Corresponds to the SQL @DEFAULT@ expression.+--+-- This 'Expr' is unsafe for numerous reasons, and should be used with care:+--+-- 1. This 'Expr' only makes sense in an @INSERT@ or @UPDATE@ statement.+--+-- 2. Rel8 is not able to verify that a particular column actually has a+-- @DEFAULT@ value. Trying to use @unsafeDefault@ where there is no default+-- will cause a runtime crash+--+-- 3. @DEFAULT@ values can not be transformed. For example, the innocuous Rel8+-- code @unsafeDefault + 1@ will crash, despite type checking.+--+-- Given all these caveats, we suggest avoiding the use of default values where+-- possible, instead being explicit. A common scenario where default values are+-- used is with auto-incrementing identifier columns. In this case, we suggest+-- using 'Rel8.nextval' instead.+unsafeDefault :: Expr a+unsafeDefault = fromPrimExpr Opaleye.DefaultInsertExpr
src/Rel8/Expr/Eq.hs view
@@ -22,7 +22,7 @@ import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye -- rel8-import {-# SOURCE #-} Rel8.Expr ( Expr )+import Rel8.Expr ( Expr ) import Rel8.Expr.Bool ( (&&.), (||.), false, or_, coalesce ) import Rel8.Expr.Null ( isNull, unsafeLiftOpNull ) import Rel8.Expr.Opaleye ( fromPrimExpr, toPrimExpr, zipPrimExprsWith )
src/Rel8/FCF.hs view
@@ -5,7 +5,7 @@ module Rel8.FCF ( Exp, Eval- , Compose+ , Id ) where @@ -22,5 +22,5 @@ type family Eval a -data Compose :: (Type -> Type) -> (Type -> Exp Type) -> Type -> Exp Type-type instance Eval (Compose f g a) = f (Eval (g a))+data Id :: a -> Exp a+type instance Eval (Id a) = a
src/Rel8/Generic/Construction.hs view
@@ -2,9 +2,6 @@ {-# language ConstraintKinds #-} {-# language DataKinds #-} {-# language FlexibleContexts #-}-{-# language FlexibleInstances #-}-{-# language LambdaCase #-}-{-# language MultiParamTypeClasses #-} {-# language NamedFieldPuns #-} {-# language ScopedTypeVariables #-} {-# language StandaloneKindSignatures #-}@@ -28,18 +25,17 @@ import Data.Bifunctor ( first ) import Data.Kind ( Constraint, Type ) import Data.List.NonEmpty ( NonEmpty( (:|) ) )-import Data.Type.Equality ( (:~:)( Refl ) ) import GHC.TypeLits ( Symbol ) import Prelude -- rel8-import Rel8.Aggregate ( Col( A ), Aggregate( Aggregate ) )-import Rel8.Expr ( Col( E ), Expr )+import Rel8.Aggregate ( Aggregate( Aggregate ) )+import Rel8.Expr ( Expr ) import Rel8.Expr.Aggregate ( groupByExpr ) import Rel8.Expr.Eq ( (==.) ) import Rel8.Expr.Null ( nullify, snull, unsafeUnnullify ) import Rel8.Expr.Serialize ( litExpr )-import Rel8.FCF ( Eval, Exp )+import Rel8.FCF ( Eval, Exp, Id ) import Rel8.Generic.Construction.ADT ( GConstructorADT, GMakeableADT, gmakeADT , GConstructableADT@@ -59,18 +55,16 @@ , KnownAlgebra, algebraSing ) import qualified Rel8.Kind.Algebra as K-import Rel8.Schema.Context.Nullify ( runTag )+import Rel8.Schema.Context.Nullify ( sguard, snullify ) import Rel8.Schema.HTable ( HTable )-import Rel8.Schema.HTable.Identity ( HIdentity( HType ) )+import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Name ( Col( N ), Name( Name ) )+import Rel8.Schema.Name ( Name( Name ) ) import Rel8.Schema.Null ( Nullity( Null, NotNull ) )-import Rel8.Schema.Spec ( SSpec( SSpec, nullity, info ) )-import Rel8.Schema.Reify ( Col( Reify ), Reify, hreify, hunreify )-import Rel8.Schema.Result ( Result )+import Rel8.Schema.Spec ( Spec( Spec, nullity, info ) ) import Rel8.Table- ( TTable, TColumns, TUnreify- , Table, fromColumns, toColumns, reify, unreify+ ( TTable, TColumns+ , Table, fromColumns, toColumns ) import Rel8.Table.Bool ( case_ ) import Rel8.Type.Tag ( Tag )@@ -79,10 +73,10 @@ type GGBuildable :: K.Algebra -> Symbol -> (K.Context -> Exp (Type -> Type)) -> Constraint type GGBuildable algebra name rep = ( KnownAlgebra algebra- , Eval (GGColumns algebra TColumns (Eval (rep (Reify Aggregate)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))- , Eval (GGColumns algebra TColumns (Eval (rep (Reify Expr)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))- , Eval (GGColumns algebra TColumns (Eval (rep (Reify Name)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))- , HTable (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))))+ , Eval (GGColumns algebra TColumns (Eval (rep Aggregate))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))+ , Eval (GGColumns algebra TColumns (Eval (rep Expr))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))+ , Eval (GGColumns algebra TColumns (Eval (rep Name))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))+ , HTable (Eval (GGColumns algebra TColumns (Eval (rep Expr)))) , GGBuildable' algebra name rep ) @@ -90,65 +84,63 @@ type GGBuildable' :: K.Algebra -> Symbol -> (K.Context -> Exp (Type -> Type)) -> Constraint type family GGBuildable' algebra name rep where GGBuildable' 'K.Product name rep =- ( name ~ GConstructor (Eval (rep (Reify Expr)))- , Representable TUnreify (Eval (rep (Reify Expr)))- , GConstructable (TTable (Reify Expr)) TColumns TUnreify (Col (Reify Expr)) (Eval (rep (Reify Expr)))+ ( name ~ GConstructor (Eval (rep Expr))+ , Representable Id (Eval (rep Expr))+ , GConstructable (TTable Expr) TColumns Id Expr (Eval (rep Expr)) ) GGBuildable' 'K.Sum name rep =- ( Representable TUnreify (GConstructorADT name (Eval (rep (Reify Expr))))- , GMakeableADT (TTable (Reify Expr)) TColumns TUnreify (Col (Reify Expr)) name (Eval (rep (Reify Expr)))+ ( Representable Id (GConstructorADT name (Eval (rep Expr)))+ , GMakeableADT (TTable Expr) TColumns Id Expr name (Eval (rep Expr)) ) type GGBuild :: K.Algebra -> Symbol -> (K.Context -> Exp (Type -> Type)) -> Type -> Type type family GGBuild algebra name rep r where GGBuild 'K.Product _name rep r =- GConstruct TUnreify (Eval (rep (Reify Expr))) r+ GConstruct Id (Eval (rep Expr)) r GGBuild 'K.Sum name rep r =- GConstruct TUnreify (GConstructorADT name (Eval (rep (Reify Expr)))) r+ GConstruct Id (GConstructorADT name (Eval (rep Expr))) r ggbuild :: forall algebra name rep a. GGBuildable algebra name rep- => (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Expr) -> a)+ => (Eval (GGColumns algebra TColumns (Eval (rep Expr))) Expr -> a) -> GGBuild algebra name rep a ggbuild gfromColumns = case algebraSing @algebra of SProduct ->- gtabulate @TUnreify @(Eval (rep (Reify Expr))) @a $+ gtabulate @Id @(Eval (rep Expr)) @a $ gfromColumns .- hunreify . gconstruct- @(TTable (Reify Expr))+ @(TTable Expr) @TColumns- @TUnreify- @(Col (Reify Expr))- @(Eval (rep (Reify Expr)))- (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)+ @Id+ @Expr+ @(Eval (rep Expr))+ (const toColumns) SSum ->- gtabulate @TUnreify @(GConstructorADT name (Eval (rep (Reify Expr)))) @a $+ gtabulate @Id @(GConstructorADT name (Eval (rep Expr))) @a $ gfromColumns .- hunreify . gmakeADT- @(TTable (Reify Expr))+ @(TTable Expr) @TColumns- @TUnreify- @(Col (Reify Expr))+ @Id+ @Expr @name- @(Eval (rep (Reify Expr)))- (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)- (\SSpec {info} -> Reify (E (snull info)))- (\SSpec {nullity} -> case nullity of+ @(Eval (rep Expr))+ (const toColumns)+ (\Spec {info} -> snull info)+ (\Spec {nullity} -> case nullity of Null -> id- NotNull -> \(Reify (E a)) -> Reify (E (nullify a)))- (HType . Reify . E . litExpr)+ NotNull -> nullify)+ (HIdentity . litExpr) type GGConstructable :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Constraint type GGConstructable algebra rep = ( KnownAlgebra algebra- , Eval (GGColumns algebra TColumns (Eval (rep (Reify Aggregate)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))- , Eval (GGColumns algebra TColumns (Eval (rep (Reify Expr)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))- , Eval (GGColumns algebra TColumns (Eval (rep (Reify Name)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))- , HTable (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))))+ , Eval (GGColumns algebra TColumns (Eval (rep Aggregate))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))+ , Eval (GGColumns algebra TColumns (Eval (rep Expr))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))+ , Eval (GGColumns algebra TColumns (Eval (rep Name))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))+ , HTable (Eval (GGColumns algebra TColumns (Eval (rep Expr)))) , GGConstructable' algebra rep ) @@ -156,105 +148,100 @@ type GGConstructable' :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Constraint type family GGConstructable' algebra rep where GGConstructable' 'K.Product rep =- ( Representable TUnreify (Eval (rep (Reify Aggregate)))- , Representable TUnreify (Eval (rep (Reify Expr)))- , Representable TUnreify (Eval (rep (Reify Name)))- , GConstructable (TTable (Reify Aggregate)) TColumns TUnreify (Col (Reify Aggregate)) (Eval (rep (Reify Aggregate)))- , GConstructable (TTable (Reify Expr)) TColumns TUnreify (Col (Reify Expr)) (Eval (rep (Reify Expr)))- , GConstructable (TTable (Reify Name)) TColumns TUnreify (Col (Reify Name)) (Eval (rep (Reify Name)))+ ( Representable Id (Eval (rep Aggregate))+ , Representable Id (Eval (rep Expr))+ , Representable Id (Eval (rep Name))+ , GConstructable (TTable Aggregate) TColumns Id Aggregate (Eval (rep Aggregate))+ , GConstructable (TTable Expr) TColumns Id Expr (Eval (rep Expr))+ , GConstructable (TTable Name) TColumns Id Name (Eval (rep Name)) ) GGConstructable' 'K.Sum rep =- ( RepresentableConstructors TUnreify (Eval (rep (Reify Expr)))- , RepresentableFields TUnreify (Eval (rep (Reify Aggregate)))- , RepresentableFields TUnreify (Eval (rep (Reify Expr)))- , RepresentableFields TUnreify (Eval (rep (Reify Name)))- , Functor (GConstructors TUnreify (Eval (rep (Reify Expr))))- , GConstructableADT (TTable (Reify Aggregate)) TColumns TUnreify (Col (Reify Aggregate)) (Eval (rep (Reify Aggregate)))- , GConstructableADT (TTable (Reify Expr)) TColumns TUnreify (Col (Reify Expr)) (Eval (rep (Reify Expr)))- , GConstructableADT (TTable (Reify Name)) TColumns TUnreify (Col (Reify Name)) (Eval (rep (Reify Name)))+ ( RepresentableConstructors Id (Eval (rep Expr))+ , RepresentableFields Id (Eval (rep Aggregate))+ , RepresentableFields Id (Eval (rep Expr))+ , RepresentableFields Id (Eval (rep Name))+ , Functor (GConstructors Id (Eval (rep Expr)))+ , GConstructableADT (TTable Aggregate) TColumns Id Aggregate (Eval (rep Aggregate))+ , GConstructableADT (TTable Expr) TColumns Id Expr (Eval (rep Expr))+ , GConstructableADT (TTable Name) TColumns Id Name (Eval (rep Name)) ) type GGConstruct :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type type family GGConstruct algebra rep r where- GGConstruct 'K.Product rep r =- GConstruct TUnreify (Eval (rep (Reify Expr))) r -> r- GGConstruct 'K.Sum rep r =- GConstructADT TUnreify (Eval (rep (Reify Expr))) r r+ GGConstruct 'K.Product rep r = GConstruct Id (Eval (rep Expr)) r -> r+ GGConstruct 'K.Sum rep r = GConstructADT Id (Eval (rep Expr)) r r ggconstruct :: forall algebra rep a. GGConstructable algebra rep- => (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Expr) -> a)+ => (Eval (GGColumns algebra TColumns (Eval (rep Expr))) Expr -> a) -> GGConstruct algebra rep a -> a ggconstruct gfromColumns f = case algebraSing @algebra of SProduct -> f $- gtabulate @TUnreify @(Eval (rep (Reify Expr))) @a $+ gtabulate @Id @(Eval (rep Expr)) @a $ gfromColumns .- hunreify . gconstruct- @(TTable (Reify Expr))+ @(TTable Expr) @TColumns- @TUnreify- @(Col (Reify Expr))- @(Eval (rep (Reify Expr)))- (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)+ @Id+ @Expr+ @(Eval (rep Expr))+ (const toColumns) SSum ->- gcindex @TUnreify @(Eval (rep (Reify Expr))) @a f $- fmap (gfromColumns . hunreify) $+ gcindex @Id @(Eval (rep Expr)) @a f $+ fmap gfromColumns $ gconstructADT- @(TTable (Reify Expr))+ @(TTable Expr) @TColumns- @TUnreify- @(Col (Reify Expr))- @(Eval (rep (Reify Expr)))- (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)- (\SSpec {info} -> Reify (E (snull info)))- (\SSpec {nullity} -> case nullity of+ @Id+ @Expr+ @(Eval (rep Expr))+ (const toColumns)+ (\Spec {info} -> snull info)+ (\Spec {nullity} -> case nullity of Null -> id- NotNull -> \(Reify (E a)) -> Reify (E (nullify a)))- (HType . Reify . E . litExpr)+ NotNull -> nullify)+ (HIdentity . litExpr) type GGDeconstruct :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type -> Type type family GGDeconstruct algebra rep a r where GGDeconstruct 'K.Product rep a r =- GConstruct TUnreify (Eval (rep (Reify Expr))) r -> a -> r+ GConstruct Id (Eval (rep Expr)) r -> a -> r GGDeconstruct 'K.Sum rep a r =- GConstructADT TUnreify (Eval (rep (Reify Expr))) r (a -> r)+ GConstructADT Id (Eval (rep Expr)) r (a -> r) ggdeconstruct :: forall algebra rep a r. (GGConstructable algebra rep, Table Expr r)- => (a -> Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Expr))+ => (a -> Eval (GGColumns algebra TColumns (Eval (rep Expr))) Expr) -> GGDeconstruct algebra rep a r ggdeconstruct gtoColumns = case algebraSing @algebra of SProduct -> \build ->- gindex @TUnreify @(Eval (rep (Reify Expr))) @r build .+ gindex @Id @(Eval (rep Expr)) @r build . gdeconstruct- @(TTable (Reify Expr))+ @(TTable Expr) @TColumns- @TUnreify- @(Col (Reify Expr))- @(Eval (rep (Reify Expr)))- (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns) .- hreify .+ @Id+ @Expr+ @(Eval (rep Expr))+ (const fromColumns) . gtoColumns SSum ->- gctabulate @TUnreify @(Eval (rep (Reify Expr))) @r @(a -> r) $ \constructors as ->+ gctabulate @Id @(Eval (rep Expr)) @r @(a -> r) $ \constructors as -> let- (HType (Reify (E tag)), cases) =+ (HIdentity tag, cases) = gdeconstructADT- @(TTable (Reify Expr))+ @(TTable Expr) @TColumns- @TUnreify- @(Col (Reify Expr))- @(Eval (rep (Reify Expr)))- (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns)- (\SSpec {nullity} -> case nullity of+ @Id+ @Expr+ @(Eval (rep Expr))+ (const fromColumns)+ (\Spec {nullity} -> case nullity of Null -> id- NotNull -> \(Reify (E a)) -> Reify (E (unsafeUnnullify a)))+ NotNull -> unsafeUnnullify) constructors $- hreify $ gtoColumns as in case cases of@@ -264,108 +251,102 @@ type GGName :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type type family GGName algebra rep a where- GGName 'K.Product rep a = GConstruct TUnreify (Eval (rep (Reify Name))) a- GGName 'K.Sum rep a = Name Tag -> GBuildADT TUnreify (Eval (rep (Reify Name))) a+ GGName 'K.Product rep a = GConstruct Id (Eval (rep Name)) a+ GGName 'K.Sum rep a = Name Tag -> GBuildADT Id (Eval (rep Name)) a ggname :: forall algebra rep a. GGConstructable algebra rep- => (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Name) -> a)+ => (Eval (GGColumns algebra TColumns (Eval (rep Expr))) Name -> a) -> GGName algebra rep a ggname gfromColumns = case algebraSing @algebra of SProduct ->- gtabulate @TUnreify @(Eval (rep (Reify Name))) @a $+ gtabulate @Id @(Eval (rep Name)) @a $ gfromColumns .- hunreify . gconstruct- @(TTable (Reify Name))+ @(TTable Name) @TColumns- @TUnreify- @(Col (Reify Name))- @(Eval (rep (Reify Name)))- (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)+ @Id+ @Name+ @(Eval (rep Name))+ (const toColumns) SSum -> \tag ->- gftabulate @TUnreify @(Eval (rep (Reify Name))) @a $+ gftabulate @Id @(Eval (rep Name)) @a $ gfromColumns .- hunreify . gbuildADT- @(TTable (Reify Name))+ @(TTable Name) @TColumns- @TUnreify- @(Col (Reify Name))- @(Eval (rep (Reify Name)))- (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)- (\_ _ (Reify (N (Name a))) -> Reify (N (Name a)))- (HType (Reify (N tag)))+ @Id+ @Name+ @(Eval (rep Name))+ (const toColumns)+ (\_ _ (Name a) -> Name a)+ (HIdentity tag) type GGAggregate :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type type family GGAggregate algebra rep r where GGAggregate 'K.Product rep r =- GConstruct TUnreify (Eval (rep (Reify Aggregate))) r ->- GConstruct TUnreify (Eval (rep (Reify Expr))) r+ GConstruct Id (Eval (rep Aggregate)) r ->+ GConstruct Id (Eval (rep Expr)) r GGAggregate 'K.Sum rep r =- GBuildADT TUnreify (Eval (rep (Reify Aggregate))) r ->- GBuildADT TUnreify (Eval (rep (Reify Expr))) r+ GBuildADT Id (Eval (rep Aggregate)) r ->+ GBuildADT Id (Eval (rep Expr)) r ggaggregate :: forall algebra rep exprs agg. GGConstructable algebra rep- => (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Aggregate) -> agg)- -> (exprs -> Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Expr))+ => (Eval (GGColumns algebra TColumns (Eval (rep Expr))) Aggregate -> agg)+ -> (exprs -> Eval (GGColumns algebra TColumns (Eval (rep Expr))) Expr) -> GGAggregate algebra rep agg -> exprs -> agg ggaggregate gfromColumns gtoColumns agg es = case algebraSing @algebra of SProduct -> flip f exprs $ gfromColumns .- hunreify . gconstruct- @(TTable (Reify Aggregate))+ @(TTable Aggregate) @TColumns- @TUnreify- @(Col (Reify Aggregate))- @(Eval (rep (Reify Aggregate)))- (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)+ @Id+ @Aggregate+ @(Eval (rep Aggregate))+ (const toColumns) where f =- gindex @TUnreify @(Eval (rep (Reify Expr))) @agg .+ gindex @Id @(Eval (rep Expr)) @agg . agg .- gtabulate @TUnreify @(Eval (rep (Reify Aggregate))) @agg+ gtabulate @Id @(Eval (rep Aggregate)) @agg exprs = gdeconstruct- @(TTable (Reify Expr))+ @(TTable Expr) @TColumns- @TUnreify- @(Col (Reify Expr))- @(Eval (rep (Reify Expr)))- (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns) $- hreify $+ @Id+ @Expr+ @(Eval (rep Expr))+ (const fromColumns) $ gtoColumns es SSum -> flip f exprs $ gfromColumns .- hunreify . gbuildADT- @(TTable (Reify Aggregate))+ @(TTable Aggregate) @TColumns- @TUnreify- @(Col (Reify Aggregate))- @(Eval (rep (Reify Aggregate)))- (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)- (\tag' SSpec {nullity} (Reify (A (Aggregate a))) ->- Reify $ A $ Aggregate $ runTag nullity (tag ==. litExpr tag') <$> a)- (HType (Reify (A (groupByExpr tag))))+ @Id+ @Aggregate+ @(Eval (rep Aggregate))+ (const toColumns)+ (\tag' Spec {nullity} (Aggregate a) ->+ Aggregate $ sguard (tag ==. litExpr tag') . snullify nullity <$> a)+ (HIdentity (groupByExpr tag)) where f =- gfindex @TUnreify @(Eval (rep (Reify Expr))) @agg .+ gfindex @Id @(Eval (rep Expr)) @agg . agg .- gftabulate @TUnreify @(Eval (rep (Reify Aggregate))) @agg- (HType (Reify (E tag)), exprs) =+ gftabulate @Id @(Eval (rep Aggregate)) @agg+ (HIdentity tag, exprs) = gunbuildADT- @(TTable (Reify Expr))+ @(TTable Expr) @TColumns- @TUnreify- @(Col (Reify Expr))- @(Eval (rep (Reify Expr)))- (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns)- (\SSpec {nullity} -> case nullity of+ @Id+ @Expr+ @(Eval (rep Expr))+ (const fromColumns)+ (\Spec {nullity} -> case nullity of Null -> id- NotNull -> \(Reify (E a)) -> Reify (E (unsafeUnnullify a))) $- hreify $+ NotNull -> unsafeUnnullify) $ gtoColumns es
src/Rel8/Generic/Construction/ADT.hs view
@@ -48,14 +48,13 @@ ) import Rel8.Generic.Table.ADT ( GColumnsADT, GColumnsADT' ) import Rel8.Generic.Table.Record ( GColumns )-import Rel8.Schema.Context.Label ( HLabelable, hlabeler, hunlabeler ) import Rel8.Schema.HTable ( HTable )-import Rel8.Schema.HTable.Identity ( HType )+import Rel8.Schema.HTable.Identity ( HIdentity ) import Rel8.Schema.HTable.Label ( HLabel, hlabel, hunlabel ) import Rel8.Schema.HTable.Nullify ( HNullify, hnulls, hnullify, hunnullify ) import Rel8.Schema.HTable.Product ( HProduct( HProduct ) ) import Rel8.Schema.Null ( Nullify )-import Rel8.Schema.Spec ( Spec( Spec ), SSpec )+import Rel8.Schema.Spec ( Spec ) import qualified Rel8.Schema.Kind as K import Rel8.Type.Tag ( Tag( Tag ) ) @@ -63,24 +62,16 @@ import Data.Text ( pack ) -type Null :: K.HContext -> Type-type Null context = forall labels a. ()- => SSpec ('Spec labels a)- -> context ('Spec labels (Nullify a))+type Null :: K.Context -> Type+type Null context = forall a. Spec a -> context (Nullify a) -type Nullifier :: K.HContext -> Type-type Nullifier context = forall labels a. ()- => SSpec ('Spec labels a)- -> context ('Spec labels a)- -> context ('Spec labels (Nullify a))+type Nullifier :: K.Context -> Type+type Nullifier context = forall a. Spec a -> context a -> context (Nullify a) -type Unnullifier :: K.HContext -> Type-type Unnullifier context = forall labels a. ()- => SSpec ('Spec labels a)- -> context ('Spec labels (Nullify a))- -> context ('Spec labels a)+type Unnullifier :: K.Context -> Type+type Unnullifier context = forall a. Spec a -> context (Nullify a) -> context a type NoConstructor :: Symbol -> Symbol -> ErrorMessage@@ -189,12 +180,12 @@ :: (Type -> Exp Constraint) -> (Type -> Exp K.HTable) -> (Type -> Exp Type)- -> K.HContext -> (Type -> Type) -> Constraint+ -> K.Context -> (Type -> Type) -> Constraint class GConstructableADT _Table _Columns f context rep where gbuildADT :: () => ToColumns _Table _Columns f context -> (Tag -> Nullifier context)- -> HType Tag context+ -> HIdentity Tag context -> GFieldsADT f rep -> GColumnsADT _Columns rep context @@ -202,13 +193,13 @@ => FromColumns _Table _Columns f context -> Unnullifier context -> GColumnsADT _Columns rep context- -> (HType Tag context, GFieldsADT f rep)+ -> (HIdentity Tag context, GFieldsADT f rep) gconstructADT :: () => ToColumns _Table _Columns f context -> Null context -> Nullifier context- -> (Tag -> HType Tag context)+ -> (Tag -> HIdentity Tag context) -> GConstructors f rep (GColumnsADT _Columns rep context) gdeconstructADT :: ()@@ -216,30 +207,29 @@ -> Unnullifier context -> GConstructors f rep r -> GColumnsADT _Columns rep context- -> (HType Tag context, NonEmpty (Tag, r))+ -> (HIdentity Tag context, NonEmpty (Tag, r)) instance- ( htable ~ HLabel "tag" (HType Tag)+ ( htable ~ HLabel "tag" (HIdentity Tag) , GConstructableADT' _Table _Columns f context htable rep- , HLabelable context ) => GConstructableADT _Table _Columns f context (M1 D meta rep) where gbuildADT toColumns nullifier = gbuildADT' @_Table @_Columns @f @context @htable @rep toColumns nullifier .- hlabel hlabeler+ hlabel gunbuildADT fromColumns unnullifier =- first (hunlabel hunlabeler) .+ first hunlabel . gunbuildADT' @_Table @_Columns @f @context @htable @rep fromColumns unnullifier gconstructADT toColumns null nullifier mk = gconstructADT' @_Table @_Columns @f @context @htable @rep toColumns null nullifier- (hlabel hlabeler . mk)+ (hlabel . mk) gdeconstructADT fromColumns unnullifier cases =- first (hunlabel hunlabeler) .+ first hunlabel . gdeconstructADT' @_Table @_Columns @f @context @htable @rep fromColumns unnullifier cases @@ -247,7 +237,7 @@ :: (Type -> Exp Constraint) -> (Type -> Exp K.HTable) -> (Type -> Exp Type)- -> K.HContext -> K.HTable -> (Type -> Type) -> Constraint+ -> K.Context -> K.HTable -> (Type -> Type) -> Constraint class GConstructableADT' _Table _Columns f context htable rep where gbuildADT' :: () => ToColumns _Table _Columns f context@@ -334,7 +324,6 @@ ( HTable (GColumns _Columns rep) , KnownSymbol label , meta ~ 'MetaCons label _fixity _isRecord- , HLabelable context , GConstructable _Table _Columns f context rep , GColumnsADT' _Columns htable (M1 C meta rep) ~ HProduct htable (HLabel label (HNullify (GColumns _Columns rep)))@@ -343,7 +332,7 @@ where gbuildADT' toColumns nullifier htable = HProduct htable .- hlabel hlabeler .+ hlabel . hnullify (nullifier tag) . gconstruct @_Table @_Columns @f @context @rep toColumns where@@ -354,13 +343,13 @@ , gdeconstruct @_Table @_Columns @f @context @rep fromColumns $ runIdentity $ hunnullify (\spec -> pure . unnullifier spec) $- hunlabel hunlabeler+ hunlabel a ) gconstructADT' toColumns _ nullifier mk = HProduct htable .- hlabel hlabeler .+ hlabel . hnullify nullifier . gconstruct @_Table @_Columns @f @context @rep toColumns where@@ -375,35 +364,34 @@ a = gdeconstruct @_Table @_Columns @f @context @rep fromColumns $ runIdentity $ hunnullify (\spec -> pure . unnullifier spec) $- hunlabel hunlabeler+ hunlabel columns tag = Tag $ pack $ symbolVal (Proxy @label) - gfill null htable = HProduct htable (hlabel hlabeler (hnulls null))+ gfill null htable = HProduct htable (hlabel (hnulls null)) type GMakeableADT :: (Type -> Exp Constraint) -> (Type -> Exp K.HTable) -> (Type -> Exp Type)- -> K.HContext -> Symbol -> (Type -> Type) -> Constraint+ -> K.Context -> Symbol -> (Type -> Type) -> Constraint class GMakeableADT _Table _Columns f context name rep where gmakeADT :: () => ToColumns _Table _Columns f context -> Null context -> Nullifier context- -> (Tag -> HType Tag context)+ -> (Tag -> HIdentity Tag context) -> GFields f (GConstructorADT name rep) -> GColumnsADT _Columns rep context instance- ( htable ~ HLabel "tag" (HType Tag)+ ( htable ~ HLabel "tag" (HIdentity Tag) , meta ~ 'MetaData datatype _module _package _newtype , fallback ~ TypeError (NoConstructor datatype name) , fields ~ GFields f (GConstructorADT' name rep fallback) , GMakeableADT' _Table _Columns f context htable name rep fields- , HLabelable context , KnownSymbol name ) => GMakeableADT _Table _Columns f context name (M1 D meta rep)@@ -414,14 +402,14 @@ toColumns null nullifier htable where tag = Tag $ pack $ symbolVal (Proxy @name)- htable = hlabel hlabeler (wrap tag)+ htable = hlabel (wrap tag) type GMakeableADT' :: (Type -> Exp Constraint) -> (Type -> Exp K.HTable) -> (Type -> Exp Type)- -> K.HContext -> K.HTable -> Symbol -> (Type -> Type) -> Type -> Constraint+ -> K.Context -> K.HTable -> Symbol -> (Type -> Type) -> Type -> Constraint class GMakeableADT' _Table _Columns f context htable name rep fields where gmakeADT' :: () => ToColumns _Table _Columns f context@@ -462,8 +450,6 @@ instance {-# OVERLAPS #-} ( HTable (GColumns _Columns rep)- , KnownSymbol name- , HLabelable context , GConstructable _Table _Columns f context rep , fields ~ GFields f rep , GColumnsADT' _Columns htable (M1 C ('MetaCons name _fixity _isRecord) rep) ~@@ -473,15 +459,13 @@ where gmakeADT' toColumns _ nullifier htable = HProduct htable .- hlabel hlabeler .+ hlabel . hnullify nullifier . gconstruct @_Table @_Columns @f @context @rep toColumns instance {-# OVERLAPPABLE #-} ( HTable (GColumns _Columns rep)- , KnownSymbol label- , HLabelable context , GColumnsADT' _Columns htable (M1 C ('MetaCons label _fixity _isRecord) rep) ~ HProduct htable (HLabel label (HNullify (GColumns _Columns rep))) )@@ -489,5 +473,5 @@ where gmakeADT' _ null _ htable _ = HProduct htable $- hlabel hlabeler $+ hlabel $ hnulls null
src/Rel8/Generic/Construction/Record.hs view
@@ -27,15 +27,13 @@ ) import GHC.TypeLits ( ErrorMessage( (:<>:), Text ), TypeError- , Symbol, KnownSymbol+ , Symbol ) import Prelude -- rel8 import Rel8.FCF ( Eval, Exp ) import Rel8.Generic.Table.Record ( GColumns )-import Rel8.Schema.Context.Label ( HLabelable, hlabeler, hunlabeler )-import Rel8.Schema.HTable ( HTable ) import Rel8.Schema.HTable.Label ( hlabel, hunlabel ) import Rel8.Schema.HTable.Product ( HProduct( HProduct ) ) import qualified Rel8.Schema.Kind as K@@ -45,7 +43,7 @@ :: (Type -> Exp Constraint) -> (Type -> Exp K.HTable) -> (Type -> Exp Type)- -> K.HContext+ -> K.Context -> Type type FromColumns _Table _Columns f context = forall proxy x. Eval (_Table x) => proxy x -> Eval (_Columns x) context -> Eval (f x)@@ -55,7 +53,7 @@ :: (Type -> Exp Constraint) -> (Type -> Exp K.HTable) -> (Type -> Exp Type)- -> K.HContext+ -> K.Context -> Type type ToColumns _Table _Columns f context = forall proxy x. Eval (_Table x) => proxy x -> Eval (f x) -> Eval (_Columns x) context@@ -119,7 +117,7 @@ :: (Type -> Exp Constraint) -> (Type -> Exp K.HTable) -> (Type -> Exp Type)- -> K.HContext -> (Type -> Type) -> Constraint+ -> K.Context -> (Type -> Type) -> Constraint class GConstructable _Table _Columns f context rep where gconstruct :: () => ToColumns _Table _Columns f context@@ -162,12 +160,9 @@ instance ( Eval (_Table a)- , HTable (Eval (_Columns a))- , HLabelable context- , KnownSymbol label , meta ~ 'MetaSel ('Just label) _su _ss _ds ) => GConstructable _Table _Columns f context (M1 S meta (K1 i a)) where- gconstruct toColumns = hlabel hlabeler . toColumns (Proxy @a)- gdeconstruct fromColumns = fromColumns (Proxy @a) . hunlabel hunlabeler+ gconstruct toColumns = hlabel . toColumns (Proxy @a)+ gdeconstruct fromColumns = fromColumns (Proxy @a) . hunlabel
src/Rel8/Generic/Map.hs view
@@ -1,26 +1,19 @@-{-# language AllowAmbiguousTypes #-} {-# language DataKinds #-}-{-# language EmptyCase #-}-{-# language FlexibleInstances #-}-{-# language LambdaCase #-}-{-# language MultiParamTypeClasses #-}-{-# language RankNTypes #-}-{-# language ScopedTypeVariables #-} {-# language StandaloneKindSignatures #-}-{-# language TypeApplications #-} {-# language TypeFamilies #-} {-# language TypeOperators #-} {-# language UndecidableInstances #-} module Rel8.Generic.Map- ( GMap, GMappable (gmap, gunmap)+ ( GMap+ , Map ) where -- base-import Data.Kind ( Constraint, Type )+import Data.Kind ( Type ) import GHC.Generics- ( (:+:)( L1, R1 ), (:*:)( (:*:) ), K1( K1 ), M1( M1 ), U1( U1 ), V1+ ( (:+:), (:*:), K1, M1, U1, V1 ) import Prelude () @@ -38,56 +31,19 @@ GMap f (K1 i a) = K1 i (Eval (f a)) -type GMappable :: (Type -> Exp Constraint) -> (Type -> Type) -> Constraint-class GMappable constraint rep where- gmap :: ()- => proxy f- -> (forall a. Eval (constraint a) => a -> Eval (f a))- -> rep x- -> GMap f rep x-- gunmap :: ()- => proxy f- -> (forall a. Eval (constraint a) => Eval (f a) -> a)- -> GMap f rep x- -> rep x---instance GMappable constraint rep => GMappable constraint (M1 i c rep) where- gmap proxy f (M1 a) = M1 (gmap @constraint proxy f a)- gunmap proxy f (M1 a) = M1 (gunmap @constraint proxy f a)---instance GMappable constraint V1 where- gmap _ _ = \case- gunmap _ _ = \case---instance (GMappable constraint rep1, GMappable constraint rep2) =>- GMappable constraint (rep1 :+: rep2)- where- gmap proxy f = \case- L1 a -> L1 (gmap @constraint proxy f a)- R1 a -> R1 (gmap @constraint proxy f a)- gunmap proxy f = \case- L1 a -> L1 (gunmap @constraint proxy f a)- R1 a -> R1 (gunmap @constraint proxy f a)---instance GMappable constraint U1 where- gmap _ _ U1 = U1- gunmap _ _ U1 = U1---instance (GMappable constraint rep1, GMappable constraint rep2) =>- GMappable constraint (rep1 :*: rep2)- where- gmap proxy f (a :*: b) =- gmap @constraint proxy f a :*: gmap @constraint proxy f b- gunmap proxy f (a :*: b) =- gunmap @constraint proxy f a :*: gunmap @constraint proxy f b---instance Eval (constraint a) => GMappable constraint (K1 i a) where- gmap _ f (K1 a) = K1 (f a)- gunmap _ f (K1 a) = K1 (f a)+-- | Map a @Type -> Type@ function over the @Type@-kinded type variables in+-- of a type constructor.+type Map :: (Type -> Exp Type) -> Type -> Type+type family Map f a where+ Map p (t a b c d e f g) =+ t (Eval (p a)) (Eval (p b)) (Eval (p c)) (Eval (p d)) (Eval (p e))+ (Eval (p f)) (Eval (p g))+ Map p (t a b c d e f) =+ t (Eval (p a)) (Eval (p b)) (Eval (p c)) (Eval (p d)) (Eval (p e))+ (Eval (p f))+ Map p (t a b c d e) =+ t (Eval (p a)) (Eval (p b)) (Eval (p c)) (Eval (p d)) (Eval (p e))+ Map p (t a b c d) = t (Eval (p a)) (Eval (p b)) (Eval (p c)) (Eval (p d))+ Map p (t a b c) = t (Eval (p a)) (Eval (p b)) (Eval (p c))+ Map p (t a b) = t (Eval (p a)) (Eval (p b))+ Map p (t a) = t (Eval (p a))
− src/Rel8/Generic/Reify.hs
@@ -1,24 +0,0 @@-{-# language StandaloneKindSignatures #-}-{-# language TypeFamilies #-}--module Rel8.Generic.Reify- ( ARep- )-where---- base-import Data.Functor.Contravariant ( Contravariant, contramap )-import Data.Kind ( Type )-import Data.Void ( Void, absurd )-import GHC.Generics ( Generic, Rep, from, to )-import Prelude---type ARep :: (Type -> Type) -> Type-newtype ARep rep = ARep (rep Void)---instance (Contravariant rep, Functor rep) => Generic (ARep rep) where- type Rep (ARep rep) = rep- from (ARep a) = fmap absurd a- to = ARep . contramap absurd
src/Rel8/Generic/Rel8able.hs view
@@ -1,13 +1,18 @@ {-# language AllowAmbiguousTypes #-}+{-# language ConstraintKinds #-} {-# language DataKinds #-} {-# language DefaultSignatures #-} {-# language FlexibleContexts #-} {-# language FlexibleInstances #-}+{-# language LambdaCase #-} {-# language MultiParamTypeClasses #-}+{-# language PolyKinds #-}+{-# language QuantifiedConstraints #-} {-# language ScopedTypeVariables #-} {-# language StandaloneKindSignatures #-} {-# language TypeApplications #-}-{-# language TypeFamilies #-}+{-# language TypeFamilyDependencies #-}+{-# language TypeOperators #-} {-# language UndecidableInstances #-} module Rel8.Generic.Rel8able@@ -15,36 +20,39 @@ , Algebra , GRep , GColumns, gfromColumns, gtoColumns- , greify, gunreify- , TUnreifyContext+ , GFromExprs, gfromResult, gtoResult+ , TSerialize, serialize, deserialize ) where -- base+import Data.Functor.Identity ( Identity ) import Data.Kind ( Constraint, Type )-import Data.Proxy ( Proxy( Proxy ) )-import Data.Type.Equality ( (:~:)( Refl ) )+import Data.Type.Bool ( type (&&) ) import GHC.Generics ( Generic, Rep, from, to ) import Prelude -- rel8-import Rel8.Kind.Context ( Reifiable )-import Rel8.FCF ( Eval, Exp )-import Rel8.Generic.Map ( GMap, GMappable, gmap, gunmap )+import Rel8.Aggregate ( Aggregate )+import Rel8.Expr ( Expr )+import Rel8.FCF ( Exp, Eval ) import Rel8.Generic.Record ( Record(..) ) import Rel8.Generic.Table ( GAlgebra ) import qualified Rel8.Generic.Table.Record as G import qualified Rel8.Kind.Algebra as K ( Algebra(..) )-import Rel8.Schema.Context ( Col )-import Rel8.Schema.Context.Label ( Labelable )+import Rel8.Kind.Context ( SContext(..) )+import Rel8.Schema.Field ( Field ) import Rel8.Schema.HTable ( HTable ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Reify ( Reify, UnwrapReify )+import Rel8.Schema.Name ( Name ) import Rel8.Schema.Result ( Result ) import Rel8.Table- ( fromColumns, toColumns, reify, unreify- , TTable, TColumns, TContext, TUnreify+ ( Table, Columns, Context, fromColumns, toColumns+ , FromExprs, fromResult, toResult+ , Transpose+ , TTable, TColumns )+import Rel8.Table.Transpose ( Transposes ) -- | The kind of 'Rel8able' types@@ -52,6 +60,54 @@ type KRel8able = K.Rel8able +-- This is almost 'Data.Type.Equality.==', but we add an extra case.+type (==) :: k -> k -> Bool+type family a == b where+ -- This extra case is needed to solve the equation "a == Identity a", + -- which occurs when we have polymorphic Rel8ables + -- (e.g., newtype T a f = T { x :: Column f a })+ a == Identity a = 'False+ + -- These cases are exactly the same as those in 'Data.Type.Equality.==.+ f a == g b = f == g && a == b+ a == a = 'True+ _ == _ = 'False+++type Serialize :: Bool -> Type -> Type -> Constraint+class transposition ~ (a == Transpose Result expr) =>+ Serialize transposition expr a+ where+ serialize :: a -> Columns expr Result+ deserialize :: Columns expr Result -> a+++instance+ ( (a == Transpose Result expr) ~ 'True+ , Transposes Expr Result expr a+ )+ => Serialize 'True expr a+ where+ serialize = toColumns+ deserialize = fromColumns+++instance+ ( (a == Transpose Result expr) ~ 'False+ , Table (Context expr) expr+ , FromExprs expr ~ a+ )+ => Serialize 'False expr a+ where+ serialize = toResult @_ @expr+ deserialize = fromResult @_ @expr+++data TSerialize :: Type -> Type -> Exp Constraint+type instance Eval (TSerialize expr a) =+ Serialize (a == Transpose Result expr) expr a++ -- | This type class allows you to define custom 'Table's using higher-kinded -- data types. Higher-kinded data types are data types of the pattern: --@@ -97,79 +153,124 @@ type Rel8able :: K.Rel8able -> Constraint class HTable (GColumns t) => Rel8able t where type GColumns t :: K.HTable-- gfromColumns :: (Labelable context, Reifiable context)- => GColumns t (Col (Reify context)) -> t (Reify context)-- gtoColumns :: (Labelable context, Reifiable context)- => t (Reify context) -> GColumns t (Col (Reify context))+ type GFromExprs t :: Type - greify :: (Labelable context, Reifiable context)- => t context -> t (Reify context)+ gfromColumns :: SContext context -> GColumns t context -> t context+ gtoColumns :: SContext context -> t context -> GColumns t context - gunreify :: (Labelable context, Reifiable context)- => t (Reify context) -> t context+ gfromResult :: GColumns t Result -> GFromExprs t+ gtoResult :: GFromExprs t -> GColumns t Result - type GColumns t = G.GColumns TColumns (GRep t (Reify Result))+ type GColumns t = G.GColumns TColumns (GRep t Expr)+ type GFromExprs t = t Result default gfromColumns :: forall context.- ( Generic (Record (t (Reify context)))- , G.GTable (TTable (Reify context)) TColumns (Col (Reify context)) (GRep t (Reify context))- , G.GColumns TColumns (GRep t (Reify context)) ~ GColumns t+ ( SRel8able t Aggregate+ , SRel8able t Expr+ , forall table. SRel8able t (Field table)+ , SRel8able t Name+ , SSerialize t )- => GColumns t (Col (Reify context)) -> t (Reify context)- gfromColumns =- unrecord .- to .- G.gfromColumns @(TTable (Reify context)) @TColumns fromColumns+ => SContext context -> GColumns t context -> t context+ gfromColumns = \case+ SAggregate -> sfromColumns+ SExpr -> sfromColumns+ SField -> sfromColumns+ SName -> sfromColumns+ SResult -> sfromResult default gtoColumns :: forall context.- ( Generic (Record (t (Reify context)))- , G.GTable (TTable (Reify context)) TColumns (Col (Reify context)) (GRep t (Reify context))- , G.GColumns TColumns (GRep t (Reify context)) ~ GColumns t+ ( SRel8able t Aggregate+ , SRel8able t Expr+ , forall table. SRel8able t (Field table)+ , SRel8able t Name+ , SSerialize t )- => t (Reify context) -> GColumns t (Col (Reify context))- gtoColumns =- G.gtoColumns @(TTable (Reify context)) @TColumns toColumns .- from .- Record+ => SContext context -> t context -> GColumns t context+ gtoColumns = \case+ SAggregate -> stoColumns+ SExpr -> stoColumns+ SField -> stoColumns+ SName -> stoColumns+ SResult -> stoResult - default greify :: forall context.- ( Generic (Record (t context))- , Generic (Record (t (Reify context)))- , GMappable (TTable (Reify context)) (GRep t (Reify context))- , GRep t context ~ GMap TUnreify (GRep t (Reify context))- )- => t context -> t (Reify context)- greify =- unrecord .- to .- gunmap @(TTable (Reify context)) (Proxy @TUnreify) (reify Refl) .- from .- Record+ default gfromResult :: (SSerialize t, GFromExprs t ~ t Result)+ => GColumns t Result -> GFromExprs t+ gfromResult = sfromResult - default gunreify :: forall context.- ( Generic (Record (t context))- , Generic (Record (t (Reify context)))- , GMappable (TTable (Reify context)) (GRep t (Reify context))- , GRep t context ~ GMap TUnreify (GRep t (Reify context))- )- => t (Reify context) -> t context- gunreify =- unrecord .- to .- gmap @(TTable (Reify context)) (Proxy @TUnreify) (unreify Refl) .- from .- Record+ default gtoResult :: (SSerialize t, GFromExprs t ~ t Result)+ => GFromExprs t -> GColumns t Result+ gtoResult = stoResult type Algebra :: K.Rel8able -> K.Algebra-type Algebra t = GAlgebra (GRep t (Reify Result))+type Algebra t = GAlgebra (GRep t Expr) type GRep :: K.Rel8able -> K.Context -> Type -> Type type GRep t context = Rep (Record (t context)) -data TUnreifyContext :: Type -> Exp K.Context-type instance Eval (TUnreifyContext a) = UnwrapReify (Eval (TContext a))+type SRel8able :: K.Rel8able -> K.Context -> Constraint+class+ ( Generic (Record (t context))+ , G.GTable (TTable context) TColumns (GRep t context)+ , G.GColumns TColumns (GRep t context) ~ GColumns t+ )+ => SRel8able t context+instance+ ( Generic (Record (t context))+ , G.GTable (TTable context) TColumns (GRep t context)+ , G.GColumns TColumns (GRep t context) ~ GColumns t+ )+ => SRel8able t context+++type SSerialize :: K.Rel8able -> Constraint+type SSerialize t =+ ( Generic (Record (t Result))+ , G.GSerialize TSerialize TColumns (GRep t Expr) (GRep t Result)+ , G.GColumns TColumns (GRep t Expr) ~ GColumns t+ )+++sfromColumns :: forall t context. SRel8able t context+ => GColumns t context -> t context+sfromColumns =+ unrecord .+ to .+ G.gfromColumns @(TTable context) @TColumns fromColumns+++stoColumns :: forall t context. SRel8able t context+ => t context -> GColumns t context+stoColumns =+ G.gtoColumns @(TTable context) @TColumns toColumns .+ from .+ Record+++sfromResult :: forall t. SSerialize t+ => GColumns t Result -> t Result+sfromResult =+ unrecord .+ to .+ G.gfromResult+ @TSerialize+ @TColumns+ @(GRep t Expr)+ @(GRep t Result)+ (\(_ :: proxy x) -> deserialize @_ @x)+++stoResult :: forall t. SSerialize t+ => t Result -> GColumns t Result+stoResult =+ G.gtoResult+ @TSerialize+ @TColumns+ @(GRep t Expr)+ @(GRep t Result)+ (\(_ :: proxy x) -> serialize @_ @x) .+ from .+ Record
− src/Rel8/Generic/Rel8able/Test.hs
@@ -1,157 +0,0 @@-{-# language DataKinds #-}-{-# language DeriveAnyClass #-}-{-# language DeriveGeneric #-}-{-# language DerivingVia #-}-{-# language DuplicateRecordFields #-}-{-# language FlexibleInstances #-}-{-# language MultiParamTypeClasses #-}-{-# language StandaloneDeriving #-}-{-# language TypeFamilies #-}-{-# language UndecidableInstances #-}--{-# options_ghc -O0 #-}--module Rel8.Generic.Rel8able.Test- ( module Rel8.Generic.Rel8able.Test- )-where---- base-import GHC.Generics ( Generic )-import Prelude---- rel8-import Rel8---- text-import Data.Text ( Text )---data TableTest f = TableTest- { foo :: Column f Bool- , bar :: Column f (Maybe Bool)- }- deriving stock Generic- deriving anyclass Rel8able---data TablePair f = TablePair- { foo :: Column f Bool- , bars :: (Column f Text, Column f Text)- }- deriving stock Generic- deriving anyclass Rel8able---data TableMaybe f = TableMaybe- { foo :: Column f [Maybe Bool]- , bars :: HMaybe f (TablePair f, TablePair f)- }- deriving stock Generic- deriving anyclass Rel8able---data TableEither f = TableEither- { foo :: Column f Bool- , bars :: HEither f (HMaybe f (TablePair f, TablePair f)) (Column f Char)- }- deriving stock Generic- deriving anyclass Rel8able---data TableThese f = TableThese- { foo :: Column f Bool- , bars :: HThese f (TableMaybe f) (TableEither f)- }- deriving stock Generic- deriving anyclass Rel8able---data TableList f = TableList- { foo :: Column f Bool- , bars :: HList f (TableThese f)- }- deriving stock Generic- deriving anyclass Rel8able---data TableNonEmpty f = TableNonEmpty- { foo :: Column f Bool- , bars :: HNonEmpty f (TableList f, TableMaybe f)- }- deriving stock Generic- deriving anyclass Rel8able---data TableNest f = TableNest- { foo :: Column f Bool- , bars :: HList f (HMaybe f (TablePair f))- }- deriving stock Generic- deriving anyclass Rel8able---data S3Object = S3Object- { bucketName :: Text- , objectKey :: Text- }- deriving stock Generic---deriving via HKDT S3Object- instance Table Result S3Object---deriving via HKDT S3Object- instance x ~ HKD S3Object Expr => ToExprs x S3Object---data HKDSum = HKDSumA Text | HKDSumB Bool Char | HKDSumC- deriving stock Generic---deriving via HKDT HKDSum- instance Table Result HKDSum---deriving via HKDT HKDSum- instance x ~ HKD HKDSum Expr => ToExprs x HKDSum---data HKDTest f = HKDTest- { s3Object :: Lift f S3Object- , hkdSum :: Lift f HKDSum- } - deriving stock Generic- deriving anyclass Rel8able---data NonRecord f = NonRecord- (Column f Bool)- (Column f Char)- (Column f Char)- (Column f Char)- (Column f Char)- (Column f Char)- (Column f Char)- (Column f Char)- (Column f Char)- (Column f Char)- deriving stock Generic- deriving anyclass Rel8able---data TableSum f- = TableSumA (Column f Bool) (Column f Text)- | TableSumB- | TableSumC (Column f Text)- deriving stock Generic---data TableProduct f = TableProduct- { sum :: HADT f TableSum- , list :: TableList f- , foos :: HList f (HADT f TableSum, Lift f HKDSum, HKDTest f)- }- deriving stock Generic- deriving anyclass Rel8able
src/Rel8/Generic/Table.hs view
@@ -12,8 +12,7 @@ {-# language UndecidableInstances #-} module Rel8.Generic.Table- ( GGTable, GGColumns, GGContext, ggfromColumns, ggtoColumns, ggtable- , GGToExprs, ggfromResult, ggtoResult+ ( GGSerialize, GGColumns, ggfromResult, ggtoResult , GAlgebra ) where@@ -26,43 +25,19 @@ -- rel8 import Rel8.FCF ( Eval, Exp ) import Rel8.Generic.Table.ADT- ( GTableADT, GColumnsADT, gfromColumnsADT, gtoColumnsADT, gtableADT- , GToExprsADT, gtoResultADT, gfromResultADT- )-import Rel8.Generic.Table.Record- ( GTable, GColumns, GContext, gfromColumns, gtoColumns, gtable- , GToExprs, gtoResult, gfromResult+ ( GSerializeADT, GColumnsADT, gtoResultADT, gfromResultADT )+import Rel8.Generic.Table.Record ( GSerialize, GColumns, gtoResult, gfromResult ) import Rel8.Kind.Algebra ( Algebra( Product, Sum ) , SAlgebra( SProduct, SSum ) , KnownAlgebra, algebraSing )-import Rel8.Schema.Context ( Col ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Null ( Nullify )-import Rel8.Schema.Spec ( Spec( Spec ), SSpec ) import Rel8.Schema.Result ( Result ) -data GGTable- :: Algebra- -> (Type -> Exp Constraint)- -> (Type -> Exp K.HTable)- -> K.HContext- -> (Type -> Type)- -> Exp Constraint---type instance Eval (GGTable 'Product _Table _Columns context rep) =- GTable _Table _Columns context rep---type instance Eval (GGTable 'Sum _Table _Columns context rep) =- GTableADT _Table _Columns context rep---data GGToExprs+data GGSerialize :: Algebra -> (Type -> Type -> Exp Constraint) -> (Type -> Exp K.HTable)@@ -71,12 +46,12 @@ -> Exp Constraint -type instance Eval (GGToExprs 'Product _ToExprs _Columns exprs rep) =- GToExprs _ToExprs _Columns exprs rep+type instance Eval (GGSerialize 'Product _Serialize _Columns exprs rep) =+ GSerialize _Serialize _Columns exprs rep -type instance Eval (GGToExprs 'Sum _ToExprs _Columns exprs rep) =- GToExprsADT _ToExprs _Columns exprs rep+type instance Eval (GGSerialize 'Sum _Serialize _Columns exprs rep) =+ GSerializeADT _Serialize _Columns exprs rep data GGColumns@@ -92,19 +67,6 @@ type instance Eval (GGColumns 'Sum _Columns rep) = GColumnsADT _Columns rep -data GGContext- :: Algebra- -> (Type -> Exp K.Context)- -> (Type -> Type)- -> Exp K.Context---type instance Eval (GGContext 'Product _Context rep) = GContext _Context rep---type instance Eval (GGContext 'Sum _Context _rep) = Result-- type GAlgebra :: (Type -> Type) -> Algebra type family GAlgebra rep where GAlgebra (M1 _ _ rep) = GAlgebra rep@@ -115,74 +77,27 @@ GAlgebra (K1 _ _) = 'Product -ggfromColumns :: forall algebra _Table _Columns rep context x.- ( KnownAlgebra algebra- , Eval (GGTable algebra _Table _Columns context rep)- )- => (forall spec. algebra ~ 'Sum => context spec -> Col Result spec)- -> (forall spec. algebra ~ 'Sum => Col Result spec -> context spec)- -> (forall a. Eval (_Table a) => Eval (_Columns a) context -> a)- -> Eval (GGColumns algebra _Columns rep) context- -> rep x-ggfromColumns = case algebraSing @algebra of- SProduct -> \_ _ -> gfromColumns @_Table @_Columns- SSum -> gfromColumnsADT @_Table @_Columns---ggtoColumns :: forall algebra _Table _Columns rep context x.- ( KnownAlgebra algebra- , Eval (GGTable algebra _Table _Columns context rep)- )- => (forall spec. algebra ~ 'Sum => context spec -> Col Result spec)- -> (forall spec. algebra ~ 'Sum => Col Result spec -> context spec)- -> (forall a. Eval (_Table a) => a -> Eval (_Columns a) context)- -> rep x- -> Eval (GGColumns algebra _Columns rep) context-ggtoColumns = case algebraSing @algebra of- SProduct -> \_ _ -> gtoColumns @_Table @_Columns- SSum -> gtoColumnsADT @_Table @_Columns---ggtable :: forall algebra _Table _Columns rep context.- ( KnownAlgebra algebra- , Eval (GGTable algebra _Table _Columns context rep)- )- => (forall a proxy. Eval (_Table a) => proxy a -> Eval (_Columns a) context)- -> (forall a labels. ()- => SSpec ('Spec labels a)- -> context ('Spec labels a)- -> context ('Spec labels (Nullify a)))- -> Eval (GGColumns algebra _Columns rep) context-ggtable = case algebraSing @algebra of- SProduct -> \table _ -> gtable @_Table @_Columns @_ @rep table- SSum -> gtableADT @_Table @_Columns @_ @rep---ggfromResult :: forall algebra _ToExprs _Columns exprs rep x.+ggfromResult :: forall algebra _Serialize _Columns exprs rep x. ( KnownAlgebra algebra- , Eval (GGToExprs algebra _ToExprs _Columns exprs rep)+ , Eval (GGSerialize algebra _Serialize _Columns exprs rep) )- => (forall expr a proxy. Eval (_ToExprs expr a)- => proxy expr- -> Eval (_Columns expr) (Col Result)- -> a)- -> Eval (GGColumns algebra _Columns exprs) (Col Result)+ => (forall expr a proxy. Eval (_Serialize expr a)+ => proxy expr -> Eval (_Columns expr) Result -> a)+ -> Eval (GGColumns algebra _Columns exprs) Result -> rep x-ggfromResult = case algebraSing @algebra of- SProduct -> gfromResult @_ToExprs @_Columns @exprs- SSum -> gfromResultADT @_ToExprs @_Columns @exprs+ggfromResult f x = case algebraSing @algebra of+ SProduct -> gfromResult @_Serialize @_Columns @exprs @rep f x+ SSum -> gfromResultADT @_Serialize @_Columns @exprs @rep f x -ggtoResult :: forall algebra _ToExprs _Columns exprs rep x.+ggtoResult :: forall algebra _Serialize _Columns exprs rep x. ( KnownAlgebra algebra- , Eval (GGToExprs algebra _ToExprs _Columns exprs rep)+ , Eval (GGSerialize algebra _Serialize _Columns exprs rep) )- => (forall expr a proxy. Eval (_ToExprs expr a)- => proxy expr- -> a- -> Eval (_Columns expr) (Col Result))+ => (forall expr a proxy. Eval (_Serialize expr a)+ => proxy expr -> a -> Eval (_Columns expr) Result) -> rep x- -> Eval (GGColumns algebra _Columns exprs) (Col Result)-ggtoResult = case algebraSing @algebra of- SProduct -> gtoResult @_ToExprs @_Columns @exprs- SSum -> gtoResultADT @_ToExprs @_Columns @exprs+ -> Eval (GGColumns algebra _Columns exprs) Result+ggtoResult f x = case algebraSing @algebra of+ SProduct -> gtoResult @_Serialize @_Columns @exprs @rep f x+ SSum -> gtoResultADT @_Serialize @_Columns @exprs @rep f x
src/Rel8/Generic/Table/ADT.hs view
@@ -1,10 +1,8 @@ {-# language AllowAmbiguousTypes #-} {-# language DataKinds #-}-{-# language FlexibleContexts #-} {-# language FlexibleInstances #-} {-# language LambdaCase #-} {-# language MultiParamTypeClasses #-}-{-# language PolyKinds #-} {-# language RankNTypes #-} {-# language ScopedTypeVariables #-} {-# language StandaloneKindSignatures #-}@@ -14,13 +12,13 @@ {-# language UndecidableInstances #-} module Rel8.Generic.Table.ADT- ( GTableADT, GColumnsADT, gfromColumnsADT, gtoColumnsADT, gtableADT- , GTableADT', GColumnsADT'- , GToExprsADT, gfromResultADT, gtoResultADT+ ( GSerializeADT, GColumnsADT, gfromResultADT, gtoResultADT+ , GSerializeADT', GColumnsADT' ) where -- base+import Data.Functor.Identity ( Identity( Identity ) ) import Data.Kind ( Constraint, Type ) import Data.Proxy ( Proxy( Proxy ) ) import GHC.Generics@@ -33,23 +31,14 @@ -- rel8 import Rel8.FCF ( Eval, Exp )-import Rel8.Generic.Table.Record- ( GTable, GColumns, gtable- , GToExprs, gfromResult, gtoResult- )-import Rel8.Schema.Context.Label ( HLabelable, hlabeler, labeler, unlabeler )-import Rel8.Schema.HTable ( HTable, hmap )-import Rel8.Schema.HTable.Identity ( HIdentity( HType ), HType )+import Rel8.Generic.Table.Record ( GSerialize, GColumns, gfromResult, gtoResult )+import Rel8.Schema.HTable ( HTable )+import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) ) import Rel8.Schema.HTable.Label ( HLabel, hlabel, hunlabel ) import Rel8.Schema.HTable.Nullify ( HNullify, hnulls, hnullify, hunnullify ) import Rel8.Schema.HTable.Product ( HProduct( HProduct ) ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Null ( Nullify )-import Rel8.Schema.Spec ( Spec( Spec ), SSpec )-import Rel8.Schema.Result- ( Col( R ), Result- , null, nullifier, unnullifier- )+import Rel8.Schema.Result ( Result, null, nullifier, unnullifier ) import Rel8.Type.Tag ( Tag( Tag ) ) -- text@@ -61,7 +50,7 @@ -> (Type -> Type) -> K.HTable type family GColumnsADT _Columns rep where GColumnsADT _Columns (M1 D _ rep) =- GColumnsADT' _Columns (HLabel "tag" (HType Tag)) rep+ GColumnsADT' _Columns (HLabel "tag" (HIdentity Tag)) rep type GColumnsADT'@@ -75,246 +64,118 @@ HProduct htable (HLabel label (HNullify (GColumns _Columns rep))) -type GTableADT- :: (Type -> Exp Constraint)- -> (Type -> Exp K.HTable)- -> K.HContext -> (Type -> Type) -> Constraint-class GTableADT _Table _Columns context rep where- gfromColumnsADT :: ()- => (forall spec. context spec -> Col Result spec)- -> (forall spec. Col Result spec -> context spec)- -> (forall a. Eval (_Table a) => Eval (_Columns a) context -> a)- -> GColumnsADT _Columns rep context- -> rep x-- gtoColumnsADT :: ()- => (forall spec. context spec -> Col Result spec)- -> (forall spec. Col Result spec -> context spec)- -> (forall a. Eval (_Table a) => a -> Eval (_Columns a) context)- -> rep x- -> GColumnsADT _Columns rep context-- gtableADT :: ()- => (forall a proxy. Eval (_Table a) => proxy a -> Eval (_Columns a) context)- -> (forall a labels. ()- => SSpec ('Spec labels a)- -> context ('Spec labels a)- -> context ('Spec labels (Nullify a)))- -> GColumnsADT _Columns rep context---instance- ( htable ~ HLabel "tag" (HType Tag)- , HTable (GColumnsADT _Columns (M1 D meta rep))- , Eval (_Table (htable context))- , Eval (_Columns (htable context)) ~ htable- , GTableADT' _Table _Columns htable context rep- , GToExprsADT' (Const _Table) _Columns htable rep rep- )- => GTableADT _Table _Columns context (M1 D meta rep)- where- gfromColumnsADT fromContext toContext fromColumns =- gfromResultADT @(Const _Table) @_Columns @(M1 D meta rep) @(M1 D meta rep)- (const (fromColumns . hmap toContext)) .- hmap fromContext-- gtoColumnsADT fromContext toContext toColumns =- hmap toContext .- gtoResultADT @(Const _Table) @_Columns @(M1 D meta rep) @(M1 D meta rep)- (const (hmap fromContext . toColumns))-- gtableADT table hnullifier =- gtableADT' @_Table @_Columns @htable @context @rep table hnullifier htable- where- htable = table (Proxy @(htable context))---type GTableADT'- :: (Type -> Exp Constraint)- -> (Type -> Exp K.HTable)- -> K.HTable -> K.HContext -> (Type -> Type) -> Constraint-class GTableADT' _Table _Columns htable context rep where- gtableADT' :: ()- => (forall a proxy. Eval (_Table a) => proxy a -> Eval (_Columns a) context)- -> (forall a labels. ()- => SSpec ('Spec labels a)- -> context ('Spec labels a)- -> context ('Spec labels (Nullify a)))- -> htable context- -> GColumnsADT' _Columns htable rep context---instance- ( htable' ~ GColumnsADT' _Columns htable a- , GTableADT' _Table _Columns htable context a- , GTableADT' _Table _Columns htable' context b- )- => GTableADT' _Table _Columns htable context (a :+: b)- where- gtableADT' table hnullifier =- gtableADT' @_Table @_Columns @_ @_ @b table hnullifier .- gtableADT' @_Table @_Columns @_ @_ @a table hnullifier---instance meta ~ 'MetaCons label _fixity _isRecord =>- GTableADT' _Table _Columns htable context (M1 C meta U1)- where- gtableADT' _ _ = id---instance {-# OVERLAPPABLE #-}- ( HTable (GColumns _Columns rep)- , GTable _Table _Columns context rep- , HLabelable context- , meta ~ 'MetaCons label _fixity _isRecord- , KnownSymbol label- , GColumnsADT' _Columns htable (M1 C ('MetaCons label _fixity _isRecord) rep) ~- HProduct htable (HLabel label (HNullify (GColumns _Columns rep)))- )- => GTableADT' _Table _Columns htable context (M1 C meta rep)- where- gtableADT' table hnullifier htable =- HProduct htable $- hlabel hlabeler $- hnullify hnullifier $- gtable @_Table @_Columns @_ @rep table---type GToExprsADT+type GSerializeADT :: (Type -> Type -> Exp Constraint) -> (Type -> Exp K.HTable) -> (Type -> Type) -> (Type -> Type) -> Constraint-class GToExprsADT _ToExprs _Columns exprs rep where+class GSerializeADT _Serialize _Columns exprs rep where gfromResultADT :: ()- => (forall expr a proxy.- ( Eval (_ToExprs expr a)- , HTable (Eval (_Columns expr))- )- => proxy expr- -> Eval (_Columns expr) (Col Result)- -> a)- -> GColumnsADT _Columns exprs (Col Result)+ => (forall expr a proxy. Eval (_Serialize expr a)+ => proxy expr -> Eval (_Columns expr) Result -> a)+ -> GColumnsADT _Columns exprs Result -> rep x gtoResultADT :: ()- => (forall expr a proxy.- ( Eval (_ToExprs expr a)- , HTable (Eval (_Columns expr))- )- => proxy expr- -> a- -> Eval (_Columns expr) (Col Result))+ => (forall expr a proxy. Eval (_Serialize expr a)+ => proxy expr -> a -> Eval (_Columns expr) Result) -> rep x- -> GColumnsADT _Columns exprs (Col Result)+ -> GColumnsADT _Columns exprs Result instance- ( htable ~ HLabel "tag" (HType Tag)- , GToExprsADT' _ToExprs _Columns htable exprs rep+ ( htable ~ HLabel "tag" (HIdentity Tag)+ , GSerializeADT' _Serialize _Columns htable exprs rep )- => GToExprsADT _ToExprs _Columns (M1 D meta exprs) (M1 D meta rep)+ => GSerializeADT _Serialize _Columns (M1 D meta exprs) (M1 D meta rep) where gfromResultADT fromResult columns =- case gfromResultADT' @_ToExprs @_Columns @_ @exprs fromResult tag columns of+ case gfromResultADT' @_Serialize @_Columns @htable @exprs @rep fromResult tag columns of Just rep -> M1 rep _ -> error "ADT.fromColumns: mismatch between tag and data" where- tag = (\(HType (R a)) -> a) . hunlabel @_ @"tag" unlabeler+ tag = (\(HIdentity (Identity a)) -> a) . hunlabel @"tag" gtoResultADT toResult (M1 rep) =- gtoResultADT' @_ToExprs @_Columns @_ @exprs toResult tag (Just rep)+ gtoResultADT' @_Serialize @_Columns @htable @exprs @rep toResult tag (Just rep) where- tag = hlabel @_ @"tag" labeler . HType . R+ tag = hlabel @"tag" . HIdentity . Identity -type GToExprsADT'+type GSerializeADT' :: (Type -> Type -> Exp Constraint) -> (Type -> Exp K.HTable) -> K.HTable -> (Type -> Type) -> (Type -> Type) -> Constraint-class GToExprsADT' _ToExprs _Columns htable exprs rep where- gfromResultADT' :: ()- => (forall expr a proxy.- ( Eval (_ToExprs expr a)- , HTable (Eval (_Columns expr))- )- => proxy expr- -> Eval (_Columns expr) (Col Result)- -> a)- -> (htable (Col Result) -> Tag)- -> GColumnsADT' _Columns htable exprs (Col Result)+class GSerializeADT' _Serialize _Columns htable exprs rep where+ gfromResultADT' :: context ~ Result+ => (forall expr a proxy. Eval (_Serialize expr a)+ => proxy expr -> Eval (_Columns expr) context -> a)+ -> (htable Result -> Tag)+ -> GColumnsADT' _Columns htable exprs context -> Maybe (rep x) - gtoResultADT' :: ()- => (forall expr a proxy.- ( Eval (_ToExprs expr a)- , HTable (Eval (_Columns expr))- )- => proxy expr- -> a- -> Eval (_Columns expr) (Col Result))- -> (Tag -> htable (Col Result))+ gtoResultADT' :: context ~ Result+ => (forall expr a proxy. Eval (_Serialize expr a)+ => proxy expr -> a -> Eval (_Columns expr) context)+ -> (Tag -> htable Result) -> Maybe (rep x)- -> GColumnsADT' _Columns htable exprs (Col Result)+ -> GColumnsADT' _Columns htable exprs context extract :: GColumnsADT' _Columns htable exprs context -> htable context instance- ( htable' ~ GColumnsADT' _Columns htable a- , GToExprsADT' _ToExprs _Columns htable a rep1- , GToExprsADT' _ToExprs _Columns htable' b rep2+ ( htable' ~ GColumnsADT' _Columns htable exprs1+ , GSerializeADT' _Serialize _Columns htable exprs1 a+ , GSerializeADT' _Serialize _Columns htable' exprs2 b )- => GToExprsADT' _ToExprs _Columns htable (a :+: b) (rep1 :+: rep2)+ => GSerializeADT' _Serialize _Columns htable (exprs1 :+: exprs2) (a :+: b) where gfromResultADT' fromResult f columns = case ma of Just a -> Just (L1 a) Nothing -> R1 <$>- gfromResultADT' @_ToExprs @_Columns @_ @b @rep2+ gfromResultADT' @_Serialize @_Columns @_ @exprs2 @b fromResult- (f . extract @_ToExprs @_Columns @_ @a @rep1)+ (f . extract @_Serialize @_Columns @_ @exprs1 @a) columns where ma =- gfromResultADT' @_ToExprs @_Columns @_ @a @rep1+ gfromResultADT' @_Serialize @_Columns @_ @exprs1 @a fromResult f- (extract @_ToExprs @_Columns @_ @b @rep2 columns)+ (extract @_Serialize @_Columns @_ @exprs2 @b columns) gtoResultADT' toResult tag = \case Just (L1 a) ->- gtoResultADT' @_ToExprs @_Columns @_ @b @rep2+ gtoResultADT' @_Serialize @_Columns @_ @exprs2 @b toResult- (\_ -> gtoResultADT' @_ToExprs @_Columns @_ @a @rep1+ (\_ -> gtoResultADT' @_Serialize @_Columns @_ @exprs1 @a toResult tag (Just a)) Nothing Just (R1 b) ->- gtoResultADT' @_ToExprs @_Columns @_ @b @rep2+ gtoResultADT' @_Serialize @_Columns @_ @exprs2 @b toResult (\tag' ->- gtoResultADT' @_ToExprs @_Columns @_ @a @rep1+ gtoResultADT' @_Serialize @_Columns @_ @exprs1 @a toResult (\_ -> tag tag') Nothing) (Just b) Nothing ->- gtoResultADT' @_ToExprs @_Columns @_ @b @rep2+ gtoResultADT' @_Serialize @_Columns @_ @exprs2 @b toResult- (\_ -> gtoResultADT' @_ToExprs @_Columns @_ @a @rep1 toResult tag Nothing)+ (\_ -> gtoResultADT' @_Serialize @_Columns @_ @exprs1 @a toResult tag Nothing) Nothing extract =- extract @_ToExprs @_Columns @_ @a @rep1 .- extract @_ToExprs @_Columns @_ @b @rep2+ extract @_Serialize @_Columns @_ @exprs1 @a .+ extract @_Serialize @_Columns @_ @exprs2 @b -instance- ( meta ~ 'MetaCons label _fixity _isRecord- , KnownSymbol label- )- => GToExprsADT' _ToExprs _Columns htable (M1 C meta U1) (M1 C meta U1)+instance (meta ~ 'MetaCons label _fixity _isRecord, KnownSymbol label) =>+ GSerializeADT' _Serialize _Columns _htable (M1 C meta U1) (M1 C meta U1) where gfromResultADT' _ tag columns | tag columns == tag' = Just (M1 U1)@@ -331,33 +192,29 @@ instance {-# OVERLAPPABLE #-} ( HTable (GColumns _Columns exprs)- , GToExprs _ToExprs _Columns exprs rep+ , GSerialize _Serialize _Columns exprs rep , meta ~ 'MetaCons label _fixity _isRecord , KnownSymbol label- , GColumnsADT' _Columns htable (M1 C meta exprs) ~+ , GColumnsADT' _Columns htable (M1 C ('MetaCons label _fixity _isRecord) exprs) ~ HProduct htable (HLabel label (HNullify (GColumns _Columns exprs))) )- => GToExprsADT' _ToExprs _Columns htable (M1 C meta exprs) (M1 C meta rep)+ => GSerializeADT' _Serialize _Columns htable (M1 C meta exprs) (M1 C meta rep) where gfromResultADT' fromResult tag (HProduct a b) | tag a == tag' =- M1 . gfromResult @_ToExprs @_Columns @exprs fromResult <$>- hunnullify unnullifier (hunlabel unlabeler b)+ M1 . gfromResult @_Serialize @_Columns @exprs @rep fromResult <$>+ hunnullify unnullifier (hunlabel b) | otherwise = Nothing where tag' = Tag $ pack $ symbolVal (Proxy @label) gtoResultADT' toResult tag = \case- Nothing -> HProduct (tag tag') (hlabel labeler (hnulls (const null)))+ Nothing -> HProduct (tag tag') (hlabel (hnulls (const null))) Just (M1 rep) -> HProduct (tag tag') $- hlabel labeler $+ hlabel $ hnullify nullifier $- gtoResult @_ToExprs @_Columns @exprs toResult rep+ gtoResult @_Serialize @_Columns @exprs @rep toResult rep where tag' = Tag $ pack $ symbolVal (Proxy @label) extract (HProduct a _) = a---data Const :: (a -> Exp Constraint) -> a -> a -> Exp Constraint-type instance Eval (Const f x a) = (Eval (f a), x ~ a)
src/Rel8/Generic/Table/Record.hs view
@@ -14,7 +14,7 @@ module Rel8.Generic.Table.Record ( GTable, GColumns, GContext, gfromColumns, gtoColumns, gtable- , GToExprs, gfromResult, gtoResult+ , GSerialize, gfromResult, gtoResult ) where @@ -26,18 +26,13 @@ , C, D, S , Meta( MetaSel ) )-import GHC.TypeLits ( KnownSymbol ) import Prelude hiding ( null ) -- rel8 import Rel8.FCF ( Eval, Exp )-import Rel8.Schema.Context ( Col )-import Rel8.Schema.Context.Label ( HLabelable, hlabeler, hunlabeler )-import Rel8.Schema.HTable ( HTable ) import Rel8.Schema.HTable.Label ( HLabel, hlabel, hunlabel ) import Rel8.Schema.HTable.Product ( HProduct(..) ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Result ( Result ) type GColumns :: (Type -> Exp K.HTable) -> (Type -> Type) -> K.HTable@@ -60,8 +55,8 @@ type GTable :: (Type -> Exp Constraint) -> (Type -> Exp K.HTable)- -> K.HContext -> (Type -> Type) -> Constraint-class GTable _Table _Columns context rep+ -> (Type -> Type) -> Constraint+class GTable _Table _Columns rep where gfromColumns :: () => (forall a. Eval (_Table a) => Eval (_Columns a) context -> a)@@ -78,129 +73,109 @@ -> GColumns _Columns rep context -instance GTable _Table _Columns context rep =>- GTable _Table _Columns context (M1 D c rep)+type GSerialize+ :: (Type -> Type -> Exp Constraint)+ -> (Type -> Exp K.HTable)+ -> (Type -> Type) -> (Type -> Type) -> Constraint+class GSerialize _Serialize _Columns exprs rep where- gfromColumns fromColumns =- M1 . gfromColumns @_Table @_Columns @context @rep fromColumns- gtoColumns toColumns (M1 a) =- gtoColumns @_Table @_Columns @context @rep toColumns a- gtable = gtable @_Table @_Columns @context @rep+ gfromResult :: ()+ => (forall expr a proxy. Eval (_Serialize expr a)+ => proxy expr -> Eval (_Columns expr) context -> a)+ -> GColumns _Columns exprs context+ -> rep x + gtoResult :: ()+ => (forall expr a proxy. Eval (_Serialize expr a)+ => proxy expr -> a -> Eval (_Columns expr) context)+ -> rep x+ -> GColumns _Columns exprs context -instance GTable _Table _Columns context rep =>- GTable _Table _Columns context (M1 C c rep)++instance GTable _Table _Columns rep => GTable _Table _Columns (M1 D c rep) where gfromColumns fromColumns =- M1 . gfromColumns @_Table @_Columns @context @rep fromColumns+ M1 . gfromColumns @_Table @_Columns @rep fromColumns gtoColumns toColumns (M1 a) =- gtoColumns @_Table @_Columns @context @rep toColumns a- gtable = gtable @_Table @_Columns @context @rep+ gtoColumns @_Table @_Columns @rep toColumns a+ gtable = gtable @_Table @_Columns @rep -instance- ( GTable _Table _Columns context rep1- , GTable _Table _Columns context rep2- )- => GTable _Table _Columns context (rep1 :*: rep2)+instance GSerialize _Serialize _Columns exprs rep =>+ GSerialize _Serialize _Columns (M1 D c exprs) (M1 D c rep) where- gfromColumns fromColumns (HProduct a b) =- gfromColumns @_Table @_Columns @context @rep1 fromColumns a :*:- gfromColumns @_Table @_Columns @context @rep2 fromColumns b- gtoColumns toColumns (a :*: b) = HProduct- (gtoColumns @_Table @_Columns @context @rep1 toColumns a)- (gtoColumns @_Table @_Columns @context @rep2 toColumns b)- gtable table = HProduct- (gtable @_Table @_Columns @context @rep1 table)- (gtable @_Table @_Columns @context @rep2 table)+ gfromResult fromResult =+ M1 . gfromResult @_Serialize @_Columns @exprs @rep fromResult+ gtoResult toResult (M1 a) =+ gtoResult @_Serialize @_Columns @exprs @rep toResult a -instance- ( HTable (Eval (_Columns a))- , Eval (_Table a)- , HLabelable context- , KnownSymbol label- , meta ~ 'MetaSel ('Just label) _su _ss _ds- , k1 ~ K1 i a- )- => GTable _Table _Columns context (M1 S meta k1)+instance GTable _Table _Columns rep => GTable _Table _Columns (M1 C c rep) where- gfromColumns fromColumns = M1 . K1 . fromColumns . hunlabel hunlabeler- gtoColumns toColumns (M1 (K1 a)) = hlabel hlabeler (toColumns a)- gtable table = hlabel hlabeler (table (Proxy @a))---type GToExprs- :: (Type -> Type -> Exp Constraint)- -> (Type -> Exp K.HTable)- -> (Type -> Type) -> (Type -> Type) -> Constraint-class GToExprs _ToExprs _Columns exprs rep where- gfromResult :: ()- => (forall expr a proxy.- ( Eval (_ToExprs expr a)- , HTable (Eval (_Columns expr))- )- => proxy expr- -> Eval (_Columns expr) (Col Result)- -> a)- -> GColumns _Columns exprs (Col Result)- -> rep x- gtoResult :: ()- => (forall expr a proxy.- ( Eval (_ToExprs expr a)- , HTable (Eval (_Columns expr))- )- => proxy expr- -> a- -> Eval (_Columns expr) (Col Result))- -> rep x- -> GColumns _Columns exprs (Col Result)+ gfromColumns fromColumns =+ M1 . gfromColumns @_Table @_Columns @rep fromColumns+ gtoColumns toColumns (M1 a) =+ gtoColumns @_Table @_Columns @rep toColumns a+ gtable = gtable @_Table @_Columns @rep -instance GToExprs _ToExprs _Columns exprs rep =>- GToExprs _ToExprs _Columns (M1 D c exprs) (M1 D c rep)+instance GSerialize _Serialize _Columns exprs rep =>+ GSerialize _Serialize _Columns (M1 C c exprs) (M1 C c rep) where gfromResult fromResult =- M1 . gfromResult @_ToExprs @_Columns @exprs fromResult+ M1 . gfromResult @_Serialize @_Columns @exprs @rep fromResult gtoResult toResult (M1 a) =- gtoResult @_ToExprs @_Columns @exprs toResult a+ gtoResult @_Serialize @_Columns @exprs @rep toResult a -instance GToExprs _ToExprs _Columns exprs rep =>- GToExprs _ToExprs _Columns (M1 C c exprs) (M1 C c rep)+instance (GTable _Table _Columns rep1, GTable _Table _Columns rep2) =>+ GTable _Table _Columns (rep1 :*: rep2) where- gfromResult fromResult =- M1 . gfromResult @_ToExprs @_Columns @exprs fromResult- gtoResult toResult (M1 a) =- gtoResult @_ToExprs @_Columns @exprs toResult a+ gfromColumns fromColumns (HProduct a b) =+ gfromColumns @_Table @_Columns @rep1 fromColumns a :*:+ gfromColumns @_Table @_Columns @rep2 fromColumns b+ gtoColumns toColumns (a :*: b) = HProduct+ (gtoColumns @_Table @_Columns @rep1 toColumns a)+ (gtoColumns @_Table @_Columns @rep2 toColumns b)+ gtable table = HProduct+ (gtable @_Table @_Columns @rep1 table)+ (gtable @_Table @_Columns @rep2 table) instance- ( GToExprs _ToExprs _Columns exprs1 rep1- , GToExprs _ToExprs _Columns exprs2 rep2+ ( GSerialize _Serialize _Columns expr1 rep1+ , GSerialize _Serialize _Columns expr2 rep2 )- => GToExprs _ToExprs _Columns (exprs1 :*: exprs2) (rep1 :*: rep2)+ => GSerialize _Serialize _Columns (expr1 :*: expr2) (rep1 :*: rep2) where gfromResult fromResult (HProduct a b) =- gfromResult @_ToExprs @_Columns @exprs1 fromResult a :*:- gfromResult @_ToExprs @_Columns @exprs2 fromResult b+ gfromResult @_Serialize @_Columns @expr1 @rep1 fromResult a :*:+ gfromResult @_Serialize @_Columns @expr2 @rep2 fromResult b gtoResult toResult (a :*: b) = HProduct- (gtoResult @_ToExprs @_Columns @exprs1 toResult a)- (gtoResult @_ToExprs @_Columns @exprs2 toResult b)+ (gtoResult @_Serialize @_Columns @expr1 @rep1 toResult a)+ (gtoResult @_Serialize @_Columns @expr2 @rep2 toResult b) instance- ( Eval (_ToExprs exprs a)- , HTable (Eval (_Columns exprs))- , KnownSymbol label+ ( Eval (_Table a) , meta ~ 'MetaSel ('Just label) _su _ss _ds- , k1 ~ K1 i exprs+ , k1 ~ K1 i a+ )+ => GTable _Table _Columns (M1 S meta k1)+ where+ gfromColumns fromColumns = M1 . K1 . fromColumns . hunlabel+ gtoColumns toColumns (M1 (K1 a)) = hlabel (toColumns a)+ gtable table = hlabel (table (Proxy @a))+++instance+ ( Eval (_Serialize expr a)+ , meta ~ 'MetaSel ('Just label) _su _ss _ds+ , k1 ~ K1 i expr , k1' ~ K1 i a )- => GToExprs _ToExprs _Columns (M1 S meta k1) (M1 S meta k1')+ => GSerialize _Serialize _Columns (M1 S meta k1) (M1 S meta k1') where- gfromResult fromResult =- M1 . K1 . fromResult (Proxy @exprs) . hunlabel hunlabeler- gtoResult toResult (M1 (K1 a)) =- hlabel hlabeler (toResult (Proxy @exprs) a)+ gfromResult fromResult = M1 . K1 . fromResult (Proxy @expr) . hunlabel+ gtoResult toResult (M1 (K1 a)) = hlabel (toResult (Proxy @expr) a)
src/Rel8/Kind/Context.hs view
@@ -1,13 +1,11 @@ {-# language DataKinds #-} {-# language GADTs #-}-{-# language LambdaCase #-} {-# language StandaloneKindSignatures #-}+{-# language TypeSynonymInstances #-} module Rel8.Kind.Context ( Reifiable( contextSing ) , SContext(..)- , sReifiable- , sLabelable ) where @@ -18,12 +16,9 @@ -- rel8 import Rel8.Aggregate ( Aggregate ) import Rel8.Expr ( Expr )-import Rel8.Schema.Dict ( Dict( Dict ) )-import Rel8.Schema.Context ( Interpretation )-import Rel8.Schema.Context.Label ( Labelable )+import Rel8.Schema.Field ( Field ) import Rel8.Schema.Kind ( Context ) import Rel8.Schema.Name ( Name )-import Rel8.Schema.Reify ( Reify ) import Rel8.Schema.Result ( Result ) @@ -31,13 +26,13 @@ data SContext context where SAggregate :: SContext Aggregate SExpr :: SContext Expr+ SField :: SContext (Field table) SName :: SContext Name SResult :: SContext Result- SReify :: SContext context -> SContext (Reify context) type Reifiable :: Context -> Constraint-class Interpretation context => Reifiable context where+class Reifiable context where contextSing :: SContext context @@ -49,33 +44,13 @@ contextSing = SExpr +instance Reifiable (Field table) where+ contextSing = SField++ instance Reifiable Result where contextSing = SResult instance Reifiable Name where contextSing = SName---instance Reifiable context => Reifiable (Reify context) where- contextSing = SReify contextSing---sReifiable :: SContext context -> Dict Reifiable context-sReifiable = \case- SAggregate -> Dict- SExpr -> Dict- SName -> Dict- SResult -> Dict- SReify context -> case sReifiable context of- Dict -> Dict---sLabelable :: SContext context -> Dict Labelable context-sLabelable = \case- SAggregate -> Dict- SExpr -> Dict- SName -> Dict- SResult -> Dict- SReify context -> case sLabelable context of- Dict -> Dict
− src/Rel8/Kind/Labels.hs
@@ -1,57 +0,0 @@-{-# language DataKinds #-}-{-# language FlexibleContexts #-}-{-# language FlexibleInstances #-}-{-# language GADTs #-}-{-# language LambdaCase #-}-{-# language StandaloneKindSignatures #-}-{-# language TypeOperators #-}--module Rel8.Kind.Labels- ( Labels- , SLabels( SNil, SCons )- , KnownLabels( labelsSing )- , renderLabels- )-where---- base-import Data.Kind ( Constraint, Type )-import Data.List.NonEmpty ( NonEmpty, nonEmpty )-import Data.Maybe ( fromMaybe )-import Data.Proxy ( Proxy( Proxy ) )-import GHC.TypeLits ( KnownSymbol, Symbol, symbolVal )-import Prelude---type Labels :: Type-type Labels = [Symbol]---type SLabels :: Labels -> Type-data SLabels labels where- SNil :: SLabels '[]- SCons :: KnownSymbol label => Proxy label -> SLabels labels -> SLabels (label ': labels)---type KnownLabels :: Labels -> Constraint-class KnownLabels labels where- labelsSing :: SLabels labels---instance KnownLabels '[] where- labelsSing = SNil---instance (KnownSymbol label, KnownLabels labels) =>- KnownLabels (label ': labels)- where- labelsSing = SCons Proxy labelsSing---renderLabels :: SLabels labels -> NonEmpty String-renderLabels = fromMaybe (pure "anon") . nonEmpty . go- where- go :: SLabels labels -> [String]- go = \case- SNil -> []- SCons label labels -> symbolVal label : go labels
src/Rel8/Order.hs view
@@ -26,7 +26,7 @@ -- instances. -- -- A common pattern is to use '<>' to combine multiple orderings in sequence,--- and '>$<' (from 'Contravariant') to select individual columns.+-- and 'Data.Functor.Contravariant.>$<' to select individual columns. type Order :: Type -> Type newtype Order a = Order (Opaleye.Order a) deriving newtype (Contravariant, Divisible, Decidable, Semigroup, Monoid)
src/Rel8/Query.hs view
@@ -1,5 +1,3 @@-{-# language DerivingVia #-}-{-# language GeneralizedNewtypeDeriving #-} {-# language StandaloneKindSignatures #-} module Rel8.Query@@ -8,35 +6,198 @@ where -- base+import Control.Applicative ( liftA2 )+import Control.Monad ( liftM2 ) import Data.Kind ( Type )+import Data.Monoid ( Any( Any ) ) import Prelude -- opaleye-import qualified Opaleye.Select as Opaleye+import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye+import qualified Opaleye.Internal.PackMap as Opaleye+import qualified Opaleye.Internal.PrimQuery as Opaleye+import qualified Opaleye.Internal.QueryArr as Opaleye+import qualified Opaleye.Internal.Tag as Opaleye -- rel8 import Rel8.Query.Set ( unionAll )+import Rel8.Query.Opaleye ( fromOpaleye ) import Rel8.Query.Values ( values )+import Rel8.Table ( fromColumns, toColumns ) import Rel8.Table.Alternative ( AltTable, (<|>:) , AlternativeTable, emptyTable )+import Rel8.Table.Projection ( Projectable, apply, project ) -- semigroupoids-import Data.Functor.Apply ( Apply, WrappedApplicative(..) )+import Data.Functor.Apply ( Apply, (<.>) ) import Data.Functor.Bind ( Bind, (>>-) ) -- | The @Query@ monad allows you to compose a @SELECT@ query. This monad has -- semantics similar to the list (@[]@) monad. type Query :: Type -> Type-newtype Query a = Query (Opaleye.Select a)- deriving newtype (Functor, Applicative, Monad)- deriving Apply via (WrappedApplicative Opaleye.Select)+newtype Query a =+ Query (+ -- This is based on Opaleye's Select monad, but with two addtions. We+ -- maintain a stack of PrimExprs from parent previous subselects. In+ -- practice, these are always the results of dummy calls to random().+ --+ -- We also return a Bool that indicates to the parent subselect whether+ -- or not that stack of PrimExprs were used at any point. If they weren't,+ -- then the call to random() is never added to the query.+ --+ -- This is all needed to implement evaluate. Consider the following code:+ --+ -- do+ -- x <- values [lit 'a', lit 'b', lit 'c']+ -- y <- evaluate $ nextval "user_id_seq"+ -- pure (x, y)+ --+ -- If we just used Opaleye's Select monad directly, the SQL would come out+ -- like this:+ --+ -- SELECT+ -- a, b+ -- FROM+ -- (VALUES ('a'), ('b'), ('c')) Q1(a),+ -- LATERAL (SELECT nextval('user_id_seq')) Q2(b);+ --+ -- From the Haskell code, you would intuitively expect to get back the+ -- results of three different calls to nextval(), but from Postgres' point+ -- of view, because the Q2 subquery doesn't reference anything from the Q1+ -- query, it thinks it only needs to call nextval() once. This is actually+ -- exactly the same problem you get with the deprecated ListT IO monad from+ -- the transformers package — *> behaves differently to >>=, so+ -- using ApplicativeDo can change the results of a program. ApplicativeDo+ -- is exactly the optimisation Postgres does on a "LATERAL" query that+ -- doesn't make any references to previous subselects.+ --+ -- Rel8's solution is generate the following SQL instead:+ --+ -- SELECT+ -- a, b+ -- FROM+ -- (SELECT+ -- random() AS dummy,+ -- *+ -- FROM+ -- (VALUES ('a'), ('b'), ('c')) Q1(a)) Q1,+ -- LATERAL (SELECT+ -- CASE+ -- WHEN dummy IS NOT NULL+ -- THEN nextval('user_id_seq')+ -- END) Q2(b);+ --+ -- We use random() here as the dummy value (and not some constant) because+ -- Postgres will again optimize if it sees that a value is constant+ -- (and thus only call nextval() once), but because random() is marked as+ -- VOLATILE, this inhibits Postgres from doing that optimisation.+ --+ -- Why not just reference the a column from the previous query directly+ -- instead of adding a dummy value? Basically, even if we extract out all+ -- the bindings introduced in a PrimQuery, we can't always be sure which+ -- ones refer to constant values, so if we end up laterally referencing a+ -- constant value, then all of this would be for nothing.+ --+ -- Why not just add the call to the previous subselect directly, like so:+ --+ -- SELECT+ -- a, b+ -- FROM+ -- (SELECT+ -- nextval('user_id_seq') AS eval,+ -- *+ -- FROM+ -- (VALUES ('a'), ('b'), ('c')) Q1(a)) Q1,+ -- LATERAL (SELECT eval) Q2(b);+ --+ -- That would work in this case. But consider the following Rel8 code:+ --+ -- do+ -- x <- values [lit 'a', lit 'b', lit 'c']+ -- y <- values [lit 'd', lit 'e', lit 'f']+ -- z <- evaluate $ nextval "user_id_seq"+ -- pure (x, y, z)+ --+ -- How many calls to nextval should there be? Our Haskell intuition says+ -- nine. But that's not what you would get if you used the above+ -- technique. The problem is, which VALUES query should the nextval be+ -- added to? You can choose one or the other to get three calls to+ -- nextval, but you still need to make a superfluous LATERAL references to+ -- the other if you want nine calls. So for the above Rel8 code we generate+ -- the following SQL:+ --+ -- SELECT+ -- a, b, c+ -- FROM+ -- (SELECT+ -- random() AS dummy,+ -- *+ -- FROM+ -- (VALUES ('a'), ('b'), ('c')) Q1(a)) Q1,+ -- (SELECT+ -- random() AS dummy,+ -- *+ -- FROM+ -- (VALUES ('d'), ('e'), ('f')) Q2(b)) Q2,+ -- LATERAL (SELECT+ -- CASE+ -- WHEN Q1.dummy IS NOT NULL AND Q2.dummy IS NOT NULL+ -- THEN nextval('user_id_seq')+ -- END) Q3(c);+ --+ -- This gives nine calls to nextval() as we would expect.+ [Opaleye.PrimExpr] -> Opaleye.Select (Any, a)+ ) +instance Projectable Query where+ project f = fmap (fromColumns . apply f . toColumns)+++instance Functor Query where+ fmap f (Query a) = Query (fmap (fmap (fmap f)) a)+++instance Apply Query where+ (<.>) = (<*>)+++instance Applicative Query where+ pure = fromOpaleye . pure+ liftA2 = liftM2++ instance Bind Query where (>>-) = (>>=)+++instance Monad Query where+ Query q >>= f = Query $ \dummies -> Opaleye.QueryArr $ \(_, tag) ->+ let+ Opaleye.QueryArr qa = q dummies+ ((m, a), query, tag') = qa ((), tag)+ Query q' = f a+ (dummies', query', tag'') =+ ( dummy : dummies+ , \lateral -> Opaleye.Rebind True bindings . query lateral+ , Opaleye.next tag'+ )+ where+ (dummy, bindings) = Opaleye.run $ name random+ where+ random = Opaleye.FunExpr "random" []+ name = Opaleye.extractAttr "dummy" tag'+ Opaleye.QueryArr qa' = Opaleye.lateral $ \_ -> q' dummies'+ ((m'@(Any needsDummies), b), query'', tag''') = qa' ((), tag'')+ query'''+ | needsDummies = \lateral -> query'' lateral . query' lateral+ | otherwise = \lateral -> query'' lateral . query lateral+ m'' = m <> m'+ in+ ((m'', b), query''', tag''') -- | '<|>:' = 'unionAll'.
src/Rel8/Query.hs-boot view
@@ -7,11 +7,13 @@ -- base import Data.Kind ( Type )+import Data.Monoid ( Any ) import Prelude () -- opaleye+import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye import qualified Opaleye.Select as Opaleye type Query :: Type -> Type-newtype Query a = Query (Opaleye.Select a)+newtype Query a = Query ([Opaleye.PrimExpr] -> Opaleye.Select (Any, a))
src/Rel8/Query/Each.hs view
@@ -17,7 +17,7 @@ import Rel8.Query.Opaleye ( fromOpaleye ) import Rel8.Schema.Name ( Selects ) import Rel8.Schema.Table ( TableSchema )-import Rel8.Table ( fromColumns, toColumns )+import Rel8.Table.Cols ( fromCols, toCols ) import Rel8.Table.Opaleye ( table, unpackspec ) @@ -25,8 +25,8 @@ -- table@. each :: Selects names exprs => TableSchema names -> Query exprs each =- fmap fromColumns .+ fmap fromCols . fromOpaleye . Opaleye.selectTableExplicit unpackspec . table .- fmap toColumns+ fmap toCols
src/Rel8/Query/Either.hs view
@@ -10,7 +10,11 @@ -- base import Prelude +-- comonad+import Control.Comonad ( extract )+ -- rel8+import Rel8.Expr ( Expr ) import Rel8.Expr.Eq ( (==.) ) import Rel8.Query ( Query ) import Rel8.Query.Filter ( where_ )@@ -23,17 +27,17 @@ -- | Filter 'EitherTable's, keeping only 'leftTable's.-keepLeftTable :: EitherTable a b -> Query a+keepLeftTable :: EitherTable Expr a b -> Query a keepLeftTable e@(EitherTable _ a _) = do where_ $ isLeftTable e- pure a+ pure (extract a) -- | Filter 'EitherTable's, keeping only 'rightTable's.-keepRightTable :: EitherTable a b -> Query b+keepRightTable :: EitherTable Expr a b -> Query b keepRightTable e@(EitherTable _ _ b) = do where_ $ isRightTable e- pure b+ pure (extract b) -- | @bitraverseEitherTable f g x@ will pass all @leftTable@s through @f@ and@@ -55,8 +59,8 @@ bitraverseEitherTable :: () => (a -> Query c) -> (b -> Query d)- -> EitherTable a b- -> Query (EitherTable c d)+ -> EitherTable Expr a b+ -> Query (EitherTable Expr c d) bitraverseEitherTable f g e@(EitherTable tag _ _) = do mc@(MaybeTable _ c) <- optional (f =<< keepLeftTable e) md@(MaybeTable _ d) <- optional (g =<< keepRightTable e)
src/Rel8/Query/Evaluate.hs view
@@ -1,20 +1,18 @@-{-# language DerivingStrategies #-} {-# language FlexibleContexts #-}-{-# language GeneralizedNewtypeDeriving #-}-{-# language NamedFieldPuns #-}-{-# language StandaloneKindSignatures #-}+{-# language TupleSections #-} module Rel8.Query.Evaluate- ( Evaluate- , eval- , evaluate+ ( evaluate+ , rebind ) where -- base-import Data.Kind ( Type )-import Data.Monoid ( Endo ( Endo ), appEndo )-import Prelude+import Control.Monad ( (>=>) )+import Data.Foldable ( foldl' )+import Data.List.NonEmpty ( NonEmpty( (:|) ), nonEmpty )+import Data.Monoid ( Any( Any ) )+import Prelude hiding ( undefined ) -- opaleye import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye@@ -26,57 +24,45 @@ -- rel8 import Rel8.Expr ( Expr )+import Rel8.Expr.Bool ( (&&.) )+import Rel8.Expr.Opaleye ( fromPrimExpr ) import Rel8.Query ( Query( Query ) ) import Rel8.Table ( Table )+import Rel8.Table.Bool ( case_ ) import Rel8.Table.Opaleye ( unpackspec )---- semigroupoids-import Data.Functor.Apply ( Apply )-import Data.Functor.Bind ( Bind, (>>-) )---- transformers-import Control.Monad.Trans.State.Strict ( State, get, put, runState )---type Evaluations :: Type-data Evaluations = Evaluations- { tag :: !Opaleye.Tag- , bindings :: !(Endo (Opaleye.Bindings Opaleye.PrimExpr))- }+import Rel8.Table.Undefined --- | Some PostgreSQL functions, such as 'Rel8.nextval', have side effects,--- breaking the referential transparency we would otherwise enjoy.------ To try to recover our ability to reason about such expressions, 'Evaluate'--- allows us to control the evaluation order of side-effects by sequencing--- them monadically.-type Evaluate :: Type -> Type-newtype Evaluate a = Evaluate (State Evaluations a)- deriving newtype (Functor, Apply, Applicative, Monad)+-- | 'evaluate' takes expressions that could potentially have side effects and+-- \"runs\" them in the 'Query' monad. The returned expressions have no side+-- effects and can safely be reused.+evaluate :: Table Expr a => a -> Query a+evaluate = laterally >=> rebind -instance Bind Evaluate where- (>>-) = (>>=)+laterally :: Table Expr a => a -> Query a+laterally a = Query $ \bindings -> pure $ (Any True,) $+ case nonEmpty bindings of+ Nothing -> a+ Just bindings' -> case_ [(condition, a)] undefined+ where+ condition = foldl1' (&&.) (fmap go bindings')+ where+ go = fromPrimExpr . Opaleye.UnExpr Opaleye.OpIsNotNull --- | 'eval' takes expressions that could potentially have side effects and--- \"runs\" them in the 'Evaluate' monad. The returned expressions have no--- side effetcs and can safely be reused.-eval :: Table Expr a => a -> Evaluate a-eval a = Evaluate $ do- Evaluations {tag, bindings} <- get+-- | 'rebind' takes some expressions, and binds each of them to a new+-- variable in the SQL. The @a@ returned consists only of these+-- variables. It's essentially a @let@ binding for Postgres expressions.+rebind :: Table Expr a => a -> Query a+rebind a = Query $ \_ -> Opaleye.QueryArr $ \(_, tag) -> let tag' = Opaleye.next tag- (a', bindings') = Opaleye.run $- Opaleye.runUnpackspec unpackspec (Opaleye.extractAttr "eval" tag') a- put Evaluations {tag = tag', bindings = bindings <> Endo (bindings' ++)}- pure a'+ (a', bindings) = Opaleye.run $+ Opaleye.runUnpackspec unpackspec (Opaleye.extractAttr "eval" tag) a+ in+ ((mempty, a'), \_ -> Opaleye.Rebind True bindings, tag') --- | 'evaluate' runs an 'Evaluate' inside the 'Query' monad.-evaluate :: Evaluate a -> Query a-evaluate (Evaluate m) = Query $ Opaleye.QueryArr $ \(_, query, tag) ->- case runState m (Evaluations tag mempty) of- (a, Evaluations {tag = tag', bindings}) ->- (a, Opaleye.Rebind True (appEndo bindings mempty) query, tag')+foldl1' :: (a -> a -> a) -> NonEmpty a -> a+foldl1' f (a :| as) = foldl' f a as
src/Rel8/Query/Exists.hs view
@@ -2,8 +2,8 @@ module Rel8.Query.Exists ( exists, inQuery- , whereExists, with, withBy- , whereNotExists, without, withoutBy+ , present, with, withBy+ , absent, without, withoutBy ) where @@ -11,46 +11,44 @@ import Prelude hiding ( filter ) -- opaleye-import qualified Opaleye.Operators as Opaleye+import qualified Opaleye.Exists as Opaleye+import qualified Opaleye.Operators as Opaleye hiding ( exists ) -- rel8 import Rel8.Expr ( Expr )+import Rel8.Expr.Opaleye ( fromColumn, fromPrimExpr ) import Rel8.Query ( Query ) import Rel8.Query.Filter ( filter )-import Rel8.Query.Maybe ( optional ) import Rel8.Query.Opaleye ( mapOpaleye ) import Rel8.Table.Eq ( EqTable, (==:) )-import Rel8.Table.Maybe ( isJustTable ) -- | Checks if a query returns at least one row. exists :: Query a -> Query (Expr Bool)-exists = fmap isJustTable . optional . whereExists--- FIXME: change this when b7aacc07c6392654cae439fc3b997620c3aa7a87 makes it--- into a release of Opaleye+exists = fmap (fromPrimExpr . fromColumn) . mapOpaleye Opaleye.exists inQuery :: EqTable a => a -> Query a -> Query (Expr Bool) inQuery a = exists . (>>= filter (a ==:)) --- | Produce the empty query if the given query returns no rows. @whereExists@+-- | Produce the empty query if the given query returns no rows. @present@ -- is equivalent to @WHERE EXISTS@ in SQL.-whereExists :: Query a -> Query ()-whereExists = mapOpaleye Opaleye.restrictExists+present :: Query a -> Query ()+present = mapOpaleye Opaleye.restrictExists --- | Produce the empty query if the given query returns rows. @whereNotExists@+-- | Produce the empty query if the given query returns rows. @absent@ -- is equivalent to @WHERE NOT EXISTS@ in SQL.-whereNotExists :: Query a -> Query ()-whereNotExists = mapOpaleye Opaleye.restrictNotExists+absent :: Query a -> Query ()+absent = mapOpaleye Opaleye.restrictNotExists -- | @with@ is similar to 'filter', but allows the predicate to be a full query. ----- @with f a = a <$ whereExists (f a)@, but this form matches 'filter'.+-- @with f a = a <$ present (f a)@, but this form matches 'filter'. with :: (a -> Query b) -> a -> Query a-with f a = a <$ whereExists (f a)+with f a = a <$ present (f a) -- | Like @with@, but with a custom membership test.@@ -60,7 +58,7 @@ -- | Filter rows where @a -> Query b@ yields no rows. without :: (a -> Query b) -> a -> Query a-without f a = a <$ whereNotExists (f a)+without f a = a <$ absent (f a) -- | Like @without@, but with a custom membership test.
+ src/Rel8/Query/Indexed.hs view
@@ -0,0 +1,34 @@+module Rel8.Query.Indexed+ ( indexed+ )+where++-- base+import Data.Int ( Int64 )+import Prelude++-- opaleye+import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye+import qualified Opaleye.Internal.PackMap as Opaleye+import qualified Opaleye.Internal.PrimQuery as Opaleye+import qualified Opaleye.Internal.QueryArr as Opaleye+import qualified Opaleye.Internal.Tag as Opaleye++-- rel8+import Rel8.Expr ( Expr )+import Rel8.Expr.Opaleye ( fromPrimExpr )+import Rel8.Query ( Query )+import Rel8.Query.Opaleye ( mapOpaleye )+++-- | Pair each row of a query with its index within the query.+indexed :: Query a -> Query (Expr Int64, a)+indexed = mapOpaleye $ \(Opaleye.QueryArr f) -> Opaleye.QueryArr $ \(_, tag) ->+ let+ (a, query, tag') = f ((), tag)+ tag'' = Opaleye.next tag'+ window = Opaleye.ConstExpr $ Opaleye.OtherLit "ROW_NUMBER() OVER () - 1"+ (index, bindings) = Opaleye.run $ Opaleye.extractAttr "index" tag' window+ query' lateral = Opaleye.Rebind True bindings . query lateral+ in+ ((fromPrimExpr index, a), query', tag'')
src/Rel8/Query/List.hs view
@@ -19,16 +19,18 @@ import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye -- rel8-import Rel8.Expr ( Col( E, unE ), Expr )+import Rel8.Expr ( Expr ) import Rel8.Expr.Aggregate ( listAggExpr, nonEmptyAggExpr ) import Rel8.Expr.Opaleye ( mapPrimExpr ) import Rel8.Query ( Query ) import Rel8.Query.Aggregate ( aggregate )+import Rel8.Query.Evaluate ( rebind ) import Rel8.Query.Maybe ( optional ) import Rel8.Schema.HTable.Vectorize ( hunvectorize ) import Rel8.Schema.Null ( Sql, Unnullify )-import Rel8.Schema.Spec ( SSpec( SSpec, info ) )-import Rel8.Table ( Table, fromColumns, toColumns )+import Rel8.Schema.Spec ( Spec( Spec, info ) )+import Rel8.Table ( Table, fromColumns )+import Rel8.Table.Cols ( toCols ) import Rel8.Table.Aggregate ( listAgg, nonEmptyAgg ) import Rel8.Table.List ( ListTable( ListTable ) ) import Rel8.Table.Maybe ( maybeTable )@@ -45,12 +47,12 @@ -- -- @many@ is analogous to 'Control.Applicative.many' from -- @Control.Applicative@.-many :: Table Expr a => Query a -> Query (ListTable a)+many :: Table Expr a => Query a -> Query (ListTable Expr a) many = fmap (maybeTable mempty (\(ListTable a) -> ListTable a)) . optional . aggregate .- fmap (listAgg . toColumns)+ fmap (listAgg . toCols) -- | Aggregate a 'Query' into a 'NonEmptyTable'. If the supplied query returns@@ -61,11 +63,11 @@ -- -- @some@ is analogous to 'Control.Applicative.some' from -- @Control.Applicative@.-some :: Table Expr a => Query a -> Query (NonEmptyTable a)+some :: Table Expr a => Query a -> Query (NonEmptyTable Expr a) some = fmap (\(NonEmptyTable a) -> NonEmptyTable a) . aggregate .- fmap (nonEmptyAgg . toColumns)+ fmap (nonEmptyAgg . toCols) -- | A version of 'many' specialised to single expressions.@@ -82,18 +84,18 @@ -- element of the given @ListTable@. -- -- @catListTable@ is an inverse to 'many'.-catListTable :: Table Expr a => ListTable a -> Query a-catListTable (ListTable as) = pure $ fromColumns $ runIdentity $- hunvectorize (\SSpec {info} -> pure . E . sunnest info . unE) as+catListTable :: Table Expr a => ListTable Expr a -> Query a+catListTable (ListTable as) = rebind $ fromColumns $ runIdentity $+ hunvectorize (\Spec {info} -> pure . sunnest info) as -- | Expand a 'NonEmptyTable' into a 'Query', where each row in the query is an -- element of the given @NonEmptyTable@. -- -- @catNonEmptyTable@ is an inverse to 'some'.-catNonEmptyTable :: Table Expr a => NonEmptyTable a -> Query a-catNonEmptyTable (NonEmptyTable as) = pure $ fromColumns $ runIdentity $- hunvectorize (\SSpec {info} -> pure . E . sunnest info . unE) as+catNonEmptyTable :: Table Expr a => NonEmptyTable Expr a -> Query a+catNonEmptyTable (NonEmptyTable as) = rebind $ fromColumns $ runIdentity $+ hunvectorize (\Spec {info} -> pure . sunnest info) as -- | Expand an expression that contains a list into a 'Query', where each row@@ -101,7 +103,7 @@ -- -- @catList@ is an inverse to 'manyExpr'. catList :: Sql DBType a => Expr [a] -> Query (Expr a)-catList = pure . sunnest typeInformation+catList = rebind . sunnest typeInformation -- | Expand an expression that contains a non-empty list into a 'Query', where@@ -109,7 +111,7 @@ -- -- @catNonEmpty@ is an inverse to 'someExpr'. catNonEmpty :: Sql DBType a => Expr (NonEmpty a) -> Query (Expr a)-catNonEmpty = pure . sunnest typeInformation+catNonEmpty = rebind . sunnest typeInformation sunnest :: TypeInformation (Unnullify a) -> Expr (list a) -> Expr a
src/Rel8/Query/Maybe.hs view
@@ -1,3 +1,7 @@+{-# language GADTs #-}+{-# language LambdaCase #-}+{-# language NamedFieldPuns #-}+ module Rel8.Query.Maybe ( optional , catMaybeTable@@ -8,23 +12,20 @@ -- base import Prelude +-- comonad+import Control.Comonad ( extract )+ -- opaleye-import qualified Opaleye.Internal.PackMap as Opaleye-import qualified Opaleye.Internal.PrimQuery as Opaleye-import qualified Opaleye.Internal.QueryArr as Opaleye-import qualified Opaleye.Internal.Tag as Opaleye-import qualified Opaleye.Internal.Unpackspec as Opaleye+import qualified Opaleye.Internal.MaybeFields as Opaleye -- rel8-import Rel8.Expr.Bool ( true )+import Rel8.Expr ( Expr ) import Rel8.Expr.Eq ( (==.) )-import Rel8.Expr.Opaleye ( toPrimExpr )+import Rel8.Expr.Opaleye ( fromColumn, fromPrimExpr ) import Rel8.Query ( Query ) import Rel8.Query.Filter ( where_ ) import Rel8.Query.Opaleye ( mapOpaleye )-import Rel8.Table.Maybe ( MaybeTable( MaybeTable ), isJustTable )-import Rel8.Table.Opaleye ( unpackspec )-import Rel8.Table.Tag ( Tag(..), fromExpr )+import Rel8.Table.Maybe ( MaybeTable(..), isJustTable ) -- | Convert a query that might return zero rows to a query that always returns@@ -32,18 +33,11 @@ -- -- To speak in more concrete terms, 'optional' is most useful to write @LEFT -- JOIN@s.-optional :: Query a -> Query (MaybeTable a)-optional = mapOpaleye $ Opaleye.QueryArr . go- where- go query (i, left, tag) =- (MaybeTable (fromExpr t') a, join, Opaleye.next tag')- where- (MaybeTable Tag {expr = t} a, right, tag') =- Opaleye.runSimpleQueryArr (pure <$> query) (i, tag)- (t', bindings) = Opaleye.run $- Opaleye.runUnpackspec unpackspec (Opaleye.extractAttr "maybe" tag') t- join = Opaleye.Join Opaleye.LeftJoin condition [] bindings left right- condition = toPrimExpr true+optional :: Query a -> Query (MaybeTable Expr a)+optional = mapOpaleye $ Opaleye.optionalInternal $ \tag a -> MaybeTable+ { tag = fromPrimExpr $ fromColumn tag+ , just = pure a+ } -- | Filter out 'MaybeTable's, returning only the tables that are not-null.@@ -51,23 +45,24 @@ -- This operation can be used to "undo" the effect of 'optional', which -- operationally is like turning a @LEFT JOIN@ back into a full @JOIN@. You -- can think of this as analogous to 'Data.Maybe.catMaybes'.-catMaybeTable :: MaybeTable a -> Query a+catMaybeTable :: MaybeTable Expr a -> Query a catMaybeTable ma@(MaybeTable _ a) = do where_ $ isJustTable ma- pure a+ pure (extract a) -- | Extend an optional query with another query. This is useful if you want -- to step through multiple @LEFT JOINs@. -- -- Note that @traverseMaybeTable@ takes a @a -> Query b@ function, which means--- you also have the ability to "expand" one row into multiple rows. If the +-- you also have the ability to "expand" one row into multiple rows. If the -- @a -> Query b@ function returns no rows, then the resulting query will also -- have no rows. However, regardless of the given @a -> Query b@ function, if -- the input is @nothingTable@, you will always get exactly one @nothingTable@ -- back.-traverseMaybeTable :: (a -> Query b) -> MaybeTable a -> Query (MaybeTable b)+traverseMaybeTable :: (a -> Query b) -> MaybeTable Expr a -> Query (MaybeTable Expr b) traverseMaybeTable query ma@(MaybeTable input _) = do- MaybeTable output b <- optional (query =<< catMaybeTable ma)- where_ $ expr output ==. expr input- pure $ MaybeTable input b+ optional (query =<< catMaybeTable ma) >>= \case+ MaybeTable output b -> do+ where_ $ output ==. input+ pure $ MaybeTable input b
src/Rel8/Query/Opaleye.hs view
@@ -1,34 +1,70 @@+{-# language TupleSections #-}+ module Rel8.Query.Opaleye ( fromOpaleye , toOpaleye , mapOpaleye , zipOpaleyeWith+ , unsafePeekQuery ) where -- base+import Control.Applicative ( liftA2 ) import Prelude -- opaleye-import qualified Opaleye.Select as Opaleye+import qualified Opaleye.Internal.QueryArr as Opaleye+import qualified Opaleye.Internal.Tag as Opaleye -- rel8 import {-# SOURCE #-} Rel8.Query ( Query( Query ) ) fromOpaleye :: Opaleye.Select a -> Query a-fromOpaleye = Query+fromOpaleye = Query . pure . fmap pure toOpaleye :: Query a -> Opaleye.Select a-toOpaleye (Query a) = a+toOpaleye (Query a) = snd <$> a mempty mapOpaleye :: (Opaleye.Select a -> Opaleye.Select b) -> Query a -> Query b-mapOpaleye f = fromOpaleye . f . toOpaleye+mapOpaleye f (Query a) = Query (fmap (mapping f) a) zipOpaleyeWith :: () => (Opaleye.Select a -> Opaleye.Select b -> Opaleye.Select c) -> Query a -> Query b -> Query c-zipOpaleyeWith f a b = fromOpaleye $ f (toOpaleye a) (toOpaleye b)+zipOpaleyeWith f (Query a) (Query b) = Query $ liftA2 (zipping f) a b+++unsafePeekQuery :: Query a -> a+unsafePeekQuery (Query q) = case q mempty of+ Opaleye.QueryArr f -> case f ((), Opaleye.start) of+ ((_, a), _, _) -> a+++mapping :: ()+ => (Opaleye.Select a -> Opaleye.Select b)+ -> Opaleye.Select (m, a) -> Opaleye.Select (m, b)+mapping f q@(Opaleye.QueryArr qa) = Opaleye.QueryArr $ \(_, tag) ->+ let+ ((m, _), _, _) = qa ((), tag)+ Opaleye.QueryArr qa' = (m,) <$> f (snd <$> q)+ in+ qa' ((), tag)+++zipping :: Semigroup m+ => (Opaleye.Select a -> Opaleye.Select b -> Opaleye.Select c)+ -> Opaleye.Select (m, a) -> Opaleye.Select (m, b) -> Opaleye.Select (m, c)+zipping f q@(Opaleye.QueryArr qa) q'@(Opaleye.QueryArr qa') =+ Opaleye.QueryArr $ \(_, tag) ->+ let+ ((m, _), _, _) = qa ((), tag)+ ((m', _), _, _) = qa' ((), tag)+ m'' = m <> m'+ Opaleye.QueryArr qa'' = (m'',) <$> f (snd <$> q) (snd <$> q')+ in+ qa'' ((), tag)
src/Rel8/Query/SQL.hs view
@@ -1,74 +1,21 @@ {-# language FlexibleContexts #-}-{-# language TypeFamilies #-}-{-# language ViewPatterns #-}+{-# language MonoLocalBinds #-} module Rel8.Query.SQL ( showQuery- , sqlForQuery, sqlForQueryWithNames ) where -- base-import Data.Foldable ( fold )-import Data.Functor.Const ( Const( Const ), getConst )-import Data.Void ( Void ) import Prelude --- opaleye-import qualified Opaleye.Internal.HaskellDB.Sql as Opaleye-import qualified Opaleye.Internal.PrimQuery as Opaleye-import qualified Opaleye.Internal.Print as Opaleye-import qualified Opaleye.Internal.Optimize as Opaleye-import qualified Opaleye.Internal.QueryArr as Opaleye hiding ( Select )-import qualified Opaleye.Internal.Sql as Opaleye- -- rel8-import Rel8.Expr ( Expr, Col( E ) )-import Rel8.Expr.Opaleye ( toPrimExpr )+import Rel8.Expr ( Expr ) import Rel8.Query ( Query )-import Rel8.Query.Opaleye ( toOpaleye )-import Rel8.Schema.Name ( Name( Name ), Selects, Col( N ) )-import Rel8.Schema.HTable ( htabulateA, hfield )-import Rel8.Table ( Table, toColumns )-import Rel8.Table.Name ( namesFromLabels )-import Rel8.Table.Opaleye ( castTable )+import Rel8.Statement.Select ( ppSelect )+import Rel8.Table ( Table ) --- | Convert a query to a 'String' containing the query as a @SELECT@--- statement.+-- | Convert a 'Query' to a 'String' containing a @SELECT@ statement. showQuery :: Table Expr a => Query a -> String-showQuery = fold . sqlForQuery---sqlForQuery :: Table Expr a- => Query a -> Maybe String-sqlForQuery = sqlForQueryWithNames namesFromLabels . fmap toColumns---sqlForQueryWithNames :: Selects names exprs- => names -> Query exprs -> Maybe String-sqlForQueryWithNames names query =- show . Opaleye.ppSql . selectFrom names exprs <$> optimize primQuery- where- (exprs, primQuery, _) =- Opaleye.runSimpleQueryArrStart (toOpaleye query) ()---optimize :: Opaleye.PrimQuery' a -> Maybe (Opaleye.PrimQuery' Void)-optimize = Opaleye.removeEmpty . Opaleye.optimize---selectFrom :: Selects names exprs- => names -> exprs -> Opaleye.PrimQuery' Void -> Opaleye.Select-selectFrom (toColumns -> names) (toColumns . castTable -> exprs) query =- Opaleye.SelectFrom $ Opaleye.newSelect- { Opaleye.attrs = Opaleye.SelectAttrs attributes- , Opaleye.tables = Opaleye.oneTable select- }- where- select = Opaleye.foldPrimQuery Opaleye.sqlQueryGenerator query- attributes = getConst $ htabulateA $ \field -> case hfield names field of- N (Name name) -> case hfield exprs field of- E (toPrimExpr -> expr) -> Const (pure (makeAttr name expr))- makeAttr label expr =- (Opaleye.sqlExpr expr, Just (Opaleye.SqlColumn label))+showQuery = show . ppSelect
src/Rel8/Query/These.hs view
@@ -1,4 +1,5 @@ {-# language FlexibleContexts #-}+{-# language GADTs #-} module Rel8.Query.These ( alignBy@@ -14,27 +15,29 @@ -- base import Prelude +-- comonad+import Control.Comonad ( extract )+ -- opaleye-import qualified Opaleye.Internal.Join as Opaleye+import qualified Opaleye.Internal.PackMap as Opaleye import qualified Opaleye.Internal.PrimQuery as Opaleye+import qualified Opaleye.Internal.QueryArr as Opaleye+import qualified Opaleye.Internal.Tag as Opaleye -- rel8 import Rel8.Expr ( Expr ) import Rel8.Expr.Bool ( boolExpr, not_ ) import Rel8.Expr.Eq ( (==.) )-import Rel8.Expr.Opaleye ( toColumn, toPrimExpr )+import Rel8.Expr.Opaleye ( toPrimExpr, traversePrimExpr ) import Rel8.Expr.Serialize ( litExpr ) import Rel8.Query ( Query ) import Rel8.Query.Filter ( where_ ) import Rel8.Query.Maybe ( optional ) import Rel8.Query.Opaleye ( zipOpaleyeWith )-import Rel8.Table ( Table ) import Rel8.Table.Either ( EitherTable( EitherTable ) ) import Rel8.Table.Maybe ( MaybeTable( MaybeTable ), isJustTable )-import Rel8.Table.Opaleye ( unpackspec )-import Rel8.Table.Tag ( Tag(..) ) import Rel8.Table.These- ( TheseTable( TheseTable )+ ( TheseTable( TheseTable, here, there ) , hasHereTable, hasThereTable , isThisTable, isThatTable, isThoseTable )@@ -42,81 +45,91 @@ -- | Corresponds to a @FULL OUTER JOIN@ between two queries.-alignBy :: (Table Expr a, Table Expr b)+alignBy :: () => (a -> b -> Expr Bool)- -> Query a -> Query b -> Query (TheseTable a b)-alignBy condition as bs =- uncurry TheseTable <$> zipOpaleyeWith fullOuterJoin as bs- where- fullOuterJoin a b =- Opaleye.joinExplicit unpackspec unpackspec pure pure full a b on- where- full = Opaleye.FullJoin- on = toColumn . toPrimExpr . uncurry condition+ -> Query a -> Query b -> Query (TheseTable Expr a b)+alignBy condition = zipOpaleyeWith $ \left right -> Opaleye.QueryArr $ \i -> case i of+ (_, tag) -> (tab, join', tag''')+ where+ (ma, left', tag') = Opaleye.runSimpleQueryArr (pure <$> left) ((), tag)+ (mb, right', tag'') = Opaleye.runSimpleQueryArr (pure <$> right) ((), tag')+ MaybeTable hasHere a = ma+ MaybeTable hasThere b = mb+ (hasHere', lbindings) = Opaleye.run $ do+ traversePrimExpr (Opaleye.extractAttr "hasHere" tag'') hasHere+ (hasThere', rbindings) = Opaleye.run $ do+ traversePrimExpr (Opaleye.extractAttr "hasThere" tag'') hasThere+ tag''' = Opaleye.next tag''+ join lateral = Opaleye.Join Opaleye.FullJoin on left'' right''+ where+ on = toPrimExpr $ condition (extract a) (extract b)+ left'' = (lateral, Opaleye.Rebind True lbindings left')+ right'' = (lateral, Opaleye.Rebind True rbindings right')+ ma' = MaybeTable hasHere' a+ mb' = MaybeTable hasThere' b+ tab = TheseTable {here = ma', there = mb'}+ join' lateral input = Opaleye.times lateral input (join lateral) --- | Filter 'TheseTable's, keeping only 'thisTable's and 'thoseTable's.-keepHereTable :: TheseTable a b -> Query (a, MaybeTable b)+keepHereTable :: TheseTable Expr a b -> Query (a, MaybeTable Expr b) keepHereTable = loseThatTable --- | Filter 'TheseTable's, keeping on-loseHereTable :: TheseTable a b -> Query b+loseHereTable :: TheseTable Expr a b -> Query b loseHereTable = keepThatTable -keepThereTable :: TheseTable a b -> Query (MaybeTable a, b)+keepThereTable :: TheseTable Expr a b -> Query (MaybeTable Expr a, b) keepThereTable = loseThisTable -loseThereTable :: TheseTable a b -> Query a+loseThereTable :: TheseTable Expr a b -> Query a loseThereTable = keepThisTable -keepThisTable :: TheseTable a b -> Query a+keepThisTable :: TheseTable Expr a b -> Query a keepThisTable t@(TheseTable (MaybeTable _ a) _) = do where_ $ isThisTable t- pure a+ pure (extract a) -loseThisTable :: TheseTable a b -> Query (MaybeTable a, b)+loseThisTable :: TheseTable Expr a b -> Query (MaybeTable Expr a, b) loseThisTable t@(TheseTable ma (MaybeTable _ b)) = do where_ $ not_ $ isThisTable t- pure (ma, b)+ pure (ma, extract b) -keepThatTable :: TheseTable a b -> Query b+keepThatTable :: TheseTable Expr a b -> Query b keepThatTable t@(TheseTable _ (MaybeTable _ b)) = do where_ $ isThatTable t- pure b+ pure (extract b) -loseThatTable :: TheseTable a b -> Query (a, MaybeTable b)+loseThatTable :: TheseTable Expr a b -> Query (a, MaybeTable Expr b) loseThatTable t@(TheseTable (MaybeTable _ a) mb) = do where_ $ not_ $ isThatTable t- pure (a, mb)+ pure (extract a, mb) -keepThoseTable :: TheseTable a b -> Query (a, b)+keepThoseTable :: TheseTable Expr a b -> Query (a, b) keepThoseTable t@(TheseTable (MaybeTable _ a) (MaybeTable _ b)) = do where_ $ isThoseTable t- pure (a, b)+ pure (extract a, extract b) -loseThoseTable :: TheseTable a b -> Query (EitherTable a b)+loseThoseTable :: TheseTable Expr a b -> Query (EitherTable Expr a b) loseThoseTable t@(TheseTable (MaybeTable _ a) (MaybeTable _ b)) = do where_ $ not_ $ isThoseTable t- pure $ EitherTable result a b+ pure $ EitherTable tag a b where tag = boolExpr (litExpr IsLeft) (litExpr IsRight) (isThatTable t)- result = (mempty `asTypeOf` result) {expr = tag} bitraverseTheseTable :: () => (a -> Query c) -> (b -> Query d)- -> TheseTable a b- -> Query (TheseTable c d)+ -> TheseTable Expr a b+ -> Query (TheseTable Expr c d) bitraverseTheseTable f g t = do mc <- optional (f . fst =<< keepHereTable t) md <- optional (g . snd =<< keepThereTable t)
− src/Rel8/Schema/Context.hs
@@ -1,20 +0,0 @@-{-# language DataKinds #-}-{-# language StandaloneKindSignatures #-}-{-# language TypeFamilies #-}--module Rel8.Schema.Context- ( Interpretation( Col )- )-where---- base-import Data.Kind ( Constraint )-import Prelude ()---- rel8-import Rel8.Schema.Kind ( Context, HContext )---type Interpretation :: Context -> Constraint-class Interpretation context where- data Col context :: HContext
− src/Rel8/Schema/Context/Label.hs
@@ -1,62 +0,0 @@-{-# language DataKinds #-}-{-# language FlexibleInstances #-}-{-# language StandaloneKindSignatures #-}-{-# language TypeFamilies #-}-{-# language TypeOperators #-}--module Rel8.Schema.Context.Label- ( Labelable( labeler, unlabeler )- , HLabelable( hlabeler, hunlabeler )- )-where---- base-import Data.Kind ( Constraint )-import Prelude hiding ( null )---- rel8-import Rel8.Schema.Context ( Interpretation )-import Rel8.Schema.Dict ( Dict( Dict ) )-import Rel8.Schema.Kind ( Context, HContext )-import Rel8.Schema.Spec ( Spec( Spec ) )-import Rel8.Schema.Result ( Col( R ), Result )-import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType )----- | The @Labelable@ class is an internal implementation detail of Rel8, and--- indicates that we can successfully "name" all columns in a type.-type Labelable :: Context -> Constraint-class Interpretation context => Labelable context where- labeler :: ()- => Col context ('Spec labels a)- -> Col context ('Spec (label ': labels) a)-- unlabeler :: ()- => Col context ('Spec (label ': labels) a)- -> Col context ('Spec labels a)---instance Labelable Result where- labeler (R a) = R a- unlabeler (R a) = R a---type HLabelable :: HContext -> Constraint-class HLabelable context where- hlabeler :: ()- => context ('Spec labels a)- -> context ('Spec (label ': labels) a)-- hunlabeler :: ()- => context ('Spec (label ': labels) a)- -> context ('Spec labels a)---instance Labelable context => HLabelable (Col context) where- hlabeler = labeler- hunlabeler = unlabeler---instance HLabelable (Dict (ConstrainDBType constraint)) where- hlabeler Dict = Dict- hunlabeler Dict = Dict
src/Rel8/Schema/Context/Nullify.hs view
@@ -1,188 +1,152 @@ {-# language DataKinds #-}+{-# language EmptyCase #-} {-# language FlexibleContexts #-}-{-# language FlexibleInstances #-}-{-# language InstanceSigs #-}-{-# language MultiParamTypeClasses #-}+{-# language GADTs #-}+{-# language LambdaCase #-} {-# language NamedFieldPuns #-}-{-# language ScopedTypeVariables #-} {-# language StandaloneKindSignatures #-}-{-# language TypeFamilies #-} module Rel8.Schema.Context.Nullify- ( Nullifiable( ConstrainTag, encodeTag, decodeTag, nullifier, unnullifier )- , HNullifiable( HConstrainTag, hencodeTag, hdecodeTag, hnullifier, hunnullifier )- , runTag, unnull+ ( Nullifiability(..), NonNullifiability(..), nullifiableOrNot, absurd+ , Nullifiable, nullifiability+ , guarder, nullifier, unnullifier+ , sguard, snullify ) where -- base+import Data.Bool ( bool )+import Data.Functor.Identity ( Identity( Identity ) ) import Data.Kind ( Constraint, Type )-import GHC.TypeLits ( KnownSymbol )+import Data.Monoid ( getFirst ) import Prelude hiding ( null ) -- opaleye import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye -- rel8-import Rel8.Aggregate- ( Aggregate( Aggregate ), Col( A )- , mapInputs- , unsafeMakeAggregate- )-import Rel8.Expr ( Expr, Col( E ) )+import Rel8.Aggregate ( Aggregate( Aggregate ), foldInputs, mapInputs )+import Rel8.Expr ( Expr ) import Rel8.Expr.Bool ( boolExpr ) import Rel8.Expr.Null ( nullify, unsafeUnnullify ) import Rel8.Expr.Opaleye ( fromPrimExpr, toPrimExpr )-import Rel8.Schema.Context ( Interpretation )+import Rel8.Kind.Context ( SContext(..) )+import Rel8.Schema.Field ( Field ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Name ( Name( Name ), Col( N ) )-import Rel8.Schema.Null ( Nullify, Nullity( Null, NotNull ), Sql )-import Rel8.Schema.Dict ( Dict( Dict ) )-import Rel8.Schema.Spec ( Spec( Spec ), SSpec(..) )-import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType )-import qualified Rel8.Schema.Spec.ConstrainDBType as ConstrainDBType-import Rel8.Table.Tag ( Tag(..), Taggable, fromAggregate, fromExpr, fromName )+import Rel8.Schema.Name ( Name( Name ) )+import Rel8.Schema.Null ( Nullify, Nullity( Null, NotNull ) )+import Rel8.Schema.Result ( Result )+import Rel8.Schema.Spec ( Spec(..) ) -type Nullifiable :: K.Context -> Constraint-class Interpretation context => Nullifiable context where- type ConstrainTag context :: Type -> Constraint- type ConstrainTag _context = DefaultConstrainTag+type Nullifiability :: K.Context -> Type+data Nullifiability context where+ NAggregate :: Nullifiability Aggregate+ NExpr :: Nullifiability Expr+ NName :: Nullifiability Name - encodeTag ::- ( Sql (ConstrainTag context) a- , KnownSymbol label- , Taggable a- )- => Tag label a- -> Col context ('Spec labels a) - decodeTag ::- ( Sql (ConstrainTag context) a- , KnownSymbol label- , Taggable a- )- => Col context ('Spec labels a)- -> Tag label a-- nullifier :: ()- => Tag label a- -> (Expr a -> Expr Bool)- -> SSpec ('Spec labels x)- -> Col context ('Spec labels x)- -> Col context ('Spec labels (Nullify x))-- unnullifier :: ()- => SSpec ('Spec labels x)- -> Col context ('Spec labels (Nullify x))- -> Col context ('Spec labels x)+type Nullifiable :: K.Context -> Constraint+class Nullifiable context where+ nullifiability :: Nullifiability context instance Nullifiable Aggregate where- encodeTag Tag {aggregator, expr} =- A $ unsafeMakeAggregate toPrimExpr fromPrimExpr aggregator expr-- decodeTag (A aggregate) = fromAggregate aggregate-- nullifier Tag {expr} test SSpec {nullity} (A (Aggregate a)) =- A $- mapInputs (toPrimExpr . runTag nullity condition . fromPrimExpr) $- Aggregate $- runTag nullity condition <$> a- where- condition = test expr-- unnullifier SSpec {nullity} (A (Aggregate a)) =- A (Aggregate (unnull nullity <$> a))-- {-# INLINABLE encodeTag #-}- {-# INLINABLE decodeTag #-}- {-# INLINABLE nullifier #-}- {-# INLINABLE unnullifier #-}+ nullifiability = NAggregate instance Nullifiable Expr where- encodeTag Tag {expr} = E expr- decodeTag (E a) = fromExpr a- nullifier Tag {expr} test SSpec {nullity} (E a) =- E $ runTag nullity (test expr) a- unnullifier SSpec {nullity} (E a) = E $ unnull nullity a-- {-# INLINABLE encodeTag #-}- {-# INLINABLE decodeTag #-}- {-# INLINABLE nullifier #-}- {-# INLINABLE unnullifier #-}+ nullifiability = NExpr instance Nullifiable Name where- encodeTag Tag {name} = N name- decodeTag (N name) = fromName name- nullifier _ _ _ (N (Name name)) = N (Name name)- unnullifier _ (N (Name name)) = N (Name name)+ nullifiability = NName - {-# INLINABLE encodeTag #-}- {-# INLINABLE decodeTag #-}- {-# INLINABLE nullifier #-}- {-# INLINABLE unnullifier #-} +type NonNullifiability :: K.Context -> Type+data NonNullifiability context where+ NField :: NonNullifiability (Field table)+ NResult :: NonNullifiability Result -runTag :: Nullity a -> Expr Bool -> Expr a -> Expr (Nullify a)-runTag nullity tag a = case nullity of- Null -> boolExpr null a tag- NotNull -> boolExpr null (nullify a) tag- where- null = fromPrimExpr $ Opaleye.ConstExpr Opaleye.NullLit +nullifiableOrNot :: ()+ => SContext context+ -> Either (NonNullifiability context) (Nullifiability context)+nullifiableOrNot = \case+ SAggregate -> Right NAggregate+ SExpr -> Right NExpr+ SField -> Left NField+ SName -> Right NName+ SResult -> Left NResult -unnull :: Nullity a -> Expr (Nullify a) -> Expr a-unnull nullity a = case nullity of- Null -> a- NotNull -> unsafeUnnullify a +absurd :: Nullifiability context -> NonNullifiability context -> a+absurd = \case+ NAggregate -> \case+ NExpr -> \case+ NName -> \case -type HNullifiable :: K.HContext -> Constraint-class HNullifiable context where- type HConstrainTag context :: Type -> Constraint- type HConstrainTag _context = DefaultConstrainTag - hencodeTag :: (Sql (HConstrainTag context) a, KnownSymbol label, Taggable a)- => Tag label a- -> context ('Spec labels a)+guarder :: ()+ => SContext context+ -> context tag+ -> (tag -> Bool)+ -> (Expr tag -> Expr Bool)+ -> context (Maybe a)+ -> context (Maybe a)+guarder = \case+ SAggregate -> \tag _ isNonNull (Aggregate a) ->+ let+ mtag = foldInputs (\_ -> pure . fromPrimExpr) tag+ run = maybe id (sguard . isNonNull) (getFirst mtag)+ in+ mapInputs (toPrimExpr . run . fromPrimExpr) $+ Aggregate $+ run <$> a+ SExpr -> \tag _ isNonNull a -> sguard (isNonNull tag) a+ SField -> \_ _ _ field -> field+ SName -> \_ _ _ name -> name+ SResult -> \(Identity tag) isNonNull _ (Identity a) ->+ Identity (bool Nothing a (isNonNull tag)) - hdecodeTag :: (Sql (HConstrainTag context) a, KnownSymbol label, Taggable a)- => context ('Spec labels a)- -> Tag label a - hnullifier :: ()- => Tag label a- -> (Expr a -> Expr Bool)- -> SSpec ('Spec labels x)- -> context ('Spec labels x)- -> context ('Spec labels (Nullify x))+nullifier :: ()+ => Nullifiability context+ -> Spec a+ -> context a+ -> context (Nullify a)+nullifier = \case+ NAggregate -> \Spec {nullity} (Aggregate a) ->+ Aggregate $ snullify nullity <$> a+ NExpr -> \Spec {nullity} a -> snullify nullity a+ NName -> \_ (Name a) -> Name a - hunnullifier :: ()- => SSpec ('Spec labels x)- -> context ('Spec labels (Nullify x))- -> context ('Spec labels x) +unnullifier :: ()+ => Nullifiability context+ -> Spec a+ -> context (Nullify a)+ -> context a+unnullifier = \case+ NAggregate -> \Spec {nullity} (Aggregate a) ->+ Aggregate $ sunnullify nullity <$> a+ NExpr -> \Spec {nullity} a -> sunnullify nullity a+ NName -> \_ (Name a) -> Name a -instance Nullifiable context => HNullifiable (Col context) where- type HConstrainTag (Col context) = ConstrainTag context- hencodeTag = encodeTag- hdecodeTag = decodeTag- hnullifier = nullifier- hunnullifier = unnullifier +sguard :: Expr Bool -> Expr (Maybe a) -> Expr (Maybe a)+sguard condition a = boolExpr null a condition+ where+ null = fromPrimExpr $ Opaleye.ConstExpr Opaleye.NullLit -instance HNullifiable (Dict (ConstrainDBType constraint)) where- type HConstrainTag (Dict (ConstrainDBType constraint)) = constraint - hencodeTag _ = Dict- hdecodeTag = mempty- hnullifier _ _ = ConstrainDBType.nullifier- hunnullifier = ConstrainDBType.unnullifier+snullify :: Nullity a -> Expr a -> Expr (Nullify a)+snullify nullity a = case nullity of+ Null -> a+ NotNull -> nullify a -type DefaultConstrainTag :: Type -> Constraint-class DefaultConstrainTag a-instance DefaultConstrainTag a+sunnullify :: Nullity a -> Expr (Nullify a) -> Expr a+sunnullify nullity a = case nullity of+ Null -> a+ NotNull -> unsafeUnnullify a
+ src/Rel8/Schema/Field.hs view
@@ -0,0 +1,50 @@+{-# language DataKinds #-}+{-# language FlexibleContexts #-}+{-# language MultiParamTypeClasses #-}+{-# language StandaloneKindSignatures #-}+{-# language TypeFamilies #-}++module Rel8.Schema.Field+ ( Field(..)+ , fields+ )+where++-- base+import Data.Functor.Identity ( Identity( Identity ) )+import Data.Kind ( Type )+import Prelude++-- rel8+import Rel8.Schema.HTable ( HField, htabulate )+import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )+import Rel8.Schema.Kind as K+import Rel8.Schema.Null ( Sql )+import Rel8.Table+ ( Table, Columns, Context, fromColumns, toColumns+ , FromExprs, fromResult, toResult+ , Transpose+ )+import Rel8.Table.Transpose ( Transposes )+import Rel8.Type ( DBType )+++-- | A special context used in the construction of 'Rel8.Projection's.+type Field :: Type -> K.Context+newtype Field table a = Field (HField (Columns table) a)+++instance Sql DBType a => Table (Field table) (Field table a) where+ type Columns (Field table a) = HIdentity a+ type Context (Field table a) = Field table+ type FromExprs (Field table a) = a+ type Transpose to (Field table a) = to a++ toColumns = HIdentity+ fromColumns (HIdentity a) = a+ toResult a = HIdentity (Identity a)+ fromResult (HIdentity (Identity a)) = a+++fields :: Transposes context (Field table) table fields => fields+fields = fromColumns $ htabulate Field
src/Rel8/Schema/HTable.hs view
@@ -34,7 +34,7 @@ -- rel8 import Rel8.Schema.Dict ( Dict )-import Rel8.Schema.Spec ( Spec, SSpec )+import Rel8.Schema.Spec ( Spec ) import Rel8.Schema.HTable.Product ( HProduct( HProduct ) ) import qualified Rel8.Schema.Kind as K @@ -50,14 +50,14 @@ -- instances yourself or specify this constraint. type HTable :: K.HTable -> Constraint class HTable t where- type HField t = (field :: Spec -> Type) | field -> t- type HConstrainTable t (c :: Spec -> Constraint) :: Constraint+ type HField t = (field :: Type -> Type) | field -> t+ type HConstrainTable t (c :: Type -> Constraint) :: Constraint - hfield :: t context -> HField t spec -> context spec- htabulate :: (forall spec. HField t spec -> context spec) -> t context- htraverse :: Apply m => (forall spec. f spec -> m (g spec)) -> t f -> m (t g)+ hfield :: t context -> HField t a -> context a+ htabulate :: (forall a. HField t a -> context a) -> t context+ htraverse :: Apply m => (forall a. f a -> m (g a)) -> t f -> m (t g) hdicts :: HConstrainTable t c => t (Dict c)- hspecs :: t SSpec+ hspecs :: t Spec type HField t = GHField t type HConstrainTable t c = HConstrainTable (GHColumns (Rep (t Proxy))) c@@ -68,7 +68,7 @@ , HField (GHColumns (Rep (t Proxy))) ~ HField (GHColumns (Rep (t context))) , GHTable context (Rep (t context)) )- => t context -> HField t spec -> context spec+ => t context -> HField t a -> context a hfield table (GHField field) = hfield (toGHColumns (from table)) field default htabulate ::@@ -77,7 +77,7 @@ , HField (GHColumns (Rep (t Proxy))) ~ HField (GHColumns (Rep (t context))) , GHTable context (Rep (t context)) )- => (forall spec. HField t spec -> context spec) -> t context+ => (forall a. HField t a -> context a) -> t context htabulate f = to $ fromGHColumns $ htabulate (f . GHField) default htraverse@@ -87,7 +87,7 @@ , Generic (t g), GHTable g (Rep (t g)) , GHColumns (Rep (t f)) ~ GHColumns (Rep (t g)) )- => (forall spec. f spec -> m (g spec)) -> t f -> m (t g)+ => (forall a. f a -> m (g a)) -> t f -> m (t g) htraverse f = fmap (to . fromGHColumns) . htraverse f . toGHColumns . from default hdicts@@ -101,10 +101,10 @@ hdicts = to $ fromGHColumns (hdicts @(GHColumns (Rep (t Proxy))) @c) default hspecs ::- ( Generic (t SSpec)- , GHTable SSpec (Rep (t SSpec))+ ( Generic (t Spec)+ , GHTable Spec (Rep (t Spec)) )- => t SSpec+ => t Spec hspecs = to $ fromGHColumns hspecs {-# INLINABLE hfield #-}@@ -115,21 +115,21 @@ hmap :: HTable t- => (forall spec. context spec -> context' spec) -> t context -> t context'+ => (forall a. context a -> context' a) -> t context -> t context' hmap f a = htabulate $ \field -> f (hfield a field) htabulateA :: (HTable t, Apply m)- => (forall spec. HField t spec -> m (context spec)) -> m (t context)+ => (forall a. HField t a -> m (context a)) -> m (t context) htabulateA f = htraverse getCompose $ htabulate $ Compose . f {-# INLINABLE htabulateA #-} -type GHField :: K.HTable -> Spec -> Type-newtype GHField t spec = GHField (HField (GHColumns (Rep (t Proxy))) spec)+type GHField :: K.HTable -> Type -> Type+newtype GHField t a = GHField (HField (GHColumns (Rep (t Proxy))) a) -type GHTable :: K.HContext -> (Type -> Type) -> Constraint+type GHTable :: K.Context -> (Type -> Type) -> Constraint class HTable (GHColumns rep) => GHTable context rep | rep -> context where type GHColumns rep :: K.HTable toGHColumns :: rep x -> GHColumns rep context@@ -155,10 +155,10 @@ -- | A HField type for indexing into HProduct.-type HProductField :: K.HTable -> K.HTable -> Spec -> Type-data HProductField x y spec- = HFst (HField x spec)- | HSnd (HField y spec)+type HProductField :: K.HTable -> K.HTable -> Type -> Type+data HProductField x y a+ = HFst (HField x a)+ | HSnd (HField y a) instance (HTable x, HTable y) => HTable (HProduct x y) where
src/Rel8/Schema/HTable/Either.hs view
@@ -15,17 +15,16 @@ -- rel8 import Rel8.Schema.HTable ( HTable )-import Rel8.Schema.HTable.Identity ( HIdentity(..) )+import Rel8.Schema.HTable.Identity ( HIdentity ) import Rel8.Schema.HTable.Label ( HLabel ) import Rel8.Schema.HTable.Nullify ( HNullify ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Spec ( Spec( Spec ) ) import Rel8.Type.Tag ( EitherTag ) type HEitherTable :: K.HTable -> K.HTable -> K.HTable data HEitherTable left right context = HEitherTable- { htag :: HIdentity ('Spec '["isRight"] EitherTag) context+ { htag :: HLabel "isRight" (HIdentity EitherTag) context , hleft :: HLabel "Left" (HNullify left) context , hright :: HLabel "Right" (HNullify right) context }
src/Rel8/Schema/HTable/Identity.hs view
@@ -1,17 +1,17 @@ {-# language DataKinds #-}-{-# language GADTs #-}-{-# language PatternSynonyms #-}+{-# language FlexibleContexts #-} {-# language StandaloneKindSignatures #-} {-# language TypeFamilies #-}+{-# language UndecidableInstances #-} module Rel8.Schema.HTable.Identity- ( HIdentity( HIdentity, HType, unHIdentity )- , HType+ ( HIdentity( HIdentity, unHIdentity ) ) where -- base import Data.Kind ( Type )+import Data.Type.Equality ( (:~:)( Refl ) ) import Prelude -- rel8@@ -21,35 +21,23 @@ , hfield, htabulate, htraverse, hdicts, hspecs ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Spec ( Spec( Spec ), KnownSpec, specSing )---type HType :: Type -> K.HTable-type HType a = HIdentity ('Spec '[] a)---pattern HType :: context ('Spec '[] a) -> HType a context-pattern HType a = HIdentity a-{-# COMPLETE HType #-}+import Rel8.Schema.Null ( Sql )+import Rel8.Schema.Spec ( specification )+import Rel8.Type ( DBType ) -type HIdentity :: Spec -> K.HTable-newtype HIdentity spec context = HIdentity- { unHIdentity :: context spec+type HIdentity :: Type -> K.HTable+newtype HIdentity a context = HIdentity+ { unHIdentity :: context a } -type HIdentityField :: Spec -> Spec -> Type-data HIdentityField _spec spec where- HIdentityField :: HIdentityField spec spec---instance KnownSpec spec => HTable (HIdentity spec) where- type HConstrainTable (HIdentity spec) c = c spec- type HField (HIdentity spec) = HIdentityField spec+instance Sql DBType a => HTable (HIdentity a) where+ type HConstrainTable (HIdentity a) constraint = constraint a+ type HField (HIdentity a) = (:~:) a - hfield (HIdentity a) HIdentityField = a- htabulate f = HIdentity $ f HIdentityField+ hfield (HIdentity a) Refl = a+ htabulate f = HIdentity $ f Refl htraverse f (HIdentity a) = HIdentity <$> f a hdicts = HIdentity Dict- hspecs = HIdentity specSing+ hspecs = HIdentity specification
src/Rel8/Schema/HTable/Label.hs view
@@ -1,83 +1,70 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE LambdaCase #-}-{-# language ConstraintKinds #-} {-# language DataKinds #-}-{-# language FlexibleInstances #-}-{-# language GADTs #-}-{-# language MultiParamTypeClasses #-}-{-# language QuantifiedConstraints #-} {-# language RankNTypes #-} {-# language RecordWildCards #-} {-# language ScopedTypeVariables #-} {-# language StandaloneKindSignatures #-}+{-# language TypeApplications #-} {-# language TypeFamilies #-}-{-# language TypeOperators #-}-{-# language UndecidableInstances #-} module Rel8.Schema.HTable.Label- ( HLabel, Label- , hlabel, hunlabel+ ( HLabel, hlabel, hrelabel, hunlabel+ , hproject ) where -- base+import Data.Kind ( Type ) import Data.Proxy ( Proxy( Proxy ) )-import GHC.TypeLits ( KnownSymbol, Symbol )+import GHC.TypeLits ( KnownSymbol, Symbol, symbolVal ) import Prelude -- rel8-import Rel8.Kind.Labels ( SLabels( SCons ) ) import Rel8.Schema.HTable- ( HTable- , hfield, htabulate, hspecs+ ( HTable, HConstrainTable, HField+ , htabulate, hfield, htraverse, hdicts, hspecs ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Spec ( Spec( Spec ), SSpec(..) )-import Rel8.FCF-import Rel8.Schema.HTable.MapTable-import GHC.Generics (Generic)+import Rel8.Schema.Spec ( Spec(..) ) type HLabel :: Symbol -> K.HTable -> K.HTable-newtype HLabel label table context = HLabel (HMapTable (Label label) table context)- deriving stock Generic- deriving anyclass HTable+newtype HLabel label table context = HLabel (table context) -data Label :: Symbol -> Spec -> Exp Spec+type HLabelField :: Symbol -> K.HTable -> Type -> Type+newtype HLabelField label table a = HLabelField (HField table a) -type instance Eval (Label label ('Spec labels a)) = 'Spec (label : labels) a-+instance (HTable table, KnownSymbol label) => HTable (HLabel label table) where+ type HField (HLabel label table) = HLabelField label table+ type HConstrainTable (HLabel label table) constraint =+ HConstrainTable table constraint -instance KnownSymbol l => MapSpec (Label l) where- mapInfo = \case- SSpec {..} -> SSpec {labels = SCons Proxy labels, ..}+ hfield (HLabel a) (HLabelField field) = hfield a field+ htabulate f = HLabel (htabulate (f . HLabelField))+ htraverse f (HLabel a) = HLabel <$> htraverse f a+ hdicts = HLabel (hdicts @table)+ hspecs = HLabel $ htabulate $ \field -> case hfield (hspecs @table) field of+ Spec {..} -> Spec {labels = symbolVal (Proxy @label) : labels, ..}+ {-# INLINABLE hspecs #-} -hlabel :: (HTable t, KnownSymbol label)- => (forall labels a. ()- => context ('Spec labels a)- -> context ('Spec (label ': labels) a))- -> t context- -> HLabel label t context-hlabel labeler a = HLabel $ htabulate $ \(HMapTableField field) ->- case hfield hspecs field of- SSpec {} -> labeler (hfield a field)+hlabel :: forall label t context. t context -> HLabel label t context+hlabel = HLabel {-# INLINABLE hlabel #-} -hunlabel :: (HTable t, KnownSymbol label)- => (forall labels a. ()- => context ('Spec (label ': labels) a)- -> context ('Spec labels a))- -> HLabel label t context- -> t context-hunlabel unlabler (HLabel as) =- htabulate $ \field -> - case hfield hspecs field of- SSpec {} -> case hfield as (HMapTableField field) of- a -> unlabler a+hrelabel :: forall label' label t context. HLabel label t context -> HLabel label' t context+hrelabel = hlabel . hunlabel+{-# INLINABLE hrelabel #-}+++hunlabel :: forall label t context. HLabel label t context -> t context+hunlabel (HLabel a) = a {-# INLINABLE hunlabel #-}+++hproject :: ()+ => (forall ctx. t ctx -> t' ctx)+ -> HLabel label t context -> HLabel label t' context+hproject f (HLabel a) = HLabel (f a)
src/Rel8/Schema/HTable/MapTable.hs view
@@ -6,7 +6,7 @@ {-# language GADTs #-} {-# language InstanceSigs #-} {-# language MultiParamTypeClasses #-}-{-# language PolyKinds #-}+{-# language RankNTypes #-} {-# language ScopedTypeVariables #-} {-# language StandaloneKindSignatures #-} {-# language TypeApplications #-}@@ -19,34 +19,38 @@ , MapSpec(..) , Precompose(..) , HMapTableField(..)+ , hproject ) where -- base import Data.Kind ( Constraint, Type )-import Prelude ( ($), (.), (<$>), fmap )+import Prelude -- rel8-import Rel8.FCF+import Rel8.FCF ( Exp, Eval ) import Rel8.Schema.HTable-import Rel8.Schema.Spec ( Spec, SSpec )+ ( HTable, HConstrainTable, HField+ , hfield, htabulate, htraverse, hdicts, hspecs+ )+import Rel8.Schema.Spec ( Spec ) import qualified Rel8.Schema.Kind as K import Rel8.Schema.Dict ( Dict( Dict ) ) -type HMapTable :: (a -> Exp b) -> ((a -> Type) -> Type) -> (b -> Type) -> Type-newtype HMapTable f t g = HMapTable- { unHMapTable :: t (Precompose f g)+type HMapTable :: (Type -> Exp Type) -> K.HTable -> K.HTable+newtype HMapTable f t context = HMapTable+ { unHMapTable :: t (Precompose f context) } -type Precompose :: (a -> Exp b) -> (b -> Type) -> a -> Type+type Precompose :: (Type -> Exp Type) -> K.Context -> K.Context newtype Precompose f g x = Precompose { precomposed :: g (Eval (f x)) } -type HMapTableField :: (Spec -> Exp a) -> K.HTable -> a -> Type+type HMapTableField :: (Type -> Exp Type) -> K.HTable -> K.Context data HMapTableField f t x where HMapTableField :: HField t a -> HMapTableField f t (Eval (f a)) @@ -79,11 +83,17 @@ {-# INLINABLE hspecs #-} -type MapSpec :: (Spec -> Exp Spec) -> Constraint+type MapSpec :: (Type -> Exp Type) -> Constraint class MapSpec f where- mapInfo :: SSpec x -> SSpec (Eval (f x))+ mapInfo :: Spec x -> Spec (Eval (f x)) -type ComposeConstraint :: (a -> Exp b) -> (b -> Constraint) -> a -> Constraint+type ComposeConstraint :: (Type -> Exp Type) -> (Type -> Constraint) -> Type -> Constraint class c (Eval (f a)) => ComposeConstraint f c a instance c (Eval (f a)) => ComposeConstraint f c a+++hproject :: ()+ => (forall ctx. t ctx -> t' ctx)+ -> HMapTable f t context -> HMapTable f t' context+hproject f (HMapTable a) = HMapTable (f a)
src/Rel8/Schema/HTable/Maybe.hs view
@@ -15,17 +15,16 @@ -- rel8 import Rel8.Schema.HTable ( HTable )-import Rel8.Schema.HTable.Identity ( HIdentity(..) )+import Rel8.Schema.HTable.Identity ( HIdentity ) import Rel8.Schema.HTable.Label ( HLabel ) import Rel8.Schema.HTable.Nullify ( HNullify ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Spec ( Spec( Spec ) ) import Rel8.Type.Tag ( MaybeTag ) type HMaybeTable :: K.HTable -> K.HTable data HMaybeTable table context = HMaybeTable- { htag :: HIdentity ('Spec '["isJust"] (Maybe MaybeTag)) context+ { htag :: HLabel "isJust" (HIdentity (Maybe MaybeTag)) context , hjust :: HLabel "Just" (HNullify table) context } deriving stock Generic
src/Rel8/Schema/HTable/Nullify.hs view
@@ -10,6 +10,7 @@ {-# language NamedFieldPuns #-} {-# language QuantifiedConstraints #-} {-# language RankNTypes #-}+{-# language RecordWildCards #-} {-# language ScopedTypeVariables #-} {-# language StandaloneKindSignatures #-} {-# language TypeFamilies #-}@@ -18,25 +19,34 @@ module Rel8.Schema.HTable.Nullify ( HNullify( HNullify ) , Nullify- , hnulls, hnullify, hunnullify+ , hguard+ , hnulls+ , hnullify+ , hunnullify+ , hproject ) where -- base+import Data.Kind ( Type )+import GHC.Generics ( Generic ) import Prelude hiding ( null ) -- rel8+import Rel8.FCF ( Eval, Exp ) import Rel8.Schema.HTable ( HTable, hfield, htabulate, htabulateA, hspecs )+import Rel8.Schema.HTable.MapTable+ ( HMapTable, HMapTableField( HMapTableField )+ , MapSpec, mapInfo+ )+import qualified Rel8.Schema.HTable.MapTable as HMapTable ( hproject ) import qualified Rel8.Schema.Kind as K import Rel8.Schema.Null ( Nullity( Null, NotNull ) ) import qualified Rel8.Schema.Null as Type ( Nullify )-import Rel8.Schema.Spec ( Spec( Spec ), SSpec(..) )+import Rel8.Schema.Spec ( Spec(..) ) -- semigroupoids import Data.Functor.Apply ( Apply )-import Rel8.Schema.HTable.MapTable-import Rel8.FCF-import GHC.Generics (Generic) type HNullify :: K.HTable -> K.HTable@@ -45,58 +55,63 @@ deriving anyclass HTable ---- | Transform a 'Spec' by allowing it to be @null@.-data Nullify :: Spec -> Exp Spec---type instance Eval (Nullify ('Spec labels a)) =- 'Spec labels (Type.Nullify a)+-- | Transform a 'Type' by allowing it to be @null@.+data Nullify :: Type -> Exp Type+type instance Eval (Nullify a) = Type.Nullify a instance MapSpec Nullify where mapInfo = \case- SSpec{labels, info, nullity} -> SSpec- { labels- , info- , nullity = case nullity of+ Spec {nullity, ..} -> Spec+ { nullity = case nullity of Null -> Null NotNull -> Null- } + , ..+ } +hguard :: HTable t+ => (forall a. context (Maybe a) -> context (Maybe a))+ -> HNullify t context -> HNullify t context+hguard guarder (HNullify as) = HNullify $ htabulate $ \(HMapTableField field) ->+ case hfield hspecs field of+ Spec {nullity} -> case hfield as (HMapTableField field) of+ a -> case nullity of+ Null -> guarder a+ NotNull -> guarder a++ hnulls :: HTable t- => (forall labels a. ()- => SSpec ('Spec labels a)- -> context ('Spec labels (Type.Nullify a)))+ => (forall a. Spec a -> context (Type.Nullify a)) -> HNullify t context-hnulls null = HNullify $ htabulate $ \(HMapTableField field) -> case hfield hspecs field of- spec@SSpec {} -> null spec+hnulls null = HNullify $ htabulate $ \(HMapTableField field) ->+ case hfield hspecs field of+ spec@Spec {} -> null spec {-# INLINABLE hnulls #-} hnullify :: HTable t- => (forall labels a. ()- => SSpec ('Spec labels a)- -> context ('Spec labels a)- -> context ('Spec labels (Type.Nullify a)))+ => (forall a. Spec a -> context a -> context (Type.Nullify a)) -> t context -> HNullify t context hnullify nullifier a = HNullify $ htabulate $ \(HMapTableField field) -> case hfield hspecs field of- spec@SSpec {} -> nullifier spec (hfield a field)+ spec@Spec {} -> nullifier spec (hfield a field) {-# INLINABLE hnullify #-} hunnullify :: (HTable t, Apply m)- => (forall labels a. ()- => SSpec ('Spec labels a)- -> context ('Spec labels (Type.Nullify a))- -> m (context ('Spec labels a)))+ => (forall a. Spec a -> context (Type.Nullify a) -> m (context a)) -> HNullify t context -> m (t context) hunnullify unnullifier (HNullify as) = htabulateA $ \field -> case hfield hspecs field of- spec@SSpec {} -> case hfield as (HMapTableField field) of+ spec@Spec {} -> case hfield as (HMapTableField field) of a -> unnullifier spec a {-# INLINABLE hunnullify #-}+++hproject :: ()+ => (forall ctx. t ctx -> t' ctx)+ -> HNullify t context -> HNullify t' context+hproject f (HNullify a) = HNullify (HMapTable.hproject f a)
src/Rel8/Schema/HTable/These.hs view
@@ -19,15 +19,14 @@ import Rel8.Schema.HTable.Label ( HLabel ) import Rel8.Schema.HTable.Nullify ( HNullify ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Spec ( Spec( Spec ) ) import Rel8.Type.Tag ( MaybeTag ) type HTheseTable :: K.HTable -> K.HTable -> K.HTable data HTheseTable here there context = HTheseTable- { hhereTag :: HIdentity ('Spec '["hasHere"] (Maybe MaybeTag)) context+ { hhereTag :: HLabel "hereTag" (HIdentity (Maybe MaybeTag)) context , hhere :: HLabel "Here" (HNullify here) context- , hthereTag :: HIdentity ('Spec '["hasThere"] (Maybe MaybeTag)) context+ , hthereTag :: HLabel "thereTag" (HIdentity (Maybe MaybeTag)) context , hthere :: HLabel "There" (HNullify there) context } deriving stock Generic
src/Rel8/Schema/HTable/Vectorize.hs view
@@ -9,8 +9,6 @@ {-# language GADTs #-} {-# language LambdaCase #-} {-# language MultiParamTypeClasses #-}-{-# language NamedFieldPuns #-}-{-# language QuantifiedConstraints #-} {-# language RankNTypes #-} {-# language RecordWildCards #-} {-# language ScopedTypeVariables #-}@@ -23,32 +21,33 @@ ( HVectorize , hvectorize, hunvectorize , happend, hempty+ , hproject ) where -- base import Data.Kind ( Type ) import Data.List.NonEmpty ( NonEmpty )+import GHC.Generics (Generic) import Prelude -- rel8-import Rel8.Schema.Context.Label ( HLabelable, hlabeler, hunlabeler )+import Rel8.FCF ( Eval, Exp ) import Rel8.Schema.Dict ( Dict( Dict ) ) import qualified Rel8.Schema.Kind as K-import Rel8.Schema.HTable- ( HTable- , hfield, htabulate, htabulateA, hspecs+import Rel8.Schema.HTable ( HTable, hfield, htabulate, htabulateA, hspecs )+import Rel8.Schema.HTable.MapTable+ ( HMapTable, HMapTableField( HMapTableField )+ , MapSpec, mapInfo )+import qualified Rel8.Schema.HTable.MapTable as HMapTable ( hproject ) import Rel8.Schema.Null ( Unnullify, NotNull, Nullity( NotNull ) )-import Rel8.Schema.Spec ( Spec( Spec ), SSpec(..) )+import Rel8.Schema.Spec ( Spec(..) ) import Rel8.Type.Array ( listTypeInformation, nonEmptyTypeInformation ) import Rel8.Type.Information ( TypeInformation ) -- semialign import Data.Zip ( Unzip, Zip, Zippy(..) )-import Rel8.FCF-import Rel8.Schema.HTable.MapTable-import GHC.Generics (Generic) class Vector list where@@ -75,16 +74,16 @@ deriving anyclass HTable -data Vectorize :: (Type -> Type) -> Spec -> Exp Spec+data Vectorize :: (Type -> Type) -> Type -> Exp Type -type instance Eval (Vectorize list ('Spec labels a)) = 'Spec labels (list a)+type instance Eval (Vectorize list a) = list a instance Vector list => MapSpec (Vectorize list) where mapInfo = \case- SSpec {..} -> case listNotNull @list nullity of- Dict -> SSpec+ Spec {..} -> case listNotNull @list nullity of+ Dict -> Spec { nullity = NotNull , info = vectorTypeInformation nullity info , ..@@ -92,60 +91,45 @@ hvectorize :: (HTable t, Unzip f, Vector list)- => (forall labels a. ()- => SSpec ('Spec labels a)- -> f (context ('Spec labels a))- -> context' ('Spec labels (list a)))+ => (forall a. Spec a -> f (context a) -> context' (list a)) -> f (t context) -> HVectorize list t context' hvectorize vectorizer as = HVectorize $ htabulate $ \(HMapTableField field) -> case hfield hspecs field of- spec@SSpec {} -> vectorizer spec (fmap (`hfield` field) as)+ spec -> vectorizer spec (fmap (`hfield` field) as) {-# INLINABLE hvectorize #-} hunvectorize :: (HTable t, Zip f, Vector list)- => (forall labels a. ()- => SSpec ('Spec labels a)- -> context ('Spec labels (list a))- -> f (context' ('Spec labels a)))+ => (forall a. Spec a -> context (list a) -> f (context' a)) -> HVectorize list t context -> f (t context') hunvectorize unvectorizer (HVectorize table) = getZippy $ htabulateA $ \field -> case hfield hspecs field of- spec@SSpec{} -> case hfield table (HMapTableField field) of+ spec -> case hfield table (HMapTableField field) of a -> Zippy (unvectorizer spec a) {-# INLINABLE hunvectorize #-} -happend :: (HTable t, Vector list) =>- ( forall labels a. ()- => Nullity a- -> TypeInformation (Unnullify a)- -> context ('Spec labels (list a))- -> context ('Spec labels (list a))- -> context ('Spec labels (list a))- )+happend :: (HTable t, Vector list)+ => (forall a. Spec a -> context (list a) -> context (list a) -> context (list a)) -> HVectorize list t context -> HVectorize list t context -> HVectorize list t context happend append (HVectorize as) (HVectorize bs) = HVectorize $ htabulate $ \field@(HMapTableField j) -> case (hfield as field, hfield bs field) of (a, b) -> case hfield hspecs j of- SSpec {nullity, info} -> append nullity info a b+ spec -> append spec a b -hempty :: HTable t =>- ( forall labels a. ()- => Nullity a- -> TypeInformation (Unnullify a)- -> context ('Spec labels [a])- )+hempty :: HTable t+ => (forall a. Spec a -> context [a]) -> HVectorize [] t context-hempty empty = HVectorize $ htabulate $ \(HMapTableField field) -> case hfield hspecs field of- SSpec {nullity, info} -> empty nullity info+hempty empty = HVectorize $ htabulate $ \(HMapTableField field) ->+ empty (hfield hspecs field) -instance HLabelable g => HLabelable (Precompose (Vectorize list) g) where- hlabeler = Precompose . hlabeler . precomposed- hunlabeler = Precompose . hunlabeler . precomposed+hproject :: ()+ => (forall ctx. t ctx -> t' ctx)+ -> HVectorize list t context -> HVectorize list t' context+hproject f (HVectorize a) = HVectorize (HMapTable.hproject f a)
src/Rel8/Schema/Kind.hs view
@@ -1,8 +1,9 @@ {-# language StandaloneKindSignatures #-} module Rel8.Schema.Kind- ( Context, Rel8able- , HContext, HTable+ ( Rel8able+ , Context+ , HTable ) where @@ -10,23 +11,13 @@ import Data.Kind ( Type ) import Prelude () --- rel8-import Rel8.Schema.Spec ( Spec ) --type HContext :: Type-type HContext = Spec -> Type+type Context :: Type+type Context = Type -> Type type HTable :: Type-type HTable = HContext -> Type---data X---type Context :: Type-type Context = X -> Type+type HTable = Context -> Type type Rel8able :: Type
src/Rel8/Schema/Name.hs view
@@ -3,41 +3,44 @@ {-# language FlexibleContexts #-} {-# language FlexibleInstances #-} {-# language GADTs #-}+{-# language GeneralizedNewtypeDeriving #-} {-# language MultiParamTypeClasses #-}-{-# language PolyKinds #-} {-# language RankNTypes #-}-{-# language StandaloneDeriving #-} {-# language StandaloneKindSignatures #-} {-# language TypeFamilies #-} {-# language UndecidableInstances #-}-{-# language UndecidableSuperClasses #-} module Rel8.Schema.Name ( Name(..)- , Col( N, unN ) , Selects+ , ppColumn ) where -- base-import Data.Functor.Identity ( Identity )+import Data.Functor.Identity ( Identity( Identity ) ) import Data.Kind ( Constraint, Type )-import Data.String ( IsString, fromString )+import Data.String ( IsString ) import Prelude +-- opaleye+import qualified Opaleye.Internal.HaskellDB.Sql as Opaleye+import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye++-- pretty+import Text.PrettyPrint ( Doc )+ -- rel8 import Rel8.Expr ( Expr )-import Rel8.Schema.Context ( Interpretation, Col )-import Rel8.Schema.Context.Label ( Labelable, labeler, unlabeler )-import Rel8.Schema.HTable.Identity ( HIdentity( HType ), HType )+import qualified Rel8.Schema.Kind as K+import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) ) import Rel8.Schema.Null ( Sql )-import Rel8.Schema.Reify ( notReify )-import Rel8.Schema.Result ( Result )-import Rel8.Schema.Spec ( Spec( Spec ) ) import Rel8.Table- ( Table, Columns, Context, fromColumns, toColumns, reify, unreify+ ( Table, Columns, Context, fromColumns, toColumns+ , FromExprs, fromResult, toResult+ , Transpose )-import Rel8.Table.Recontextualize ( Recontextualize )+import Rel8.Table.Transpose ( Transposes ) import Rel8.Type ( DBType ) @@ -45,55 +48,30 @@ -- schema definition. You can construct names by using the @OverloadedStrings@ -- extension and writing string literals. This is typically done when providing -- a 'TableSchema' value.-type Name :: k -> Type-data Name a where- Name :: k ~ Type => !String -> Name (a :: k)---deriving stock instance Show (Name a)---instance k ~ Type => IsString (Name (a :: k)) where- fromString = Name+type Name :: K.Context+newtype Name a = Name String+ deriving stock Show+ deriving newtype IsString instance Sql DBType a => Table Name (Name a) where- type Columns (Name a) = HType a+ type Columns (Name a) = HIdentity a type Context (Name a) = Name-- toColumns a = HType (N a)- fromColumns (HType (N a)) = a- reify = notReify- unreify = notReify---instance Sql DBType a => Recontextualize Expr Name (Expr a) (Name a)---instance Sql DBType a => Recontextualize Result Name (Identity a) (Name a)---instance Sql DBType a => Recontextualize Name Expr (Name a) (Expr a)---instance Sql DBType a => Recontextualize Name Result (Name a) (Identity a)---instance Sql DBType a => Recontextualize Name Name (Name a) (Name a)---instance Interpretation Name where- data Col Name _spec where- N :: {unN :: !(Name a)} -> Col Name ('Spec labels a)-+ type FromExprs (Name a) = a+ type Transpose to (Name a) = to a -instance Labelable Name where- labeler (N a) = N a- unlabeler (N a) = N a+ toColumns a = HIdentity a+ fromColumns (HIdentity a) = a+ toResult a = HIdentity (Identity a)+ fromResult (HIdentity (Identity a)) = a -- | @Selects a b@ means that @a@ is a schema (i.e., a 'Table' of 'Name's) for -- the 'Expr' columns in @b@. type Selects :: Type -> Type -> Constraint-class Recontextualize Name Expr names exprs => Selects names exprs-instance Recontextualize Name Expr names exprs => Selects names exprs+class Transposes Name Expr names exprs => Selects names exprs+instance Transposes Name Expr names exprs => Selects names exprs+++ppColumn :: String -> Doc+ppColumn = Opaleye.ppSqlExpr . Opaleye.ColumnSqlExpr . Opaleye.SqlColumn
+ src/Rel8/Schema/Name.hs-boot view
@@ -0,0 +1,11 @@+{-# language PolyKinds #-}+{-# language RoleAnnotations #-}+{-# language StandaloneKindSignatures #-}++module Rel8.Schema.Name where++import Data.Kind ( Type )++type Name :: k -> Type+type role Name nominal+data Name a
− src/Rel8/Schema/Reify.hs
@@ -1,74 +0,0 @@-{-# language AllowAmbiguousTypes #-}-{-# language EmptyCase #-}-{-# language DataKinds #-}-{-# language FlexibleInstances #-}-{-# language ScopedTypeVariables #-}-{-# language StandaloneKindSignatures #-}-{-# language TypeApplications #-}-{-# language TypeFamilies #-}-{-# language TypeOperators #-}-{-# language UndecidableInstances #-}--module Rel8.Schema.Reify- ( Reify, Col( Reify ), hreify, hunreify- , UnwrapReify- , NotReify, notReify- )-where---- base-import Data.Kind ( Constraint )-import Data.Type.Equality ( (:~:)( Refl ) )-import Prelude---- rel8-import Rel8.Schema.Context ( Interpretation, Col )-import Rel8.Schema.Context.Label ( Labelable, labeler, unlabeler )-import Rel8.Schema.HTable ( HTable, hmap )-import Rel8.Schema.Kind ( Context )---type Reify :: Context -> Context-data Reify context a---instance Interpretation (Reify context) where- newtype Col (Reify context) spec = Reify (Col context spec)---instance Labelable context => Labelable (Reify context) where- labeler (Reify a) = Reify (labeler a)- unlabeler (Reify a) = Reify (unlabeler a)---hreify :: HTable t => t (Col context) -> t (Col (Reify context))-hreify = hmap Reify---hunreify :: HTable t => t (Col (Reify context)) -> t (Col context)-hunreify = hmap (\(Reify a) -> a)---type UnwrapReify :: Context -> Context-type family UnwrapReify context where- UnwrapReify (Reify context) = context---type IsReify :: Context -> Bool-type family IsReify context where- IsReify (Reify _) = 'True- IsReify _ = 'False---type NotReify :: Context -> Constraint-class IsReify context ~ 'False => NotReify context-instance IsReify context ~ 'False => NotReify context---notReify :: forall context ctx a. NotReify context => context :~: Reify ctx -> a-notReify refl = case lemma @context of- Refl -> case refl of---lemma :: NotReify context => IsReify context :~: 'False-lemma = Refl
src/Rel8/Schema/Result.hs view
@@ -5,22 +5,20 @@ {-# language TypeFamilies #-} module Rel8.Schema.Result- ( Col( R, unR ), Result- , relabel+ ( Result , null, nullifier, unnullifier , vectorizer, unvectorizer ) where -- base+import Data.Functor.Identity ( Identity( Identity), runIdentity ) import Prelude hiding ( null ) -- rel8-import Rel8.Schema.Context ( Interpretation( Col ) )-import Rel8.Schema.HTable.Identity ( HIdentity(..) ) import Rel8.Schema.Kind ( Context ) import Rel8.Schema.Null ( Nullify, Nullity( Null, NotNull ) )-import Rel8.Schema.Spec ( Spec( Spec ), SSpec(..) )+import Rel8.Schema.Spec ( Spec(..) ) -- | The @Result@ context is the context used for decoded query results.@@ -28,52 +26,29 @@ -- When a query is executed against a PostgreSQL database, Rel8 parses the -- returned rows, decoding each row into the @Result@ context. type Result :: Context-data Result a---instance Interpretation Result where- data Col Result _spec where- R :: {unR :: !a} -> Col Result ('Spec labels a)---relabel :: ()- => HIdentity ('Spec labels a) (Col Result)- -> HIdentity ('Spec relabels a) (Col Result)-relabel (HIdentity (R a)) = HIdentity (R a)+type Result = Identity -null :: Col Result ('Spec labels (Maybe a))-null = R Nothing+null :: Result (Maybe a)+null = Identity Nothing -nullifier :: ()- => SSpec ('Spec labels a)- -> Col Result ('Spec labels a)- -> Col Result ('Spec labels (Nullify a))-nullifier SSpec {nullity} (R a) = R $ case nullity of+nullifier :: Spec a -> Result a -> Result (Nullify a)+nullifier Spec {nullity} (Identity a) = Identity $ case nullity of Null -> a NotNull -> Just a -unnullifier :: ()- => SSpec ('Spec labels a)- -> Col Result ('Spec labels (Nullify a))- -> Maybe (Col Result ('Spec labels a))-unnullifier SSpec {nullity} (R a) =+unnullifier :: Spec a -> Result (Nullify a) -> Maybe (Result a)+unnullifier Spec {nullity} (Identity a) = case nullity of- Null -> pure $ R a- NotNull -> R <$> a+ Null -> pure $ Identity a+ NotNull -> Identity <$> a -vectorizer :: Functor f- => SSpec ('Spec labels a)- -> f (Col Result ('Spec labels a))- -> Col Result ('Spec labels (f a))-vectorizer _ = R . fmap unR+vectorizer :: Functor f => Spec a -> f (Result a) -> Result (f a)+vectorizer _ = Identity . fmap runIdentity -unvectorizer :: Functor f- => SSpec ('Spec labels a)- -> Col Result ('Spec labels (f a))- -> f (Col Result ('Spec labels a))-unvectorizer _ (R results) = R <$> results+unvectorizer :: Functor f => Spec a -> Result (f a) -> f (Result a)+unvectorizer _ (Identity results) = Identity <$> results
src/Rel8/Schema/Spec.hs view
@@ -1,54 +1,34 @@-{-# language DataKinds #-} {-# language FlexibleContexts #-}-{-# language GADTs #-}+{-# language MonoLocalBinds #-} {-# language StandaloneKindSignatures #-}-{-# language UndecidableInstances #-} module Rel8.Schema.Spec- ( Spec( Spec )- , SSpec( SSpec, labels, info, nullity )- , KnownSpec( specSing )+ ( Spec( Spec, labels, info, nullity )+ , specification ) where -- base-import Data.Kind ( Constraint, Type )-import Prelude ()+import Data.Kind ( Type )+import Prelude -- rel8-import Rel8.Kind.Labels ( Labels, SLabels, KnownLabels, labelsSing ) import Rel8.Schema.Null ( Nullity, Sql, Unnullify, nullable ) import Rel8.Type ( DBType, typeInformation ) import Rel8.Type.Information ( TypeInformation ) -type Spec :: Type-data Spec = Spec Labels Type---type SSpec :: Spec -> Type-data SSpec spec where- SSpec ::- { labels :: SLabels labels- , info :: TypeInformation (Unnullify a)- , nullity :: Nullity a- }- -> SSpec ('Spec labels a)---type KnownSpec :: Spec -> Constraint-class KnownSpec spec where- specSing :: SSpec spec+type Spec :: Type -> Type+data Spec a = Spec+ { labels :: [String]+ , info :: TypeInformation (Unnullify a)+ , nullity :: Nullity a+ } -instance- ( KnownLabels labels- , Sql DBType a- )- => KnownSpec ('Spec labels a)- where- specSing = SSpec- { labels = labelsSing- , info = typeInformation- , nullity = nullable- }+specification :: Sql DBType a => Spec a+specification = Spec+ { labels = []+ , info = typeInformation+ , nullity = nullable+ }
− src/Rel8/Schema/Spec/ConstrainDBType.hs
@@ -1,89 +0,0 @@-{-# language ConstraintKinds #-}-{-# language DataKinds #-}-{-# language FlexibleInstances #-}-{-# language MultiParamTypeClasses #-}-{-# language NamedFieldPuns #-}-{-# language QuantifiedConstraints #-}-{-# language StandaloneKindSignatures #-}-{-# language TypeFamilies #-}-{-# language UndecidableInstances #-}--module Rel8.Schema.Spec.ConstrainDBType- ( ConstrainDBType- , dbTypeNullity, dbTypeDict- , nullifier, unnullifier- )-where---- base-import Data.Kind ( Constraint, Type )-import Prelude---- rel8-import Rel8.Schema.Dict ( Dict( Dict ) )-import Rel8.Schema.Null- ( Nullify, Unnullify- , Nullity( Null, NotNull )- , Sql, nullable- )-import Rel8.Schema.Spec ( Spec( Spec ), SSpec( SSpec, nullity ) )---type ConstrainDBType :: (Type -> Constraint) -> Spec -> Constraint-class- ( forall c labels a. ()- => (spec ~ 'Spec labels a)- => (forall x. (constraint x => c x)) => Sql c a- )- => ConstrainDBType constraint spec-instance- ( spec ~ 'Spec labels a- , Sql constraint a- )- => ConstrainDBType constraint spec---dbTypeNullity :: Dict (ConstrainDBType c) ('Spec l a) -> Nullity a-dbTypeNullity = step2 . step1- where- step1 :: Dict (ConstrainDBType c) ('Spec l a) -> Dict (Sql c) a- step1 Dict = Dict-- step2 :: Dict (Sql c) a -> Nullity a- step2 Dict = nullable---dbTypeDict :: Dict (ConstrainDBType c) ('Spec l a) -> Dict c (Unnullify a)-dbTypeDict = step2 . step1- where- step1 :: Dict (ConstrainDBType c) ('Spec l a) -> Dict (Sql c) a- step1 Dict = Dict-- step2 :: Dict (Sql c) a -> Dict c (Unnullify a)- step2 Dict = Dict---fromNullityDict :: Nullity a -> Dict c (Unnullify a) -> Dict (ConstrainDBType c) ('Spec l a)-fromNullityDict Null Dict = Dict-fromNullityDict NotNull Dict = Dict---nullifier :: ()- => SSpec ('Spec labels a)- -> Dict (ConstrainDBType c) ('Spec labels a)- -> Dict (ConstrainDBType c) ('Spec labels (Nullify a))-nullifier SSpec {} dict = case dbTypeDict dict of- Dict -> case dbTypeNullity dict of- Null -> Dict- NotNull -> Dict---unnullifier :: ()- => SSpec ('Spec labels a)- -> Dict (ConstrainDBType c) ('Spec labels (Nullify a))- -> Dict (ConstrainDBType c) ('Spec labels a)-unnullifier SSpec {nullity} dict = case dbTypeDict dict of- Dict -> case nullity of- Null -> Dict- NotNull -> case dbTypeNullity dict of- Null -> fromNullityDict nullity Dict
src/Rel8/Schema/Table.hs view
@@ -1,15 +1,25 @@ {-# language DeriveFunctor #-} {-# language DerivingStrategies #-}+{-# language DisambiguateRecordFields #-}+{-# language NamedFieldPuns #-} module Rel8.Schema.Table ( TableSchema(..)+ , ppTable ) where -- base import Prelude +-- opaleye+import qualified Opaleye.Internal.HaskellDB.Sql as Opaleye+import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye +-- pretty+import Text.PrettyPrint ( Doc )++ -- | The schema for a table. This is used to specify the name and schema that a -- table belongs to (the @FROM@ part of a SQL query), along with the schema of -- the columns within this table.@@ -27,3 +37,10 @@ -- data type here, parameterized by the 'Rel8.ColumnSchema.ColumnSchema' functor. } deriving stock Functor+++ppTable :: TableSchema a -> Doc+ppTable TableSchema {name, schema} = Opaleye.ppTable Opaleye.SqlTable+ { sqlTableSchemaName = schema+ , sqlTableName = name+ }
src/Rel8/Statement/Delete.hs view
@@ -1,40 +1,37 @@ {-# language DuplicateRecordFields #-} {-# language GADTs #-} {-# language NamedFieldPuns #-}-{-# language ScopedTypeVariables #-}+{-# language RankNTypes #-}+{-# language RecordWildCards #-} {-# language StandaloneKindSignatures #-}-{-# language TypeApplications #-}+{-# language StrictData #-} module Rel8.Statement.Delete ( Delete(..) , delete+ , ppDelete ) where -- base-import Control.Exception ( throwIO ) import Data.Kind ( Type ) import Prelude -- hasql-import Hasql.Connection ( Connection )-import qualified Hasql.Decoders as Hasql import qualified Hasql.Encoders as Hasql-import qualified Hasql.Session as Hasql import qualified Hasql.Statement as Hasql --- opaleye-import qualified Opaleye.Internal.Manipulation as Opaleye+-- pretty+import Text.PrettyPrint ( Doc, (<+>), ($$), text ) -- rel8 import Rel8.Expr ( Expr )-import Rel8.Expr.Opaleye ( toColumn, toPrimExpr )+import Rel8.Query ( Query ) import Rel8.Schema.Name ( Selects )-import Rel8.Schema.Table ( TableSchema )-import Rel8.Statement.Returning ( Returning( NumberOfRowsAffected, Projection ) )-import Rel8.Table ( fromColumns, toColumns )-import Rel8.Table.Opaleye ( castTable, table, unpackspec )-import Rel8.Table.Serialize ( Serializable, parse )+import Rel8.Schema.Table ( TableSchema, ppTable )+import Rel8.Statement.Returning ( Returning, decodeReturning, ppReturning )+import Rel8.Statement.Using ( ppUsing )+import Rel8.Statement.Where ( ppWhere ) -- text import qualified Data.Text as Text@@ -47,7 +44,10 @@ Delete :: Selects names exprs => { from :: TableSchema names -- ^ Which table to delete from.- , deleteWhere :: exprs -> Expr Bool+ , using :: Query using+ -- ^ @USING@ clause — this can be used to join against other tables,+ -- and its results can be referenced in the @WHERE@ clause+ , deleteWhere :: using -> exprs -> Expr Bool -- ^ Which rows should be selected for deletion. , returning :: Returning names a -- ^ What to return from the @DELETE@ statement.@@ -55,38 +55,25 @@ -> Delete a --- | Run a @DELETE@ statement.-delete :: Connection -> Delete a -> IO a-delete c Delete {from, deleteWhere, returning} =- case returning of- NumberOfRowsAffected -> Hasql.run session c >>= either throwIO pure- where- session = Hasql.statement () statement- statement = Hasql.Statement bytes params decode prepare- bytes = encodeUtf8 $ Text.pack sql- params = Hasql.noParams- decode = Hasql.rowsAffected- prepare = False- sql = Opaleye.arrangeDeleteSql from' where'- where- from' = table $ toColumns <$> from- where' = toColumn . toPrimExpr . deleteWhere . fromColumns+ppDelete :: Delete a -> Doc+ppDelete Delete {..} = case ppUsing using of+ Nothing ->+ text "DELETE FROM" <+> ppTable from $$+ text "WHERE false"+ Just (usingDoc, i) ->+ text "DELETE FROM" <+> ppTable from $$+ usingDoc $$+ ppWhere from (deleteWhere i) $$+ ppReturning from returning - Projection project -> Hasql.run session c >>= either throwIO pure- where- session = Hasql.statement () statement- statement = Hasql.Statement bytes params decode prepare- bytes = encodeUtf8 $ Text.pack sql- params = Hasql.noParams- decode = decoder project- prepare = False- sql =- Opaleye.arrangeDeleteReturningSql unpackspec from' where' project'- where- from' = table $ toColumns <$> from- where' = toColumn . toPrimExpr . deleteWhere . fromColumns- project' = castTable . toColumns . project . fromColumns++-- | Run a 'Delete' statement.+delete :: Delete a -> Hasql.Statement () a+delete d@Delete {returning} = Hasql.Statement bytes params decode prepare where- decoder :: forall exprs projection a. Serializable projection a- => (exprs -> projection) -> Hasql.Result [a]- decoder _ = Hasql.rowList (parse @projection @a)+ bytes = encodeUtf8 $ Text.pack sql+ params = Hasql.noParams+ decode = decodeReturning returning+ prepare = False+ sql = show doc+ doc = ppDelete d
src/Rel8/Statement/Insert.hs view
@@ -1,64 +1,59 @@ {-# language DuplicateRecordFields #-}+{-# language FlexibleContexts #-} {-# language GADTs #-} {-# language NamedFieldPuns #-}-{-# language ScopedTypeVariables #-}+{-# language RecordWildCards #-} {-# language StandaloneKindSignatures #-}-{-# language TypeApplications #-}+{-# language StrictData #-} module Rel8.Statement.Insert ( Insert(..)- , OnConflict(..) , insert+ , ppInsert+ , ppInto ) where -- base-import Control.Exception ( throwIO )-import Data.List.NonEmpty ( NonEmpty( (:|) ) )+import Data.Foldable ( toList ) import Data.Kind ( Type ) import Prelude -- hasql-import Hasql.Connection ( Connection )-import qualified Hasql.Decoders as Hasql import qualified Hasql.Encoders as Hasql-import qualified Hasql.Session as Hasql import qualified Hasql.Statement as Hasql -- opaleye-import qualified Opaleye.Internal.Manipulation as Opaleye-import qualified Opaleye.Manipulation as Opaleye+import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye +-- pretty+import Text.PrettyPrint ( Doc, (<+>), ($$), parens, text )+ -- rel8-import Rel8.Schema.Name ( Selects )-import Rel8.Schema.Table ( TableSchema )-import Rel8.Statement.Returning ( Returning( Projection, NumberOfRowsAffected ) )-import Rel8.Table ( fromColumns, toColumns )-import Rel8.Table.Opaleye ( castTable, table, unpackspec )-import Rel8.Table.Serialize ( Serializable, parse )+import Rel8.Query ( Query )+import Rel8.Schema.Name ( Name, Selects, ppColumn )+import Rel8.Schema.Table ( TableSchema(..), ppTable )+import Rel8.Statement.OnConflict ( OnConflict, ppOnConflict )+import Rel8.Statement.Returning ( Returning, decodeReturning, ppReturning )+import Rel8.Statement.Select ( ppRows )+import Rel8.Table ( Table )+import Rel8.Table.Name ( showNames ) -- text import qualified Data.Text as Text ( pack ) import Data.Text.Encoding ( encodeUtf8 ) --- | @OnConflict@ allows you to add an @ON CONFLICT@ clause to an @INSERT@--- statement.-type OnConflict :: Type-data OnConflict- = Abort -- ^ @ON CONFLICT ABORT@- | DoNothing -- ^ @ON CONFLICT DO NOTHING@-- -- | The constituent parts of a SQL @INSERT@ statement. type Insert :: Type -> Type data Insert a where Insert :: Selects names exprs => { into :: TableSchema names -- ^ Which table to insert into.- , rows :: [exprs]- -- ^ The rows to insert.- , onConflict :: OnConflict+ , rows :: Query exprs+ -- ^ The rows to insert. This can be an arbitrary query — use+ -- 'Rel8.values' insert a static list of rows.+ , onConflict :: OnConflict names -- ^ What to do if the inserted rows conflict with data already in the -- table. , returning :: Returning names a@@ -67,52 +62,28 @@ -> Insert a --- | Run an @INSERT@ statement-insert :: Connection -> Insert a -> IO a-insert c Insert {into, rows, onConflict, returning} =- case (rows, returning) of- ([], NumberOfRowsAffected) -> pure 0- ([], Projection _) -> pure []+ppInsert :: Insert a -> Doc+ppInsert Insert {..} =+ text "INSERT INTO" <+>+ ppInto into $$+ ppRows rows $$+ ppOnConflict into onConflict $$+ ppReturning into returning - (x:xs, NumberOfRowsAffected) -> Hasql.run session c >>= either throwIO pure- where- session = Hasql.statement () statement- statement = Hasql.Statement bytes params decode prepare- bytes = encodeUtf8 $ Text.pack sql- params = Hasql.noParams- decode = Hasql.rowsAffected- prepare = False- sql = Opaleye.arrangeInsertManySql into' rows' onConflict'- where- into' = table $ toColumns <$> into- rows' = toColumns <$> x :| xs - (x:xs, Projection project) -> Hasql.run session c >>= either throwIO pure- where- session = Hasql.statement () statement- statement = Hasql.Statement bytes params decode prepare- bytes = encodeUtf8 $ Text.pack sql- params = Hasql.noParams- decode = decoder project- prepare = False- sql =- Opaleye.arrangeInsertManyReturningSql- unpackspec- into'- rows'- project'- onConflict'- where- into' = table $ toColumns <$> into- rows' = toColumns <$> x :| xs- project' = castTable . toColumns . project . fromColumns+ppInto :: Table Name a => TableSchema a -> Doc+ppInto table@TableSchema {columns} =+ ppTable table <+>+ parens (Opaleye.commaV ppColumn (toList (showNames columns))) - where- onConflict' =- case onConflict of- DoNothing -> Just Opaleye.DoNothing- Abort -> Nothing - decoder :: forall exprs projection a. Serializable projection a- => (exprs -> projection) -> Hasql.Result [a]- decoder _ = Hasql.rowList (parse @projection @a)+-- | Run an 'Insert' statement.+insert :: Insert a -> Hasql.Statement () a+insert i@Insert {returning} = Hasql.Statement bytes params decode prepare+ where+ bytes = encodeUtf8 $ Text.pack sql+ params = Hasql.noParams+ decode = decodeReturning returning+ prepare = False+ sql = show doc+ doc = ppInsert i
+ src/Rel8/Statement/OnConflict.hs view
@@ -0,0 +1,106 @@+{-# language DuplicateRecordFields #-}+{-# language FlexibleContexts #-}+{-# language GADTs #-}+{-# language LambdaCase #-}+{-# language NamedFieldPuns #-}+{-# language RecordWildCards #-}+{-# language StandaloneKindSignatures #-}+{-# language StrictData #-}++module Rel8.Statement.OnConflict+ ( OnConflict(..)+ , Upsert(..)+ , ppOnConflict+ )+where++-- base+import Data.Foldable ( toList )+import Data.Kind ( Type )+import Prelude++-- opaleye+import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye++-- pretty+import Text.PrettyPrint ( Doc, (<+>), ($$), parens, text )++-- rel8+import Rel8.Expr ( Expr )+import Rel8.Schema.Name ( Name, Selects, ppColumn )+import Rel8.Schema.Table ( TableSchema(..) )+import Rel8.Statement.Set ( ppSet )+import Rel8.Statement.Where ( ppWhere )+import Rel8.Table ( Table, toColumns )+import Rel8.Table.Cols ( Cols( Cols ) )+import Rel8.Table.Name ( showNames )+import Rel8.Table.Opaleye ( attributes )+import Rel8.Table.Projection ( Projecting, Projection, apply )+++-- | 'OnConflict' represents the @ON CONFLICT@ clause of an @INSERT@+-- statement. This specifies what ought to happen when one or more of the+-- rows proposed for insertion conflict with an existing row in the table.+type OnConflict :: Type -> Type+data OnConflict names+ = Abort+ -- ^ Abort the transaction if there are conflicting rows (Postgres' default)+ | DoNothing+ -- ^ @ON CONFLICT DO NOTHING@+ | DoUpdate (Upsert names)+ -- ^ @ON CONFLICT DO UPDATE@+++-- | The @ON CONFLICT (...) DO UPDATE@ clause of an @INSERT@ statement, also+-- known as \"upsert\".+--+-- When an existing row conflicts with a row proposed for insertion,+-- @ON CONFLICT DO UPDATE@ allows you to instead update this existing row. The+-- conflicting row proposed for insertion is then \"excluded\", but its values+-- can still be referenced from the @SET@ and @WHERE@ clauses of the @UPDATE@+-- statement.+--+-- Upsert in Postgres requires an explicit set of \"conflict targets\" — the+-- set of columns comprising the @UNIQUE@ index from conflicts with which we+-- would like to recover.+type Upsert :: Type -> Type+data Upsert names where+ Upsert :: (Selects names exprs, Projecting names index, excluded ~ exprs) =>+ { index :: Projection names index+ -- ^ The set of conflict targets, projected from the set of columns for+ -- the whole table+ , set :: excluded -> exprs -> exprs+ -- ^ How to update each selected row.+ , updateWhere :: excluded -> exprs -> Expr Bool+ -- ^ Which rows to select for update.+ }+ -> Upsert names+++ppOnConflict :: TableSchema names -> OnConflict names -> Doc+ppOnConflict schema = \case+ Abort -> mempty+ DoNothing -> text "ON CONFLICT DO NOTHING"+ DoUpdate upsert -> ppUpsert schema upsert+++ppUpsert :: TableSchema names -> Upsert names -> Doc+ppUpsert schema@TableSchema {columns} Upsert {..} =+ text "ON CONFLICT" <+>+ ppIndex schema index <+>+ text "DO UPDATE" $$+ ppSet schema (set excluded) $$+ ppWhere schema (updateWhere excluded)+ where+ excluded = attributes TableSchema+ { schema = Nothing+ , name = "excluded"+ , columns+ }+++ppIndex :: (Table Name names, Projecting names index)+ => TableSchema names -> Projection names index -> Doc+ppIndex TableSchema {columns} index =+ parens $ Opaleye.commaV ppColumn $ toList $+ showNames $ Cols $ apply index $ toColumns columns
src/Rel8/Statement/Returning.hs view
@@ -1,29 +1,133 @@ {-# language GADTs #-}+{-# language LambdaCase #-}+{-# language NamedFieldPuns #-}+{-# language RankNTypes #-}+{-# language ScopedTypeVariables #-} {-# language StandaloneKindSignatures #-}+{-# language StrictData #-}+{-# language TypeApplications #-} module Rel8.Statement.Returning- ( Returning(..)+ ( Returning( NumberOfRowsAffected, Projection )+ , decodeReturning+ , ppReturning ) where -- base+import Control.Applicative ( liftA2 )+import Data.Foldable ( toList ) import Data.Int ( Int64 ) import Data.Kind ( Type )-import Prelude ()+import Data.List.NonEmpty ( NonEmpty )+import Prelude +-- hasql+import qualified Hasql.Decoders as Hasql++-- opaleye+import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye+import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye+import qualified Opaleye.Internal.Sql as Opaleye++-- pretty+import Text.PrettyPrint ( Doc, (<+>), text )+ -- rel8 import Rel8.Schema.Name ( Selects )-import Rel8.Table.Serialize ( Serializable )+import Rel8.Schema.Table ( TableSchema(..) )+import Rel8.Table.Opaleye ( castTable, exprs, view )+import Rel8.Table.Serialize ( Serializable, parse ) +-- semigropuoids+import Data.Functor.Apply ( Apply, (<.>) ) --- | @INSERT@, @UPDATE@ and @DELETE@ all support returning either the number of--- rows affected, or the actual rows modified. 'Projection' allows you to--- project out of these returned rows, which can be useful if you want to log--- exactly which rows were deleted, or to view a generated id (for example, if--- using a column with an autoincrementing counter as a default value).++-- | 'Rel8.Insert', 'Rel8.Update' and 'Rel8.Delete' all support returning either+-- the number of rows affected, or the actual rows modified. type Returning :: Type -> Type -> Type data Returning names a where+ Pure :: a -> Returning names a+ Ap :: Returning names (a -> b) -> Returning names a -> Returning names b++ -- | Return the number of rows affected. NumberOfRowsAffected :: Returning names Int64- Projection :: (Selects names exprs, Serializable projection a)- => (exprs -> projection)++ -- | 'Projection' allows you to project out of the affected rows, which can+ -- be useful if you want to log exactly which rows were deleted, or to view+ -- a generated id (for example, if using a column with an autoincrementing+ -- counter via 'Rel8.nextval').+ Projection :: (Selects names exprs, Serializable returning a)+ => (exprs -> returning) -> Returning names [a]+++instance Functor (Returning names) where+ fmap f = \case+ Pure a -> Pure (f a)+ Ap g a -> Ap (fmap (f .) g) a+ m -> Ap (Pure f) m+++instance Apply (Returning names) where+ (<.>) = Ap+++instance Applicative (Returning names) where+ pure = Pure+ (<*>) = Ap+++projections :: ()+ => TableSchema names -> Returning names a -> Maybe (NonEmpty Opaleye.PrimExpr)+projections schema@TableSchema {columns} = \case+ Pure _ -> Nothing+ Ap f a -> projections schema f <> projections schema a+ NumberOfRowsAffected -> Nothing+ Projection f -> Just (exprs (castTable (f (view columns))))+++runReturning :: ()+ => ((Int64 -> a) -> r)+ -> (forall x. Hasql.Row x -> ([x] -> a) -> r)+ -> Returning names a+ -> r+runReturning rowCount rowList = \case+ Pure a -> rowCount (const a)+ Ap fs as ->+ runReturning+ (\withCount ->+ runReturning+ (\withCount' -> rowCount (withCount <*> withCount'))+ (\decoder -> rowList decoder . liftA2 withCount length64)+ as)+ (\decoder withRows ->+ runReturning+ (\withCount -> rowList decoder $ withRows <*> withCount . length64)+ (\decoder' withRows' ->+ rowList (liftA2 (,) decoder decoder') $+ withRows <$> fmap fst <*> withRows' . fmap snd)+ as)+ fs+ NumberOfRowsAffected -> rowCount id+ Projection (_ :: exprs -> returning) -> rowList decoder' id+ where+ decoder' = parse @returning+ where+ length64 :: Foldable f => f x -> Int64+ length64 = fromIntegral . length+++decodeReturning :: Returning names a -> Hasql.Result a+decodeReturning = runReturning+ (<$> Hasql.rowsAffected)+ (\decoder withRows -> withRows <$> Hasql.rowList decoder)+++ppReturning :: TableSchema names -> Returning names a -> Doc+ppReturning schema returning = case projections schema returning of+ Nothing -> mempty+ Just columns ->+ text "RETURNING" <+> Opaleye.commaV Opaleye.ppSqlExpr (toList sqlExprs)+ where+ sqlExprs = Opaleye.sqlExpr <$> columns
+ src/Rel8/Statement/SQL.hs view
@@ -0,0 +1,29 @@+module Rel8.Statement.SQL+ ( showDelete+ , showInsert+ , showUpdate+ )+where++-- base+import Prelude++-- rel8+import Rel8.Statement.Delete ( Delete, ppDelete )+import Rel8.Statement.Insert ( Insert, ppInsert )+import Rel8.Statement.Update ( Update, ppUpdate )+++-- | Convert a 'Delete' to a 'String' containing a @DELETE@ statement.+showDelete :: Delete a -> String+showDelete = show . ppDelete+++-- | Convert an 'Insert' to a 'String' containing an @INSERT@ statement.+showInsert :: Insert a -> String+showInsert = show . ppInsert+++-- | Convert an 'Update' to a 'String' containing an @UPDATE@ statement.+showUpdate :: Update a -> String+showUpdate = show . ppUpdate
src/Rel8/Statement/Select.hs view
@@ -1,62 +1,145 @@+{-# language DeriveTraversable #-}+{-# language DerivingStrategies #-}+{-# language FlexibleContexts #-} {-# language MonoLocalBinds #-} {-# language ScopedTypeVariables #-} {-# language TypeApplications #-} module Rel8.Statement.Select ( select- , selectWithNames+ , ppSelect++ , Optimized(..)+ , ppPrimSelect+ , ppRows ) where -- base-import Control.Exception ( throwIO )-import Prelude+import Data.Foldable ( toList )+import Data.List.NonEmpty ( NonEmpty( (:|) ) )+import Data.Void ( Void )+import Prelude hiding ( undefined ) -- hasql-import Hasql.Connection ( Connection ) import qualified Hasql.Decoders as Hasql import qualified Hasql.Encoders as Hasql-import qualified Hasql.Session as Hasql import qualified Hasql.Statement as Hasql +-- opaleye+import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye+import qualified Opaleye.Internal.HaskellDB.Sql as Opaleye+import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye+import qualified Opaleye.Internal.PrimQuery as Opaleye+import qualified Opaleye.Internal.Print as Opaleye+import qualified Opaleye.Internal.Optimize as Opaleye+import qualified Opaleye.Internal.QueryArr as Opaleye hiding ( Select )+import qualified Opaleye.Internal.Sql as Opaleye hiding ( Values )+import qualified Opaleye.Internal.Tag as Opaleye++-- pretty+import Text.PrettyPrint ( Doc )+ -- rel8+import Rel8.Expr ( Expr )+import Rel8.Expr.Bool ( false )+import Rel8.Expr.Opaleye ( toPrimExpr ) import Rel8.Query ( Query )-import Rel8.Query.SQL ( sqlForQuery, sqlForQueryWithNames )+import Rel8.Query.Opaleye ( toOpaleye ) import Rel8.Schema.Name ( Selects )+import Rel8.Table ( Table )+import Rel8.Table.Cols ( toCols )+import Rel8.Table.Name ( namesFromLabels )+import Rel8.Table.Opaleye ( castTable, exprsWithNames )+import qualified Rel8.Table.Opaleye as T import Rel8.Table.Serialize ( Serializable, parse )+import Rel8.Table.Undefined ( undefined ) -- text import qualified Data.Text as Text import Data.Text.Encoding ( encodeUtf8 ) --- | Run a @SELECT@ query, returning all rows.+-- | Run a @SELECT@ statement, returning all rows. select :: forall exprs a. Serializable exprs a- => Connection -> Query exprs -> IO [a]-select c query = case sqlForQuery query of- Nothing -> pure []- Just sql -> Hasql.run session c >>= either throwIO pure- where- session = Hasql.statement () statement- statement = Hasql.Statement bytes params decode prepare- bytes = encodeUtf8 (Text.pack sql)- params = Hasql.noParams- decode = Hasql.rowList (parse @exprs @a)- prepare = False+ => Query exprs -> Hasql.Statement () [a]+select query = Hasql.Statement bytes params decode prepare+ where+ bytes = encodeUtf8 (Text.pack sql)+ params = Hasql.noParams+ decode = Hasql.rowList (parse @exprs @a)+ prepare = False+ sql = show doc+ doc = ppSelect query -selectWithNames :: forall exprs a names.- ( Selects names exprs- , Serializable exprs a- )- => Connection -> names -> Query exprs -> IO [a]-selectWithNames c names query = case sqlForQueryWithNames names query of- Nothing -> pure []- Just sql -> Hasql.run session c >>= either throwIO pure- where- session = Hasql.statement () statement- statement = Hasql.Statement bytes params decode prepare- bytes = encodeUtf8 (Text.pack sql)- params = Hasql.noParams- decode = Hasql.rowList (parse @exprs @a)- prepare = False+ppSelect :: Table Expr a => Query a -> Doc+ppSelect query =+ Opaleye.ppSql $ primSelectWith names (toCols exprs') primQuery'+ where+ names = namesFromLabels+ (exprs, primQuery, _) =+ Opaleye.runSimpleQueryArrStart (toOpaleye query) ()+ (exprs', primQuery') = case optimize primQuery of+ Empty -> (undefined, Opaleye.Product (pure (pure Opaleye.Unit)) never)+ Unit -> (exprs, Opaleye.Unit)+ Optimized pq -> (exprs, pq)+ never = pure (toPrimExpr false)+++ppRows :: Table Expr a => Query a -> Doc+ppRows query = case optimize primQuery of+ -- Special case VALUES because we can't use DEFAULT inside a SELECT+ Optimized (Opaleye.Product ((_, Opaleye.Values symbols rows) :| []) [])+ | eqSymbols symbols (toList (T.exprs a)) ->+ Opaleye.ppValues_ (map Opaleye.sqlExpr <$> toList rows)+ _ -> ppSelect query+ where+ (a, primQuery, _) = Opaleye.runSimpleQueryArrStart (toOpaleye query) ()++ eqSymbols (symbol : symbols) (Opaleye.AttrExpr symbol' : exprs)+ | eqSymbol symbol symbol' = eqSymbols symbols exprs+ | otherwise = False+ eqSymbols [] [] = True+ eqSymbols _ _ = False++ eqSymbol+ (Opaleye.Symbol name (Opaleye.UnsafeTag tag))+ (Opaleye.Symbol name' (Opaleye.UnsafeTag tag'))+ = name == name' && tag == tag'+++ppPrimSelect :: Query a -> (Optimized Doc, a)+ppPrimSelect query =+ (Opaleye.ppSql . primSelect <$> optimize primQuery, a)+ where+ (a, primQuery, _) = Opaleye.runSimpleQueryArrStart (toOpaleye query) ()+++data Optimized a = Empty | Unit | Optimized a+ deriving stock (Functor, Foldable, Traversable)+++optimize :: Opaleye.PrimQuery' a -> Optimized (Opaleye.PrimQuery' Void)+optimize query = case Opaleye.removeEmpty (Opaleye.optimize query) of+ Nothing -> Empty+ Just Opaleye.Unit -> Unit+ Just query' -> Optimized query'+++primSelect :: Opaleye.PrimQuery' Void -> Opaleye.Select+primSelect = Opaleye.foldPrimQuery Opaleye.sqlQueryGenerator+++primSelectWith :: Selects names exprs+ => names -> exprs -> Opaleye.PrimQuery' Void -> Opaleye.Select+primSelectWith names exprs query =+ Opaleye.SelectFrom $ Opaleye.newSelect+ { Opaleye.attrs = Opaleye.SelectAttrs attrs+ , Opaleye.tables = Opaleye.oneTable (primSelect query)+ }+ where+ attrs = makeAttr <$> exprsWithNames names (castTable exprs)+ where+ makeAttr (label, expr) =+ (Opaleye.sqlExpr expr, Just (Opaleye.SqlColumn label))
+ src/Rel8/Statement/Set.hs view
@@ -0,0 +1,33 @@+{-# language MonoLocalBinds #-}+{-# language NamedFieldPuns #-}++module Rel8.Statement.Set+ ( ppSet+ )+where++-- base+import Data.Foldable ( toList )+import Prelude ()++-- opaleye+import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye+import qualified Opaleye.Internal.Sql as Opaleye++-- pretty+import Text.PrettyPrint ( Doc, (<+>), equals, text )++-- rel8+import Rel8.Schema.Name ( Selects, ppColumn )+import Rel8.Schema.Table ( TableSchema(..) )+import Rel8.Table.Opaleye ( attributes, exprsWithNames )+++ppSet :: Selects names exprs+ => TableSchema names -> (exprs -> exprs) -> Doc+ppSet schema@TableSchema {columns} f =+ text "SET" <+> Opaleye.commaV ppAssign (toList assigns)+ where+ assigns = exprsWithNames columns (f (attributes schema))+ ppAssign (column, expr) =+ ppColumn column <+> equals <+> Opaleye.ppSqlExpr (Opaleye.sqlExpr expr)
src/Rel8/Statement/Update.hs view
@@ -1,39 +1,37 @@+{-# language DuplicateRecordFields #-} {-# language GADTs #-} {-# language NamedFieldPuns #-}-{-# language ScopedTypeVariables #-}+{-# language RecordWildCards #-} {-# language StandaloneKindSignatures #-}-{-# language TypeApplications #-}+{-# language StrictData #-} module Rel8.Statement.Update ( Update(..) , update+ , ppUpdate ) where -- base-import Control.Exception ( throwIO ) import Data.Kind ( Type ) import Prelude -- hasql-import Hasql.Connection ( Connection )-import qualified Hasql.Decoders as Hasql import qualified Hasql.Encoders as Hasql-import qualified Hasql.Session as Hasql import qualified Hasql.Statement as Hasql --- opaleye-import qualified Opaleye.Internal.Manipulation as Opaleye+-- pretty+import Text.PrettyPrint ( Doc, (<+>), ($$), text ) -- rel8 import Rel8.Expr ( Expr )-import Rel8.Expr.Opaleye ( toColumn, toPrimExpr )+import Rel8.Query ( Query ) import Rel8.Schema.Name ( Selects )-import Rel8.Schema.Table ( TableSchema )-import Rel8.Statement.Returning ( Returning( Projection, NumberOfRowsAffected ) )-import Rel8.Table ( fromColumns, toColumns )-import Rel8.Table.Opaleye ( castTable, table, unpackspec )-import Rel8.Table.Serialize ( Serializable, parse )+import Rel8.Schema.Table ( TableSchema(..), ppTable )+import Rel8.Statement.Returning ( Returning, decodeReturning, ppReturning )+import Rel8.Statement.Set ( ppSet )+import Rel8.Statement.Using ( ppFrom )+import Rel8.Statement.Where ( ppWhere ) -- text import qualified Data.Text as Text@@ -46,9 +44,12 @@ Update :: Selects names exprs => { target :: TableSchema names -- ^ Which table to update.- , set :: exprs -> exprs+ , from :: Query from+ -- ^ @FROM@ clause — this can be used to join against other tables,+ -- and its results can be referenced in the @SET@ and @WHERE@ clauses.+ , set :: from -> exprs -> exprs -- ^ How to update each selected row.- , updateWhere :: exprs -> Expr Bool+ , updateWhere :: from -> exprs -> Expr Bool -- ^ Which rows to select for update. , returning :: Returning names a -- ^ What to return from the @UPDATE@ statement.@@ -56,46 +57,27 @@ -> Update a --- | Run an @UPDATE@ statement.-update :: Connection -> Update a -> IO a-update c Update {target, set, updateWhere, returning} =- case returning of- NumberOfRowsAffected -> Hasql.run session c >>= either throwIO pure- where- session = Hasql.statement () statement- statement = Hasql.Statement bytes params decode prepare- bytes = encodeUtf8 $ Text.pack sql- params = Hasql.noParams- decode = Hasql.rowsAffected- prepare = False- sql = Opaleye.arrangeUpdateSql target' set' where'- where- target' = table $ toColumns <$> target- set' = toColumns . set . fromColumns- where' = toColumn . toPrimExpr . updateWhere . fromColumns+ppUpdate :: Update a -> Doc+ppUpdate Update {..} = case ppFrom from of+ Nothing ->+ text "UPDATE" <+> ppTable target $$+ ppSet target id $$+ text "WHERE false"+ Just (fromDoc, i) ->+ text "UPDATE" <+> ppTable target $$+ ppSet target (set i) $$+ fromDoc $$+ ppWhere target (updateWhere i) $$+ ppReturning target returning - Projection project -> Hasql.run session c >>= either throwIO pure- where- session = Hasql.statement () statement- statement = Hasql.Statement bytes params decode prepare- bytes = encodeUtf8 $ Text.pack sql- params = Hasql.noParams- decode = decoder project- prepare = False- sql =- Opaleye.arrangeUpdateReturningSql- unpackspec- target'- set'- where'- project'- where- target' = table $ toColumns <$> target- set' = toColumns . set . fromColumns- where' = toColumn . toPrimExpr . updateWhere . fromColumns- project' = castTable . toColumns . project . fromColumns +-- | Run an @UPDATE@ statement.+update :: Update a -> Hasql.Statement () a+update u@Update {returning} = Hasql.Statement bytes params decode prepare where- decoder :: forall exprs projection a. Serializable projection a- => (exprs -> projection) -> Hasql.Result [a]- decoder _ = Hasql.rowList (parse @projection @a)+ bytes = encodeUtf8 $ Text.pack sql+ params = Hasql.noParams+ decode = decodeReturning returning+ prepare = False+ sql = show doc+ doc = ppUpdate u
+ src/Rel8/Statement/Using.hs view
@@ -0,0 +1,36 @@+module Rel8.Statement.Using+ ( ppFrom+ , ppUsing+ )+where++-- base+import Prelude++-- pretty+import Text.PrettyPrint ( Doc, (<+>), parens, text )++-- rel8+import Rel8.Query ( Query )+import Rel8.Schema.Table ( TableSchema(..), ppTable )+import Rel8.Statement.Select ( Optimized(..), ppPrimSelect )+++ppFrom :: Query a -> Maybe (Doc, a)+ppFrom = ppJoin "FROM"+++ppUsing :: Query a -> Maybe (Doc, a)+ppUsing = ppJoin "USING"+++ppJoin :: String -> Query a -> Maybe (Doc, a)+ppJoin clause join = do+ doc <- case ofrom of+ Empty -> Nothing+ Unit -> Just mempty+ Optimized doc -> Just $ text clause <+> parens doc <+> ppTable alias+ pure (doc, a)+ where+ alias = TableSchema {name = "T1", schema = Nothing, columns = ()}+ (ofrom, a) = ppPrimSelect join
src/Rel8/Statement/View.hs view
@@ -7,26 +7,23 @@ where -- base-import Control.Exception ( throwIO )-import Control.Monad ( (>=>) )-import Data.Foldable ( fold )-import Data.Maybe ( fromMaybe ) import Prelude -- hasql-import Hasql.Connection ( Connection ) import qualified Hasql.Decoders as Hasql import qualified Hasql.Encoders as Hasql-import qualified Hasql.Session as Hasql import qualified Hasql.Statement as Hasql -- rel8 import Rel8.Query ( Query )-import Rel8.Query.SQL ( sqlForQueryWithNames ) import Rel8.Schema.Name ( Selects )-import Rel8.Schema.Table ( TableSchema( TableSchema ) )-import Rel8.Table.Alternative ( emptyTable )+import Rel8.Schema.Table ( TableSchema )+import Rel8.Statement.Insert ( ppInto )+import Rel8.Statement.Select ( ppSelect ) +-- pretty+import Text.PrettyPrint ( Doc, (<+>), ($$), text )+ -- text import qualified Data.Text as Text import Data.Text.Encoding ( encodeUtf8 )@@ -36,28 +33,21 @@ -- statement that will save the given query as a view. This can be useful if -- you want to share Rel8 queries with other applications. createView :: Selects names exprs- => TableSchema names -> Query exprs -> Connection -> IO ()-createView (TableSchema name mschema names) query =- Hasql.run session >=> either throwIO pure+ => TableSchema names -> Query exprs -> Hasql.Statement () ()+createView schema query = Hasql.Statement bytes params decode prepare where- session = Hasql.statement () statement- statement = Hasql.Statement bytes params decode prepare bytes = encodeUtf8 (Text.pack sql) params = Hasql.noParams decode = Hasql.noResult prepare = False- sql = "CREATE VIEW " <> title <> " AS " <> select- where- title = case mschema of- Nothing -> quote name- Just schema -> quote schema <> "." <> quote name- select = fromMaybe fallback $ sqlForQueryWithNames names query- where- fallback = fold $ sqlForQueryWithNames names emptyTable+ sql = show doc+ doc = ppCreateView schema query -quote :: String -> String-quote string = "\"" <> concatMap go string <> "\""- where- go '"' = "\"\""- go c = [c]+ppCreateView :: Selects names exprs+ => TableSchema names -> Query exprs -> Doc+ppCreateView schema query =+ text "CREATE VIEW" <+>+ ppInto schema $$+ text "AS" <+>+ ppSelect query
+ src/Rel8/Statement/Where.hs view
@@ -0,0 +1,31 @@+{-# language MonoLocalBinds #-}++module Rel8.Statement.Where+ ( ppWhere+ )+where++-- base+import Prelude++-- opaleye+import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye+import qualified Opaleye.Internal.Sql as Opaleye++-- pretty+import Text.PrettyPrint ( Doc, (<+>), text )++-- rel8+import Rel8.Expr ( Expr )+import Rel8.Expr.Opaleye ( toPrimExpr )+import Rel8.Schema.Name ( Selects )+import Rel8.Schema.Table ( TableSchema )+import Rel8.Table.Opaleye ( attributes )+++ppWhere :: Selects names exprs+ => TableSchema names -> (exprs -> Expr Bool) -> Doc+ppWhere schema where_ = text "WHERE" <+> ppExpr condition+ where+ ppExpr = Opaleye.ppSqlExpr . Opaleye.sqlExpr . toPrimExpr+ condition = where_ (attributes schema)
src/Rel8/Table.hs view
@@ -1,79 +1,49 @@ {-# language AllowAmbiguousTypes #-} {-# language DataKinds #-} {-# language DefaultSignatures #-}-{-# language DisambiguateRecordFields #-} {-# language FlexibleContexts #-} {-# language FlexibleInstances #-} {-# language FunctionalDependencies #-}-{-# language LambdaCase #-}-{-# language NamedFieldPuns #-} {-# language ScopedTypeVariables #-} {-# language StandaloneKindSignatures #-} {-# language TypeApplications #-} {-# language TypeFamilies #-}-{-# language TypeOperators #-} {-# language UndecidableInstances #-} module Rel8.Table- ( Table (Columns, Context, Unreify, toColumns, fromColumns, reify, unreify)+ ( Table+ ( Columns, Context, fromColumns, toColumns+ , FromExprs, fromResult, toResult+ , Transpose+ ) , Congruent- , TTable, TColumns, TContext, TUnreify+ , TTable, TColumns, TContext, TFromExprs, TTranspose+ , TSerialize ) where -- base-import Data.Functor ( ($>) ) import Data.Functor.Identity ( Identity( Identity ) ) import Data.Kind ( Constraint, Type )-import Data.List.NonEmpty ( NonEmpty )-import Data.Proxy ( Proxy( Proxy ) )-import Data.Type.Equality ( (:~:)( Refl ) ) import GHC.Generics ( Generic, Rep, from, to ) import Prelude hiding ( null ) -- rel8 import Rel8.FCF ( Eval, Exp )-import Rel8.Kind.Algebra ( KnownAlgebra )-import Rel8.Generic.Map ( GMap, GMappable, gmap, gunmap )-import Rel8.Generic.Table- ( GGTable, GGColumns, GGContext, ggfromColumns, ggtoColumns- , GAlgebra+import Rel8.Generic.Map ( Map )+import Rel8.Generic.Table.Record+ ( GTable, GColumns, GContext, gfromColumns, gtoColumns+ , GSerialize, gfromResult, gtoResult ) import Rel8.Generic.Record ( Record(..) )-import Rel8.Generic.Reify ( ARep )-import Rel8.Schema.Context.Label ( Labelable, labeler, unlabeler ) import Rel8.Schema.HTable ( HTable )-import Rel8.Schema.HTable.Either ( HEitherTable(..) )-import Rel8.Schema.HTable.Identity ( HIdentity(..), HType )-import Rel8.Schema.HTable.Label ( hlabel, hunlabel )-import Rel8.Schema.HTable.List ( HListTable )-import Rel8.Schema.HTable.Maybe ( HMaybeTable(..) )-import Rel8.Schema.HTable.NonEmpty ( HNonEmptyTable )-import Rel8.Schema.HTable.Nullify ( hnulls, hnullify, hunnullify )-import Rel8.Schema.HTable.These ( HTheseTable(..) )-import Rel8.Schema.HTable.Vectorize ( hvectorize, hunvectorize )+import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) ) import qualified Rel8.Schema.Kind as K import Rel8.Schema.Null ( Sql )-import Rel8.Schema.Reify- ( Reify, Col( Reify ), hreify, hunreify- , UnwrapReify- , notReify- )-import Rel8.Schema.Result- ( Col( R ), Result- , relabel- , null, nullifier, unnullifier- , vectorizer, unvectorizer- )-import Rel8.Schema.Spec ( KnownSpec )+import Rel8.Schema.Result ( Result ) import Rel8.Type ( DBType )-import Rel8.Type.Tag ( EitherTag( IsLeft, IsRight ), MaybeTag( IsJust ) ) --- these-import Data.These ( These( This, That, These ) )-import Data.These.Combinators ( justHere, justThere ) - -- | @Table@s are one of the foundational elements of Rel8, and describe data -- types that have a finite number of columns. Each of these columns contains -- data under a shared context, and contexts describe how to interpret the@@ -86,92 +56,104 @@ -- yourself, as anything that's an instance of 'Rel8.Rel8able' is always a -- 'Table'. type Table :: K.Context -> Type -> Constraint-class (HTable (Columns a), context ~ Context a) => Table context a | a -> context where+class+ ( HTable (Columns a)+ , context ~ Context a+ , a ~ Transpose context a+ )+ => Table context a | a -> context+ where -- | The 'HTable' functor that describes the schema of this table. type Columns a :: K.HTable -- | The common context that all columns use as an interpretation. type Context a :: K.Context - type Unreify a :: Type+ -- | The @FromExprs@ type family maps a type in the @Expr@ context to the+ -- corresponding Haskell type.+ type FromExprs a :: Type - toColumns :: a -> Columns a (Col context)- fromColumns :: Columns a (Col context) -> a+ type Transpose (context' :: K.Context) a :: Type - reify :: context :~: Reify ctx -> Unreify a -> a- unreify :: context :~: Reify ctx -> a -> Unreify a - type Columns a = Eval (GGColumns (GAlgebra (Rep (Record a))) TColumns (Rep (Record a)))- type Context a = Eval (GGContext (GAlgebra (Rep (Record a))) TContext (Rep (Record a)))- type Unreify a = DefaultUnreify a+ toColumns :: a -> Columns a context+ fromColumns :: Columns a context -> a + fromResult :: Columns a Result -> FromExprs a+ toResult :: FromExprs a -> Columns a Result++ type Columns a = GColumns TColumns (Rep (Record a))+ type Context a = GContext TContext (Rep (Record a))+ type FromExprs a = Map TFromExprs a+ type Transpose context a = Map (TTranspose context) a+ default toColumns :: ( Generic (Record a)- , KnownAlgebra (GAlgebra (Rep (Record a)))- , Eval (GGTable (GAlgebra (Rep (Record a))) (TTable context) TColumns (Col context) (Rep (Record a)))- , Columns a ~ Eval (GGColumns (GAlgebra (Rep (Record a))) TColumns (Rep (Record a)))- , Context a ~ Eval (GGContext (GAlgebra (Rep (Record a))) TContext (Rep (Record a)))+ , GTable (TTable context) TColumns (Rep (Record a))+ , Columns a ~ GColumns TColumns (Rep (Record a)) )- => a -> Columns a (Col context)+ => a -> Columns a context toColumns =- ggtoColumns- @(GAlgebra (Rep (Record a)))- @(TTable context)- @TColumns- id- id- toColumns .+ gtoColumns @(TTable context) @TColumns toColumns . from . Record default fromColumns :: ( Generic (Record a)- , KnownAlgebra (GAlgebra (Rep (Record a)))- , Eval (GGTable (GAlgebra (Rep (Record a))) (TTable context) TColumns (Col context) (Rep (Record a)))- , Columns a ~ Eval (GGColumns (GAlgebra (Rep (Record a))) TColumns (Rep (Record a)))- , Context a ~ Eval (GGContext (GAlgebra (Rep (Record a))) TContext (Rep (Record a)))+ , GTable (TTable context) TColumns (Rep (Record a))+ , Columns a ~ GColumns TColumns (Rep (Record a)) )- => Columns a (Col context) -> a+ => Columns a context -> a fromColumns = unrecord . to .- ggfromColumns- @(GAlgebra (Rep (Record a)))- @(TTable context)- @TColumns- id- id- fromColumns+ gfromColumns @(TTable context) @TColumns fromColumns - default reify ::- ( Generic (Record a)- , Generic (Record (Unreify a))- , GMappable (TTable context) (Rep (Record a))- , Rep (Record (Unreify a)) ~ GMap TUnreify (Rep (Record a))+ default toResult ::+ ( Generic (Record (FromExprs a))+ , GSerialize TSerialize TColumns (Rep (Record a)) (Rep (Record (FromExprs a)))+ , Columns a ~ GColumns TColumns (Rep (Record a)) )- => context :~: Reify ctx -> Unreify a -> a- reify Refl =- unrecord .- to .- gunmap @(TTable context) (Proxy @TUnreify) (reify Refl) .+ => FromExprs a -> Columns a Result+ toResult =+ gtoResult+ @TSerialize+ @TColumns+ @(Rep (Record a))+ @(Rep (Record (FromExprs a)))+ (\(_ :: proxy x) -> toResult @(Context x) @x) . from . Record - default unreify ::- ( Generic (Record a)- , Generic (Record (Unreify a))- , GMappable (TTable context) (Rep (Record a))- , Rep (Record (Unreify a)) ~ GMap TUnreify (Rep (Record a))+ default fromResult ::+ ( Generic (Record (FromExprs a))+ , GSerialize TSerialize TColumns (Rep (Record a)) (Rep (Record (FromExprs a)))+ , Columns a ~ GColumns TColumns (Rep (Record a)) )- => context :~: Reify ctx -> a -> Unreify a- unreify Refl =+ => Columns a Result -> FromExprs a+ fromResult = unrecord . to .- gmap @(TTable context) (Proxy @TUnreify) (unreify Refl) .- from .- Record+ gfromResult+ @TSerialize+ @TColumns+ @(Rep (Record a))+ @(Rep (Record (FromExprs a)))+ (\(_ :: proxy x) -> fromResult @(Context x) @x) +instance Sql DBType a => Table Result (Identity a) where+ type Columns (Identity a) = HIdentity a+ type Context (Identity a) = Result+ type FromExprs (Identity a) = a+ type Transpose to (Identity a) = to a++ toColumns = HIdentity+ fromColumns (HIdentity a) = a+ toResult a = HIdentity (Identity a)+ fromResult (HIdentity (Identity a)) = a++ data TTable :: K.Context -> Type -> Exp Constraint type instance Eval (TTable context a) = Table context a @@ -184,175 +166,32 @@ type instance Eval (TContext a) = Context a -data TUnreify :: Type -> Exp Type-type instance Eval (TUnreify a) = Unreify a---type DefaultUnreify :: Type -> Type-type family DefaultUnreify a where- DefaultUnreify (t a b c d e f g) =- t (Unreify a) (Unreify b) (Unreify c) (Unreify d) (Unreify e) (Unreify f) (Unreify g)- DefaultUnreify (t a b c d e f) =- t (Unreify a) (Unreify b) (Unreify c) (Unreify d) (Unreify e) (Unreify f)- DefaultUnreify (t a b c d e) =- t (Unreify a) (Unreify b) (Unreify c) (Unreify d) (Unreify e)- DefaultUnreify (t a b c d) =- t (Unreify a) (Unreify b) (Unreify c) (Unreify d)- DefaultUnreify (t a b c) = t (Unreify a) (Unreify b) (Unreify c)- DefaultUnreify (t a b) = t (Unreify a) (Unreify b)- DefaultUnreify (t a) = t (Unreify a)- DefaultUnreify a = ARep (GMap TUnreify (Rep a))----- | Any 'HTable' is also a 'Table'.-instance HTable t => Table context (t (Col context)) where- type Columns (t (Col context)) = t- type Context (t (Col context)) = context- type Unreify (t (Col context)) = t (Col (UnwrapReify context))-- toColumns = id- fromColumns = id-- reify Refl = hreify- unreify Refl = hunreify----- | Any context is trivially a table.-instance KnownSpec spec => Table context (Col context spec) where- type Columns (Col context spec) = HIdentity spec- type Context (Col context spec) = context- type Unreify (Col context spec) = Col (UnwrapReify context) spec-- toColumns = HIdentity- fromColumns = unHIdentity-- reify Refl = Reify- unreify Refl (Reify a) = a---instance Sql DBType a => Table Result (Identity a) where- type Columns (Identity a) = HType a- type Context (Identity a) = Result-- toColumns (Identity a) = HType (R a)- fromColumns (HType (R a)) = Identity a-- reify = notReify- unreify = notReify---instance (Table Result a, Table Result b) => Table Result (Either a b) where- type Columns (Either a b) = HEitherTable (Columns a) (Columns b)- type Context (Either a b) = Result-- toColumns = \case- Left table -> HEitherTable- { htag = HIdentity (R IsLeft)- , hleft = hlabel labeler (hnullify nullifier (toColumns table))- , hright = hlabel labeler (hnulls (const null))- }- Right table -> HEitherTable- { htag = HIdentity (R IsRight)- , hleft = hlabel labeler (hnulls (const null))- , hright = hlabel labeler (hnullify nullifier (toColumns table))- }-- fromColumns HEitherTable {htag, hleft, hright} = case htag of- HIdentity (R tag) -> case tag of- IsLeft -> maybe err (Left . fromColumns) $ hunnullify unnullifier (hunlabel unlabeler hleft)- IsRight -> maybe err (Right . fromColumns) $ hunnullify unnullifier (hunlabel unlabeler hright)- where- err = error "Either.fromColumns: mismatch between tag and data"---instance Table Result a => Table Result [a] where- type Columns [a] = HListTable (Columns a)- type Context [a] = Result-- toColumns = hvectorize vectorizer . fmap toColumns- fromColumns = fmap fromColumns . hunvectorize unvectorizer---instance Table Result a => Table Result (Maybe a) where- type Columns (Maybe a) = HMaybeTable (Columns a)- type Context (Maybe a) = Result-- toColumns = \case- Nothing -> HMaybeTable- { htag = HIdentity (R Nothing)- , hjust = hlabel labeler (hnulls (const null))- }- Just table -> HMaybeTable- { htag = HIdentity (R (Just IsJust))- , hjust = hlabel labeler (hnullify nullifier (toColumns table))- }-- fromColumns HMaybeTable {htag, hjust} = case htag of- HIdentity (R tag) -> tag $>- case hunnullify unnullifier (hunlabel unlabeler hjust) of- Nothing -> error "Maybe.fromColumns: mismatch between tag and data"- Just just -> fromColumns just+data TFromExprs :: Type -> Exp Type+type instance Eval (TFromExprs a) = FromExprs a -instance Table Result a => Table Result (NonEmpty a) where- type Columns (NonEmpty a) = HNonEmptyTable (Columns a)- type Context (NonEmpty a) = Result-- toColumns = hvectorize vectorizer . fmap toColumns- fromColumns = fmap fromColumns . hunvectorize unvectorizer+data TTranspose :: K.Context -> Type -> Exp Type+type instance Eval (TTranspose context a) = Transpose context a -instance (Table Result a, Table Result b) => Table Result (These a b) where- type Columns (These a b) = HTheseTable (Columns a) (Columns b)- type Context (These a b) = Result-- toColumns tables = HTheseTable- { hhereTag = relabel hhereTag- , hhere = hlabel labeler (hunlabel unlabeler (toColumns hhere))- , hthereTag = relabel hthereTag- , hthere = hlabel labeler (hunlabel unlabeler (toColumns hthere))- }- where- HMaybeTable- { htag = hhereTag- , hjust = hhere- } = toColumns (justHere tables)- HMaybeTable- { htag = hthereTag- , hjust = hthere- } = toColumns (justThere tables)-- fromColumns HTheseTable {hhereTag, hhere, hthereTag, hthere} =- case (fromColumns mhere, fromColumns mthere) of- (Just a, Nothing) -> This (fromColumns a)- (Nothing, Just b) -> That (fromColumns b)- (Just a, Just b) -> These (fromColumns a) (fromColumns b)- _ -> error "These.fromColumns: mismatch between tags and data"- where- mhere = HMaybeTable- { htag = relabel hhereTag- , hjust = hlabel labeler (hunlabel unlabeler hhere)- }- mthere = HMaybeTable- { htag = relabel hthereTag- , hjust = hlabel labeler (hunlabel unlabeler hthere)- }+data TSerialize :: Type -> Type -> Exp Constraint+type instance Eval (TSerialize expr a) =+ ( Table (Context expr) expr+ , a ~ FromExprs expr+ ) -instance (Table context a, Table context b, Labelable context)- => Table context (a, b)+instance (Table context a, Table context b) => Table context (a, b) instance ( Table context a, Table context b, Table context c- , Labelable context ) => Table context (a, b, c) instance ( Table context a, Table context b, Table context c, Table context d- , Labelable context ) => Table context (a, b, c, d) @@ -360,7 +199,6 @@ instance ( Table context a, Table context b, Table context c, Table context d , Table context e- , Labelable context ) => Table context (a, b, c, d, e) @@ -368,7 +206,6 @@ instance ( Table context a, Table context b, Table context c, Table context d , Table context e, Table context f- , Labelable context ) => Table context (a, b, c, d, e, f) @@ -376,7 +213,6 @@ instance ( Table context a, Table context b, Table context c, Table context d , Table context e, Table context f, Table context g- , Labelable context ) => Table context (a, b, c, d, e, f, g)
src/Rel8/Table/ADT.hs view
@@ -7,13 +7,13 @@ {-# language ScopedTypeVariables #-} {-# language StandaloneKindSignatures #-} {-# language TypeApplications #-}-{-# language TypeFamilies #-}+{-# language TypeFamilyDependencies #-} {-# language UndecidableInstances #-} {-# language UndecidableSuperClasses #-} module Rel8.Table.ADT ( ADT( ADT )- , ADTable, fromADT, toADT+ , ADTable , BuildableADT , BuildADT, buildADT , ConstructableADT@@ -27,9 +27,7 @@ -- base import Data.Kind ( Constraint, Type )-import Data.Proxy ( Proxy( Proxy ) )-import Data.Type.Equality ( (:~:)( Refl ) )-import GHC.Generics ( Generic, Rep, from, to )+import GHC.Generics ( Generic, from, to ) import GHC.TypeLits ( Symbol ) import Prelude @@ -46,95 +44,65 @@ , GGName, ggname , GGAggregate, ggaggregate )-import Rel8.Generic.Map ( GMappable, GMap, gmap, gunmap )-import Rel8.Generic.Record ( GRecordable, GRecord, grecord, gunrecord )+import Rel8.Generic.Record ( Record( Record ), unrecord ) import Rel8.Generic.Rel8able ( Rel8able , GRep, GColumns, gfromColumns, gtoColumns- , greify, gunreify+ , GFromExprs, gfromResult, gtoResult+ , TSerialize, deserialize, serialize ) import qualified Rel8.Generic.Table.ADT as G import qualified Rel8.Kind.Algebra as K-import Rel8.Schema.Context ( Col ) import Rel8.Schema.HTable ( HTable ) import qualified Rel8.Schema.Kind as K import Rel8.Schema.Name ( Name )-import Rel8.Schema.Reify ( Col( Reify ), Reify, hreify, hunreify ) import Rel8.Schema.Result ( Result )-import Rel8.Table- ( Table- , fromColumns, toColumns, reify, unreify- , TTable, TColumns, TUnreify- )+import Rel8.Table ( Table, TColumns ) type ADT :: K.Rel8able -> K.Rel8able-newtype ADT t context = ADT (GColumnsADT t (Col context))+newtype ADT t context = ADT (GColumnsADT t context) instance ADTable t => Rel8able (ADT t) where type GColumns (ADT t) = GColumnsADT t-- gfromColumns = ADT- gtoColumns (ADT a) = a-- greify (ADT a) = ADT (hreify a)- gunreify (ADT a) = ADT (hunreify a)-+ type GFromExprs (ADT t) = t Result -instance (ADTable t, context ~ Result) => Generic (ADT t context) where- type Rep (ADT t context) = Rep (t context)+ gfromColumns _ = ADT+ gtoColumns _ (ADT a) = a - from =- gmap @(TTable (Reify Result)) (Proxy @TUnreify) (unreify Refl) .- gunrecord @(Rep (t (Reify Result))) .- G.gfromColumnsADT- @(TTable (Reify Result))+ gfromResult =+ unrecord .+ to .+ G.gfromResultADT+ @TSerialize @TColumns- (\(Reify a) -> a)- Reify- fromColumns .- hreify .- (\(ADT a) -> a)+ @(Eval (ADTRep t Expr))+ @(Eval (ADTRep t Result))+ (\(_ :: proxy x) -> deserialize @_ @x) - to =- ADT .- hunreify .- G.gtoColumnsADT- @(TTable (Reify Result))+ gtoResult =+ G.gtoResultADT+ @TSerialize @TColumns- (\(Reify a) -> a)- Reify- toColumns .- grecord @(Rep (t (Reify Result))) .- gunmap @(TTable (Reify Result)) (Proxy @TUnreify) (reify Refl)---fromADT :: ADTable t => ADT t Result -> t Result-fromADT = to . from---toADT :: ADTable t => t Result -> ADT t Result-toADT = to . from+ @(Eval (ADTRep t Expr))+ @(Eval (ADTRep t Result))+ (\(_ :: proxy x) -> serialize @_ @x) .+ from .+ Record type ADTable :: K.Rel8able -> Constraint class- ( Generic (t Result)+ ( Generic (Record (t Result)) , HTable (GColumnsADT t)- , G.GTableADT (TTable (Reify Result)) TColumns (Col (Reify Result)) (GRecord (Rep (t (Reify Result))))- , GRecordable (Rep (t (Reify Result)))- , GMappable (TTable (Reify Result)) (Rep (t (Reify Result)))- , GMap TUnreify (Rep (t (Reify Result))) ~ Rep (t Result)+ , G.GSerializeADT TSerialize TColumns (Eval (ADTRep t Expr)) (Eval (ADTRep t Result)) ) => ADTable t instance- ( Generic (t Result)+ ( Generic (Record (t Result)) , HTable (GColumnsADT t)- , G.GTableADT (TTable (Reify Result)) TColumns (Col (Reify Result)) (GRecord (Rep (t (Reify Result))))- , GRecordable (Rep (t (Reify Result)))- , GMappable (TTable (Reify Result)) (Rep (t (Reify Result)))- , GMap TUnreify (Rep (t (Reify Result))) ~ Rep (t Result)+ , G.GSerializeADT TSerialize TColumns (Eval (ADTRep t Expr)) (Eval (ADTRep t Result)) ) => ADTable t @@ -202,4 +170,4 @@ type GColumnsADT :: K.Rel8able -> K.HTable-type GColumnsADT t = G.GColumnsADT TColumns (GRep t (Reify Result))+type GColumnsADT t = G.GColumnsADT TColumns (GRep t Expr)
src/Rel8/Table/Aggregate.hs view
@@ -16,8 +16,8 @@ import Prelude -- rel8-import Rel8.Aggregate ( Aggregate, Aggregates, Col( A ) )-import Rel8.Expr ( Expr, Col( E ) )+import Rel8.Aggregate ( Aggregate, Aggregates )+import Rel8.Expr ( Expr ) import Rel8.Expr.Aggregate ( groupByExpr , slistAggExpr@@ -26,8 +26,8 @@ import Rel8.Schema.Dict ( Dict( Dict ) ) import Rel8.Schema.HTable ( HTable, hfield, htabulate ) import Rel8.Schema.HTable.Vectorize ( hvectorize )-import Rel8.Schema.Spec ( SSpec( SSpec, info ) )-import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType )+import Rel8.Schema.Null ( Sql )+import Rel8.Schema.Spec ( Spec( Spec, info ) ) import Rel8.Table ( toColumns, fromColumns ) import Rel8.Table.Eq ( EqTable, eqTable ) import Rel8.Table.List ( ListTable )@@ -42,12 +42,11 @@ groupBy = fromColumns . hgroupBy (eqTable @exprs) . toColumns -hgroupBy :: HTable t- => t (Dict (ConstrainDBType DBEq)) -> t (Col Expr) -> t (Col Aggregate)-hgroupBy eqs exprs = fromColumns $ htabulate $ \field ->+hgroupBy :: HTable t => t (Dict (Sql DBEq)) -> t Expr -> t Aggregate+hgroupBy eqs exprs = htabulate $ \field -> case hfield eqs field of Dict -> case hfield exprs field of- E expr -> A $ groupByExpr expr+ expr -> groupByExpr expr -- | Aggregate rows into a single row containing an array of all aggregated@@ -60,22 +59,22 @@ -- items: -- -- @--- ordersWithItems :: Query (Order Expr, ListTable (Item Expr))+-- ordersWithItems :: Query (Order Expr, ListTable Expr (Item Expr)) -- ordersWithItems = do -- order <- each orderSchema -- items <- aggregate $ listAgg <$> itemsFromOrder order -- return (order, items) -- @-listAgg :: Aggregates aggregates exprs => exprs -> ListTable aggregates+listAgg :: Aggregates aggregates exprs => exprs -> ListTable Aggregate aggregates listAgg (toColumns -> exprs) = fromColumns $ hvectorize- (\SSpec {info} (Identity (E a)) -> A $ slistAggExpr info a)+ (\Spec {info} (Identity a) -> slistAggExpr info a) (pure exprs) -- | Like 'listAgg', but the result is guaranteed to be a non-empty list.-nonEmptyAgg :: Aggregates aggregates exprs => exprs -> NonEmptyTable aggregates+nonEmptyAgg :: Aggregates aggregates exprs => exprs -> NonEmptyTable Aggregate aggregates nonEmptyAgg (toColumns -> exprs) = fromColumns $ hvectorize- (\SSpec {info} (Identity (E a)) -> A $ snonEmptyAggExpr info a)+ (\Spec {info} (Identity a) -> snonEmptyAggExpr info a) (pure exprs)
src/Rel8/Table/Bool.hs view
@@ -13,7 +13,7 @@ import Prelude -- rel8-import Rel8.Expr ( Expr, Col( E, unE ) )+import Rel8.Expr ( Expr ) import Rel8.Expr.Bool ( boolExpr, caseExpr ) import Rel8.Expr.Null ( isNull, unsafeUnnullify ) import Rel8.Schema.HTable ( htabulate, hfield )@@ -28,8 +28,7 @@ bool (toColumns -> false) (toColumns -> true) condition = fromColumns $ htabulate $ \field -> case (hfield false field, hfield true field) of- (E falseExpr, E trueExpr) ->- E (boolExpr falseExpr trueExpr condition)+ (falseExpr, trueExpr) -> boolExpr falseExpr trueExpr condition {-# INLINABLE bool #-} @@ -39,9 +38,9 @@ case_ :: Table Expr a => [(Expr Bool, a)] -> a -> a case_ (map (fmap toColumns) -> branches) (toColumns -> fallback) = fromColumns $ htabulate $ \field -> case hfield fallback field of- E fallbackExpr ->- case map (fmap (unE . (`hfield` field))) branches of- branchExprs -> E (caseExpr branchExprs fallbackExpr)+ fallbackExpr ->+ case map (fmap (`hfield` field)) branches of+ branchExprs -> caseExpr branchExprs fallbackExpr -- | Like 'maybe', but to eliminate @null@.
+ src/Rel8/Table/Cols.hs view
@@ -0,0 +1,50 @@+{-# language DataKinds #-}+{-# language FlexibleInstances #-}+{-# language MultiParamTypeClasses #-}+{-# language StandaloneKindSignatures #-}+{-# language TypeFamilies #-}+{-# language UndecidableInstances #-}++module Rel8.Table.Cols+ ( Cols( Cols )+ , fromCols+ , toCols+ )+where++-- base+import Data.Kind ( Type )+import Prelude++-- rel8+import qualified Rel8.Schema.Kind as K+import Rel8.Schema.HTable ( HTable )+import Rel8.Schema.Result ( Result )+import Rel8.Table ( Table(..) )+++type Cols :: K.Context -> K.HTable -> Type+newtype Cols context columns = Cols (columns context)+++instance (HTable columns, context ~ context') =>+ Table context' (Cols context columns)+ where+ type Columns (Cols context columns) = columns+ type Context (Cols context columns) = context+ type FromExprs (Cols context columns) = Cols Result columns+ type Transpose to (Cols context columns) = Cols to columns++ toColumns (Cols a) = a+ fromColumns = Cols++ toResult (Cols a) = a+ fromResult = Cols+++fromCols :: Table context a => Cols context (Columns a) -> a+fromCols (Cols a) = fromColumns a+++toCols :: Table context a => a -> Cols context (Columns a)+toCols = Cols . toColumns
src/Rel8/Table/Either.hs view
@@ -18,46 +18,44 @@ ( EitherTable(..) , eitherTable, leftTable, rightTable , isLeftTable, isRightTable+ , aggregateEitherTable , nameEitherTable ) where -- base-import Control.Applicative ( liftA2 ) import Data.Bifunctor ( Bifunctor, bimap )-import Data.Functor.Identity ( runIdentity )+import Data.Functor.Identity ( Identity( Identity ) ) import Data.Kind ( Type ) import Prelude hiding ( undefined ) +-- comonad+import Control.Comonad ( extract )+ -- rel8+import Rel8.Aggregate ( Aggregate ) import Rel8.Expr ( Expr )+import Rel8.Expr.Aggregate ( groupByExpr ) import Rel8.Expr.Serialize ( litExpr )-import Rel8.Schema.Context.Label- ( Labelable- , HLabelable, hlabeler, hunlabeler- )-import Rel8.Schema.Context.Nullify- ( Nullifiable, ConstrainTag- , HNullifiable, HConstrainTag- , hencodeTag, hdecodeTag- , hnullifier, hunnullifier- )-import Rel8.Schema.HTable ( HTable )+import Rel8.Kind.Context ( Reifiable )+import Rel8.Schema.Context.Nullify ( Nullifiable )+import Rel8.Schema.Dict ( Dict( Dict ) ) import Rel8.Schema.HTable.Either ( HEitherTable(..) ) import Rel8.Schema.HTable.Identity ( HIdentity(..) ) import Rel8.Schema.HTable.Label ( hlabel, hunlabel )-import Rel8.Schema.HTable.Nullify ( hnullify, hunnullify )+import qualified Rel8.Schema.Kind as K import Rel8.Schema.Name ( Name ) import Rel8.Table ( Table, Columns, Context, fromColumns, toColumns- , reify, unreify+ , FromExprs, fromResult, toResult+ , Transpose ) import Rel8.Table.Bool ( bool ) import Rel8.Table.Eq ( EqTable, eqTable )+import Rel8.Table.Nullify ( Nullify, aggregateNullify, guard ) import Rel8.Table.Ord ( OrdTable, ordTable )-import Rel8.Table.Recontextualize ( Recontextualize )-import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )-import Rel8.Table.Tag ( Tag(..), fromExpr, fromName )+import Rel8.Table.Projection ( Biprojectable, Projectable, biproject, project )+import Rel8.Table.Serialize ( ToExprs ) import Rel8.Table.Undefined ( undefined ) import Rel8.Type.Tag ( EitherTag( IsLeft, IsRight ), isLeft, isRight ) @@ -72,119 +70,162 @@ -- -- An @EitherTable@ is operationally the same as Haskell's 'Either' type, but -- adapted to work with Rel8.-type EitherTable :: Type -> Type -> Type-data EitherTable a b = EitherTable- { tag :: Tag "isRight" EitherTag- , left :: a- , right :: b+type EitherTable :: K.Context -> Type -> Type -> Type+data EitherTable context a b = EitherTable+ { tag :: context EitherTag+ , left :: Nullify context a+ , right :: Nullify context b } deriving stock Functor -instance Bifunctor EitherTable where- bimap f g (EitherTable tag a b) = EitherTable tag (f a) (g b)+instance Biprojectable (EitherTable context) where+ biproject f g (EitherTable tag a b) =+ EitherTable tag (project f a) (project g b) -instance Table Expr a => Apply (EitherTable a) where+instance Nullifiable context => Bifunctor (EitherTable context) where+ bimap f g (EitherTable tag a b) = EitherTable tag (fmap f a) (fmap g b)+++instance Projectable (EitherTable context a) where+ project f (EitherTable tag a b) = EitherTable tag a (project f b)+++instance (context ~ Expr, Table Expr a) => Apply (EitherTable context a) where EitherTable tag l1 f <.> EitherTable tag' l2 a =- EitherTable (tag <> tag') (bool l1 l2 (isLeft (expr tag))) (f a)+ EitherTable (tag <> tag') (bool l1 l2 (isLeft tag)) (f <.> a) -instance Table Expr a => Applicative (EitherTable a) where+instance (context ~ Expr, Table Expr a) => Applicative (EitherTable context a) where pure = rightTable (<*>) = (<.>) -instance Table Expr a => Bind (EitherTable a) where- EitherTable tag l1 a >>- f = case f a of+instance (context ~ Expr, Table Expr a) => Bind (EitherTable context a) where+ EitherTable tag l1 a >>- f = case f (extract a) of EitherTable tag' l2 b ->- EitherTable (tag <> tag') (bool l1 l2 (isRight (expr tag))) b+ EitherTable (tag <> tag') (bool l1 l2 (isRight tag)) b -instance Table Expr a => Monad (EitherTable a) where+instance (context ~ Expr, Table Expr a) => Monad (EitherTable context a) where (>>=) = (>>-) -instance (Table Expr a, Table Expr b) => Semigroup (EitherTable a b) where+instance (context ~ Expr, Table Expr a, Table Expr b) =>+ Semigroup (EitherTable context a b)+ where a <> b = bool a b (isRightTable a) instance ( Table context a, Table context b- , Labelable context, Nullifiable context, ConstrainTag context EitherTag- ) =>- Table context (EitherTable a b)+ , Reifiable context, context ~ context'+ )+ => Table context' (EitherTable context a b) where- type Columns (EitherTable a b) = HEitherTable (Columns a) (Columns b)- type Context (EitherTable a b) = Context a-- toColumns = toColumns2 toColumns toColumns- fromColumns = fromColumns2 fromColumns fromColumns- reify = liftA2 bimap reify reify- unreify = liftA2 bimap unreify unreify+ type Columns (EitherTable context a b) = HEitherTable (Columns a) (Columns b)+ type Context (EitherTable context a b) = Context a+ type FromExprs (EitherTable context a b) = Either (FromExprs a) (FromExprs b)+ type Transpose to (EitherTable context a b) =+ EitherTable to (Transpose to a) (Transpose to b) + toColumns EitherTable {tag, left, right} = HEitherTable+ { htag = hlabel $ HIdentity tag+ , hleft = hlabel $ guard tag (== IsLeft) isLeft $ toColumns left+ , hright = hlabel $ guard tag (== IsRight) isRight $ toColumns right+ } -instance- ( Nullifiable from, Labelable from, ConstrainTag from EitherTag- , Nullifiable to, Labelable to, ConstrainTag to EitherTag- , Recontextualize from to a1 b1- , Recontextualize from to a2 b2- )- => Recontextualize from to (EitherTable a1 a2) (EitherTable b1 b2)+ fromColumns HEitherTable {htag, hleft, hright} = EitherTable+ { tag = unHIdentity $ hunlabel htag+ , left = fromColumns $ hunlabel hleft+ , right = fromColumns $ hunlabel hright+ } + toResult = \case+ Left table -> HEitherTable+ { htag = hlabel (HIdentity (Identity IsLeft))+ , hleft = hlabel (toResult @_ @(Nullify context a) (Just table))+ , hright = hlabel (toResult @_ @(Nullify context b) Nothing)+ }+ Right table -> HEitherTable+ { htag = hlabel (HIdentity (Identity IsRight))+ , hleft = hlabel (toResult @_ @(Nullify context a) Nothing)+ , hright = hlabel (toResult @_ @(Nullify context b) (Just table))+ } -instance (EqTable a, EqTable b) => EqTable (EitherTable a b) where- eqTable = toColumns2 id id (rightTableWith (eqTable @a) (eqTable @b))+ fromResult HEitherTable {htag, hleft, hright} = case hunlabel htag of+ HIdentity (Identity tag) -> case tag of+ IsLeft -> maybe err Left $ fromResult @_ @(Nullify context a) (hunlabel hleft)+ IsRight -> maybe err Right $ fromResult @_ @(Nullify context b) (hunlabel hright)+ where+ err = error "Either.fromColumns: mismatch between tag and data" -instance (OrdTable a, OrdTable b) => OrdTable (EitherTable a b) where- ordTable = toColumns2 id id (rightTableWith (ordTable @a) (ordTable @b))+instance (EqTable a, EqTable b, context ~ Expr) =>+ EqTable (EitherTable context a b)+ where+ eqTable = HEitherTable+ { htag = hlabel (HIdentity Dict)+ , hleft = hlabel (eqTable @(Nullify context a))+ , hright = hlabel (eqTable @(Nullify context b))+ } -type instance FromExprs (EitherTable a b) = Either (FromExprs a) (FromExprs b)+instance (OrdTable a, OrdTable b, context ~ Expr) =>+ OrdTable (EitherTable context a b)+ where+ ordTable = HEitherTable+ { htag = hlabel (HIdentity Dict)+ , hleft = hlabel (ordTable @(Nullify context a))+ , hright = hlabel (ordTable @(Nullify context b))+ } -instance (ToExprs exprs1 a, ToExprs exprs2 b, x ~ EitherTable exprs1 exprs2) =>+instance (ToExprs exprs1 a, ToExprs exprs2 b, x ~ EitherTable Expr exprs1 exprs2) => ToExprs x (Either a b)- where- fromResult =- bimap (fromResult @exprs1) (fromResult @exprs2) .- fromColumns- toResult =- toColumns .- bimap (toResult @exprs1) (toResult @exprs2) -- | Test if an 'EitherTable' is a 'leftTable'.-isLeftTable :: EitherTable a b -> Expr Bool-isLeftTable = isLeft . expr . tag+isLeftTable :: EitherTable Expr a b -> Expr Bool+isLeftTable EitherTable {tag} = isLeft tag -- | Test if an 'EitherTable' is a 'rightTable'.-isRightTable :: EitherTable a b -> Expr Bool-isRightTable = isRight . expr . tag+isRightTable :: EitherTable Expr a b -> Expr Bool+isRightTable EitherTable {tag} = isRight tag -- | Pattern match/eliminate an 'EitherTable', by providing mappings from a -- 'leftTable' and 'rightTable'. eitherTable :: Table Expr c- => (a -> c) -> (b -> c) -> EitherTable a b -> c+ => (a -> c) -> (b -> c) -> EitherTable Expr a b -> c eitherTable f g EitherTable {tag, left, right} =- bool (f left) (g right) (isRight (expr tag))+ bool (f (extract left)) (g (extract right)) (isRight tag) -- | Construct a left 'EitherTable'. Like 'Left'.-leftTable :: Table Expr b => a -> EitherTable a b-leftTable a = EitherTable (fromExpr (litExpr IsLeft)) a undefined+leftTable :: Table Expr b => a -> EitherTable Expr a b+leftTable a = EitherTable (litExpr IsLeft) (pure a) undefined -- | Construct a right 'EitherTable'. Like 'Right'.-rightTable :: Table Expr a => b -> EitherTable a b-rightTable = rightTableWith undefined+rightTable :: Table Expr a => b -> EitherTable Expr a b+rightTable = EitherTable (litExpr IsRight) undefined . pure -rightTableWith :: a -> b -> EitherTable a b-rightTableWith = EitherTable (fromExpr (litExpr IsRight))+-- | Lift a pair of aggregating functions to operate on an 'EitherTable'.+-- @leftTable@s and @rightTable@s are grouped separately.+aggregateEitherTable :: ()+ => (exprs -> aggregates)+ -> (exprs' -> aggregates')+ -> EitherTable Expr exprs exprs'+ -> EitherTable Aggregate aggregates aggregates'+aggregateEitherTable f g (EitherTable tag a b) = EitherTable+ { tag = groupByExpr tag+ , left = aggregateNullify f a+ , right = aggregateNullify g b+ } -- | Construct a 'EitherTable' in the 'Name' context. This can be useful if you@@ -198,51 +239,5 @@ -- ^ Names of the columns in the @a@ table. -> b -- ^ Names of the columns in the @b@ table.- -> EitherTable a b-nameEitherTable = EitherTable . fromName---toColumns2 ::- ( HTable t- , HTable u- , HConstrainTag context EitherTag- , HLabelable context- , HNullifiable context- )- => (a -> t context)- -> (b -> u context)- -> EitherTable a b- -> HEitherTable t u context-toColumns2 f g EitherTable {tag, left, right} = HEitherTable- { htag- , hleft = hlabel hlabeler $ hnullify (hnullifier tag isLeft) $ f left- , hright = hlabel hlabeler $ hnullify (hnullifier tag isRight) $ g right- }- where- htag = HIdentity (hencodeTag tag)---fromColumns2 ::- ( HTable t- , HTable u- , HConstrainTag context EitherTag- , HLabelable context- , HNullifiable context- )- => (t context -> a)- -> (u context -> b)- -> HEitherTable t u context- -> EitherTable a b-fromColumns2 f g HEitherTable {htag, hleft, hright} = EitherTable- { tag- , left = f $ runIdentity $- hunnullify (\a -> pure . hunnullifier a) $- hunlabel hunlabeler- hleft- , right = g $ runIdentity $- hunnullify (\a -> pure . hunnullifier a) $- hunlabel hunlabeler- hright- }- where- tag = hdecodeTag $ unHIdentity htag+ -> EitherTable Name a b+nameEitherTable tag left right = EitherTable tag (pure left) (pure right)
src/Rel8/Table/Eq.hs view
@@ -11,7 +11,6 @@ {-# language TypeFamilies #-} {-# language TypeOperators #-} {-# language UndecidableInstances #-}-{-# language UndecidableSuperClasses #-} {-# language ViewPatterns #-} module Rel8.Table.Eq@@ -28,24 +27,16 @@ import Prelude -- rel8-import Rel8.Expr ( Expr, Col( E ) )+import Rel8.Expr ( Expr ) import Rel8.Expr.Bool ( (||.), (&&.) ) import Rel8.Expr.Eq ( (==.), (/=.) ) import Rel8.FCF ( Eval, Exp ) import Rel8.Generic.Record ( Record )-import Rel8.Generic.Table- ( GGTable, GGColumns, ggtable- , GAlgebra- )-import Rel8.Kind.Algebra ( KnownAlgebra )+import Rel8.Generic.Table.Record ( GTable, GColumns, gtable ) import Rel8.Schema.Dict ( Dict( Dict ) )-import Rel8.Schema.HTable- ( HTable, HConstrainTable- , htabulateA, hfield, hdicts- )-import Rel8.Schema.HTable.Identity ( HIdentity( HType ) )+import Rel8.Schema.HTable ( htabulateA, hfield )+import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) ) import Rel8.Schema.Null ( Sql )-import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType, nullifier ) import Rel8.Table ( Table, Columns, toColumns, TColumns ) import Rel8.Type.Eq ( DBEq ) @@ -55,22 +46,14 @@ -- means "all columns in a 'Table' have an instance of 'DBEq'". type EqTable :: Type -> Constraint class Table Expr a => EqTable a where- eqTable :: Columns a (Dict (ConstrainDBType DBEq))+ eqTable :: Columns a (Dict (Sql DBEq)) default eqTable ::- ( KnownAlgebra (GAlgebra (Rep (Record a)))- , Eval (GGTable (GAlgebra (Rep (Record a))) TEqTable TColumns (Dict (ConstrainDBType DBEq)) (Rep (Record a)))- , Columns a ~ Eval (GGColumns (GAlgebra (Rep (Record a))) TColumns (Rep (Record a)))+ ( GTable TEqTable TColumns (Rep (Record a))+ , Columns a ~ GColumns TColumns (Rep (Record a)) )- => Columns a (Dict (ConstrainDBType DBEq))- eqTable =- ggtable- @(GAlgebra (Rep (Record a)))- @TEqTable- @TColumns- @(Rep (Record a))- table- nullifier+ => Columns a (Dict (Sql DBEq))+ eqTable = gtable @TEqTable @TColumns @(Rep (Record a)) table where table (_ :: proxy x) = eqTable @x @@ -79,18 +62,8 @@ type instance Eval (TEqTable a) = EqTable a -instance- ( HTable t- , f ~ Col Expr- , HConstrainTable t (ConstrainDBType DBEq)- )- => EqTable (t f)- where- eqTable = hdicts @(Columns (t f)) @(ConstrainDBType DBEq)-- instance Sql DBEq a => EqTable (Expr a) where- eqTable = HType Dict+ eqTable = HIdentity Dict instance (EqTable a, EqTable b) => EqTable (a, b)@@ -124,7 +97,7 @@ (toColumns -> as) ==: (toColumns -> bs) = foldl1' (&&.) $ getConst $ htabulateA $ \field -> case (hfield as field, hfield bs field) of- (E a, E b) -> case hfield (eqTable @a) field of+ (a, b) -> case hfield (eqTable @a) field of Dict -> Const (pure (a ==. b)) infix 4 ==: @@ -136,7 +109,7 @@ (toColumns -> as) /=: (toColumns -> bs) = foldl1' (||.) $ getConst $ htabulateA $ \field -> case (hfield as field, hfield bs field) of- (E a, E b) -> case hfield (eqTable @a) field of+ (a, b) -> case hfield (eqTable @a) field of Dict -> Const (pure (a /=. b)) infix 4 /=:
src/Rel8/Table/HKD.hs view
@@ -13,7 +13,7 @@ module Rel8.Table.HKD ( HKD( HKD )- , HKDable, fromHKD, toHKD, HKDT(..)+ , HKDable , BuildableHKD , BuildHKD, buildHKD , ConstructableHKD@@ -27,9 +27,6 @@ -- base import Data.Kind ( Constraint, Type )-import Data.Proxy ( Proxy( Proxy ) )-import Data.Type.Equality ( (:~:)( Refl ) )-import Data.Void ( Void ) import GHC.Generics ( Generic, Rep, from, to ) import GHC.TypeLits ( Symbol ) import Prelude@@ -49,161 +46,123 @@ , GGName, ggname , GGAggregate, ggaggregate )-import Rel8.Generic.Map ( GMap, GMappable, gmap, gunmap )-import Rel8.Generic.Record ( GRecord, GRecordable, grecord, gunrecord )+import Rel8.Generic.Map ( GMap )+import Rel8.Generic.Record+ ( GRecord, GRecordable, grecord, gunrecord+ , Record( Record ), unrecord+ ) import Rel8.Generic.Rel8able ( Rel8able , GColumns, gfromColumns, gtoColumns- , greify, gunreify- , TUnreifyContext+ , GFromExprs, gfromResult, gtoResult ) import Rel8.Generic.Table- ( GGTable, GGColumns, GGContext, ggfromColumns, ggtoColumns- , GAlgebra+ ( GGSerialize, GGColumns, GAlgebra, ggfromResult, ggtoResult )-import Rel8.Schema.Context ( Col )+import Rel8.Generic.Table.Record ( GTable, GContext )+import qualified Rel8.Generic.Table.Record as G import qualified Rel8.Schema.Kind as K import Rel8.Schema.HTable ( HTable ) import Rel8.Schema.Name ( Name )-import Rel8.Schema.Reify ( Col( Reify ), Reify, hreify, hunreify, notReify ) import Rel8.Schema.Result ( Result ) import Rel8.Table- ( Table, Columns, Context, Unreify- , fromColumns, toColumns, reify, unreify- , TTable, TColumns, TUnreify+ ( Table, fromColumns, toColumns, fromResult, toResult+ , TTable, TColumns, TContext+ , TSerialize )-import Rel8.Table.Serialize ( ToExprs, fromResult, toResult ) type GColumnsHKD :: Type -> K.HTable type GColumnsHKD a =- Eval (GGColumns (GAlgebra (Rep a)) TColumns (GRecord (GMap (TColumn (Reify Result)) (Rep a))))+ Eval (GGColumns (GAlgebra (Rep a)) TColumns (GRecord (GMap (TColumn Expr) (Rep a)))) type HKD :: Type -> K.Rel8able-newtype HKD a f = HKD (GColumnsHKD a (Col f))+newtype HKD a f = HKD (GColumnsHKD a f) instance HKDable a => Rel8able (HKD a) where type GColumns (HKD a) = GColumnsHKD a+ type GFromExprs (HKD a) = a - gfromColumns = HKD- gtoColumns (HKD a) = a+ gfromColumns _ = HKD+ gtoColumns _ (HKD a) = a - greify (HKD a) = HKD (hreify a)- gunreify (HKD a) = HKD (hunreify a)+ gfromResult =+ unrecord .+ to .+ ggfromResult+ @(GAlgebra (Rep a))+ @TSerialize+ @TColumns+ @(Eval (HKDRep a Expr))+ @(Eval (HKDRep a Result))+ (\(_ :: proxy x) -> fromResult @_ @x) + gtoResult =+ ggtoResult+ @(GAlgebra (Rep a))+ @TSerialize+ @TColumns+ @(Eval (HKDRep a Expr))+ @(Eval (HKDRep a Result))+ (\(_ :: proxy x) -> toResult @_ @x) .+ from .+ Record + instance- ( KnownAlgebra (GAlgebra (Rep a))- , HTable (GColumnsHKD a)- , Eval (GGTable (GAlgebra (Rep a)) (TTable (Reify f)) TColumns (Col (Reify f)) (GRecord (GMap (TColumn (Reify f)) (Rep a))))- , Eval (GGColumns (GAlgebra (Rep a)) TColumns (GRecord (GMap (TColumn (Reify f)) (Rep a)))) ~ GColumnsHKD a- , Eval (GGContext (GAlgebra (Rep a)) TUnreifyContext (GRecord (GMap (TColumn (Reify f)) (Rep a)))) ~ f- , GRecordable (GMap (TColumn (Reify f)) (Rep a))- , GMappable (TTable (Reify f)) (GMap (TColumn (Reify f)) (Rep a))- , GMap TUnreify (GMap (TColumn (Reify f)) (Rep a)) ~ GMap (TColumn f) (Rep a)+ ( GTable (TTable f) TColumns (GRecord (GMap (TColumn f) (Rep a)))+ , G.GColumns TColumns (GRecord (GMap (TColumn f) (Rep a))) ~ GColumnsHKD a+ , GContext TContext (GRecord (GMap (TColumn f) (Rep a))) ~ f+ , GRecordable (GMap (TColumn f) (Rep a)) ) => Generic (HKD a f) where type Rep (HKD a f) = GMap (TColumn f) (Rep a) from =- gmap @(TTable (Reify f)) (Proxy @TUnreify) (unreify Refl) .- gunrecord @(GMap (TColumn (Reify f)) (Rep a)) .- ggfromColumns- @(GAlgebra (Rep a))- @(TTable (Reify f))+ gunrecord @(GMap (TColumn f) (Rep a)) .+ G.gfromColumns+ @(TTable f) @TColumns- (\(Reify a) -> a)- Reify fromColumns .- hreify . (\(HKD a) -> a) to = HKD .- hunreify .- ggtoColumns- @(GAlgebra (Rep a))- @(TTable (Reify f))+ G.gtoColumns+ @(TTable f) @TColumns- (\(Reify a) -> a)- Reify toColumns .- grecord @(GMap (TColumn (Reify f)) (Rep a)) .- gunmap @(TTable (Reify f)) (Proxy @TUnreify) (reify Refl)---type HKDT :: Type -> Type-newtype HKDT a = HKDT- { unHKDT :: a- }---instance HKDable a => Table Result (HKDT a) where- type Columns (HKDT a) = GColumnsHKD a- type Context (HKDT a) = Result- type Unreify (HKDT a) = Void-- fromColumns = HKDT . fromHKD . HKD- toColumns = (\(HKD a) -> a) . toHKD . (\(HKDT a) -> a)- reify = notReify- unreify = notReify---instance- ( Table Expr (HKD a Expr)- , Columns (HKD a Expr) ~ GColumns (HKD a)- , HKDable a- , x ~ HKD a Expr- )- => ToExprs x (HKDT a)- where- toResult = (\(HKD a) -> a) . toHKD . (\(HKDT a) -> a)- fromResult = HKDT . fromHKD . HKD---fromHKD :: HKDable a => HKD a Result -> a-fromHKD = to . gunmap @Top (Proxy @(TColumn Result)) id . from---toHKD :: HKDable a => a -> HKD a Result-toHKD = to . gmap @Top (Proxy @(TColumn Result)) id . from---class Top_-instance Top_---data Top :: Type -> Exp Constraint-type instance Eval (Top _) = Top_+ grecord @(GMap (TColumn f) (Rep a)) +type HKDable :: Type -> Constraint class- ( Generic a+ ( Generic (Record a) , HTable (GColumns (HKD a)) , KnownAlgebra (GAlgebra (Rep a))- , Eval (GGTable (GAlgebra (Rep a)) (TTable (Reify Result)) TColumns (Col (Reify Result)) (GRecord (GMap (TColumn (Reify Result)) (Rep a))))- , Eval (GGContext (GAlgebra (Rep a)) TUnreifyContext (GRecord (GMap (TColumn (Reify Result)) (Rep a)))) ~ Result- , GRecordable (GMap (TColumn (Reify Result)) (Rep a))- , GMappable Top (Rep a)- , GMappable (TTable (Reify Result)) (GMap (TColumn (Reify Result)) (Rep a))- , GMap TUnreify (GMap (TColumn (Reify Result)) (Rep a)) ~ GMap (TColumn Result) (Rep a)+ , Eval (GGSerialize (GAlgebra (Rep a)) TSerialize TColumns (Eval (HKDRep a Expr)) (Eval (HKDRep a Result)))+ , GRecord (GMap (TColumn Result) (Rep a)) ~ Rep (Record a) ) => HKDable a instance- ( Generic a+ ( Generic (Record a) , HTable (GColumns (HKD a)) , KnownAlgebra (GAlgebra (Rep a))- , Eval (GGTable (GAlgebra (Rep a)) (TTable (Reify Result)) TColumns (Col (Reify Result)) (GRecord (GMap (TColumn (Reify Result)) (Rep a))))- , Eval (GGContext (GAlgebra (Rep a)) TUnreifyContext (GRecord (GMap (TColumn (Reify Result)) (Rep a)))) ~ Result- , GRecordable (GMap (TColumn (Reify Result)) (Rep a))- , GMappable Top (Rep a)- , GMappable (TTable (Reify Result)) (GMap (TColumn (Reify Result)) (Rep a))- , GMap TUnreify (GMap (TColumn (Reify Result)) (Rep a)) ~ GMap (TColumn Result) (Rep a)+ , Eval (GGSerialize (GAlgebra (Rep a)) TSerialize TColumns (Eval (HKDRep a Expr)) (Eval (HKDRep a Result)))+ , GRecord (GMap (TColumn Result) (Rep a)) ~ Rep (Record a) ) => HKDable a+++class Top_+instance Top_+++data Top :: Type -> Exp Constraint+type instance Eval (Top _) = Top_ type BuildableHKD :: Type -> Symbol -> Constraint
src/Rel8/Table/List.hs view
@@ -1,3 +1,4 @@+{-# language DataKinds #-} {-# language FlexibleContexts #-} {-# language FlexibleInstances #-} {-# language MultiParamTypeClasses #-}@@ -17,23 +18,27 @@ -- base import Data.Functor.Identity ( Identity( Identity ) ) import Data.Kind ( Type )-import Data.Type.Equality ( (:~:)( Refl ) ) import Prelude -- rel8-import Rel8.Expr ( Expr, Col( E, unE ) )+import Rel8.Expr ( Expr ) import Rel8.Expr.Array ( sappend, sempty, slistOf ) import Rel8.Schema.Dict ( Dict( Dict ) ) import Rel8.Schema.HTable.List ( HListTable )-import Rel8.Schema.HTable.Vectorize ( happend, hempty, hvectorize )-import Rel8.Schema.Name ( Col( N ), Name( Name ) )+import Rel8.Schema.HTable.Vectorize+ ( hvectorize, hunvectorize+ , happend, hempty+ , hproject+ )+import qualified Rel8.Schema.Kind as K+import Rel8.Schema.Name ( Name( Name ) ) import Rel8.Schema.Null ( Nullity( Null, NotNull ) )-import Rel8.Schema.Spec ( SSpec(..) )-import Rel8.Schema.Spec.ConstrainDBType ( dbTypeDict, dbTypeNullity )-import Rel8.Schema.Reify ( hreify, hunreify )+import Rel8.Schema.Result ( vectorizer, unvectorizer )+import Rel8.Schema.Spec ( Spec(..) ) import Rel8.Table ( Table, Context, Columns, fromColumns, toColumns- , reify, unreify+ , FromExprs, fromResult, toResult+ , Transpose ) import Rel8.Table.Alternative ( AltTable, (<|>:)@@ -41,87 +46,81 @@ ) import Rel8.Table.Eq ( EqTable, eqTable ) import Rel8.Table.Ord ( OrdTable, ordTable )-import Rel8.Table.Recontextualize ( Recontextualize )-import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )-import Rel8.Table.Unreify ( Unreifies )+import Rel8.Table.Projection ( Projectable, project, apply )+import Rel8.Table.Serialize ( ToExprs ) -- | A @ListTable@ value contains zero or more instances of @a@. You construct -- @ListTable@s with 'Rel8.many' or 'Rel8.listAgg'.-type ListTable :: Type -> Type-newtype ListTable a = ListTable (HListTable (Columns a) (Col (Context a)))+type ListTable :: K.Context -> Type -> Type+newtype ListTable context a =+ ListTable (HListTable (Columns a) (Context a)) -instance (Table context a, Unreifies context a) =>- Table context (ListTable a)+instance Projectable (ListTable context) where+ project f (ListTable a) = ListTable (hproject (apply f) a)+++instance (Table context a, context ~ context') =>+ Table context' (ListTable context a) where- type Columns (ListTable a) = HListTable (Columns a)- type Context (ListTable a) = Context a+ type Columns (ListTable context a) = HListTable (Columns a)+ type Context (ListTable context a) = Context a+ type FromExprs (ListTable context a) = [FromExprs a]+ type Transpose to (ListTable context a) = ListTable to (Transpose to a) fromColumns = ListTable toColumns (ListTable a) = a-- reify Refl (ListTable a) = ListTable (hreify a)- unreify Refl (ListTable a) = ListTable (hunreify a)---instance- ( Unreifies from a, Unreifies to b- , Recontextualize from to a b- )- => Recontextualize from to (ListTable a) (ListTable b)+ fromResult = fmap (fromResult @_ @a) . hunvectorize unvectorizer+ toResult = hvectorize vectorizer . fmap (toResult @_ @a) -instance EqTable a => EqTable (ListTable a) where+instance (EqTable a, context ~ Expr) => EqTable (ListTable context a) where eqTable = hvectorize- (\SSpec {} (Identity dict) -> case dbTypeDict dict of- Dict -> case dbTypeNullity dict of- Null -> Dict- NotNull -> Dict)+ (\Spec {nullity} (Identity Dict) -> case nullity of+ Null -> Dict+ NotNull -> Dict) (Identity (eqTable @a)) -instance OrdTable a => OrdTable (ListTable a) where+instance (OrdTable a, context ~ Expr) => OrdTable (ListTable context a) where ordTable = hvectorize- (\SSpec {} (Identity dict) -> case dbTypeDict dict of- Dict -> case dbTypeNullity dict of- Null -> Dict- NotNull -> Dict)+ (\Spec {nullity} (Identity Dict) -> case nullity of+ Null -> Dict+ NotNull -> Dict) (Identity (ordTable @a)) -type instance FromExprs (ListTable a) = [FromExprs a]---instance ToExprs exprs a => ToExprs (ListTable exprs) [a] where- fromResult = fmap (fromResult @exprs) . fromColumns- toResult = toColumns . fmap (toResult @exprs)+instance (ToExprs exprs a, context ~ Expr) =>+ ToExprs (ListTable context exprs) [a] -instance AltTable ListTable where+instance context ~ Expr => AltTable (ListTable context) where (<|>:) = (<>) -instance AlternativeTable ListTable where+instance context ~ Expr => AlternativeTable (ListTable context) where emptyTable = mempty -instance Table Expr a => Semigroup (ListTable a) where- ListTable as <> ListTable bs = ListTable $- happend (\_ _ (E a) (E b) -> E (sappend a b)) as bs+instance (context ~ Expr, Table Expr a) => Semigroup (ListTable context a)+ where+ ListTable as <> ListTable bs = ListTable $ happend (const sappend) as bs -instance Table Expr a => Monoid (ListTable a) where- mempty = ListTable $ hempty $ \_ -> E . sempty+instance (context ~ Expr, Table Expr a) =>+ Monoid (ListTable context a)+ where+ mempty = ListTable $ hempty $ \Spec {info} -> sempty info -- | Construct a @ListTable@ from a list of expressions.-listTable :: Table Expr a => [a] -> ListTable a+listTable :: Table Expr a => [a] -> ListTable Expr a listTable = ListTable .- hvectorize (\SSpec {info} -> E . slistOf info . fmap unE) .+ hvectorize (\Spec {info} -> slistOf info) . fmap toColumns @@ -131,9 +130,9 @@ nameListTable :: Table Name a => a -- ^ The names of the columns of elements of the list.- -> ListTable a+ -> ListTable Name a nameListTable = ListTable .- hvectorize (\_ (Identity (N (Name a))) -> N (Name a)) .+ hvectorize (\_ (Identity (Name a)) -> Name a) . pure . toColumns
src/Rel8/Table/Maybe.hs view
@@ -3,7 +3,6 @@ {-# language DerivingStrategies #-} {-# language FlexibleContexts #-} {-# language FlexibleInstances #-}-{-# language LambdaCase #-} {-# language MultiParamTypeClasses #-} {-# language NamedFieldPuns #-} {-# language ScopedTypeVariables #-}@@ -17,38 +16,40 @@ , maybeTable, nothingTable, justTable , isNothingTable, isJustTable , ($?)+ , aggregateMaybeTable , nameMaybeTable ) where -- base-import Data.Functor.Identity ( runIdentity )+import Data.Functor ( ($>) )+import Data.Functor.Identity ( Identity( Identity ) ) import Data.Kind ( Type )+import Data.Maybe ( fromMaybe, isJust ) import Prelude hiding ( null, undefined ) +-- comonad+import Control.Comonad ( extract )+ -- rel8+import Rel8.Aggregate ( Aggregate ) import Rel8.Expr ( Expr )+import Rel8.Expr.Aggregate ( groupByExpr ) import Rel8.Expr.Bool ( boolExpr ) import Rel8.Expr.Null ( isNull, isNonNull, null, nullify )-import Rel8.Schema.Context.Label- ( Labelable, HLabelable, hlabeler, hunlabeler- )-import Rel8.Schema.Context.Nullify- ( Nullifiable, ConstrainTag- , HNullifiable, HConstrainTag- , hencodeTag, hdecodeTag- , hnullifier, hunnullifier- )-import Rel8.Schema.HTable ( HTable )+import Rel8.Kind.Context ( Reifiable )+import Rel8.Schema.Dict ( Dict( Dict ) )+import qualified Rel8.Schema.Kind as K import Rel8.Schema.HTable.Identity ( HIdentity(..) ) import Rel8.Schema.HTable.Label ( hlabel, hunlabel ) import Rel8.Schema.HTable.Maybe ( HMaybeTable(..) )-import Rel8.Schema.HTable.Nullify ( hnullify, hunnullify ) import Rel8.Schema.Name ( Name )-import Rel8.Schema.Null ( Nullify, Nullity( Null, NotNull ), Sql, nullable )+import Rel8.Schema.Null ( Nullity( Null, NotNull ), Sql, nullable )+import qualified Rel8.Schema.Null as N import Rel8.Table ( Table, Columns, Context, fromColumns, toColumns- , reify, unreify+ , FromExprs, fromResult, toResult+ , Transpose ) import Rel8.Table.Alternative ( AltTable, (<|>:)@@ -57,12 +58,12 @@ import Rel8.Table.Bool ( bool ) import Rel8.Table.Eq ( EqTable, eqTable ) import Rel8.Table.Ord ( OrdTable, ordTable )-import Rel8.Table.Recontextualize ( Recontextualize )-import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )-import Rel8.Table.Tag ( Tag(..), fromExpr, fromName )+import Rel8.Table.Projection ( Projectable, project )+import Rel8.Table.Nullify ( Nullify, aggregateNullify, guard )+import Rel8.Table.Serialize ( ToExprs ) import Rel8.Table.Undefined ( undefined ) import Rel8.Type ( DBType )-import Rel8.Type.Tag ( MaybeTag )+import Rel8.Type.Tag ( MaybeTag( IsJust ) ) -- semigroupoids import Data.Functor.Apply ( Apply, (<.>) )@@ -77,135 +78,155 @@ -- entirely of nulls, you can't distinguish if you matched an all null row, or -- if the match failed. For this reason @MaybeTable@ contains an extra field - -- a "nullTag" - to track whether or not the outer join produced any rows.-type MaybeTable :: Type -> Type-data MaybeTable a = MaybeTable- { tag :: Tag "isJust" (Maybe MaybeTag)- , just :: a+type MaybeTable :: K.Context -> Type -> Type+data MaybeTable context a = MaybeTable+ { tag :: context (Maybe MaybeTag)+ , just :: Nullify context a } deriving stock Functor -instance Apply MaybeTable where- MaybeTable tag f <.> MaybeTable tag' a = MaybeTable (tag <> tag') (f a)+instance Projectable (MaybeTable context) where+ project f (MaybeTable tag a) = MaybeTable tag (project f a) +instance context ~ Expr => Apply (MaybeTable context) where+ MaybeTable tag f <.> MaybeTable tag' a = MaybeTable (tag <> tag') (f <.> a)++ -- | Has the same behavior as the @Applicative@ instance for @Maybe@. See also: -- 'Rel8.traverseMaybeTable'.-instance Applicative MaybeTable where+instance context ~ Expr => Applicative (MaybeTable context) where (<*>) = (<.>) pure = justTable -instance Bind MaybeTable where- MaybeTable tag a >>- f = case f a of+instance context ~ Expr => Bind (MaybeTable context) where+ MaybeTable tag a >>- f = case f (extract a) of MaybeTable tag' b -> MaybeTable (tag <> tag') b -- | Has the same behavior as the @Monad@ instance for @Maybe@.-instance Monad MaybeTable where+instance context ~ Expr => Monad (MaybeTable context) where (>>=) = (>>-) -instance AltTable MaybeTable where- ma@(MaybeTable tag a) <|>: MaybeTable tag' b = MaybeTable- { tag = (tag <> tag')- { expr = boolExpr (expr tag) (expr tag') condition- }- , just = bool a b condition- }- where- condition = isNothingTable ma+instance context ~ Expr => AltTable (MaybeTable context) where+ ma <|>: mb = bool ma mb (isNothingTable ma) -instance AlternativeTable MaybeTable where+instance context ~ Expr => AlternativeTable (MaybeTable context) where emptyTable = nothingTable -instance (Table Expr a, Semigroup a) => Semigroup (MaybeTable a) where+instance (context ~ Expr, Table Expr a, Semigroup a) =>+ Semigroup (MaybeTable context a)+ where ma <> mb = maybeTable mb (\a -> maybeTable ma (justTable . (a <>)) mb) ma -instance (Table Expr a, Semigroup a) => Monoid (MaybeTable a) where+instance (context ~ Expr, Table Expr a, Semigroup a) =>+ Monoid (MaybeTable context a)+ where mempty = nothingTable -instance- ( Table context a- , Labelable context, Nullifiable context- , ConstrainTag context MaybeTag- ) => Table context (MaybeTable a)+instance (Table context a, Reifiable context, context ~ context') =>+ Table context' (MaybeTable context a) where- type Columns (MaybeTable a) = HMaybeTable (Columns a)- type Context (MaybeTable a) = Context a-- toColumns = toColumns1 toColumns- fromColumns = fromColumns1 fromColumns- reify = fmap fmap reify- unreify = fmap fmap unreify+ type Columns (MaybeTable context a) = HMaybeTable (Columns a)+ type Context (MaybeTable context a) = Context a+ type FromExprs (MaybeTable context a) = Maybe (FromExprs a)+ type Transpose to (MaybeTable context a) = MaybeTable to (Transpose to a) + toColumns MaybeTable {tag, just} = HMaybeTable+ { htag = hlabel $ HIdentity tag+ , hjust = hlabel $ guard tag isJust isNonNull $ toColumns just+ } -instance- ( Labelable from, Nullifiable from, ConstrainTag from MaybeTag- , Labelable to, Nullifiable to, ConstrainTag to MaybeTag- , Recontextualize from to a b- )- => Recontextualize from to (MaybeTable a) (MaybeTable b)+ fromColumns HMaybeTable {htag, hjust} = MaybeTable+ { tag = unHIdentity $ hunlabel htag+ , just = fromColumns $ hunlabel hjust+ } + toResult ma = HMaybeTable+ { htag = hlabel (HIdentity (Identity (IsJust <$ ma)))+ , hjust = hlabel (toResult @_ @(Nullify context a) ma)+ } -instance EqTable a => EqTable (MaybeTable a) where- eqTable = toColumns1 id (justTable (eqTable @a))+ fromResult HMaybeTable {htag, hjust} = case hunlabel htag of+ HIdentity (Identity tag) -> tag $>+ fromMaybe err (fromResult @_ @(Nullify context a) (hunlabel hjust))+ where+ err = error "Maybe.fromColumns: mismatch between tag and data" -instance OrdTable a => OrdTable (MaybeTable a) where- ordTable = toColumns1 id (justTable (ordTable @a))+instance (EqTable a, context ~ Expr) => EqTable (MaybeTable context a) where+ eqTable = HMaybeTable+ { htag = hlabel (HIdentity Dict)+ , hjust = hlabel (eqTable @(Nullify context a))+ } -type instance FromExprs (MaybeTable a) = Maybe (FromExprs a)+instance (OrdTable a, context ~ Expr) => OrdTable (MaybeTable context a) where+ ordTable = HMaybeTable+ { htag = hlabel (HIdentity Dict)+ , hjust = hlabel (ordTable @(Nullify context a))+ } -instance ToExprs exprs a => ToExprs (MaybeTable exprs) (Maybe a) where- fromResult = fmap (fromResult @exprs) . fromColumns- toResult = toColumns . fmap (toResult @exprs)+instance (ToExprs exprs a, context ~ Expr) =>+ ToExprs (MaybeTable context exprs) (Maybe a) -- | Check if a @MaybeTable@ is absent of any row. Like 'Data.Maybe.isNothing'.-isNothingTable :: MaybeTable a -> Expr Bool-isNothingTable (MaybeTable tag _) = isNull (expr tag)+isNothingTable :: MaybeTable Expr a -> Expr Bool+isNothingTable (MaybeTable tag _) = isNull tag -- | Check if a @MaybeTable@ contains a row. Like 'Data.Maybe.isJust'.-isJustTable :: MaybeTable a -> Expr Bool-isJustTable (MaybeTable tag _) = isNonNull (expr tag)+isJustTable :: MaybeTable Expr a -> Expr Bool+isJustTable (MaybeTable tag _) = isNonNull tag -- | Perform case analysis on a 'MaybeTable'. Like 'maybe'.-maybeTable :: Table Expr b => b -> (a -> b) -> MaybeTable a -> b-maybeTable b f ma@(MaybeTable _ a) = bool (f a) b (isNothingTable ma)+maybeTable :: Table Expr b => b -> (a -> b) -> MaybeTable Expr a -> b+maybeTable b f ma@(MaybeTable _ a) = bool (f (extract a)) b (isNothingTable ma) {-# INLINABLE maybeTable #-} -- | The null table. Like 'Nothing'.-nothingTable :: Table Expr a => MaybeTable a-nothingTable = MaybeTable (fromExpr null) undefined+nothingTable :: Table Expr a => MaybeTable Expr a+nothingTable = MaybeTable null (pure undefined) -- | Lift any table into 'MaybeTable'. Like 'Just'. Note you can also use -- 'pure'.-justTable :: a -> MaybeTable a-justTable = MaybeTable (fromExpr mempty)+justTable :: a -> MaybeTable Expr a+justTable = MaybeTable mempty . pure -- | Project a single expression out of a 'MaybeTable'. You can think of this -- operator like the '$' operator, but it also has the ability to return -- @null@. ($?) :: forall a b. Sql DBType b- => (a -> Expr b) -> MaybeTable a -> Expr (Nullify b)+ => (a -> Expr b) -> MaybeTable Expr a -> Expr (N.Nullify b) f $? ma@(MaybeTable _ a) = case nullable @b of- Null -> boolExpr (f a) null (isNothingTable ma)- NotNull -> boolExpr (nullify (f a)) null (isNothingTable ma)+ Null -> boolExpr (f (extract a)) null (isNothingTable ma)+ NotNull -> boolExpr (nullify (f (extract a))) null (isNothingTable ma) infixl 4 $? +-- | Lift an aggregating function to operate on a 'MaybeTable'.+-- @nothingTable@s and @justTable@s are grouped separately.+aggregateMaybeTable :: ()+ => (exprs -> aggregates)+ -> MaybeTable Expr exprs+ -> MaybeTable Aggregate aggregates+aggregateMaybeTable f (MaybeTable tag a) =+ MaybeTable (groupByExpr tag) (aggregateNullify f a)++ -- | Construct a 'MaybeTable' in the 'Name' context. This can be useful if you -- have a 'MaybeTable' that you are storing in a table and need to construct a -- 'TableSchema'.@@ -215,40 +236,5 @@ -- 'nothingTable'. -> a -- ^ Names of the columns in @a@.- -> MaybeTable a-nameMaybeTable = MaybeTable . fromName---toColumns1 ::- ( HTable t- , HConstrainTag context MaybeTag- , HLabelable context- , HNullifiable context- )- => (a -> t context)- -> MaybeTable a- -> HMaybeTable t context-toColumns1 f MaybeTable {tag, just} = HMaybeTable- { htag- , hjust = hlabel hlabeler $ hnullify (hnullifier tag isNonNull) $ f just- }- where- htag = HIdentity (hencodeTag tag)---fromColumns1 ::- ( HTable t- , HConstrainTag context MaybeTag- , HLabelable context- , HNullifiable context- )- => (t context -> a)- -> HMaybeTable t context- -> MaybeTable a-fromColumns1 f HMaybeTable {htag = HIdentity htag, hjust} = MaybeTable- { tag- , just = f $ runIdentity $- hunnullify (\a -> pure . hunnullifier a) (hunlabel hunlabeler hjust)- }- where- tag = hdecodeTag htag+ -> MaybeTable Name a+nameMaybeTable tag = MaybeTable tag . pure
src/Rel8/Table/Name.hs view
@@ -1,14 +1,17 @@+{-# language DataKinds #-} {-# language FlexibleContexts #-}+{-# language FlexibleInstances #-}+{-# language MultiParamTypeClasses #-} {-# language NamedFieldPuns #-} {-# language ScopedTypeVariables #-} {-# language TypeApplications #-} {-# language TypeFamilies #-}+{-# language UndecidableInstances #-} {-# language ViewPatterns #-} module Rel8.Table.Name ( namesFromLabels , namesFromLabelsWith- , showExprs , showLabels , showNames )@@ -17,20 +20,15 @@ -- base import Data.Foldable ( fold ) import Data.Functor.Const ( Const( Const ), getConst )-import Data.List.NonEmpty ( NonEmpty, intersperse )+import Data.List.NonEmpty ( NonEmpty, intersperse, nonEmpty )+import Data.Maybe ( fromMaybe ) import Prelude --- opaleye-import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye- -- rel8-import Rel8.Expr ( Expr, Col( E ) )-import Rel8.Expr.Opaleye ( toPrimExpr )-import Rel8.Kind.Labels ( renderLabels ) import Rel8.Schema.HTable ( htabulate, htabulateA, hfield, hspecs )-import Rel8.Schema.Name ( Name( Name ), Col( N ) )-import Rel8.Schema.Spec ( SSpec(..) )-import Rel8.Table ( Table, Columns, Context, fromColumns, toColumns )+import Rel8.Schema.Name ( Name( Name ) )+import Rel8.Schema.Spec ( Spec(..) )+import Rel8.Table ( Table(..) ) -- | Construct a table in the 'Name' context containing the names of all@@ -62,23 +60,20 @@ => (NonEmpty String -> String) -> a namesFromLabelsWith f = fromColumns $ htabulate $ \field -> case hfield hspecs field of- SSpec {labels} -> N (Name (f (renderLabels labels)))---showExprs :: Table Expr a => a -> [(String, Opaleye.PrimExpr)]-showExprs as = case (namesFromLabels, toColumns as) of- (names, exprs) -> getConst $ htabulateA $ \field ->- case (hfield names field, hfield exprs field) of- (N (Name name), E expr) -> Const [(name, toPrimExpr expr)]+ Spec {labels} -> Name (f (renderLabels labels)) showLabels :: forall a. Table (Context a) a => a -> [NonEmpty String] showLabels _ = getConst $ htabulateA @(Columns a) $ \field -> case hfield hspecs field of- SSpec {labels} -> Const [renderLabels labels]+ Spec {labels} -> Const (pure (renderLabels labels)) -showNames :: forall a. Table Name a => a -> [String]+showNames :: forall a. Table Name a => a -> NonEmpty String showNames (toColumns -> names) = getConst $ htabulateA @(Columns a) $ \field -> case hfield names field of- N (Name name) -> Const [name]+ Name name -> Const (pure name)+++renderLabels :: [String] -> NonEmpty String+renderLabels labels = fromMaybe (pure "anon") (nonEmpty labels )
src/Rel8/Table/NonEmpty.hs view
@@ -1,3 +1,4 @@+{-# language DataKinds #-} {-# language FlexibleContexts #-} {-# language FlexibleInstances #-} {-# language MultiParamTypeClasses #-}@@ -18,102 +19,102 @@ import Data.Functor.Identity ( Identity( Identity ) ) import Data.Kind ( Type ) import Data.List.NonEmpty ( NonEmpty )-import Data.Type.Equality ( (:~:)( Refl ) )-import Prelude+import Prelude hiding ( id ) -- rel8-import Rel8.Expr ( Expr, Col( E, unE ) )+import Rel8.Expr ( Expr ) import Rel8.Expr.Array ( sappend1, snonEmptyOf ) import Rel8.Schema.Dict ( Dict( Dict ) ) import Rel8.Schema.HTable.NonEmpty ( HNonEmptyTable )-import Rel8.Schema.HTable.Vectorize ( happend, hvectorize )-import Rel8.Schema.Name ( Col( N ), Name( Name ) )+import Rel8.Schema.HTable.Vectorize+ ( hvectorize, hunvectorize+ , happend+ , hproject+ )+import qualified Rel8.Schema.Kind as K+import Rel8.Schema.Name ( Name( Name ) ) import Rel8.Schema.Null ( Nullity( Null, NotNull ) )-import Rel8.Schema.Reify ( hreify, hunreify )-import Rel8.Schema.Spec ( SSpec(..) )-import Rel8.Schema.Spec.ConstrainDBType ( dbTypeDict, dbTypeNullity )+import Rel8.Schema.Result ( vectorizer, unvectorizer )+import Rel8.Schema.Spec ( Spec(..) ) import Rel8.Table ( Table, Context, Columns, fromColumns, toColumns- , reify, unreify+ , FromExprs, fromResult, toResult+ , Transpose ) import Rel8.Table.Alternative ( AltTable, (<|>:) ) import Rel8.Table.Eq ( EqTable, eqTable ) import Rel8.Table.Ord ( OrdTable, ordTable )-import Rel8.Table.Recontextualize ( Recontextualize )-import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )-import Rel8.Table.Unreify ( Unreifies )+import Rel8.Table.Projection ( Projectable, project, apply )+import Rel8.Table.Serialize ( ToExprs ) -- | A @NonEmptyTable@ value contains one or more instances of @a@. You -- construct @NonEmptyTable@s with 'Rel8.some' or 'nonEmptyAgg'.-type NonEmptyTable :: Type -> Type-newtype NonEmptyTable a =- NonEmptyTable (HNonEmptyTable (Columns a) (Col (Context a)))+type NonEmptyTable :: K.Context -> Type -> Type+newtype NonEmptyTable context a =+ NonEmptyTable (HNonEmptyTable (Columns a) (Context a)) -instance (Table context a, Unreifies context a) =>- Table context (NonEmptyTable a)+instance Projectable (NonEmptyTable context) where+ project f (NonEmptyTable a) = NonEmptyTable (hproject (apply f) a)+++instance (Table context a, context ~ context') =>+ Table context' (NonEmptyTable context a) where- type Columns (NonEmptyTable a) = HNonEmptyTable (Columns a)- type Context (NonEmptyTable a) = Context a+ type Columns (NonEmptyTable context a) = HNonEmptyTable (Columns a)+ type Context (NonEmptyTable context a) = Context a+ type FromExprs (NonEmptyTable context a) = NonEmpty (FromExprs a)+ type Transpose to (NonEmptyTable context a) =+ NonEmptyTable to (Transpose to a) fromColumns = NonEmptyTable toColumns (NonEmptyTable a) = a-- reify Refl (NonEmptyTable a) = NonEmptyTable (hreify a)- unreify Refl (NonEmptyTable a) = NonEmptyTable (hunreify a)---instance- ( Unreifies from a, Unreifies to b- , Recontextualize from to a b- )- => Recontextualize from to (NonEmptyTable a) (NonEmptyTable b)+ fromResult = fmap (fromResult @_ @a) . hunvectorize unvectorizer+ toResult = hvectorize vectorizer . fmap (toResult @_ @a) -instance EqTable a => EqTable (NonEmptyTable a) where+instance (EqTable a, context ~ Expr) =>+ EqTable (NonEmptyTable context a)+ where eqTable = hvectorize- (\SSpec {} (Identity dict) -> case dbTypeDict dict of- Dict -> case dbTypeNullity dict of- Null -> Dict- NotNull -> Dict)+ (\Spec {nullity} (Identity Dict) -> case nullity of+ Null -> Dict+ NotNull -> Dict) (Identity (eqTable @a)) -instance OrdTable a => OrdTable (NonEmptyTable a) where+instance (OrdTable a, context ~ Expr) =>+ OrdTable (NonEmptyTable context a)+ where ordTable = hvectorize- (\SSpec {} (Identity dict) -> case dbTypeDict dict of- Dict -> case dbTypeNullity dict of- Null -> Dict- NotNull -> Dict)+ (\Spec {nullity} (Identity Dict) -> case nullity of+ Null -> Dict+ NotNull -> Dict) (Identity (ordTable @a)) -type instance FromExprs (NonEmptyTable a) = NonEmpty (FromExprs a)---instance ToExprs exprs a => ToExprs (NonEmptyTable exprs) (NonEmpty a)- where- fromResult = fmap (fromResult @exprs) . fromColumns- toResult = toColumns . fmap (toResult @exprs)+instance (ToExprs exprs a, context ~ Expr) =>+ ToExprs (NonEmptyTable context exprs) (NonEmpty a) -instance AltTable NonEmptyTable where+instance context ~ Expr => AltTable (NonEmptyTable context) where (<|>:) = (<>) -instance Table Expr a => Semigroup (NonEmptyTable a) where+instance (Table Expr a, context ~ Expr) => Semigroup (NonEmptyTable context a)+ where NonEmptyTable as <> NonEmptyTable bs = NonEmptyTable $- happend (\_ _ (E a) (E b) -> E (sappend1 a b)) as bs+ happend (const sappend1) as bs -- | Construct a @NonEmptyTable@ from a non-empty list of expressions.-nonEmptyTable :: Table Expr a => NonEmpty a -> NonEmptyTable a+nonEmptyTable :: Table Expr a => NonEmpty a -> NonEmptyTable Expr a nonEmptyTable = NonEmptyTable .- hvectorize (\SSpec {info} -> E . snonEmptyOf info . fmap unE) .+ hvectorize (\Spec {info} -> snonEmptyOf info) . fmap toColumns @@ -123,9 +124,9 @@ nameNonEmptyTable :: Table Name a => a -- ^ The names of the columns of elements of the list.- -> NonEmptyTable a+ -> NonEmptyTable Name a nameNonEmptyTable = NonEmptyTable .- hvectorize (\_ (Identity (N (Name a))) -> N (Name a)) .+ hvectorize (\_ (Identity (Name a)) -> Name a) . pure . toColumns
+ src/Rel8/Table/Nullify.hs view
@@ -0,0 +1,184 @@+{-# language DataKinds #-}+{-# language FlexibleInstances #-}+{-# language LambdaCase #-}+{-# language MultiParamTypeClasses #-}+{-# language ScopedTypeVariables #-}+{-# language StandaloneKindSignatures #-}+{-# language TypeApplications #-}+{-# language TypeFamilies #-}+{-# language UndecidableInstances #-}++module Rel8.Table.Nullify+ ( Nullify+ , aggregateNullify+ , guard+ )+where++-- base+import Control.Applicative ( liftA2 )+import Data.Functor.Identity ( runIdentity )+import Data.Kind ( Type )+import Prelude++-- comonad+import Control.Comonad ( Comonad, duplicate, extract, ComonadApply, (<@>) )++-- rel8+import Rel8.Aggregate ( Aggregate )+import Rel8.Expr ( Expr )+import Rel8.Kind.Context ( Reifiable, contextSing )+import Rel8.Schema.Context.Nullify+ ( Nullifiability( NAggregate, NExpr )+ , NonNullifiability+ , Nullifiable, nullifiability+ , nullifiableOrNot, absurd+ , guarder+ , nullifier+ , unnullifier+ )+import Rel8.Schema.Dict ( Dict( Dict ) )+import Rel8.Schema.HTable ( HTable )+import Rel8.Schema.HTable.Nullify+ ( HNullify, hnulls, hnullify, hunnullify+ , hguard+ , hproject+ )+import qualified Rel8.Schema.Kind as K+import qualified Rel8.Schema.Result as R+import Rel8.Table+ ( Table, Columns, Context, toColumns, fromColumns+ , FromExprs, fromResult, toResult+ , Transpose+ )+import Rel8.Table.Eq ( EqTable, eqTable )+import Rel8.Table.Ord ( OrdTable, ordTable )+import Rel8.Table.Projection ( Projectable, apply, project )++-- semigroupoids+import Data.Functor.Apply ( Apply, (<.>), liftF2 )+import Data.Functor.Bind ( Bind, (>>-) )+import Data.Functor.Extend ( Extend, duplicated )+++type Nullify :: K.Context -> Type -> Type+data Nullify context a+ = Table (Nullifiability context) a+ | Fields (NonNullifiability context) (HNullify (Columns a) (Context a))+++instance Projectable (Nullify context) where+ project f = \case+ Table nullifiable a -> Table nullifiable (fromColumns (apply f (toColumns a)))+ Fields nonNullifiable a -> Fields nonNullifiable (hproject (apply f) a)+++instance Nullifiable context => Functor (Nullify context) where+ fmap f = \case+ Table nullifiable a -> Table nullifiable (f a)+ Fields notNullifiable _ -> absurd nullifiability notNullifiable+++instance Nullifiable context => Foldable (Nullify context) where+ foldMap f = \case+ Table _ a -> f a+ Fields notNullifiable _ -> absurd nullifiability notNullifiable+++instance Nullifiable context => Traversable (Nullify context) where+ traverse f = \case+ Table nullifiable a -> Table nullifiable <$> f a+ Fields notNullifiable _ -> absurd nullifiability notNullifiable+++instance Nullifiable context => Apply (Nullify context) where+ liftF2 f = \case+ Table nullifiable a -> \case+ Table _ b -> Table nullifiable (f a b)+ Fields notNullifiable _ -> absurd nullifiable notNullifiable+ Fields notNullifiable _ -> absurd nullifiability notNullifiable+++instance Nullifiable context => Applicative (Nullify context) where+ pure = Table nullifiability+ liftA2 = liftF2+++instance Nullifiable context => Bind (Nullify context) where+ Table _ a >>- f = f a+ Fields notNullifiable _ >>- _ = absurd nullifiability notNullifiable+++instance Nullifiable context => Monad (Nullify context) where+ (>>=) = (>>-)+++instance Nullifiable context => Extend (Nullify context) where+ duplicated = \case+ Table nullifiable a -> Table nullifiable (Table nullifiable a)+ Fields notNullifiable _ -> absurd nullifiability notNullifiable+++instance Nullifiable context => Comonad (Nullify context) where+ extract = \case+ Table _ a -> a+ Fields notNullifiable _ -> absurd nullifiability notNullifiable+ duplicate = duplicated+++instance Nullifiable context => ComonadApply (Nullify context) where+ (<@>) = (<.>)+++instance (Table context a, Reifiable context, context ~ context') =>+ Table context' (Nullify context a)+ where+ type Columns (Nullify context a) = HNullify (Columns a)+ type Context (Nullify context a) = Context a+ type FromExprs (Nullify context a) = Maybe (FromExprs a)+ type Transpose to (Nullify context a) = Nullify to (Transpose to a)++ fromColumns = case nullifiableOrNot contextSing of+ Left notNullifiable -> Fields notNullifiable+ Right nullifiable ->+ Table nullifiable .+ fromColumns .+ runIdentity .+ hunnullify (\spec -> pure . unnullifier nullifiable spec)++ toColumns = \case+ Table nullifiable a -> hnullify (nullifier nullifiable) (toColumns a)+ Fields _ a -> a++ fromResult = fmap (fromResult @_ @a) . hunnullify R.unnullifier++ toResult =+ maybe (hnulls (const R.null)) (hnullify R.nullifier) .+ fmap (toResult @_ @a)+++instance (EqTable a, context ~ Expr) => EqTable (Nullify context a) where+ eqTable = hnullify (\_ Dict -> Dict) (eqTable @a)+++instance (OrdTable a, context ~ Expr) => OrdTable (Nullify context a) where+ ordTable = hnullify (\_ Dict -> Dict) (ordTable @a)+++aggregateNullify :: ()+ => (exprs -> aggregates)+ -> Nullify Expr exprs+ -> Nullify Aggregate aggregates+aggregateNullify f = \case+ Table _ a -> Table NAggregate (f a)+ Fields notNullifiable _ -> absurd NExpr notNullifiable+++guard :: (Reifiable context, HTable t)+ => context tag+ -> (tag -> Bool)+ -> (Expr tag -> Expr Bool)+ -> HNullify t context+ -> HNullify t context+guard tag isNonNull isNonNullExpr =+ hguard (guarder contextSing tag isNonNull isNonNullExpr)
src/Rel8/Table/Opaleye.hs view
@@ -1,24 +1,31 @@ {-# language BlockArguments #-}+{-# language DataKinds #-} {-# language DisambiguateRecordFields #-} {-# language FlexibleContexts #-}-{-# language LambdaCase #-} {-# language NamedFieldPuns #-}+{-# language RankNTypes #-} {-# language TypeFamilies #-} {-# language ViewPatterns #-} module Rel8.Table.Opaleye ( aggregator+ , attributes , binaryspec , distinctspec+ , exprs+ , exprsWithNames , table , tableFields , unpackspec , valuesspec+ , view , castTable ) where -- base+import Data.Functor.Const ( Const( Const ), getConst )+import Data.List.NonEmpty ( NonEmpty ) import Prelude hiding ( undefined ) -- opaleye@@ -35,8 +42,8 @@ import Data.Profunctor ( dimap, lmap ) -- rel8-import Rel8.Aggregate ( Col( A ), Aggregate( Aggregate ), Aggregates )-import Rel8.Expr ( Expr, Col(..) )+import Rel8.Aggregate ( Aggregate( Aggregate ), Aggregates )+import Rel8.Expr ( Expr ) import Rel8.Expr.Opaleye ( fromPrimExpr, toPrimExpr , traversePrimExpr@@ -44,9 +51,9 @@ , scastExpr ) import Rel8.Schema.HTable ( htabulateA, hfield, htraverse, hspecs, htabulate )-import Rel8.Schema.Name ( Col( N ), Name( Name ), Selects )-import Rel8.Schema.Spec ( SSpec(..) )-import Rel8.Schema.Table ( TableSchema(..) )+import Rel8.Schema.Name ( Name( Name ), Selects, ppColumn )+import Rel8.Schema.Spec ( Spec(..) )+import Rel8.Schema.Table ( TableSchema(..), ppTable ) import Rel8.Table ( Table, fromColumns, toColumns ) import Rel8.Table.Undefined ( undefined ) @@ -58,16 +65,24 @@ aggregator = Opaleye.Aggregator $ Opaleye.PackMap $ \f aggregates -> fmap fromColumns $ unwrapApplicative $ htabulateA $ \field -> WrapApplicative $ case hfield (toColumns aggregates) field of- A (Aggregate (Opaleye.Aggregator (Opaleye.PackMap inner))) ->- E <$> inner f ()+ Aggregate (Opaleye.Aggregator (Opaleye.PackMap inner)) ->+ inner f () +attributes :: Selects names exprs => TableSchema names -> exprs+attributes schema@TableSchema {columns} = fromColumns $ htabulate $ \field ->+ case hfield (toColumns columns) field of+ Name column -> fromPrimExpr $ Opaleye.ConstExpr $+ Opaleye.OtherLit $+ show (ppTable schema) <> "." <> show (ppColumn column)++ binaryspec :: Table Expr a => Opaleye.Binaryspec a a binaryspec = Opaleye.Binaryspec $ Opaleye.PackMap $ \f (as, bs) -> fmap fromColumns $ unwrapApplicative $ htabulateA $ \field -> WrapApplicative $ case (hfield (toColumns as) field, hfield (toColumns bs) field) of- (E a, E b) -> E . fromPrimExpr <$> f (toPrimExpr a, toPrimExpr b)+ (a, b) -> fromPrimExpr <$> f (toPrimExpr a, toPrimExpr b) distinctspec :: Table Expr a => Opaleye.Distinctspec a a@@ -76,37 +91,48 @@ fmap fromColumns . unwrapApplicative . htraverse- (\(E a) ->- WrapApplicative $ E . fromPrimExpr <$> f (Nothing, toPrimExpr a)) .+ (\a -> WrapApplicative $ fromPrimExpr <$> f (Nothing, toPrimExpr a)) . toColumns -table ::Selects names exprs => TableSchema names -> Opaleye.Table exprs exprs+exprs :: Table Expr a => a -> NonEmpty Opaleye.PrimExpr+exprs (toColumns -> as) = getConst $ htabulateA $ \field ->+ case hfield as field of+ expr -> Const (pure (toPrimExpr expr))+++exprsWithNames :: Selects names exprs+ => names -> exprs -> NonEmpty (String, Opaleye.PrimExpr)+exprsWithNames names as = getConst $ htabulateA $ \field ->+ case (hfield (toColumns names) field, hfield (toColumns as) field) of+ (Name name, expr) -> Const (pure (name, toPrimExpr expr))+++table :: Selects names exprs => TableSchema names -> Opaleye.Table exprs exprs table (TableSchema name schema columns) = case schema of Nothing -> Opaleye.Table name (tableFields columns) Just schemaName -> Opaleye.TableWithSchema schemaName name (tableFields columns) -tableFields ::Selects names exprs+tableFields :: Selects names exprs => names -> Opaleye.TableFields exprs exprs tableFields (toColumns -> names) = dimap toColumns fromColumns $ unwrapApplicative $ htabulateA $ \field -> WrapApplicative $ case hfield names field of name -> lmap (`hfield` field) (go name) where- go :: Col Name spec -> Opaleye.TableFields (Col Expr spec) (Col Expr spec)- go (N (Name name)) =- lmap (\(E a) -> toColumn $ toPrimExpr a) $- E . fromPrimExpr . fromColumn <$>- Opaleye.requiredTableField name+ go :: Name a -> Opaleye.TableFields (Expr a) (Expr a)+ go (Name name) =+ dimap (toColumn . toPrimExpr) (fromPrimExpr . fromColumn) $+ Opaleye.requiredTableField name unpackspec :: Table Expr a => Opaleye.Unpackspec a a unpackspec = Opaleye.Unpackspec $ Opaleye.PackMap $ \f -> fmap fromColumns . unwrapApplicative .- htraverse (\(E a) -> WrapApplicative $ E <$> traversePrimExpr f a) .+ htraverse (WrapApplicative . traversePrimExpr f) . toColumns {-# INLINABLE unpackspec #-} @@ -115,12 +141,18 @@ valuesspec = Opaleye.ValuesspecSafe (toPackMap undefined) unpackspec +view :: Selects names exprs => names -> exprs+view columns = fromColumns $ htabulate $ \field ->+ case hfield (toColumns columns) field of+ Name column -> fromPrimExpr $ Opaleye.BaseTableAttrExpr column++ toPackMap :: Table Expr a => a -> Opaleye.PackMap Opaleye.PrimExpr Opaleye.PrimExpr () a toPackMap as = Opaleye.PackMap $ \f () -> fmap fromColumns $ unwrapApplicative .- htraverse (\(E a) -> WrapApplicative $ E <$> traversePrimExpr f a) $+ htraverse (WrapApplicative . traversePrimExpr f) $ toColumns as @@ -128,9 +160,7 @@ -- finalising a SELECT or RETURNING statement, guaranteed that the output -- matches what is encoded in each columns TypeInformation. castTable :: Table Expr a => a -> a-castTable (toColumns -> as) = fromColumns $ htabulate \i ->- case hfield hspecs i of- SSpec{info} -> - case hfield as i of- E expr ->- E (scastExpr info expr)+castTable (toColumns -> as) = fromColumns $ htabulate \field ->+ case hfield hspecs field of+ Spec {info} -> case hfield as field of+ expr -> scastExpr info expr
src/Rel8/Table/Ord.hs view
@@ -10,7 +10,6 @@ {-# language TypeFamilies #-} {-# language TypeOperators #-} {-# language UndecidableInstances #-}-{-# language UndecidableSuperClasses #-} {-# language ViewPatterns #-} module Rel8.Table.Ord@@ -25,29 +24,20 @@ import Prelude hiding ( seq ) -- rel8-import Rel8.Expr ( Expr, Col( E ) )+import Rel8.Expr ( Expr ) import Rel8.Expr.Bool ( (||.), (&&.), false, true ) import Rel8.Expr.Eq ( (==.) ) import Rel8.Expr.Ord ( (<.), (>.) ) import Rel8.FCF ( Eval, Exp ) import Rel8.Generic.Record ( Record )-import Rel8.Generic.Table- ( GGTable, GGColumns, ggtable- , GAlgebra- )-import Rel8.Kind.Algebra ( KnownAlgebra )+import Rel8.Generic.Table.Record ( GTable, GColumns, gtable ) import Rel8.Schema.Dict ( Dict( Dict ) )-import Rel8.Schema.HTable- ( HTable, HConstrainTable- , htabulateA, hfield, hdicts- )-import Rel8.Schema.HTable.Identity ( HIdentity( HType ) )+import Rel8.Schema.HTable ( htabulateA, hfield )+import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) ) import Rel8.Schema.Null (Sql)-import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType, nullifier ) import Rel8.Table ( Columns, toColumns, TColumns ) import Rel8.Table.Bool ( bool ) import Rel8.Table.Eq ( EqTable )-import Rel8.Type.Eq ( DBEq ) import Rel8.Type.Ord ( DBOrd ) @@ -56,22 +46,14 @@ -- columns in a 'Table' have an instance of 'DBOrd'". type OrdTable :: Type -> Constraint class EqTable a => OrdTable a where- ordTable :: Columns a (Dict (ConstrainDBType DBOrd))+ ordTable :: Columns a (Dict (Sql DBOrd)) default ordTable ::- ( KnownAlgebra (GAlgebra (Rep (Record a)))- , Eval (GGTable (GAlgebra (Rep (Record a))) TOrdTable TColumns (Dict (ConstrainDBType DBOrd)) (Rep (Record a)))- , Columns a ~ Eval (GGColumns (GAlgebra (Rep (Record a))) TColumns (Rep (Record a)))+ ( GTable TOrdTable TColumns (Rep (Record a))+ , Columns a ~ GColumns TColumns (Rep (Record a)) )- => Columns a (Dict (ConstrainDBType DBOrd))- ordTable =- ggtable- @(GAlgebra (Rep (Record a)))- @TOrdTable- @TColumns- @(Rep (Record a))- table- nullifier+ => Columns a (Dict (Sql DBOrd))+ ordTable = gtable @TOrdTable @TColumns @(Rep (Record a)) table where table (_ :: proxy x) = ordTable @x @@ -80,19 +62,8 @@ type instance Eval (TOrdTable a) = OrdTable a -instance- ( HTable t- , f ~ Col Expr- , HConstrainTable t (ConstrainDBType DBEq)- , HConstrainTable t (ConstrainDBType DBOrd)- )- => OrdTable (t f)- where- ordTable = hdicts @(Columns (t f)) @(ConstrainDBType DBOrd)-- instance Sql DBOrd a => OrdTable (Expr a) where- ordTable = HType Dict+ ordTable = HIdentity Dict instance (OrdTable a, OrdTable b) => OrdTable (a, b)@@ -127,7 +98,7 @@ (toColumns -> as) <: (toColumns -> bs) = foldr @[] go false $ getConst $ htabulateA $ \field -> case (hfield as field, hfield bs field) of- (E a, E b) -> case hfield (ordTable @a) field of+ (a, b) -> case hfield (ordTable @a) field of Dict -> Const [(a <. b, a ==. b)] where go (lt, eq) a = lt ||. (eq &&. a)@@ -140,7 +111,7 @@ (toColumns -> as) <=: (toColumns -> bs) = foldr @[] go true $ getConst $ htabulateA $ \field -> case (hfield as field, hfield bs field) of- (E a, E b) -> case hfield (ordTable @a) field of+ (a, b) -> case hfield (ordTable @a) field of Dict -> Const [(a <. b, a ==. b)] where go (lt, eq) a = lt ||. (eq &&. a)@@ -153,7 +124,7 @@ (toColumns -> as) >: (toColumns -> bs) = foldr @[] go false $ getConst $ htabulateA $ \field -> case (hfield as field, hfield bs field) of- (E a, E b) -> case hfield (ordTable @a) field of+ (a, b) -> case hfield (ordTable @a) field of Dict -> Const [(a >. b, a ==. b)] where go (gt, eq) a = gt ||. (eq &&. a)@@ -166,7 +137,7 @@ (toColumns -> as) >=: (toColumns -> bs) = foldr @[] go true $ getConst $ htabulateA $ \field -> case (hfield as field, hfield bs field) of- (E a, E b) -> case hfield (ordTable @a) field of+ (a, b) -> case hfield (ordTable @a) field of Dict -> Const [(a >. b, a ==. b)] where go (gt, eq) a = gt ||. (eq &&. a)
src/Rel8/Table/Order.hs view
@@ -1,4 +1,5 @@ {-# language DataKinds #-}+{-# language NamedFieldPuns #-} {-# language ScopedTypeVariables #-} {-# language TypeApplications #-} {-# language TypeFamilies #-}@@ -15,16 +16,14 @@ import Prelude -- rel8-import Rel8.Expr ( unE ) import Rel8.Expr.Order ( asc, desc, nullsFirst, nullsLast ) import Rel8.Order ( Order ) import Rel8.Schema.Dict ( Dict( Dict ) ) import Rel8.Schema.HTable (htabulateA, hfield, hspecs) import Rel8.Schema.Null ( Nullity( Null, NotNull ) )-import Rel8.Schema.Spec ( SSpec( SSpec ) )-import Rel8.Schema.Spec.ConstrainDBType ( dbTypeDict, dbTypeNullity )+import Rel8.Schema.Spec ( Spec( Spec, nullity ) ) import Rel8.Table ( Columns, toColumns )-import Rel8.Table.Ord+import Rel8.Table.Ord ( OrdTable, ordTable ) -- | Construct an 'Order' for a 'Table' by sorting all columns into ascending@@ -32,12 +31,11 @@ ascTable :: forall a. OrdTable a => Order a ascTable = contramap toColumns $ getConst $ htabulateA @(Columns a) $ \field -> case hfield hspecs field of- SSpec {} -> case hfield (ordTable @a) field of- dict@Dict -> case dbTypeDict dict of- Dict -> Const $ unE . (`hfield` field) >$<- case dbTypeNullity dict of- Null -> nullsFirst asc- NotNull -> asc+ Spec {nullity} -> case hfield (ordTable @a) field of+ Dict -> Const $ (`hfield` field) >$<+ case nullity of+ Null -> nullsFirst asc+ NotNull -> asc -- | Construct an 'Order' for a 'Table' by sorting all columns into descending@@ -45,9 +43,8 @@ descTable :: forall a. OrdTable a => Order a descTable = contramap toColumns $ getConst $ htabulateA @(Columns a) $ \field -> case hfield hspecs field of- SSpec {} -> case hfield (ordTable @a) field of- dict@Dict -> case dbTypeDict dict of- Dict -> Const $ unE . (`hfield` field) >$<- case dbTypeNullity dict of- Null -> nullsLast desc- NotNull -> desc+ Spec {nullity} -> case hfield (ordTable @a) field of+ Dict -> Const $ (`hfield` field) >$<+ case nullity of+ Null -> nullsLast desc+ NotNull -> desc
+ src/Rel8/Table/Projection.hs view
@@ -0,0 +1,73 @@+{-# language FlexibleContexts #-}+{-# language FlexibleInstances #-}+{-# language MonoLocalBinds #-}+{-# language MultiParamTypeClasses #-}+{-# language StandaloneKindSignatures #-}+{-# language UndecidableInstances #-}++module Rel8.Table.Projection+ ( Projection+ , Projectable( project )+ , Biprojectable( biproject )+ , Projecting+ , apply+ )+where++-- base+import Data.Kind ( Constraint, Type )+import Prelude++-- rel8+import Rel8.Schema.Field ( Field( Field ), fields )+import Rel8.Schema.HTable ( hfield, htabulate )+import Rel8.Table ( Columns, Context, Transpose, toColumns )+import Rel8.Table.Transpose ( Transposes )+++-- | The constraint @'Projecting' a b@ ensures that @'Projection' a b@ is a+-- usable 'Projection'.+type Projecting :: Type -> Type -> Constraint+class+ ( Transposes (Context a) (Field a) a (Transpose (Field a) a)+ , Transposes (Context a) (Field a) b (Transpose (Field a) b)+ )+ => Projecting a b+instance+ ( Transposes (Context a) (Field a) a (Transpose (Field a) a)+ , Transposes (Context a) (Field a) b (Transpose (Field b) b)+ )+ => Projecting a b+++-- | A @'Projection' a b@s is a special type of function @a -> b@ whereby the+-- resulting @b@ is guaranteed to be composed only from columns contained in+-- @a@.+type Projection :: Type -> Type -> Type+type Projection a b = Transpose (Field a) a -> Transpose (Field a) b+++-- | @'Projectable' f@ means that @f@ is a kind of functor on 'Rel8.Table's+-- that allows the mapping of a 'Projection' over its underlying columns.+type Projectable :: (Type -> Type) -> Constraint+class Projectable f where+ -- | Map a 'Projection' over @f@.+ project :: Projecting a b+ => Projection a b -> f a -> f b+++-- | @'Biprojectable' p@ means that @p@ is a kind of bifunctor on+-- 'Rel8.Table's that allows the mapping of a pair of 'Projection's over its+-- underlying columns.+type Biprojectable :: (Type -> Type -> Type) -> Constraint+class Biprojectable p where+ -- | Map a pair of 'Projection's over @p@.+ biproject :: (Projecting a b, Projecting c d)+ => Projection a b -> Projection c d -> p a c -> p b d+++apply :: Projecting a b+ => Projection a b -> Columns a context -> Columns b context+apply f a = case toColumns (f fields) of+ bs -> htabulate $ \field -> case hfield bs field of+ Field field' -> hfield a field'
− src/Rel8/Table/Recontextualize.hs
@@ -1,90 +0,0 @@-{-# language DataKinds #-}-{-# language FlexibleInstances #-}-{-# language FunctionalDependencies #-}-{-# language StandaloneKindSignatures #-}-{-# language TypeFamilies #-}-{-# language UndecidableInstances #-}-{-# language UndecidableSuperClasses #-}--module Rel8.Table.Recontextualize- ( Recontextualize- )-where---- base-import Data.Functor.Identity ( Identity )-import Data.Kind ( Constraint, Type )-import Prelude ()---- rel8-import Rel8.Schema.Context ( Col )-import Rel8.Schema.Context.Label ( Labelable )-import Rel8.Schema.HTable ( HTable )-import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Null ( Sql )-import Rel8.Schema.Result ( Result )-import Rel8.Table ( Table, Congruent )-import Rel8.Type ( DBType )----- | @Recontextualize from to a b@ is evidence that the types @a@ and @b@ are--- related, and that @b@ is the same type as @a@, but after changing the--- context from the initial context @from@, to the new context @to@.-type Recontextualize :: K.Context -> K.Context -> Type -> Type -> Constraint-class- ( Table from a- , Table to b- , Congruent a b- , Recontextualize from from a a- , Recontextualize to to b b- , Recontextualize to from b a- ) => Recontextualize from to a b- | a -> from- , b -> to- , a to -> b- , b from -> a---instance Sql DBType a => Recontextualize Result Result (Identity a) (Identity a)---instance HTable t => Recontextualize from to (t (Col from)) (t (Col to))---instance- ( Recontextualize from to a1 b1- , Recontextualize from to a2 b2- , Labelable from- , Labelable to- )- => Recontextualize from to (a1, a2) (b1, b2)---instance- ( Recontextualize from to a1 b1- , Recontextualize from to a2 b2- , Recontextualize from to a3 b3- , Labelable from, Labelable to- )- => Recontextualize from to (a1, a2, a3) (b1, b2, b3)---instance- ( Recontextualize from to a1 b1- , Recontextualize from to a2 b2- , Recontextualize from to a3 b3- , Recontextualize from to a4 b4- , Labelable from, Labelable to- )- => Recontextualize from to (a1, a2, a3, a4) (b1, b2, b3, b4)---instance- ( Recontextualize from to a1 b1- , Recontextualize from to a2 b2- , Recontextualize from to a3 b3- , Recontextualize from to a4 b4- , Recontextualize from to a5 b5- , Labelable from, Labelable to- )- => Recontextualize from to (a1, a2, a3, a4, a5) (b1, b2, b3, b4, b5)
src/Rel8/Table/Rel8able.hs view
@@ -1,6 +1,6 @@ {-# language DataKinds #-} {-# language FlexibleInstances #-}-{-# language FunctionalDependencies #-}+{-# language MultiParamTypeClasses #-} {-# language ScopedTypeVariables #-} {-# language StandaloneKindSignatures #-} {-# language TypeApplications #-}@@ -15,134 +15,78 @@ where -- base-import Data.Kind ( Constraint, Type )-import Data.Type.Equality ( (:~:)( Refl ) )-import Prelude+import Prelude () -- rel8 import Rel8.Expr ( Expr ) import qualified Rel8.Kind.Algebra as K-import Rel8.Kind.Context- ( SContext( SReify )- , Reifiable, contextSing- , sLabelable, sReifiable- )+import Rel8.Kind.Context ( Reifiable, contextSing ) import Rel8.Generic.Rel8able ( Rel8able, Algebra , GColumns, gfromColumns, gtoColumns- , greify, gunreify+ , GFromExprs, gfromResult, gtoResult )-import Rel8.Schema.Context ( Col )-import Rel8.Schema.Context.Label ( Labelable )-import Rel8.Schema.Dict ( Dict( Dict ) ) import qualified Rel8.Schema.Kind as K import Rel8.Schema.HTable ( HConstrainTable, hdicts )-import Rel8.Schema.Reify ( hreify, hunreify, UnwrapReify )+import Rel8.Schema.Null ( Sql ) import Rel8.Schema.Result ( Result ) import Rel8.Table- ( Table, Columns, Context, Congruent, fromColumns, toColumns- , Unreify, reify, unreify+ ( Table, Columns, Context, fromColumns, toColumns+ , FromExprs, fromResult, toResult+ , Transpose )-import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType )-import Rel8.Table.ADT ( ADT( ADT ), ADTable, fromADT, toADT )+import Rel8.Table.ADT ( ADT ) import Rel8.Table.Eq ( EqTable, eqTable )-import Rel8.Table.HKD ( HKD ) import Rel8.Table.Ord ( OrdTable, ordTable )-import Rel8.Table.Recontextualize ( Recontextualize )-import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )+import Rel8.Table.Serialize ( ToExprs ) import Rel8.Type.Eq ( DBEq ) import Rel8.Type.Ord ( DBOrd ) -instance (Rel8able t, Labelable context, Reifiable context) =>- Table context (t context)+instance (Rel8able t, Reifiable context, context ~ context') =>+ Table context' (t context) where type Columns (t context) = GColumns t type Context (t context) = context- type Unreify (t context) = t (UnwrapReify context)-- fromColumns = gunreify . gfromColumns . hreify- toColumns = hunreify . gtoColumns . greify-- reify Refl = case contextSing @context of- SReify context -> case sLabelable context of- Dict -> case sReifiable context of- Dict -> greify-- unreify Refl = case contextSing @context of- SReify context -> case sLabelable context of- Dict -> case sReifiable context of- Dict -> gunreify-+ type FromExprs (t context) = GFromExprs t+ type Transpose to (t context) = t to -instance- ( Rel8able t- , Labelable from, Reifiable from- , Labelable to, Reifiable to- , Congruent (t from) (t to)- )- => Recontextualize from to (t from) (t to)+ fromColumns = gfromColumns contextSing+ toColumns = gtoColumns contextSing+ fromResult = gfromResult @t+ toResult = gtoResult @t instance ( context ~ Expr , Rel8able t- , HConstrainTable (Columns (t context)) (ConstrainDBType DBEq)+ , HConstrainTable (Columns (t context)) (Sql DBEq) ) => EqTable (t context) where- eqTable = hdicts @(Columns (t context)) @(ConstrainDBType DBEq)+ eqTable = hdicts @(Columns (t context)) @(Sql DBEq) instance ( context ~ Expr , Rel8able t- , HConstrainTable (Columns (t context)) (ConstrainDBType DBEq)- , HConstrainTable (Columns (t context)) (ConstrainDBType DBOrd)+ , HConstrainTable (Columns (t context)) (Sql DBEq)+ , HConstrainTable (Columns (t context)) (Sql DBOrd) ) => OrdTable (t context) where- ordTable = hdicts @(Columns (t context)) @(ConstrainDBType DBOrd)---type instance FromExprs (t Expr) = FromExprs' t+ ordTable = hdicts @(Columns (t context)) @(Sql DBOrd) instance- ( x ~ t' Expr+ ( Rel8able t', t' ~ Choose (Algebra t) t+ , x ~ t' Expr , result ~ Result- , ToExprs' (Algebra t) t' t ) => ToExprs x (t result)- where- fromResult = fromResult' @(Algebra t) @t'- toResult = toResult' @(Algebra t) @t' -type FromExprs' :: K.Rel8able -> Type-type family FromExprs' t where- FromExprs' (ADT t) = t Result- FromExprs' (HKD a) = a- FromExprs' t = t Result---type ToExprs' :: K.Algebra -> K.Rel8able -> K.Rel8able -> Constraint-class (algebra ~ Algebra t, Rel8able t') =>- ToExprs' algebra t' t | algebra t -> t'- where- fromResult' :: GColumns t' (Col Result) -> t Result- toResult' :: t Result -> GColumns t' (Col Result)---instance (Algebra t ~ 'K.Product, Rel8able t, t ~ t') =>- ToExprs' 'K.Product t' t- where- fromResult' = fromColumns- toResult' = toColumns---instance (Algebra t ~ 'K.Sum, ADTable t, t' ~ ADT t) =>- ToExprs' 'K.Sum t' t- where- fromResult' = fromADT . ADT- toResult' = (\(ADT a) -> a) . toADT+type Choose :: K.Algebra -> K.Rel8able -> K.Rel8able+type family Choose algebra t where+ Choose 'K.Sum t = ADT t+ Choose 'K.Product t = t
src/Rel8/Table/Serialize.hs view
@@ -1,7 +1,4 @@ {-# language AllowAmbiguousTypes #-}-{-# language DataKinds #-}-{-# language DefaultSignatures #-}-{-# language DisambiguateRecordFields #-} {-# language FlexibleContexts #-} {-# language FlexibleInstances #-} {-# language FunctionalDependencies #-}@@ -10,41 +7,31 @@ {-# language StandaloneKindSignatures #-} {-# language TypeApplications #-} {-# language TypeFamilies #-}-{-# language TypeOperators #-} {-# language UndecidableInstances #-} module Rel8.Table.Serialize- ( Serializable, lit, parse- , ToExprs(..), FromExprs- , TToExprs+ ( Serializable, lit, litHTable, parse+ , ToExprs ) where -- base+import Data.Functor.Identity ( Identity( Identity ) ) import Data.Kind ( Constraint, Type ) import Data.List.NonEmpty ( NonEmpty )-import GHC.Generics ( Generic, Rep, from, to ) import Prelude -- hasql import qualified Hasql.Decoders as Hasql -- rel8-import Rel8.Expr ( Expr, Col( E ) )+import Rel8.Expr ( Expr ) import Rel8.Expr.Serialize ( slitExpr, sparseValue )-import Rel8.FCF ( Eval, Exp )-import Rel8.Generic.Record ( Record(..) )-import Rel8.Generic.Table- ( GGToExprs, GGColumns, ggfromResult, ggtoResult- , GAlgebra- )-import Rel8.Kind.Algebra ( KnownAlgebra ) import Rel8.Schema.HTable ( HTable, htabulate, htabulateA, hfield, hspecs )-import Rel8.Schema.HTable.Identity ( HIdentity( HType ) ) import Rel8.Schema.Null ( NotNull, Sql )-import Rel8.Schema.Result ( Col( R ), Result )-import Rel8.Schema.Spec ( SSpec(..), KnownSpec )-import Rel8.Table ( Table, Columns, fromColumns, toColumns, TColumns )+import Rel8.Schema.Result ( Result )+import Rel8.Schema.Spec ( Spec(..) )+import Rel8.Table ( Table, fromColumns, FromExprs, fromResult, toResult ) import Rel8.Type ( DBType ) -- semigroupoids@@ -53,71 +40,21 @@ -- | @ToExprs exprs a@ is evidence that the types @exprs@ and @a@ describe -- essentially the same type, but @exprs@ is in the 'Expr' context, and @a@ is--- in the 'Result' context.+-- a normal Haskell type. type ToExprs :: Type -> Type -> Constraint-class Table Expr exprs => ToExprs exprs a where- fromResult :: Columns exprs (Col Result) -> a- toResult :: a -> Columns exprs (Col Result)-- default fromResult ::- ( Generic (Record a)- , KnownAlgebra (GAlgebra (Rep (Record exprs)))- , Eval (GGToExprs (GAlgebra (Rep (Record exprs))) TToExprs TColumns (Rep (Record exprs)) (Rep (Record a)))- , Columns exprs ~ Eval (GGColumns (GAlgebra (Rep (Record exprs))) TColumns (Rep (Record exprs)))- )- => Columns exprs (Col Result) -> a- fromResult =- unrecord .- to .- ggfromResult- @(GAlgebra (Rep (Record exprs)))- @TToExprs- @TColumns- @(Rep (Record exprs))- (\(_ :: proxy expr) -> fromResult @expr)-- default toResult ::- ( Generic (Record a)- , KnownAlgebra (GAlgebra (Rep (Record exprs)))- , Eval (GGToExprs (GAlgebra (Rep (Record exprs))) TToExprs TColumns (Rep (Record exprs)) (Rep (Record a)))- , Columns exprs ~ Eval (GGColumns (GAlgebra (Rep (Record exprs))) TColumns (Rep (Record exprs)))- )- => a -> Columns exprs (Col Result)- toResult =- ggtoResult- @(GAlgebra (Rep (Record exprs)))- @TToExprs- @TColumns- @(Rep (Record exprs))- (\(_ :: proxy expr) -> toResult @expr) .- from .- Record---data TToExprs :: Type -> Type -> Exp Constraint-type instance Eval (TToExprs exprs a) = ToExprs exprs a+class Table Expr exprs => ToExprs exprs a -instance {-# OVERLAPPABLE #-} (Sql DBType a, x ~ Expr a) => ToExprs x a where- fromResult (HType (R a)) = a- toResult = HType . R+instance {-# OVERLAPPABLE #-} (Sql DBType a, x ~ Expr a) => ToExprs x a -instance (Sql DBType a, x ~ [a]) => ToExprs (Expr x) [a] where- fromResult (HType (R a)) = a- toResult = HType . R+instance (Sql DBType a, x ~ [a]) => ToExprs (Expr x) [a] instance (Sql DBType a, NotNull a, x ~ Maybe a) => ToExprs (Expr x) (Maybe a)- where- fromResult (HType (R a)) = a- toResult = HType . R instance (Sql DBType a, NotNull a, x ~ NonEmpty a) => ToExprs (Expr x) (NonEmpty a)- where- fromResult (HType (R a)) = a- toResult = HType . R instance (ToExprs exprs1 a, ToExprs exprs2 b, x ~ (exprs1, exprs2)) =>@@ -179,43 +116,6 @@ => ToExprs x (a, b, c, d, e, f, g) -instance (HTable t, result ~ Col Result, x ~ t (Col Expr)) =>- ToExprs x (t result)- where- fromResult = id- toResult = id---instance (KnownSpec spec, x ~ Col Expr spec) =>- ToExprs x (Col Result spec)- where- fromResult = fromColumns- toResult = toColumns----- | The @FromExprs@ type function maps a type in the @Expr@ context to the--- corresponding type in the @Result@ context.-type FromExprs :: Type -> Type-type family FromExprs a-type instance FromExprs (Expr a) = a-type instance FromExprs (Col Expr spec) = Col Result spec-type instance FromExprs (a, b) = (FromExprs a, FromExprs b)-type instance FromExprs (a, b, c) = (FromExprs a, FromExprs b, FromExprs c)-type instance FromExprs (a, b, c, d) =- (FromExprs a, FromExprs b, FromExprs c, FromExprs d)-type instance FromExprs (a, b, c, d, e) =- (FromExprs a, FromExprs b, FromExprs c, FromExprs d, FromExprs e)-type instance FromExprs (a, b, c, d, e, f) =- ( FromExprs a, FromExprs b, FromExprs c, FromExprs d, FromExprs e- , FromExprs f- )-type instance FromExprs (a, b, c, d, e, f, g) =- ( FromExprs a, FromExprs b, FromExprs c, FromExprs d, FromExprs e- , FromExprs f, FromExprs g- )-type instance FromExprs (t (Col Expr)) = t (Col Result)-- -- | @Serializable@ witnesses the one-to-one correspondence between the type -- @sql@, which contains SQL expressions, and the type @haskell@, which -- contains the Haskell decoding of rows containing @sql@ SQL expressions.@@ -228,21 +128,21 @@ -- | Use @lit@ to turn literal Haskell values into expressions. @lit@ is -- capable of lifting single @Expr@s to full tables. lit :: forall exprs a. Serializable exprs a => a -> exprs-lit = fromColumns . litHTable . toResult @exprs+lit = fromColumns . litHTable . toResult @_ @exprs parse :: forall exprs a. Serializable exprs a => Hasql.Row a-parse = fromResult @exprs <$> parseHTable+parse = fromResult @_ @exprs <$> parseHTable -litHTable :: HTable t => t (Col Result) -> t (Col Expr)+litHTable :: HTable t => t Result -> t Expr litHTable as = htabulate $ \field -> case hfield hspecs field of- SSpec {nullity, info} -> case hfield as field of- R value -> E (slitExpr nullity info value)+ Spec {nullity, info} -> case hfield as field of+ Identity value -> slitExpr nullity info value -parseHTable :: HTable t => Hasql.Row (t (Col Result))+parseHTable :: HTable t => Hasql.Row (t Result) parseHTable = unwrapApplicative $ htabulateA $ \field -> WrapApplicative $ case hfield hspecs field of- SSpec {nullity, info} -> R <$> sparseValue nullity info+ Spec {nullity, info} -> Identity <$> sparseValue nullity info
− src/Rel8/Table/Tag.hs
@@ -1,97 +0,0 @@-{-# language DataKinds #-}-{-# language FlexibleInstances #-}-{-# language GADTs #-}-{-# language InstanceSigs #-}-{-# language NamedFieldPuns #-}-{-# language ScopedTypeVariables #-}-{-# language StandaloneKindSignatures #-}-{-# language TypeApplications #-}-{-# language UndecidableInstances #-}--module Rel8.Table.Tag- ( Tag(..), Taggable- , fromAggregate- , fromExpr- , fromName- )-where---- base-import Control.Applicative ( (<|>), empty )-import Data.Kind ( Constraint, Type )-import Data.Foldable ( fold )-import Data.Monoid ( getFirst )-import Data.Proxy ( Proxy( Proxy ) )-import GHC.TypeLits ( KnownSymbol, Symbol, symbolVal )-import Prelude---- rel8-import Rel8.Aggregate ( Aggregate, Aggregator, foldInputs )-import Rel8.Expr ( Expr )-import Rel8.Expr.Opaleye ( fromPrimExpr )-import Rel8.Schema.Name ( Name( Name ) )-import Rel8.Schema.Null ( Sql )-import Rel8.Type.Monoid ( DBMonoid )---type Tag :: Symbol -> Type -> Type-data Tag label a = Tag- { expr :: Expr a- , aggregator :: Maybe Aggregator- , name :: Name a- }---type Taggable :: Type -> Constraint-class Taggable a where- tappend :: KnownSymbol label => Tag label a -> Tag label a -> Tag label a- tempty :: KnownSymbol label => Tag label a---instance Sql DBMonoid a => Taggable a where- tappend :: forall label. KnownSymbol label- => Tag label a -> Tag label a -> Tag label a- tappend a b = Tag- { expr = expr a <> expr b- , aggregator = aggregator a <|> aggregator b- , name = case (name a, symbolVal (Proxy @label)) of- (Name x, y)- | x == y -> name b- | otherwise -> name a- }- {-# INLINABLE tappend #-}-- tempty :: forall label. KnownSymbol label => Tag label a- tempty = Tag- { expr = mempty- , aggregator = empty- , name = Name (symbolVal (Proxy @label))- }- {-# INLINABLE tempty #-}---instance (KnownSymbol label, Taggable a) => Semigroup (Tag label a) where- (<>) = tappend---instance (KnownSymbol label, Taggable a) => Monoid (Tag label a) where- mempty = tempty---fromAggregate :: forall a label. (KnownSymbol label, Taggable a)- => Aggregate a -> Tag label a-fromAggregate = fold . getFirst . foldInputs go- where- go aggregator primExpr = pure $ (tempty @a @label)- { expr = fromPrimExpr primExpr- , aggregator- }---fromExpr :: forall label a. (KnownSymbol label, Taggable a)- => Expr a -> Tag label a-fromExpr expr = (tempty @a @label) {expr}---fromName :: forall a label. Taggable a => Name a -> Tag label a-fromName name = (tempty @a @"") {name}
src/Rel8/Table/These.hs view
@@ -5,7 +5,6 @@ {-# language FlexibleInstances #-} {-# language MultiParamTypeClasses #-} {-# language NamedFieldPuns #-}-{-# language RecordWildCards #-} {-# language ScopedTypeVariables #-} {-# language StandaloneKindSignatures #-} {-# language TupleSections #-}@@ -21,52 +20,48 @@ , isThisTable, isThatTable, isThoseTable , hasHereTable, hasThereTable , justHereTable, justThereTable+ , aggregateTheseTable , nameTheseTable ) where -- base-import Control.Applicative ( liftA2 ) import Data.Bifunctor ( Bifunctor, bimap )-import Data.Functor.Identity ( runIdentity ) import Data.Kind ( Type )+import Data.Maybe ( isJust ) import Prelude hiding ( undefined ) -- rel8+import Rel8.Aggregate ( Aggregate ) import Rel8.Expr ( Expr ) import Rel8.Expr.Bool ( (&&.), not_ ) import Rel8.Expr.Null ( isNonNull )-import Rel8.Schema.Context.Label- ( Labelable- , HLabelable, hlabeler, hunlabeler- )-import Rel8.Schema.Context.Nullify- ( Nullifiable, ConstrainTag- , HNullifiable, HConstrainTag- , hencodeTag, hdecodeTag- , hnullifier, hunnullifier- )-import Rel8.Schema.HTable ( HTable )-import Rel8.Schema.HTable.Label ( hlabel, hunlabel )+import Rel8.Kind.Context ( Reifiable )+import Rel8.Schema.Context.Nullify ( Nullifiable )+import Rel8.Schema.Dict ( Dict( Dict ) )+import Rel8.Schema.HTable.Label ( hlabel, hrelabel, hunlabel ) import Rel8.Schema.HTable.Identity ( HIdentity(..) )-import Rel8.Schema.HTable.Nullify ( hnullify, hunnullify )+import Rel8.Schema.HTable.Maybe ( HMaybeTable(..) ) import Rel8.Schema.HTable.These ( HTheseTable(..) )+import qualified Rel8.Schema.Kind as K import Rel8.Schema.Name ( Name ) import Rel8.Table ( Table, Columns, Context, fromColumns, toColumns- , reify, unreify+ , FromExprs, fromResult, toResult+ , Transpose ) import Rel8.Table.Eq ( EqTable, eqTable ) import Rel8.Table.Maybe ( MaybeTable(..) , maybeTable, justTable, nothingTable , isJustTable+ , aggregateMaybeTable , nameMaybeTable )+import Rel8.Table.Nullify ( Nullify, guard ) import Rel8.Table.Ord ( OrdTable, ordTable )-import Rel8.Table.Recontextualize ( Recontextualize )-import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )-import Rel8.Table.Tag ( Tag(..) )+import Rel8.Table.Projection ( Biprojectable, Projectable, biproject, project )+import Rel8.Table.Serialize ( ToExprs ) import Rel8.Table.Undefined ( undefined ) import Rel8.Type.Tag ( MaybeTag ) @@ -75,7 +70,8 @@ import Data.Functor.Bind ( Bind, (>>-) ) -- these-import Data.These ( These )+import Data.These ( These( This, That, These ) )+import Data.These.Combinators ( justHere, justThere ) -- | @TheseTable a b@ is a Rel8 table that contains either the table @a@, the@@ -85,32 +81,45 @@ -- -- @TheseTable@ is operationally the same as Haskell's 'These' type, but -- adapted to work with Rel8.-type TheseTable :: Type -> Type -> Type-data TheseTable a b = TheseTable- { here :: MaybeTable a- , there :: MaybeTable b+type TheseTable :: K.Context -> Type -> Type -> Type+data TheseTable context a b = TheseTable+ { here :: MaybeTable context a+ , there :: MaybeTable context b } deriving stock Functor -instance Bifunctor TheseTable where+instance Biprojectable (TheseTable context) where+ biproject f g (TheseTable a b) = TheseTable (project f a) (project g b)+++instance Nullifiable context => Bifunctor (TheseTable context) where bimap f g (TheseTable a b) = TheseTable (fmap f a) (fmap g b) -instance (Table Expr a, Semigroup a) => Apply (TheseTable a) where+instance Projectable (TheseTable context a) where+ project f (TheseTable a b) = TheseTable a (project f b)+++instance (context ~ Expr, Table Expr a, Semigroup a) =>+ Apply (TheseTable context a)+ where fs <.> as = TheseTable { here = here fs <> here as , there = there fs <.> there as } -instance (Table Expr a, Semigroup a) => Applicative (TheseTable a)+instance (context ~ Expr, Table Expr a, Semigroup a) =>+ Applicative (TheseTable context a) where pure = thatTable (<*>) = (<.>) -instance (Table Expr a, Semigroup a) => Bind (TheseTable a) where+instance (context ~ Expr, Table Expr a, Semigroup a) =>+ Bind (TheseTable context a)+ where TheseTable here1 ma >>- f = case ma >>- f' of mtb -> TheseTable { here = maybeTable here1 ((here1 <>) . fst) mtb@@ -121,12 +130,14 @@ TheseTable here2 mb -> (here2,) <$> mb -instance (Table Expr a, Semigroup a) => Monad (TheseTable a) where+instance (context ~ Expr, Table Expr a, Semigroup a) =>+ Monad (TheseTable context a)+ where (>>=) = (>>-) -instance (Table Expr a, Table Expr b, Semigroup a, Semigroup b) =>- Semigroup (TheseTable a b)+instance (context ~ Expr, Table Expr a, Table Expr b, Semigroup a, Semigroup b) =>+ Semigroup (TheseTable context a b) where a <> b = TheseTable { here = here a <> here b@@ -136,124 +147,165 @@ instance ( Table context a, Table context b- , Labelable context, Nullifiable context, ConstrainTag context MaybeTag- ) => Table context (TheseTable a b)+ , Reifiable context, context ~ context'+ )+ => Table context' (TheseTable context a b) where- type Columns (TheseTable a b) = HTheseTable (Columns a) (Columns b)- type Context (TheseTable a b) = Context a-- toColumns = toColumns2 toColumns toColumns- fromColumns = fromColumns2 fromColumns fromColumns- reify = liftA2 bimap reify reify- unreify = liftA2 bimap unreify unreify---instance- ( Labelable from, Nullifiable from, ConstrainTag from MaybeTag- , Labelable to, Nullifiable to, ConstrainTag to MaybeTag- , Recontextualize from to a1 b1- , Recontextualize from to a2 b2- ) =>- Recontextualize from to (TheseTable a1 a2) (TheseTable b1 b2)---instance (EqTable a, EqTable b) => EqTable (TheseTable a b) where- eqTable = toColumns2 id id (thoseTable (eqTable @a) (eqTable @b))+ type Columns (TheseTable context a b) = HTheseTable (Columns a) (Columns b)+ type Context (TheseTable context a b) = Context a+ type FromExprs (TheseTable context a b) =+ These (FromExprs a) (FromExprs b)+ type Transpose to (TheseTable context a b) =+ TheseTable to (Transpose to a) (Transpose to b) + toColumns TheseTable {here, there} = HTheseTable+ { hhereTag = hlabel $ HIdentity $ tag here+ , hhere =+ hlabel $ guard (tag here) isJust isNonNull $ toColumns $ just here+ , hthereTag = hlabel $ HIdentity $ tag there+ , hthere =+ hlabel $ guard (tag there) isJust isNonNull $ toColumns $ just there+ } -instance (OrdTable a, OrdTable b) => OrdTable (TheseTable a b) where- ordTable = toColumns2 id id (thoseTable (ordTable @a) (ordTable @b))+ fromColumns HTheseTable {hhereTag, hhere, hthereTag, hthere} = TheseTable+ { here = MaybeTable+ { tag = unHIdentity $ hunlabel hhereTag+ , just = fromColumns $ hunlabel hhere+ }+ , there = MaybeTable+ { tag = unHIdentity $ hunlabel hthereTag+ , just = fromColumns $ hunlabel hthere+ }+ } + toResult tables = HTheseTable+ { hhereTag = hrelabel hhereTag+ , hhere = hrelabel hhere+ , hthereTag = hrelabel hthereTag+ , hthere = hrelabel hthere+ }+ where+ HMaybeTable+ { htag = hhereTag+ , hjust = hhere+ } = toResult @_ @(MaybeTable context a) (justHere tables)+ HMaybeTable+ { htag = hthereTag+ , hjust = hthere+ } = toResult @_ @(MaybeTable context b) (justThere tables) -type instance FromExprs (TheseTable a b) = These (FromExprs a) (FromExprs b)+ fromResult HTheseTable {hhereTag, hhere, hthereTag, hthere} =+ case (here, there) of+ (Just a, Nothing) -> This a+ (Nothing, Just b) -> That b+ (Just a, Just b) -> These a b+ _ -> error "These.fromColumns: mismatch between tags and data"+ where+ here = fromResult @_ @(MaybeTable context a) mhere+ there = fromResult @_ @(MaybeTable context b) mthere+ mhere = HMaybeTable+ { htag = hrelabel hhereTag+ , hjust = hrelabel hhere+ }+ mthere = HMaybeTable+ { htag = hrelabel hthereTag+ , hjust = hrelabel hthere+ } -instance (ToExprs exprs1 a, ToExprs exprs2 b, x ~ TheseTable exprs1 exprs2) =>- ToExprs x (These a b)+instance (EqTable a, EqTable b, context ~ Expr) =>+ EqTable (TheseTable context a b) where- fromResult =- bimap (fromResult @exprs1) (fromResult @exprs2) .- fromColumns- toResult =- toColumns .- bimap (toResult @exprs1) (toResult @exprs2)+ eqTable = HTheseTable+ { hhereTag = hlabel (HIdentity Dict)+ , hhere = hlabel (eqTable @(Nullify context a))+ , hthereTag = hlabel (HIdentity Dict)+ , hthere = hlabel (eqTable @(Nullify context b))+ } -toHereTag :: Tag "isJust" a -> Tag "hasHere" a-toHereTag Tag {..} = Tag {..}+instance (OrdTable a, OrdTable b, context ~ Expr) =>+ OrdTable (TheseTable context a b)+ where+ ordTable = HTheseTable+ { hhereTag = hlabel (HIdentity Dict)+ , hhere = hlabel (ordTable @(Nullify context a))+ , hthereTag = hlabel (HIdentity Dict)+ , hthere = hlabel (ordTable @(Nullify context b))+ } -toThereTag :: Tag "isJust" a -> Tag "hasThere" a-toThereTag Tag {..} = Tag {..}+instance (ToExprs exprs1 a, ToExprs exprs2 b, x ~ TheseTable Expr exprs1 exprs2) =>+ ToExprs x (These a b) -- | Test if a 'TheseTable' was constructed with 'thisTable'. -- -- Corresponds to 'Data.These.Combinators.isThis'.-isThisTable :: TheseTable a b -> Expr Bool+isThisTable :: TheseTable Expr a b -> Expr Bool isThisTable a = hasHereTable a &&. not_ (hasThereTable a) -- | Test if a 'TheseTable' was constructed with 'thatTable'. -- -- Corresponds to 'Data.These.Combinators.isThat'.-isThatTable :: TheseTable a b -> Expr Bool+isThatTable :: TheseTable Expr a b -> Expr Bool isThatTable a = not_ (hasHereTable a) &&. hasThereTable a -- | Test if a 'TheseTable' was constructed with 'thoseTable'. -- -- Corresponds to 'Data.These.Combinators.isThese'.-isThoseTable :: TheseTable a b -> Expr Bool+isThoseTable :: TheseTable Expr a b -> Expr Bool isThoseTable a = hasHereTable a &&. hasThereTable a -- | Test if the @a@ side of @TheseTable a b@ is present. -- -- Corresponds to 'Data.These.Combinators.hasHere'.-hasHereTable :: TheseTable a b -> Expr Bool+hasHereTable :: TheseTable Expr a b -> Expr Bool hasHereTable TheseTable {here} = isJustTable here -- | Test if the @b@ table of @TheseTable a b@ is present. -- -- Corresponds to 'Data.These.Combinators.hasThere'.-hasThereTable :: TheseTable a b -> Expr Bool+hasThereTable :: TheseTable Expr a b -> Expr Bool hasThereTable TheseTable {there} = isJustTable there -- | Attempt to project out the @a@ table of a @TheseTable a b@. -- -- Corresponds to 'Data.These.Combinators.justHere'.-justHereTable :: TheseTable a b -> MaybeTable a+justHereTable :: TheseTable context a b -> MaybeTable context a justHereTable = here -- | Attempt to project out the @b@ table of a @TheseTable a b@. -- -- Corresponds to 'Data.These.Combinators.justThere'.-justThereTable :: TheseTable a b -> MaybeTable b+justThereTable :: TheseTable context a b -> MaybeTable context b justThereTable = there -- | Construct a @TheseTable@. Corresponds to 'This'.-thisTable :: Table Expr b => a -> TheseTable a b+thisTable :: Table Expr b => a -> TheseTable Expr a b thisTable a = TheseTable (justTable a) nothingTable -- | Construct a @TheseTable@. Corresponds to 'That'.-thatTable :: Table Expr a => b -> TheseTable a b+thatTable :: Table Expr a => b -> TheseTable Expr a b thatTable b = TheseTable nothingTable (justTable b) -- | Construct a @TheseTable@. Corresponds to 'These'.-thoseTable :: a -> b -> TheseTable a b+thoseTable :: a -> b -> TheseTable Expr a b thoseTable a b = TheseTable (justTable a) (justTable b) -- | Pattern match on a 'TheseTable'. Corresponds to 'these'. theseTable :: Table Expr c- => (a -> c) -> (b -> c) -> (a -> b -> c) -> TheseTable a b -> c+ => (a -> c) -> (b -> c) -> (a -> b -> c) -> TheseTable Expr a b -> c theseTable f g h TheseTable {here, there} = maybeTable (maybeTable undefined f here)@@ -261,6 +313,19 @@ there +-- | Lift a pair of aggregating functions to operate on an 'TheseTable'.+-- @thisTable@s, @thatTable@s and @thoseTable@s are grouped separately.+aggregateTheseTable :: ()+ => (exprs -> aggregates)+ -> (exprs' -> aggregates')+ -> TheseTable Expr exprs exprs'+ -> TheseTable Aggregate aggregates aggregates'+aggregateTheseTable f g (TheseTable here there) = TheseTable+ { here = aggregateMaybeTable f here+ , there = aggregateMaybeTable g there+ }++ -- | Construct a 'TheseTable' in the 'Name' context. This can be useful if you -- have a 'TheseTable' that you are storing in a table and need to construct a -- 'TableSchema'.@@ -273,69 +338,9 @@ -- ^ Names of the columns in the @a@ table. -> b -- ^ Names of the columns in the @b@ table.- -> TheseTable a b+ -> TheseTable Name a b nameTheseTable here there a b = TheseTable { here = nameMaybeTable here a , there = nameMaybeTable there b }---toColumns2 ::- ( HTable t- , HTable u- , HConstrainTag context MaybeTag- , HLabelable context- , HNullifiable context- )- => (a -> t context)- -> (b -> u context)- -> TheseTable a b- -> HTheseTable t u context-toColumns2 f g TheseTable {here, there} = HTheseTable- { hhereTag = HIdentity $ hencodeTag (toHereTag (tag here))- , hhere =- hlabel hlabeler $ hnullify (hnullifier (tag here) isNonNull) $ f (just here)- , hthereTag = HIdentity $ hencodeTag (toThereTag (tag there))- , hthere =- hlabel hlabeler $ hnullify (hnullifier (tag there) isNonNull) $ g (just there)- }---fromColumns2 ::- ( HTable t- , HTable u- , HConstrainTag context MaybeTag- , HLabelable context- , HNullifiable context- )- => (t context -> a)- -> (u context -> b)- -> HTheseTable t u context- -> TheseTable a b-fromColumns2 f g HTheseTable {hhereTag, hhere, hthereTag, hthere} = TheseTable- { here =- let- tag = hdecodeTag $ unHIdentity hhereTag- in- MaybeTable- { tag- , just = f $- runIdentity $- hunnullify (\a -> pure . hunnullifier a) $- hunlabel hunlabeler- hhere- }- , there =- let- tag = hdecodeTag $ unHIdentity hthereTag- in- MaybeTable- { tag- , just = g $- runIdentity $- hunnullify (\a -> pure . hunnullifier a) $- hunlabel hunlabeler- hthere- }- }
+ src/Rel8/Table/Transpose.hs view
@@ -0,0 +1,46 @@+{-# language DataKinds #-}+{-# language FlexibleInstances #-}+{-# language FunctionalDependencies #-}+{-# language StandaloneKindSignatures #-}+{-# language TypeFamilies #-}+{-# language UndecidableInstances #-}++module Rel8.Table.Transpose+ ( Transposes+ )+where++-- base+import Data.Kind ( Constraint, Type )+import Prelude ()++-- rel8+import qualified Rel8.Schema.Kind as K+import Rel8.Table ( Table, Transpose, Congruent )+++-- | @'Transposes' from to a b@ means that @a@ and @b@ are 'Table's, in the+-- @from@ and @to@ contexts respectively, which share the same underlying+-- structure. In other words, @b@ is a version of @a@ transposed from the+-- @from@ context to the @to@ context (and vice versa).+type Transposes :: K.Context -> K.Context -> Type -> Type -> Constraint+class+ ( Table from a+ , Table to b+ , Congruent a b+ , b ~ Transpose to a+ , a ~ Transpose from b+ )+ => Transposes from to a b+ | a -> from+ , b -> to+ , a to -> b+ , b from -> a+instance+ ( Table from a+ , Table to b+ , Congruent a b+ , b ~ Transpose to a+ , a ~ Transpose from b+ )+ => Transposes from to a b
src/Rel8/Table/Undefined.hs view
@@ -11,16 +11,16 @@ import Prelude hiding ( undefined ) -- rel8-import Rel8.Expr ( Expr, Col( E ) )+import Rel8.Expr ( Expr ) import Rel8.Expr.Null ( snull, unsafeUnnullify ) import Rel8.Schema.HTable ( htabulate, hfield, hspecs ) import Rel8.Schema.Null ( Nullity( Null, NotNull ) )-import Rel8.Schema.Spec ( SSpec(..) )+import Rel8.Schema.Spec ( Spec(..) ) import Rel8.Table ( Table, fromColumns ) undefined :: Table Expr a => a undefined = fromColumns $ htabulate $ \field -> case hfield hspecs field of- SSpec {nullity, info} -> case nullity of- Null -> E (snull info)- NotNull -> E (unsafeUnnullify (snull info))+ Spec {nullity, info} -> case nullity of+ Null -> snull info+ NotNull -> unsafeUnnullify (snull info)
− src/Rel8/Table/Unreify.hs
@@ -1,102 +0,0 @@-{-# language DataKinds #-}-{-# language FlexibleContexts #-}-{-# language FlexibleInstances #-}-{-# language GADTs #-}-{-# language LambdaCase #-}-{-# language MultiParamTypeClasses #-}-{-# language QuantifiedConstraints #-}-{-# language RankNTypes #-}-{-# language StandaloneKindSignatures #-}-{-# language TypeFamilies #-}-{-# language UndecidableInstances #-}---- | This module implements some machinery for implementing methods of the--- 'Table' class for a particular special (but important) class of polymorphic--- @Table@ types.------ This special case is characterised by a @newtype@ wrapper around a bare--- 'HTable' which is constructed by applying a type family to the polymorphic--- type variable.------ Examples of this class of @Table@ include @ListTable@ and @NonEmptyTable@.------ The tricky part about implementing @Table@ for these types is 'reify' and--- 'unreify'. There is no guarantee in general that @'Unreify' a@ is itself--- a @Table@, let alone a @Table@ with the same 'Columns' as @a@--- (e.g., @Unreify (AColumn Result Bool) = Bool@, and @Bool@ is not a--- @Table@)--module Rel8.Table.Unreify- ( Unreifiable, Unreifiability(..), unreifiability- , Unreifies- )-where---- base-import Data.Kind ( Constraint, Type )-import Prelude ()---- rel8-import Rel8.Aggregate ( Aggregate )-import Rel8.Expr ( Expr )-import Rel8.Kind.Context ( SContext(..), Reifiable, sReifiable )-import Rel8.Schema.Dict ( Dict( Dict ) )-import qualified Rel8.Schema.Kind as K-import Rel8.Schema.Name ( Name )-import Rel8.Schema.Reify ( Reify )-import Rel8.Schema.Result ( Result )-import Rel8.Table ( Table, Context, Congruent, Unreify )---type Unreifies :: K.Context -> Type -> Constraint-type family Unreifies context a where- Unreifies (Reify context) a = Unreifier context a- Unreifies _ _ = ()---type Unreifiable :: Type -> Constraint-class- ( Context a ~ Reify Aggregate => Unreifier Aggregate a- , Context a ~ Reify Expr => Unreifier Expr a- , Context a ~ Reify Name => Unreifier Name a- , (forall ctx. (Context a ~ Reify (Reify ctx), Reifiable ctx) => Unreifier (Reify ctx) a)- )- => Unreifiable a-instance- ( Context a ~ Reify Aggregate => Unreifier Aggregate a- , Context a ~ Reify Expr => Unreifier Expr a- , Context a ~ Reify Name => Unreifier Name a- , (forall ctx. (Context a ~ Reify (Reify ctx), Reifiable ctx) => Unreifier (Reify ctx) a)- )- => Unreifiable a---type Unreifier :: K.Context -> Type -> Constraint-class- ( Table context (Unreify a)- , Congruent a (Unreify a)- )- => Unreifier context a-instance- ( Table context (Unreify a)- , Congruent a (Unreify a)- )- => Unreifier context a---type Unreifiability :: K.Context -> Type -> Type-data Unreifiability context a where- UResult :: Unreifiability Result a- Unreifiability :: Unreifier context a- => SContext context -> Unreifiability context a---unreifiability :: (Context a ~ Reify context, Unreifiable a)- => SContext context -> Unreifiability context a-unreifiability = \case- SAggregate -> Unreifiability SAggregate- SExpr -> Unreifiability SExpr- SName -> Unreifiability SName- SResult -> UResult- SReify context -> case sReifiable context of- Dict -> Unreifiability (SReify context)
+ src/Rel8/Tabulate.hs view
@@ -0,0 +1,637 @@+{-# language FlexibleContexts #-}+{-# language MonoLocalBinds #-}+{-# language ScopedTypeVariables #-}+{-# language StandaloneKindSignatures #-}+{-# language TypeApplications #-}+{-# language TupleSections #-}+{-# language UndecidableInstances #-}++-- | "Rel8.Tabulate" provides an alternative API ('Tabulation') for writing+-- queries that complements the main "Rel8" API ('Query').++module Rel8.Tabulate+ (+ Tabulation++ -- * Interfacing with 'Query's+ , fromQuery+ , toQuery+ , liftQuery+ , through+ , lookup++ -- * Aggregation and Ordering+ , aggregate+ , distinct+ , order++ -- ** Magic 'Tabulation's+ -- $magic+ , count+ , optional+ , many+ , some+ , exists+ , present+ , absent++ -- * Natural joins+ , align+ , alignWith+ , leftAlign+ , leftAlignWith+ , rightAlign+ , rightAlignWith+ , zip+ , zipWith+ , similarity+ , difference+ )+where++-- base+import Control.Applicative ( (<|>), empty, liftA2 )+import Control.Monad ( liftM2 )+import Data.Bifunctor ( Bifunctor, bimap, first, second )+import Data.Foldable ( traverse_ )+import Data.Function ( on )+import Data.Functor.Contravariant ( Contravariant, (>$<), contramap )+import Data.Int ( Int64 )+import Data.Kind ( Type )+import Data.Maybe ( fromMaybe )+import Prelude hiding ( lookup, zip, zipWith )++-- bifunctors+import Data.Bifunctor.Clown ( Clown( Clown ), runClown )++-- comonad+import Control.Comonad ( extract )++-- opaleye+import qualified Opaleye.Aggregate as Opaleye+import qualified Opaleye.Internal.Order as Opaleye+import qualified Opaleye.Internal.QueryArr as Opaleye+import qualified Opaleye.Order as Opaleye ( orderBy )++-- profunctors+import Data.Profunctor ( dimap, lmap )++-- product-profunctors+import Data.Profunctor.Product+ ( ProductProfunctor, (***!)+ , SumProfunctor, (+++!)+ )+import qualified Data.Profunctor.Product as PP++-- rel8+import Rel8.Aggregate ( Aggregates )+import Rel8.Expr ( Expr )+import Rel8.Expr.Aggregate ( countStar )+import Rel8.Expr.Bool ( true )+import Rel8.Order ( Order( Order ) )+import Rel8.Query ( Query )+import qualified Rel8.Query.Exists as Q ( exists, present, absent )+import Rel8.Query.Filter ( where_ )+import Rel8.Query.Limit ( limit )+import Rel8.Query.List ( catNonEmptyTable )+import qualified Rel8.Query.Maybe as Q ( optional )+import Rel8.Query.Opaleye ( mapOpaleye, unsafePeekQuery )+import Rel8.Query.These ( alignBy )+import Rel8.Table ( Table, fromColumns, toColumns )+import Rel8.Table.Aggregate ( hgroupBy, listAgg, nonEmptyAgg )+import Rel8.Table.Alternative+ ( AltTable, (<|>:)+ , AlternativeTable, emptyTable+ )+import Rel8.Table.Cols ( fromCols, toCols )+import Rel8.Table.Eq ( EqTable, (==:), eqTable )+import Rel8.Table.List ( ListTable( ListTable ) )+import Rel8.Table.Maybe ( MaybeTable( MaybeTable ), maybeTable )+import Rel8.Table.NonEmpty ( NonEmptyTable( NonEmptyTable ) )+import Rel8.Table.Opaleye ( aggregator, unpackspec )+import Rel8.Table.Ord ( OrdTable )+import Rel8.Table.Order ( ascTable )+import Rel8.Table.Projection+ ( Biprojectable, biproject+ , Projectable, project+ , apply+ )+import Rel8.Table.These ( TheseTable( TheseTable ), theseTable )++-- semigroupoids+import Data.Functor.Apply ( Apply, liftF2 )+import Data.Functor.Bind ( Bind, (>>-) )+++type Key :: Type -> Type+type Key = Maybe+++cat :: Table Expr k => Key k -> Query k+cat = maybe emptyTable pure+++key :: (ProductProfunctor p, SumProfunctor p)+ => p a b -> p (Key a) (Key b)+key a = dimap from to (PP.empty +++! a)+ where+ from = maybe (Left ()) Right+ to = either (const Nothing) Just+++keyed :: (ProductProfunctor p, SumProfunctor p)+ => p k l -> p a b -> p (Key k, a) (Key l, b)+keyed k a = key k ***! a+++type Predicate :: Type -> Type+newtype Predicate a = Predicate (Maybe (a -> Expr Bool))+++instance Contravariant Predicate where+ contramap f (Predicate a) = Predicate (lmap f <$> a)+++instance Semigroup (Predicate k) where+ Predicate ma <> Predicate mb = Predicate $ ma <|> mb+++instance Monoid (Predicate k) where+ mempty = Predicate Nothing+++match :: EqTable k => Key k -> Predicate k+match = Predicate . fmap (==:)+++ensure :: Predicate k -> Key k -> Query ()+ensure (Predicate mp) = traverse_ (\k -> traverse_ (\p -> where_ (p k)) mp)+++-- | A @'Tabulation' k a@ is like a @'Query' a@, except that each row also+-- has a key @k@ in addition to the value @a@. 'Tabulation's can be composed+-- monadically just like 'Query's, but the resulting join is more like a+-- @NATURAL JOIN@ (based on the common key column(s) @k@) than the+-- @CROSS JOIN@ given by 'Query'.+--+-- Another way to think of @'Tabulation' k a@ is as analogous to @Map k a@ in+-- the same way @'Query' a@ is analogous to @[a]@. However, there's nothing+-- stopping a 'Tabulation' from containing multiple rows with the same key, so+-- technically @Map k (NonEmpty a)@ is more accurate.+--+-- 'Tabulation's can be created from 'Query's with 'fromQuery' and 'littQuery'+-- and converted back to 'Query's with 'lookup' and 'toQuery' (though note the+-- caveats that come with the latter).+type Tabulation :: Type -> Type -> Type+newtype Tabulation k a = Tabulation (Predicate k -> Query (Key k, a))+++instance Biprojectable Tabulation where+ biproject f g =+ bimap+ (fromColumns . apply f . toColumns)+ (fromColumns . apply g . toColumns)+++instance Bifunctor Tabulation where+ bimap f g (Tabulation a) = Tabulation $ \p ->+ bimap (fmap f) g <$> a (f >$< p)+++instance Functor (Tabulation k) where+ fmap = second+++instance Projectable (Tabulation k) where+ project f = fmap (fromColumns . apply f . toColumns)+++-- | If @'Tabulation' k a@ is @Map k (NonEmpty a)@, then @(<.>)@ is+-- @intersectionWith (liftA2 (<*>))@+instance EqTable k => Apply (Tabulation k) where+ liftF2 = liftA2+++-- | @pure = 'liftQuery' . pure@+instance EqTable k => Applicative (Tabulation k) where+ pure = liftQuery . pure+ liftA2 = liftM2+++instance EqTable k => Bind (Tabulation k) where+ Tabulation as >>- f = Tabulation $ \p -> do+ (k, a) <- as p+ case f a of+ Tabulation bs -> do+ let p' = match k+ (k', b) <- bs (p' <> p)+ ensure p' k'+ pure (k' <|> k, b)+++instance EqTable k => Monad (Tabulation k) where+ (>>=) = (>>-)+++-- | If @'Tabulation' k a@ is @Map k (NonEmpty a)@, then @(<|>:)@ is+-- @unionWith (<>)@.+instance EqTable k => AltTable (Tabulation k) where+ as <|>: bs = catNonEmptyTable `through` ((<>) `on` some) as bs+++instance EqTable k => AlternativeTable (Tabulation k) where+ emptyTable = Tabulation $ const $ fmap (empty,) emptyTable+++-- | If @'Tabulation' k a@ is @Map k (NonEmpty a)@, then @(<>)@ is+-- @unionWith (liftA2 (<>))@.+instance (EqTable k, Table Expr a, Semigroup a) => Semigroup (Tabulation k a)+ where+ (<>) = alignWith (theseTable id id (<>))+++instance (EqTable k, Table Expr a, Semigroup a) => Monoid (Tabulation k a)+ where+ mempty = emptyTable+++-- | Any 'Query' of key-value pairs @(k, a)@ can be a @'Tabulation' k a@.+fromQuery :: Query (k, a) -> Tabulation k a+fromQuery = Tabulation . const . fmap (first pure)+++-- | Convert a @'Tabulation' k a@ back into a 'Query' of key-value pairs.+--+-- Note that the result of a 'toQuery' is undefined (will always return zero+-- rows) on 'Tabulation's constructed with 'liftQuery' or 'pure'. So while+-- @toQuery . fromQuery@ is always @id@, @fromQuery . toQuery@ is not.+--+-- A safer, more predictable alternative to 'toQuery' is to use 'lookup' with+-- an explicit set of keys:+--+-- @+-- do+-- k <- keys+-- a <- lookup k tabulation+-- pure (k, a)+-- @+--+-- Having said that, in practice, most legitimate uses of 'Tabulation' will+-- have a well-defined 'toQuery'. It would be possible in theory to encode+-- the necessary invariants at the type level using an indexed monad, but we+-- would lose the ability to use @do@-notation, which is the main benefit+-- of having 'Tabulation' as a monad in the first place.+--+-- In particular, @'toQuery' t@ is well-defined for any 'Tabulation' @t@+-- defined as @t = fromQuery _@. @'toQuery' t@ is also well-defined for any+-- 'Tabulation' @t@ defined as @t = t' >>= _@ or @t = t' *> _@ where+-- @'toQuery' t'@ is well-defined. There are other valid permutations too.+-- Generally, anything that uses 'fromQuery' at some point, unless wrapped in+-- a top-level 'present' or 'absent', will have a well-defined 'toQuery'.+toQuery :: Table Expr k => Tabulation k a -> Query (k, a)+toQuery (Tabulation f) = do+ (mk, a) <- f mempty+ k <- cat mk+ pure (k, a)+++-- | A @'Query' a@ can be treated as a @'Tabulation' k a@ where the given @a@+-- values exist at every possible key @k@.+liftQuery :: Query a -> Tabulation k a+liftQuery = Tabulation . const . fmap (empty,)+++-- | Run a Kleisli arrow in the the 'Query' monad \"through\" a 'Tabulation'.+-- Useful for 'Rel8.filter'ing a 'Tabulation'.+--+-- @+-- 'Rel8.filter' ((>=. 30) . userAge) `'through'` usersById+-- @+through :: (a -> Query b) -> Tabulation k a -> Tabulation k b+through f (Tabulation as) = Tabulation $ \p -> do+ (k, a) <- as p+ b <- f a+ pure (k, b)+infixr 1 `through`+++-- | @'lookup' k t@ returns the value(s) at the key @k@ in the tabulation @t@.+lookup :: EqTable k => k -> Tabulation k a -> Query a+lookup k (Tabulation f) = do+ (k', a) <- f p+ ensure p k'+ pure a+ where+ p = match (pure k)+++-- | 'aggregate' aggregates the values within each key of a+-- 'Tabulation'. There is an implicit @GROUP BY@ on all the key columns.+aggregate :: forall k aggregates exprs.+ ( EqTable k+ , Aggregates aggregates exprs+ )+ => Tabulation k aggregates -> Tabulation k exprs+aggregate (Tabulation f) = Tabulation $+ mapOpaleye (Opaleye.aggregate (keyed haggregator aggregator)) .+ fmap (first (fmap (hgroupBy (eqTable @k) . toColumns))) .+ f+ where+ haggregator = dimap fromColumns fromCols aggregator+++-- | 'distinct' ensures a 'Tabulation' has at most one value for+-- each key, i.e., it drops duplicates. In general it keeps only the+-- \"first\" value it encounters for each key, but note that \"first\" is+-- undefined unless you first call 'order'.+distinct :: EqTable k => Tabulation k a -> Tabulation k a+distinct (Tabulation f) = Tabulation $ \p ->+ -- workaround for https://github.com/tomjaguarpaw/haskell-opaleye/pull/518+ case fst (unsafePeekQuery (f p)) of+ Nothing -> limit 1 (f p)+ Just _ ->+ mapOpaleye+ (\q ->+ Opaleye.productQueryArr+ ( Opaleye.distinctOn (key unpackspec) fst+ . Opaleye.runSimpleQueryArr q+ )+ )+ (f p)+++-- | 'order' orders the /values/ of a 'Tabulation' within their+-- respective keys. This specifies a defined order for 'distinct'.+-- It also defines the order of the lists produced by 'many' and+-- 'some'.+order :: OrdTable k => Order a -> Tabulation k a -> Tabulation k a+order ordering (Tabulation f) =+ Tabulation $ mapOpaleye (Opaleye.orderBy ordering') . f+ where+ Order ordering' = runClown (keyed (Clown ascTable) (Clown ordering))+++-- $magic+--+-- Some of the following combinators produce \"magic\" 'Tabulation's. Let's+-- use 'count' as an example to demonstrate this concept. Consider+-- the following:+--+-- @+-- count $ fromQuery $ values+-- [ (lit 'a', lit True)+-- , (lit 'a', lit False)+-- , (lit 'b', lit True)+-- ]+-- @+--+-- You might expect this to be equivalent to the following 'Tabulation':+--+-- @+-- fromQuery $ values+-- [ (lit 'a', 2)+-- , (lit 'b', 1)+-- ]+-- @+--+-- However, it isn't quite. While the resulting 'Tabulation' does effectively+-- contain the above entries, it also behaves as though it contained the value+-- @0@ at every other possible key.+--+-- This means you can do:+--+-- @+-- do+-- user <- usersById+-- orderCount <- count ordersByUserId+-- @+--+-- To see how many orders a user has (getting @0@ if they have no orders).+++-- | 'count' returns a count of how many entries are in the given+-- 'Tabulation' at each key.+--+-- The resulting 'Tabulation' is \"magic\" in that the value @0@ exists at+-- every possible key that wasn't in the given 'Tabulation'.+count :: EqTable k => Tabulation k a -> Tabulation k (Expr Int64)+count =+ fmap (maybeTable 0 id) .+ optional .+ aggregate .+ fmap (const countStar)+++-- | 'optional' produces a \"magic\" 'Tabulation' whereby each+-- entry in the given 'Tabulation' is wrapped in 'Rel8.justTable', and every+-- other possible key contains a single 'Rel8.nothingTable'.+--+-- This is used to implement 'leftAlignWith'.+optional :: Tabulation k a -> Tabulation k (MaybeTable Expr a)+optional (Tabulation f) = Tabulation $ \p -> case p of+ Predicate Nothing -> fmap pure <$> f p+ _ -> fmap (\m -> (empty, snd <$> m)) $ Q.optional $ do+ (k, a) <- f p+ ensure p k+ pure (k, a)+++-- | 'many' aggregates each entry with a particular key into a+-- single entry with all of the values contained in a 'ListTable'.+--+-- 'order' can be used to give this 'ListTable' a defined order.+--+-- The resulting 'Tabulation' is \"magic\" in that the value+-- @'Rel8.listTable []'@ exists at every possible key that wasn't in the given+-- 'Tabulation'.+many :: (EqTable k, Table Expr a)+ => Tabulation k a -> Tabulation k (ListTable Expr a)+many =+ fmap (maybeTable mempty (\(ListTable a) -> ListTable a)) .+ optional .+ aggregate .+ fmap (listAgg . toCols)+++-- | 'some' aggregates each entry with a particular key into a+-- single entry with all of the values contained in a 'NonEmptyTable'.+--+-- 'order' can be used to give this 'NonEmptyTable' a defined order.+some :: (EqTable k, Table Expr a)+ => Tabulation k a -> Tabulation k (NonEmptyTable Expr a)+some =+ fmap (\(NonEmptyTable a) -> NonEmptyTable a) .+ aggregate .+ fmap (nonEmptyAgg . toCols)+++-- | 'exists' produces a \"magic\" 'Tabulation' which contains the+-- value 'Rel8.true' at each key in the given 'Tabulation', and the value+-- 'Rel8.false' at every other possible key.+exists :: Tabulation k a -> Tabulation k (Expr Bool)+exists (Tabulation f) = Tabulation $ \p -> case p of+ Predicate Nothing -> (true <$) <$> f p+ _ -> fmap (empty,) $ Q.exists $ do+ (k, _) <- f p+ ensure p k+++-- | 'present' produces a 'Tabulation' where a single @()@ row+-- exists for every key that was present in the given 'Tabulation'.+--+-- This is used to implement 'similarity'.+present :: Tabulation k a -> Tabulation k ()+present (Tabulation f) = Tabulation $ \p -> do+ Q.present $ do+ (k, _) <- f p+ ensure p k+ pure (empty, ())+++-- | 'absent' produces a 'Tabulation' where a single @()@ row exists+-- at every possible key that absent from the given 'Tabulation'.+--+-- This is used to implement 'difference'.+absent :: Tabulation k a -> Tabulation k ()+absent (Tabulation f) = Tabulation $ \p -> do+ Q.absent $ do+ (k, _) <- f p+ ensure p k+ pure (empty, ())+++-- | Performs a @NATURAL FULL OUTER JOIN@ based on the common key columns.+--+-- Analogous to 'Data.Semialign.align'.+align :: EqTable k+ => Tabulation k a -> Tabulation k b -> Tabulation k (TheseTable Expr a b)+align = alignWith id+++-- | Performs a @NATURAL FULL OUTER JOIN@ based on the common key columns.+--+-- Analogous to 'Data.Semialign.alignWith'.+alignWith :: EqTable k+ => (TheseTable Expr a b -> c)+ -> Tabulation k a -> Tabulation k b -> Tabulation k c+alignWith f (Tabulation as) (Tabulation bs) = Tabulation $ \p -> do+ tkab <- liftF2 (alignBy condition) as bs p+ let+ k = recover $ bimap fst fst tkab+ tab = bimap snd snd tkab+ pure (k, f tab)+ where+ condition (k, _) (k', _) = fromMaybe true (liftA2 (==:) k k')+ recover (TheseTable mma@(MaybeTable _ ma) mmb@(MaybeTable _ mb)) =+ case extract ma of+ Nothing -> extract mb+ Just a -> case extract mb of+ Nothing -> extract ma+ Just b -> case a <$ mma <|>: b <$ mmb of+ MaybeTable _ c -> pure (extract c)+++-- | Performs a @NATURAL LEFT OUTER JOIN@ based on the common key columns.+--+-- Analogous to 'Data.Semialign.rpadZip'.+--+-- Note that you can achieve the same effect with 'optional' and the+-- 'Applicative' instance for 'Tabulation', i.e., this is just+-- @\left right -> liftA2 (,) left (optional right). You can also+-- use @do@-notation.+leftAlign :: EqTable k+ => Tabulation k a -> Tabulation k b -> Tabulation k (a, MaybeTable Expr b)+leftAlign = leftAlignWith (,)+++-- | Performs a @NATURAL LEFT OUTER JOIN@ based on the common key columns.+--+-- Analogous to 'Data.Semialign.rpadZipWith'.+--+-- Note that you can achieve the same effect with 'optional' and the+-- 'Applicative' instance for 'Tabulation', i.e., this is just+-- @\f left right -> liftA2 f left (optional right). You can also+-- use @do@-notation.+leftAlignWith :: EqTable k+ => (a -> MaybeTable Expr b -> c)+ -> Tabulation k a -> Tabulation k b -> Tabulation k c+leftAlignWith f left right = liftA2 f left (optional right)+++-- | Performs a @NATURAL RIGHT OUTER JOIN@ based on the common key columns.+--+-- Analogous to 'Data.Semialign.lpadZip'.+--+-- Note that you can achieve the same effect with 'optional' and the+-- 'Applicative' instance for 'Tabulation', i.e., this is just+-- @\left right -> liftA2 (flip (,)) right (optional left). You can+-- also use @do@-notation.+rightAlign :: EqTable k+ => Tabulation k a -> Tabulation k b -> Tabulation k (MaybeTable Expr a, b)+rightAlign = rightAlignWith (,)+++-- | Performs a @NATURAL RIGHT OUTER JOIN@ based on the common key columns.+--+-- Analogous to 'Data.Semialign.lpadZipWith'.+--+-- Note that you can achieve the same effect with 'optional' and the+-- 'Applicative' instance for 'Tabulation', i.e., this is just+-- @\f left right -> liftA2 (flip f) right (optional left). You can+-- also use @do@-notation.+rightAlignWith :: EqTable k+ => (MaybeTable Expr a -> b -> c)+ -> Tabulation k a -> Tabulation k b -> Tabulation k c+rightAlignWith f left right = liftA2 (flip f) right (optional left)+++-- | Performs a @NATURAL INNER JOIN@ based on the common key columns.+--+-- Analagous to 'Data.Semialign.zip'.+--+-- Note that you can achieve the same effect with the 'Applicative' instance+-- of 'Tabulation', i.e., this is just @'liftA2 (,)'@. You can also use+-- @do@-notation.+zip :: EqTable k+ => Tabulation k a -> Tabulation k b -> Tabulation k (a, b)+zip = zipWith (,)+++-- | Performs a @NATURAL INNER JOIN@ based on the common key columns.+--+-- Analagous to 'Data.Semialign.zipWith'.+--+-- Note that you can achieve the same effect with the 'Applicative' instance+-- of 'Tabulation', i.e., this is just @'liftA2'@. You can also use+-- @do@-notation.+zipWith :: EqTable k+ => (a -> b -> c) -> Tabulation k a -> Tabulation k b -> Tabulation k c+zipWith = liftA2+++-- | Performs a [@NATURAL SEMI JOIN@](https://en.wikipedia.org/wiki/Relational_algebra#Semijoin_%28%E2%8B%89%29%28%E2%8B%8A%29)+-- based on the common key columns.+--+-- The result is a subset of the left tabulation where only entries which have+-- a corresponding entry in the right tabulation are kept.+--+-- Note that you can achieve a similar effect with 'present' and the+-- 'Applicative' instance of 'Tabulation', i.e., this is just+-- @\left right -> left <* present right@. You can also use+-- @do@-notation.+similarity :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k a+similarity a b = a <* present b+++-- | Performs a [@NATURAL ANTI JOIN@](https://en.wikipedia.org/wiki/Relational_algebra#Antijoin_%28%E2%96%B7%29)+-- based on the common key columns.+--+-- The result is a subset of the left tabulation where only entries which do+-- not have a corresponding entry in the right tabulation are kept.+--+-- Note that you can achieve a similar effect with 'absent' and the+-- 'Applicative' instance of 'Tabulation', i.e., this is just+-- @\left right -> left <* absent right@. You can also use+-- @do@-notation.+difference :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k a+difference a b = a <* absent b
src/Rel8/Type/Composite.hs view
@@ -20,6 +20,7 @@ -- base import Data.Functor.Const ( Const( Const ), getConst )+import Data.Functor.Identity ( Identity( Identity ) ) import Data.Kind ( Constraint, Type ) import Prelude @@ -30,19 +31,19 @@ import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye -- rel8-import Rel8.Expr ( Col( E ), Expr )+import Rel8.Expr ( Expr ) import Rel8.Expr.Opaleye ( castExpr, fromPrimExpr, toPrimExpr )-import Rel8.Schema.HTable ( hfield, hspecs, htabulate, htabulateA )-import Rel8.Schema.Name ( Col( N ), Name( Name ) )+import Rel8.Schema.HTable ( HTable, hfield, hspecs, htabulate, htabulateA )+import Rel8.Schema.Name ( Name( Name ) ) import Rel8.Schema.Null ( Nullity( Null, NotNull ) )-import Rel8.Schema.Result ( Col( R ), Result )-import Rel8.Schema.Spec ( SSpec( SSpec, nullity, info ) )-import Rel8.Table ( Table, fromColumns, toColumns )+import Rel8.Schema.Result ( Result )+import Rel8.Schema.Spec ( Spec( Spec, nullity, info ) )+import Rel8.Table ( fromColumns, toColumns, fromResult, toResult ) import Rel8.Table.Eq ( EqTable )-import Rel8.Table.HKD ( HKD, HKDable, fromHKD, toHKD )+import Rel8.Table.HKD ( HKD, HKDable ) import Rel8.Table.Ord ( OrdTable ) import Rel8.Table.Rel8able ()-import Rel8.Table.Serialize ( lit )+import Rel8.Table.Serialize ( litHTable ) import Rel8.Type ( DBType, typeInformation ) import Rel8.Type.Eq ( DBEq ) import Rel8.Type.Information ( TypeInformation(..) )@@ -67,8 +68,8 @@ instance DBComposite a => DBType (Composite a) where typeInformation = TypeInformation- { decode = Hasql.composite (Composite . fromHKD <$> decoder)- , encode = encoder . lit . toColumns . toHKD . unComposite+ { decode = Hasql.composite (Composite . fromResult @_ @(HKD a Expr) <$> decoder)+ , encode = encoder . litHTable . toResult @_ @(HKD a Expr) . unComposite , typeName = compositeTypeName @a } @@ -103,7 +104,7 @@ -- single column expression, by combining them into a PostgreSQL composite -- type. compose :: DBComposite a => HKD a Expr -> Expr a-compose = castExpr . fromPrimExpr . encoder+compose = castExpr . fromPrimExpr . encoder . toColumns -- | Expand a composite type into a 'HKD'.@@ -112,23 +113,23 @@ decompose :: forall a. DBComposite a => Expr a -> HKD a Expr decompose (toPrimExpr -> a) = fromColumns $ htabulate \field -> case hfield names field of- N (Name name) -> case hfield hspecs field of- SSpec {} -> E $ fromPrimExpr $ Opaleye.CompositeExpr a name+ Name name -> case hfield hspecs field of+ Spec {} -> fromPrimExpr $ Opaleye.CompositeExpr a name where names = toColumns (compositeFields @a) -decoder :: Table Result a => Hasql.Composite a-decoder = fmap fromColumns $ unwrapApplicative $ htabulateA \field ->+decoder :: HTable t => Hasql.Composite (t Result)+decoder = unwrapApplicative $ htabulateA \field -> case hfield hspecs field of- SSpec {nullity, info} -> WrapApplicative $ R <$>+ Spec {nullity, info} -> WrapApplicative $ Identity <$> case nullity of Null -> Hasql.field $ Hasql.nullable $ decode info NotNull -> Hasql.field $ Hasql.nonNullable $ decode info -encoder :: Table Expr a => a -> Opaleye.PrimExpr-encoder (toColumns -> a) = Opaleye.FunExpr "ROW" exprs+encoder :: HTable t => t Expr -> Opaleye.PrimExpr+encoder a = Opaleye.FunExpr "ROW" exprs where exprs = getConst $ htabulateA \field -> case hfield a field of- E (toPrimExpr -> expr) -> Const [expr]+ expr -> Const [toPrimExpr expr]
src/Rel8/Type/Tag.hs view
@@ -21,7 +21,7 @@ import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye -- rel8-import {-# SOURCE #-} Rel8.Expr ( Expr )+import Rel8.Expr ( Expr ) import Rel8.Expr.Eq ( (==.) ) import Rel8.Expr.Opaleye ( zipPrimExprsWith ) import Rel8.Expr.Serialize ( litExpr )
tests/Main.hs view
@@ -1,30 +1,25 @@ {-# language BlockArguments #-}-{-# language DataKinds #-} {-# language DeriveAnyClass #-} {-# language DeriveGeneric #-} {-# language DerivingStrategies #-}-{-# language DisambiguateRecordFields #-} {-# language FlexibleContexts #-} {-# language FlexibleInstances #-}-{-# language GADTs #-}-{-# language NamedFieldPuns #-}+{-# language MonoLocalBinds #-} {-# language OverloadedStrings #-} {-# language RecordWildCards #-} {-# language ScopedTypeVariables #-} {-# language StandaloneDeriving #-} {-# language TypeApplications #-} -{-# options_ghc -fno-warn-redundant-constraints #-}- module Main ( main ) where -- base-import Control.Applicative ( liftA2, liftA3 )-import Control.Monad (void)-import Control.Monad.IO.Class ( MonadIO, liftIO )+import Control.Applicative ( empty, liftA2, liftA3 )+import Control.Exception ( bracket, throwIO )+import Control.Monad ( (>=>), void ) import Data.Bifunctor ( bimap ) import Data.Foldable ( for_ ) import Data.Int ( Int32, Int64 )@@ -38,25 +33,26 @@ import qualified Data.ByteString.Lazy -- case-insensitive-import Data.CaseInsensitive (mk)+import Data.CaseInsensitive ( mk ) -- containers import qualified Data.Map.Strict as Map -- hasql-import Hasql.Connection ( Connection, acquire, release )+import Hasql.Connection ( Connection, acquire, release ) import Hasql.Session ( sql, run ) +-- hasql-transaction+import Hasql.Transaction ( Transaction, condemn, statement )+import qualified Hasql.Transaction.Sessions as Hasql+ -- hedgehog import Hedgehog ( property, (===), forAll, cover, diff, evalM, PropertyT, TestT, test, Gen ) import qualified Hedgehog.Gen as Gen import qualified Hedgehog.Range as Range --- lifted-base-import Control.Exception.Lifted ( bracket, throwIO, bracket_ )---- monad-control-import Control.Monad.Trans.Control ( MonadBaseControl )+-- mmorph+import Control.Monad.Morph ( hoist ) -- rel8 import Rel8 ( Result )@@ -72,12 +68,16 @@ import Test.Tasty.Hedgehog ( testProperty ) -- text-import Data.Text (Text, pack)+import Data.Text ( Text, pack, unpack ) import qualified Data.Text.Lazy+import Data.Text.Encoding ( decodeUtf8 ) -- time import Data.Time +-- transformers+import Control.Monad.Trans.Class ( lift )+ -- tmp-postgres import qualified Database.Postgres.Temp as TmpPostgres @@ -121,6 +121,7 @@ , testSelectNestedPairs getTestDatabase , testSelectArray getTestDatabase , testNestedMaybeTable getTestDatabase+ , testEvaluate getTestDatabase ] where@@ -132,25 +133,28 @@ flip run conn do sql "CREATE EXTENSION citext" sql "CREATE TABLE test_table ( column1 text not null, column2 bool not null )"+ sql "CREATE SEQUENCE test_seq" return db stopTestDatabase = TmpPostgres.stop +connect :: TmpPostgres.DB -> IO Connection+connect = acquire . TmpPostgres.toConnectionString >=> either (maybe empty (fail . unpack . decodeUtf8)) pure++ databasePropertyTest :: TestName- -> (((Connection -> TestT IO ()) -> PropertyT IO ()) -> PropertyT IO ())+ -> ((TestT Transaction () -> PropertyT IO ()) -> PropertyT IO ()) -> IO TmpPostgres.DB -> TestTree databasePropertyTest testName f getTestDatabase =- withResource connect release $ \c ->+ withResource (connect =<< getTestDatabase) release $ \c -> testProperty testName $ property do- connection <- liftIO c- f \g -> test $ rollingBack connection $ g connection-- where-- connect = either (error . show) return =<< acquire . TmpPostgres.toConnectionString =<< getTestDatabase+ connection <- lift c+ f $ test . hoist \m -> do+ e <- run (Hasql.transaction Hasql.Serializable Hasql.Write (m <* condemn)) connection+ either throwIO pure e data TestTable f = TestTable@@ -182,18 +186,17 @@ testSelectTestTable = databasePropertyTest "Can SELECT TestTable" \transaction -> do rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable - transaction \connection -> do- void do- liftIO $ Rel8.insert connection- Rel8.Insert- { into = testTableSchema- , rows = map Rel8.lit rows- , onConflict = Rel8.DoNothing- , returning = Rel8.NumberOfRowsAffected- }+ transaction do+ selected <- lift do+ statement () $ Rel8.insert Rel8.Insert+ { into = testTableSchema+ , rows = Rel8.values $ map Rel8.lit rows+ , onConflict = Rel8.DoNothing+ , returning = pure ()+ } - selected <- liftIO $ Rel8.select connection do- Rel8.each testTableSchema+ statement () $ Rel8.select do+ Rel8.each testTableSchema sort selected === sort rows @@ -210,11 +213,12 @@ let expected = filter (\t -> testTableColumn2 t == magicBool) rows - transaction \connection -> do- selected <- liftIO $ Rel8.select connection do- t <- Rel8.values $ Rel8.lit <$> rows- Rel8.where_ $ testTableColumn2 t Rel8.==. Rel8.lit magicBool- return t+ transaction do+ selected <- lift do+ statement () $ Rel8.select do+ t <- Rel8.values $ Rel8.lit <$> rows+ Rel8.where_ $ testTableColumn2 t Rel8.==. Rel8.lit magicBool+ return t sort selected === sort expected @@ -227,11 +231,12 @@ testFilter = databasePropertyTest "filter" \transaction -> do rows <- forAll $ Gen.list (Range.linear 1 10) genTestTable - transaction \connection -> do+ transaction do let expected = filter testTableColumn2 rows - selected <- liftIO $ Rel8.select connection- $ Rel8.filter testTableColumn2 =<< Rel8.values (Rel8.lit <$> rows)+ selected <- lift do+ statement () $ Rel8.select do+ Rel8.filter testTableColumn2 =<< Rel8.values (Rel8.lit <$> rows) sort selected === sort expected @@ -246,9 +251,10 @@ n <- forAll $ Gen.integral (Range.linear 0 10) - transaction \connection -> do- selected <- liftIO $ Rel8.select connection do- Rel8.limit n $ Rel8.values (Rel8.lit <$> rows)+ transaction do+ selected <- lift do+ statement () $ Rel8.select do+ Rel8.limit n $ Rel8.values (Rel8.lit <$> rows) diff (length selected) (<=) (fromIntegral n) @@ -266,9 +272,10 @@ left <- forAll $ Gen.list (Range.linear 0 10) genTestTable right <- forAll $ Gen.list (Range.linear 0 10) genTestTable - transaction \connection -> do- selected <- liftIO $ Rel8.select connection do- Rel8.values (Rel8.lit <$> nub left) `Rel8.union` Rel8.values (Rel8.lit <$> nub right)+ transaction do+ selected <- lift do+ statement () $ Rel8.select do+ Rel8.values (Rel8.lit <$> nub left) `Rel8.union` Rel8.values (Rel8.lit <$> nub right) sort selected === sort (nub (left ++ right)) @@ -277,9 +284,11 @@ testDistinct = databasePropertyTest "DISTINCT (Rel8.distinct)" \transaction -> do rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable - transaction \connection -> do- selected <- liftIO $ Rel8.select connection $ Rel8.distinct do- Rel8.values (Rel8.lit <$> rows)+ transaction do+ selected <- lift do+ statement () $ Rel8.select do+ Rel8.distinct do+ Rel8.values (Rel8.lit <$> rows) sort selected === nub (sort rows) @@ -292,8 +301,10 @@ testExists = databasePropertyTest "EXISTS (Rel8.exists)" \transaction -> do rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable - transaction \connection -> do- exists <- liftIO $ Rel8.select connection $ Rel8.exists $ Rel8.values $ Rel8.lit <$> rows+ transaction do+ exists <- lift do+ statement () $ Rel8.select do+ Rel8.exists $ Rel8.values $ Rel8.lit <$> rows case rows of [] -> exists === [False]@@ -304,9 +315,10 @@ testOptional = databasePropertyTest "Rel8.optional" \transaction -> do rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable - transaction \connection -> do- selected <- liftIO $ Rel8.select connection do- Rel8.optional $ Rel8.values (Rel8.lit <$> rows)+ transaction do+ selected <- lift do+ statement () $ Rel8.select do+ Rel8.optional $ Rel8.values (Rel8.lit <$> rows) case rows of [] -> selected === [Nothing]@@ -317,9 +329,10 @@ testAnd = databasePropertyTest "AND (&&.)" \transaction -> do (x, y) <- forAll $ liftA2 (,) Gen.bool Gen.bool - transaction \connection -> do- [result] <- liftIO $ Rel8.select connection $ pure $- Rel8.lit x Rel8.&&. Rel8.lit y+ transaction do+ [result] <- lift do+ statement () $ Rel8.select do+ pure $ Rel8.lit x Rel8.&&. Rel8.lit y result === (x && y) @@ -328,9 +341,10 @@ testOr = databasePropertyTest "OR (||.)" \transaction -> do (x, y) <- forAll $ liftA2 (,) Gen.bool Gen.bool - transaction \connection -> do- [result] <- liftIO $ Rel8.select connection $ pure $- Rel8.lit x Rel8.||. Rel8.lit y+ transaction do+ [result] <- lift do+ statement () $ Rel8.select $ pure $+ Rel8.lit x Rel8.||. Rel8.lit y result === (x || y) @@ -339,9 +353,10 @@ testLogicalFixities = databasePropertyTest "Logical operator fixities" \transaction -> do (u, v, w, x) <- forAll $ (,,,) <$> Gen.bool <*> Gen.bool <*> Gen.bool <*> Gen.bool - transaction \connection -> do- [result] <- liftIO $ Rel8.select connection $ pure $- Rel8.lit u Rel8.||. Rel8.lit v Rel8.&&. Rel8.lit w Rel8.==. Rel8.lit x+ transaction do+ [result] <- lift do+ statement () $ Rel8.select do+ pure $ Rel8.lit u Rel8.||. Rel8.lit v Rel8.&&. Rel8.lit w Rel8.==. Rel8.lit x result === (u || v && w == x) @@ -350,9 +365,10 @@ testNot = databasePropertyTest "NOT (not_)" \transaction -> do x <- forAll Gen.bool - transaction \connection -> do- [result] <- liftIO $ Rel8.select connection $ pure $- Rel8.not_ $ Rel8.lit x+ transaction do+ [result] <- lift do+ statement () $ Rel8.select do+ pure $ Rel8.not_ $ Rel8.lit x result === not x @@ -361,9 +377,10 @@ testBool = databasePropertyTest "ifThenElse_" \transaction -> do (x, y, z) <- forAll $ liftA3 (,,) Gen.bool Gen.bool Gen.bool - transaction \connection -> do- [result] <- liftIO $ Rel8.select connection $ pure $- Rel8.bool (Rel8.lit z) (Rel8.lit y) (Rel8.lit x)+ transaction do+ [result] <- lift do+ statement () $ Rel8.select do+ pure $ Rel8.bool (Rel8.lit z) (Rel8.lit y) (Rel8.lit x) result === if x then y else z @@ -375,9 +392,10 @@ (Gen.list (Range.linear 1 10) genTestTable) (Gen.list (Range.linear 1 10) genTestTable) - transaction \connection -> do- result <- liftIO $ Rel8.select connection $ do- liftA2 (,) (Rel8.values (Rel8.lit <$> rows1)) (Rel8.values (Rel8.lit <$> rows2))+ transaction do+ result <- lift do+ statement () $ Rel8.select do+ liftA2 (,) (Rel8.values (Rel8.lit <$> rows1)) (Rel8.values (Rel8.lit <$> rows2)) sort result === sort (liftA2 (,) rows1 rows2) @@ -406,21 +424,22 @@ where dbTypeTest :: (Eq a, Show a, Rel8.DBType a) => TestName -> Gen a -> TestTree dbTypeTest name generator = testGroup name- [ databasePropertyTest name (t (==) generator) getTestDatabase- , databasePropertyTest ("Maybe " <> name) (t (==) (Gen.maybe generator)) getTestDatabase+ [ databasePropertyTest name (t generator) getTestDatabase+ , databasePropertyTest ("Maybe " <> name) (t (Gen.maybe generator)) getTestDatabase ] - t :: forall a b. (Show a, Rel8.Sql Rel8.DBType a)- => (a -> a -> Bool)- -> Gen a- -> ((Connection -> TestT IO ()) -> PropertyT IO b)+ t :: forall a b. (Eq a, Show a, Rel8.Sql Rel8.DBType a)+ => Gen a+ -> (TestT Transaction () -> PropertyT IO b) -> PropertyT IO b- t eq generator transaction = do+ t generator transaction = do x <- forAll generator - transaction \connection -> do- [res] <- liftIO $ Rel8.select connection $ pure (Rel8.litExpr x)- diff res eq x+ transaction do+ [res] <- lift do+ statement () $ Rel8.select do+ pure (Rel8.litExpr x)+ diff res (==) x genDay :: Gen Day genDay = do@@ -462,39 +481,38 @@ t :: forall a. (Eq a, Show a, Rel8.Sql Rel8.DBEq a) => Gen a- -> ((Connection -> TestT IO ()) -> PropertyT IO ())+ -> (TestT Transaction () -> PropertyT IO ()) -> PropertyT IO () t generator transaction = do (x, y) <- forAll (liftA2 (,) generator generator) - transaction \connection -> do- [res] <- liftIO $ Rel8.select connection $ pure $ Rel8.litExpr x Rel8.==. Rel8.litExpr y+ transaction do+ [res] <- lift do+ statement () $ Rel8.select do+ pure $ Rel8.litExpr x Rel8.==. Rel8.litExpr y res === (x == y) - cover 1 "Equal" $ x == y- cover 1 "Not Equal" $ x /= y - testTableEquality :: IO TmpPostgres.DB -> TestTree testTableEquality = databasePropertyTest "TestTable equality" \transaction -> do (x, y) <- forAll $ liftA2 (,) genTestTable genTestTable - transaction \connection -> do- [eq] <- liftIO $ Rel8.select connection do- pure $ Rel8.lit x Rel8.==: Rel8.lit y+ transaction do+ [eq] <- lift do+ statement () $ Rel8.select do+ pure $ Rel8.lit x Rel8.==: Rel8.lit y eq === (x == y) - cover 1 "Equal" $ x == y- cover 1 "Not Equal" $ x /= y - testFromString :: IO TmpPostgres.DB -> TestTree testFromString = databasePropertyTest "FromString" \transaction -> do str <- forAll $ Gen.list (Range.linear 0 10) Gen.unicode - transaction \connection -> do- [result] <- liftIO $ Rel8.select connection $ pure $ fromString str+ transaction do+ [result] <- lift do+ statement () $ Rel8.select do+ pure $ fromString str result === pack str @@ -502,10 +520,11 @@ testCatMaybeTable = databasePropertyTest "catMaybeTable" \transaction -> do rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable - transaction \connection -> do- selected <- liftIO $ Rel8.select connection do- testTable <- Rel8.values $ Rel8.lit <$> rows- Rel8.catMaybeTable $ Rel8.bool Rel8.nothingTable (pure testTable) (testTableColumn2 testTable)+ transaction do+ selected <- lift do+ statement () $ Rel8.select do+ testTable <- Rel8.values $ Rel8.lit <$> rows+ Rel8.catMaybeTable $ Rel8.bool Rel8.nothingTable (pure testTable) (testTableColumn2 testTable) sort selected === sort (filter testTableColumn2 rows) @@ -514,9 +533,10 @@ testCatMaybe = databasePropertyTest "catMaybe" \transaction -> evalM do rows <- forAll $ Gen.list (Range.linear 0 10) $ Gen.maybe Gen.bool - transaction \connection -> do- selected <- evalM $ liftIO $ Rel8.select connection do- Rel8.catNull =<< Rel8.values (map Rel8.lit rows)+ transaction do+ selected <- lift do+ statement () $ Rel8.select do+ Rel8.catNull =<< Rel8.values (map Rel8.lit rows) sort selected === sort (catMaybes rows) @@ -525,9 +545,10 @@ testMaybeTable = databasePropertyTest "maybeTable" \transaction -> evalM do (rows, def) <- forAll $ liftA2 (,) (Gen.list (Range.linear 0 10) genTestTable) genTestTable - transaction \connection -> do- selected <- liftIO $ Rel8.select connection $- Rel8.maybeTable (Rel8.lit def) id <$> Rel8.optional (Rel8.values (Rel8.lit <$> rows))+ transaction do+ selected <- lift do+ statement () $ Rel8.select do+ Rel8.maybeTable (Rel8.lit def) id <$> Rel8.optional (Rel8.values (Rel8.lit <$> rows)) case rows of [] -> selected === [def]@@ -554,9 +575,10 @@ Gen.list (Range.linear 0 10) $ liftA2 TwoTestTables genTestTable genTestTable - transaction \connection -> do- selected <- liftIO $ Rel8.select connection do- Rel8.values (Rel8.lit <$> rows)+ transaction do+ selected <- lift do+ statement () $ Rel8.select do+ Rel8.values (Rel8.lit <$> rows) sort selected === sort rows @@ -566,11 +588,12 @@ rows1 <- genRows rows2 <- genRows - transaction \connection -> do- selected <- liftIO $ Rel8.select connection do- as <- Rel8.optional (Rel8.values (Rel8.lit <$> rows1))- bs <- Rel8.optional (Rel8.values (Rel8.lit <$> rows2))- pure $ liftA2 (,) as bs+ transaction do+ selected <- lift do+ statement () $ Rel8.select do+ as <- Rel8.optional (Rel8.values (Rel8.lit <$> rows1))+ bs <- Rel8.optional (Rel8.values (Rel8.lit <$> rows2))+ pure $ liftA2 (,) as bs case (rows1, rows2) of ([], []) -> selected === [Nothing]@@ -582,15 +605,7 @@ genRows = forAll do Gen.list (Range.linear 0 10) $ liftA2 TestTable (Gen.text (Range.linear 0 10) Gen.unicode) (pure True) -rollingBack- :: (MonadBaseControl IO m, MonadIO m)- => Connection -> m a -> m a-rollingBack connection =- bracket_- (liftIO (run (sql "BEGIN") connection))- (liftIO (run (sql "ROLLBACK") connection)) - genTestTable :: Gen (TestTable Result) genTestTable = do testTableColumn1 <- Gen.text (Range.linear 0 5) Gen.alphaNum@@ -602,19 +617,19 @@ testUpdate = databasePropertyTest "Can UPDATE TestTable" \transaction -> do rows <- forAll $ Gen.map (Range.linear 0 5) $ liftA2 (,) genTestTable genTestTable - transaction \connection -> do- void $ liftIO $ Rel8.insert connection- Rel8.Insert+ transaction do+ selected <- lift do+ statement () $ Rel8.insert Rel8.Insert { into = testTableSchema- , rows = map Rel8.lit $ Map.keys rows+ , rows = Rel8.values $ map Rel8.lit $ Map.keys rows , onConflict = Rel8.DoNothing- , returning = Rel8.NumberOfRowsAffected+ , returning = pure () } - void $ liftIO $ Rel8.update connection- Rel8.Update+ statement () $ Rel8.update Rel8.Update { target = testTableSchema- , set = \r ->+ , from = pure ()+ , set = \_ r -> let updates = map (bimap Rel8.lit Rel8.lit) $ Map.toList rows in foldl@@ -628,12 +643,12 @@ ) r updates- , updateWhere = \_ -> Rel8.lit True- , returning = Rel8.NumberOfRowsAffected+ , updateWhere = \_ _ -> Rel8.lit True+ , returning = pure () } - selected <- liftIO $ Rel8.select connection do- Rel8.each testTableSchema+ statement () $ Rel8.select do+ Rel8.each testTableSchema sort selected === sort (Map.elems rows) @@ -646,26 +661,27 @@ testDelete = databasePropertyTest "Can DELETE TestTable" \transaction -> do rows <- forAll $ Gen.list (Range.linear 0 5) genTestTable - transaction \connection -> do- void $ liftIO $ Rel8.insert connection- Rel8.Insert+ transaction do+ (deleted, selected) <- lift do+ statement () $ Rel8.insert Rel8.Insert { into = testTableSchema- , rows = map Rel8.lit rows+ , rows = Rel8.values $ map Rel8.lit rows , onConflict = Rel8.DoNothing- , returning = Rel8.NumberOfRowsAffected+ , returning = pure () } - deleted <-- liftIO $ Rel8.delete connection- Rel8.Delete+ deleted <- statement () $ Rel8.delete Rel8.Delete { from = testTableSchema- , deleteWhere = testTableColumn2+ , using = pure ()+ , deleteWhere = const testTableColumn2 , returning = Rel8.Projection id } - selected <- liftIO $ Rel8.select connection do- Rel8.each testTableSchema+ selected <- statement () $ Rel8.select do+ Rel8.each testTableSchema + pure (deleted, selected)+ sort (deleted <> selected) === sort rows @@ -682,9 +698,10 @@ testSelectNestedPairs = databasePropertyTest "Can SELECT nested pairs" \transaction -> do rows <- forAll $ Gen.list (Range.linear 0 10) $ HKNestedPair <$> liftA2 (,) genTestTable genTestTable - transaction \connection -> do- selected <- liftIO $ Rel8.select connection do- Rel8.values $ map Rel8.lit rows+ transaction do+ selected <- lift do+ statement () $ Rel8.select do+ Rel8.values $ map Rel8.lit rows sort selected === sort rows @@ -693,16 +710,22 @@ testSelectArray = databasePropertyTest "Can SELECT Arrays (with aggregation)" \transaction -> do rows <- forAll $ Gen.list (Range.linear 1 10) Gen.bool - transaction \connection -> do- selected <- liftIO $ Rel8.select connection do- Rel8.many $ Rel8.values (map Rel8.lit rows)+ transaction do+ selected <- lift do+ statement () $ Rel8.select do+ Rel8.many $ Rel8.values (map Rel8.lit rows) selected === [foldMap pure rows] - selected' <- liftIO $ Rel8.select connection $ Rel8.catListTable =<< do- Rel8.many $ Rel8.values (map Rel8.lit rows)+ selected' <- lift do+ statement () $ Rel8.select do+ a <- Rel8.catListTable =<< do+ Rel8.many $ Rel8.values (map Rel8.lit rows)+ b <- Rel8.catListTable =<< do+ Rel8.many $ Rel8.values (map Rel8.lit rows)+ pure (a, b) - selected' === rows+ selected' === liftA2 (,) rows rows data NestedMaybeTable f = NestedMaybeTable@@ -722,9 +745,51 @@ testNestedMaybeTable = databasePropertyTest "Can nest MaybeTable within other tables" \transaction -> do let example = NestedMaybeTable { nmt1 = True, nmt2 = Just (TestTable "Hi" True) } - transaction \connection -> do- selected <- liftIO $ Rel8.select connection do- x <- Rel8.values [Rel8.lit example]- pure $ Rel8.maybeTable (Rel8.lit False) (\_ -> Rel8.lit True) (nmt2 x)+ transaction do+ selected <- lift do+ statement () $ Rel8.select do+ x <- Rel8.values [Rel8.lit example]+ pure $ Rel8.maybeTable (Rel8.lit False) (\_ -> Rel8.lit True) (nmt2 x) selected === [True]+++testEvaluate :: IO TmpPostgres.DB -> TestTree+testEvaluate = databasePropertyTest "evaluate has the evaluation order we expect" \transaction -> do++ transaction do+ selected <- lift do+ statement () $ Rel8.select do+ x <- Rel8.values (Rel8.lit <$> ['a', 'b', 'c'])+ y <- Rel8.evaluate (Rel8.nextval "test_seq")+ pure (x, (y, y))++ normalize selected ===+ [ ('a', (0, 0))+ , ('b', (1, 1))+ , ('c', (2, 2))+ ]++ selected' <- lift do+ statement () $ Rel8.select do+ x <- Rel8.values (Rel8.lit <$> ['a', 'b', 'c'])+ y <- Rel8.values (Rel8.lit <$> ['d', 'e', 'f'])+ z <- Rel8.evaluate (Rel8.nextval "test_seq")+ pure ((x, y), (z, z))++ normalize selected' ===+ [ (('a', 'd'), (0, 0))+ , (('a', 'e'), (1, 1))+ , (('a', 'f'), (2, 2))+ , (('b', 'd'), (3, 3))+ , (('b', 'e'), (4, 4))+ , (('b', 'f'), (5, 5))+ , (('c', 'd'), (6, 6))+ , (('c', 'e'), (7, 7))+ , (('c', 'f'), (8, 8))+ ]++ where+ normalize :: [(x, (Int64, Int64))] -> [(x, (Int64, Int64))]+ normalize [] = []+ normalize xs@((_, (i, _)) : _) = map (fmap (\(a, b) -> (a - i, b - i))) xs
+ tests/Rel8/Generic/Rel8able/Test.hs view
@@ -0,0 +1,179 @@+{-# language DataKinds #-}+{-# language DeriveAnyClass #-}+{-# language DeriveGeneric #-}+{-# language DerivingStrategies #-}+{-# language DuplicateRecordFields #-}+{-# language FlexibleInstances #-}+{-# language MultiParamTypeClasses #-}+{-# language TypeFamilies #-}+{-# language UndecidableInstances #-}++{-# options_ghc -O0 #-}++module Rel8.Generic.Rel8able.Test+ ( module Rel8.Generic.Rel8able.Test+ )+where++-- base+import GHC.Generics ( Generic )+import Prelude++-- rel8+import Rel8++-- text+import Data.Text ( Text )+++data TableTest f = TableTest+ { foo :: Column f Bool+ , bar :: Column f (Maybe Bool)+ }+ deriving stock Generic+ deriving anyclass Rel8able+++data TablePair f = TablePair+ { foo :: Column f Bool+ , bars :: (Column f Text, Column f Text)+ }+ deriving stock Generic+ deriving anyclass Rel8able+++data TableMaybe f = TableMaybe+ { foo :: Column f [Maybe Bool]+ , bars :: HMaybe f (TablePair f, TablePair f)+ }+ deriving stock Generic+ deriving anyclass Rel8able+++data TableEither f = TableEither+ { foo :: Column f Bool+ , bars :: HEither f (HMaybe f (TablePair f, TablePair f)) (Column f Char)+ }+ deriving stock Generic+ deriving anyclass Rel8able+++data TableThese f = TableThese+ { foo :: Column f Bool+ , bars :: HThese f (TableMaybe f) (TableEither f)+ }+ deriving stock Generic+ deriving anyclass Rel8able+++data TableList f = TableList+ { foo :: Column f Bool+ , bars :: HList f (TableThese f)+ }+ deriving stock Generic+ deriving anyclass Rel8able+++data TableNonEmpty f = TableNonEmpty+ { foo :: Column f Bool+ , bars :: HNonEmpty f (TableList f, TableMaybe f)+ }+ deriving stock Generic+ deriving anyclass Rel8able+++data TableNest f = TableNest+ { foo :: Column f Bool+ , bars :: HList f (HMaybe f (TablePair f))+ }+ deriving stock Generic+ deriving anyclass Rel8able+++data S3Object = S3Object+ { bucketName :: Text+ , objectKey :: Text+ }+ deriving stock Generic+++instance x ~ HKD S3Object Expr => ToExprs x S3Object+++data HKDSum = HKDSumA Text | HKDSumB Bool Char | HKDSumC+ deriving stock Generic+++instance x ~ HKD HKDSum Expr => ToExprs x HKDSum+++data HKDTest f = HKDTest+ { s3Object :: Lift f S3Object+ , hkdSum :: Lift f HKDSum+ }+ deriving stock Generic+ deriving anyclass Rel8able+++data NonRecord f = NonRecord+ (Column f Bool)+ (Column f Char)+ (Column f Char)+ (Column f Char)+ (Column f Char)+ (Column f Char)+ (Column f Char)+ (Column f Char)+ (Column f Char)+ (Column f Char)+ deriving stock Generic+ deriving anyclass Rel8able+++data TableSum f+ = TableSumA (Column f Bool) (Column f Text)+ | TableSumB+ | TableSumC (Column f Text)+ deriving stock Generic+++data BarbieSum f+ = BarbieSumA (f Bool) (f Text)+ | BarbieSumB+ | BarbieSumC (f Text)+ deriving stock Generic+++data TableProduct f = TableProduct+ { sum :: HADT f BarbieSum+ , list :: TableList f+ , foos :: HList f (HADT f TableSum, Lift f HKDSum, HKDTest f)+ }+ deriving stock Generic+ deriving anyclass Rel8able+++data TableTestB f = TableTestB+ { foo :: f Bool+ , bar :: f (Maybe Bool)+ }+ deriving stock Generic+ deriving anyclass Rel8able+++data NestedTableTestB f = NestedTableTestB+ { foo :: f Bool+ , bar :: f (Maybe Bool)+ , baz :: Column f Char+ , nest :: TableTestB f+ }+ deriving stock Generic+ deriving anyclass Rel8able++++newtype IdRecord a f = IdRecord { recordId :: Column f a }+ deriving stock Generic+++instance DBType a => Rel8able (IdRecord a)